diff --git a/README.md b/README.md index 272cc5f..1e08a45 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ int main(int argc, const char* argv[]) tcs_lib_init(); TcsSocket client_socket = TCS_SOCKET_INVALID; - tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4); + tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); tcs_connect_str(client_socket, "example.com", 80); uint8_t send_buffer[] = "GET / HTTP/1.1\nHost: example.com\n\n"; diff --git a/examples/tcp_client.c b/examples/tcp_client.c index b5d2d31..a241610 100644 --- a/examples/tcp_client.c +++ b/examples/tcp_client.c @@ -41,7 +41,7 @@ int main(void) TcsSocket client_socket = TCS_SOCKET_INVALID; - if (tcs_tcp_client_str(&client_socket, "localhost", 1212, 1000) != TCS_SUCCESS) + if (tcs_socket_tcp_str(&client_socket, NULL, "localhost:1212", 1000) != TCS_SUCCESS) return show_error("Could not create a socket"); char msg[] = "hello world\n"; diff --git a/examples/tcp_server.c b/examples/tcp_server.c index 3b39cba..c1dbcec 100644 --- a/examples/tcp_server.c +++ b/examples/tcp_server.c @@ -40,9 +40,12 @@ int main(void) TcsSocket listen_socket = TCS_SOCKET_INVALID; TcsSocket child_socket = TCS_SOCKET_INVALID; - if (tcs_tcp_server_str(&listen_socket, "localhost", 1212) != TCS_SUCCESS) + if (tcs_socket_tcp_str(&listen_socket, "localhost:1212", NULL, 0) != TCS_SUCCESS) return show_error("Could not create server socket"); + if (tcs_listen(listen_socket, TCS_BACKLOG_MAX) != TCS_SUCCESS) + return show_error("Could not listen on socket"); + if (tcs_accept(listen_socket, &child_socket, NULL) != TCS_SUCCESS) return show_error("Could not accept socket"); diff --git a/examples/udp_client.c b/examples/udp_client.c index f90eb88..aa1d3a5 100644 --- a/examples/udp_client.c +++ b/examples/udp_client.c @@ -38,21 +38,17 @@ int main(void) return show_error("Could not init tinycsocket"); TcsSocket socket = TCS_SOCKET_INVALID; - if (tcs_socket_preset(&socket, TCS_PRESET_UDP_IP4) != TCS_SUCCESS) + if (tcs_socket_udp_str(&socket, NULL, "localhost:1212") != TCS_SUCCESS) return show_error("Could not create socket"); - struct TcsAddress remote_info; - if (tcs_address_resolve("localhost", TCS_AF_IP4, &remote_info, 1, NULL) != TCS_SUCCESS) - return show_error("Could not resolve localhost"); - char msg[] = "hello world\n"; - if (tcs_send_to(socket, (const uint8_t*)msg, sizeof(msg), TCS_FLAG_NONE, &remote_info, NULL) != TCS_SUCCESS) + if (tcs_send(socket, (const uint8_t*)msg, sizeof(msg), TCS_FLAG_NONE, NULL) != TCS_SUCCESS) return show_error("Could not send message"); uint8_t recv_buffer[1024]; size_t recv_size = sizeof(recv_buffer) - 1; size_t bytes_received = 0; - if (tcs_receive_from(socket, recv_buffer, recv_size, TCS_FLAG_NONE, NULL, &bytes_received) != TCS_SUCCESS) + if (tcs_receive(socket, recv_buffer, recv_size, TCS_FLAG_NONE, &bytes_received) != TCS_SUCCESS) return show_error("Could not receive data"); // Makes sure it is a NULL terminated string, this is why we only accept 1023 bytes in receive diff --git a/examples/udp_server.c b/examples/udp_server.c index 10e54eb..f48dc47 100644 --- a/examples/udp_server.c +++ b/examples/udp_server.c @@ -38,7 +38,7 @@ int main(void) return show_error("Could not init tinycsocket"); TcsSocket socket = TCS_SOCKET_INVALID; - if (tcs_udp_receiver_str(&socket, "localhost", 1212) != TCS_SUCCESS) + if (tcs_socket_udp_str(&socket, "localhost:1212", NULL) != TCS_SUCCESS) return show_error("Could not create socket"); struct TcsAddress remote_address = {0}; diff --git a/include/tinycsocket.h b/include/tinycsocket.h index fb3d844..7700ead 100644 --- a/include/tinycsocket.h +++ b/include/tinycsocket.h @@ -29,7 +29,7 @@ #ifndef TINYCSOCKET_INTERNAL_H_ #define TINYCSOCKET_INTERNAL_H_ -static const char* const TCS_VERSION_TXT = "v0.3.69"; +static const char* const TCS_VERSION_TXT = "v0.3.70"; static const char* const TCS_LICENSE_TXT = "Copyright 2018 Markus Lindelöw\n" "\n" @@ -63,27 +63,14 @@ static const char* const TCS_LICENSE_TXT = * * Socket Creation: * - TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, int protocol); -* - TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); +* - TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address, int timeout_ms); +* - TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address, int timeout_ms); +* - TcsResult tcs_socket_udp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address); +* - TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); +* - TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type); +* - TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type); * - TcsResult tcs_close(TcsSocket* socket_ctx); * -* High-level Socket Creation: -* - TcsResult tcs_tcp_server(TcsSocket* socket_ctx, const struct TcsAddress* local_address); -* - TcsResult tcs_tcp_server_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); -* - TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms); -* - TcsResult tcs_tcp_client_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port, int timeout_ms); -* - TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address); -* - TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); -* - TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address); -* - TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port); -* - TcsResult tcs_udp_peer(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address); -* - TcsResult tcs_udp_peer_str(TcsSocket* socket_ctx, const char* local_address, uint16_t local_port, const char* remote_address, uint16_t remote_port); -* -* High-level Raw L2-Packet Sockets (Experimental): -* - TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); -* - TcsResult tcs_raw_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); -* - TcsResult tcs_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); -* - TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); -* * Socket Operations: * - TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address); * - TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); @@ -101,7 +88,6 @@ static const char* const TCS_LICENSE_TXT = * - TcsResult tcs_receive_from(TcsSocket socket_ctx, uint8_t* buffer, size_t buffer_size, uint32_t flags, struct TcsAddress* source_address, size_t* bytes_received); * - TcsResult tcs_receive_line(TcsSocket socket_ctx, uint8_t* buffer, size_t buffer_size, size_t* bytes_received, uint8_t delimiter); * - TcsResult tcs_receive_netstring(TcsSocket socket_ctx, uint8_t* buffer, size_t buffer_size, size_t* bytes_received); - * * Socket Pooling: * - TcsResult tcs_pool_create(struct TcsPool** pool); @@ -145,7 +131,6 @@ static const char* const TCS_LICENSE_TXT = * - TcsResult tcs_opt_multicast_interface_set(TcsSocket socket_ctx, const struct TcsAddress* local_address); * - TcsResult tcs_opt_multicast_loop_set(TcsSocket socket_ctx, bool do_loopback); * - TcsResult tcs_opt_multicast_loop_get(TcsSocket socket_ctx, bool* is_loopback); - * * Address and Interface Utilities: * - TcsResult tcs_interface_list(struct TcsInterface interfaces[], size_t capacity, size_t* out_count); @@ -344,17 +329,6 @@ extern const uint16_t TCS_PROTOCOL_IP_UDP; /**< Use UDP protocol (use with TCS_S // Ethernet protocols (host byte order) static const uint16_t TCS_ETH_P_ALL = 0x0003; /**< Receive all protocols. Use with TCS_AF_PACKET for capture. */ -// Simple socket creation -typedef enum -{ - TCS_PRESET_TCP_IP4, - TCS_PRESET_UDP_IP4, - TCS_PRESET_TCP_IP6, - TCS_PRESET_UDP_IP6, - TCS_PRESET_RAW, // Layer 2 raw, CAP_NET_RAW permission may be needed - TCS_PRESET_PACKET, // Layer 2 dgram, CAP_NET_RAW permission may be needed -} TcsPreset; - // Flags extern const uint32_t TCS_AI_PASSIVE; /**< Use this flag for pure listening sockets */ @@ -507,11 +481,10 @@ TcsResult tcs_lib_free(void); * @brief Create a new socket (BSD-style) * * This is a thin wrapper around the native socket function. - * You may want to use ::tcs_socket_preset() instead, or one of the helper functions to create and setup a socket directly: - * - ::tcs_tcp_server_str() - * - ::tcs_tcp_client_str() - * - ::tcs_udp_receiver_str() - * - ::tcs_udp_sender_str() + * You may want to use one of the helper functions to create and setup a socket directly: + * - ::tcs_socket_tcp_str() + * - ::tcs_socket_udp_str() + * - ::tcs_socket_packet_str() * * Call ::tcs_close() to stop communication and free all resources for the socket. * @@ -551,11 +524,9 @@ TcsResult tcs_lib_free(void); * @retval #TCS_ERROR_NOT_IMPLEMENTED if you have provided an address family that is not supported on this platform. * @retval #TCS_ERROR_PERMISSION_DENIED if you do not have permission to create the socket. E.g. raw sockets often require elevated permissions. * - * @see tcs_socket_preset() - * @see tcs_tcp_server_str() - * @see tcs_tcp_client_str() - * @see tcs_udp_receiver_str() - * @see tcs_udp_sender_str() + * @see tcs_socket_tcp_str() + * @see tcs_socket_udp_str() + * @see tcs_socket_packet_str() * @see tcs_close() * @see tcs_lib_init() * @see tcs_lib_free() @@ -563,745 +534,307 @@ TcsResult tcs_lib_free(void); TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, int protocol); /** - * @brief Creates a new socket with simplified options. - * - * This is a simple wrapper around tcs_socket() to make it easier to create common socket types. - * Consider using one of the helper functions instead to create and setup a socket directly: - * - ::tcs_tcp_server_str() - * - ::tcs_tcp_client_str() - * - ::tcs_udp_receiver_str() - * - ::tcs_udp_sender_str() - * - * @code - * #include "tinycsocket.h" - * - * int main() - * { - * TcsResult tcs_init_res = tcs_lib_init(); - * if (tcs_init_res != TCS_SUCCESS) - * return -1; // Failed to initialize tinycsocket - * - * TcsSocket my_socket = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID. - * TcsResult tcs_socket_res = tcs_socket_preset(&my_socket, TCS_PRESET_TCP_IP4); - * if (tcs_socket_res != TCS_SUCCESS) - * { - * tcs_lib_free(); - * return -2; // Failed to create socket - * } - * - * // Do stuff with my_socket here. See examples in the documentation. - * - * tcs_close(&my_socket); // Safe to call even if my_socket is TCS_SOCKET_INVALID - * tcs_lib_free(); - * } - * @endcode - * - * @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. - * @param[in] socket_type Socket family / transport combination. See ::TcsPreset for supported values. - * - * @return #TCS_SUCCESS if successful, otherwise the error code. - * - * @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a socket value that is not #TCS_SOCKET_INVALID. - * - * @see tcs_socket() - * @see tcs_tcp_server_str() - * @see tcs_tcp_client_str() - * @see tcs_udp_receiver_str() - * @see tcs_udp_sender_str() - * @see tcs_close() - * @see tcs_lib_init() - * @see tcs_lib_free() - */ -TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); - -/** -* @brief Closes the socket, stop communication and free all resources for the socket. -* -* This will free all resources associated with the socket and set the socket value to #TCS_SOCKET_INVALID. -* -* @param[in,out] socket_ctx is a pointer to your socket context you have previously created with ::tcs_socket() or one of the helper functions. Will be set to #TCS_SOCKET_INVALID on success. -* -* @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a NULL pointer or a socket that is already #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_SOCKET_CLOSED if the socket is already closed. -* -* @see tcs_socket() -* @see tcs_socket_preset() -* @see tcs_tcp_server_str() -* @see tcs_tcp_client_str() -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender_str() -* @see tcs_lib_init() -* @see tcs_lib_free() -*/ -TcsResult tcs_close(TcsSocket* socket_ctx); - -// ######## High-level Socket Creation ######## - -/** -* @brief Setup a socket to listen for incoming tcp connections given an address struct. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress local_address = TCS_ADDRESS_NONE; -* local_address.family = TCS_AF_IP4; -* local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; // Bind to all IPv4 interfaces -* local_address.data.ip4.port = 1212; -* -* TcsSocket server_socket = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID. -* TcsResult tcs_server_res = tcs_tcp_server(&server_socket, &local_address); -* if (tcs_server_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create server socket -* } -* -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult accept_result = tcs_accept(&server_socket, &client_socket, NULL); // Accept incoming connections -* if (accept_result != TCS_SUCCESS) -* { -* tcs_close(&server_socket); -* tcs_lib_free(); -* return -3; // Failed to accept incoming connection -* } +* @brief Create a TCP socket, optionally bind to a local address and/or connect to a remote address. * -* // Do stuff with client_socket here. See examples in the examples folder. -* -* tcs_shutdown(&client_socket, TCS_SD_BOTH); // Shutdown the accepted socket -* tcs_close(&client_socket); // Close the accepted socket -* tcs_close(&server_socket); // Safe to call even if server_socket is TCS_SOCKET_INVALID -* tcs_lib_free(); -* } -* @endcode -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address -* -* @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a socket value that is not #TCS_SOCKET_INVALID. -* -* @see tcs_socket_preset() - */ -TcsResult tcs_tcp_server(TcsSocket* socket_ctx, const struct TcsAddress* local_address); - -/** -* @brief Setup a socket to listen for incoming tcp connections. +* Creates an IPv4 or IPv6 TCP socket based on the address family of the provided address(es). +* If @p local_address is not NULL, SO_REUSEADDR is set and the socket is bound to it. +* If @p remote_address is not NULL, the socket connects to it. +* At least one of @p local_address or @p remote_address must be non-NULL. +* If both are provided, they must have the same address family. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket +* tcs_lib_init(); * -* TcsSocket server_socket = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID. -* TcsResult tcs_server_res = tcs_tcp_server_str(&server_socket, "0.0.0.0", 1212); -* if (tcs_server_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create server socket -* } +* struct TcsAddress local = TCS_ADDRESS_NONE; +* local.family = TCS_AF_IP4; +* local.data.ip4.address = TCS_ADDRESS_ANY_IP4; +* local.data.ip4.port = 8080; * -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult accept_result = tcs_accept(&server_socket, &client_socket, NULL); // Accept incoming connections -* if (accept_result != TCS_SUCCESS) +* TcsSocket server = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_tcp(&server, &local, NULL, 0); +* if (res != TCS_SUCCESS) * { -* tcs_close(&server_socket); * tcs_lib_free(); -* return -3; // Failed to accept incoming connection +* return -1; * } * -* // Do stuff with server_socket here. See examples in the documentation. +* // Socket is now bound, call tcs_listen() and tcs_accept() to accept connections * -* tcs_shutdown(&client_socket, TCS_SD_BOTH); // Shutdown the accepted socket -* tcs_close(&client_socket); // Close the accepted socket -* tcs_close(&server_socket); // Safe to call even if server_socket is TCS_SOCKET_INVALID +* tcs_close(&server); * tcs_lib_free(); * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address to bind to. Use NULL or "0.0.0.0" to bind to all interfaces. Port number in string will result in invalid argument error. -* @param[in] port is the local port to bind to. 0 will result in a random free port being assigned. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address to bind to, or NULL to skip binding. +* @param[in] remote_address address to connect to, or NULL to skip connecting. +* @param[in] timeout_ms maximum time in milliseconds to wait for connection, or #TCS_WAIT_INF for OS default timeout. Ignored if @p remote_address is NULL. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a socket value that is not #TCS_SOCKET_INVALID. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, if both addresses are NULL, or if both are provided with different address families. +* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. +* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out. * -* @see tcs_socket() -* @see tcs_socket_preset() -* @see tcs_tcp_client_str() +* @see tcs_connect() +* @see tcs_listen() * @see tcs_close() -* @see tcs_lib_init() -* @see tcs_lib_free() */ -TcsResult tcs_tcp_server_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address, + int timeout_ms); /** -* @brief Setup a socket to connect to a remote TCP server using a specific address structure. +* @brief Create a TCP socket from string addresses, optionally bind and/or connect. * -* This will create a TCP client socket and attempt to connect to the specified remote address. -* The function blocks until connected or until the specified timeout elapses. -* Use tcs_tcp_client_str() if you want to specify the address as a hostname and port instead. +* Parses the address strings with ::tcs_address_parse() and delegates to ::tcs_socket_tcp(). +* Addresses must include a port, e.g. "127.0.0.1:8080" or "[::1]:8080". +* At least one of @p local_address or @p remote_address must be non-NULL. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; +* tcs_lib_init(); * -* struct TcsAddress remote_addr = {0}; -* remote_addr.family = TCS_AF_IP4; -* remote_addr.data.ip4.address = 0x7F000001; // 127.0.0.1 loopback -* remote_addr.data.ip4.port = 8080; -* -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult connect_res = tcs_tcp_client(&client_socket, &remote_addr, 1000); // 1000 milliseconds timeout -* if (connect_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_tcp_str(&socket, NULL, "127.0.0.1:8080", 5000); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; +* return -1; * } * -* // Use the connected socket -* uint8_t buffer[] = "Hello, server!"; -* size_t bytes_sent = 0; -* tcs_send(client_socket, buffer, sizeof(buffer)-1, TCS_MSG_SENDALL, &bytes_sent); +* // Socket is now connected and ready for communication * -* tcs_close(&client_socket); +* tcs_close(&socket); * tcs_lib_free(); * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use -* @param[in] remote_address is the remote address structure to connect to -* @param[in] timeout_ms is the maximum time in milliseconds to wait until connected, use #TCS_WAIT_INF to wait indefinitely +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address string to bind to, or NULL to skip binding. +* @param[in] remote_address address string to connect to, or NULL to skip connecting. +* @param[in] timeout_ms maximum time in milliseconds to wait for connection, or #TCS_WAIT_INF for OS default timeout. Ignored if @p remote_address is NULL. * -* @return #TCS_SUCCESS if successful, otherwise the error code -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID -* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection -* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the address could not be resolved +* @return #TCS_SUCCESS if successful, otherwise the error code. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if both addresses are NULL. +* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. +* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out. * -* @see tcs_tcp_client_str() +* @see tcs_socket_tcp() * @see tcs_close() */ -TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms); +TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, + const char* local_address, + const char* remote_address, + int timeout_ms); /** -* @brief Setup a socket and connect to a remote TCP server. -* -* This will create a TCP client socket and attempt to connect to the specified remote address. -* The function blocks until connected or until the specified timeout elapses. -* Use tcs_tcp_client() if you want to specify the address as a struct TcsAddress instead. +* @brief Create a UDP socket, optionally bind to a local address and/or connect to a remote address. +* +* Creates an IPv4 or IPv6 UDP socket based on the address family of the provided address(es). +* If @p local_address is not NULL, SO_REUSEADDR is set and the socket is bound to it. +* If @p remote_address is not NULL and is a unicast address, the socket is connected to it, +* allowing use of ::tcs_send() instead of ::tcs_send_to(). +* If @p remote_address is a multicast address, the socket joins the multicast group. +* When only @p remote_address is given (no @p local_address), the socket is also connected, +* allowing use of ::tcs_send(). When both are given, the socket is not connected to avoid +* filtering out multicast traffic — use ::tcs_send_to() instead. +* At least one of @p local_address or @p remote_address must be non-NULL. +* If both are provided, they must have the same address family. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; -* -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult connect_res = tcs_tcp_client_str(&client_socket, "127.0.0.1", 8080, 1000); // 1000 milliseconds timeout -* if (connect_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; -* } -* -* // Use the connected socket -* uint8_t buffer[] = "Hello, server!"; -* size_t bytes_sent = 0; -* tcs_send(client_socket, buffer, sizeof(buffer)-1, TCS_MSG_SENDALL, &bytes_sent); -* -* tcs_close(&client_socket); -* tcs_lib_free(); -* return 0; -* } -* @endcode +* tcs_lib_init(); * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use -* @param[in] remote_address is the remote address to connect to. Hostname or IP address string. -* @param[in] port is the remote port to connect to -* @param[in] timeout_ms is the maximum time in milliseconds to wait until connected, use #TCS_WAIT_INF to wait indefinitely +* struct TcsAddress local = TCS_ADDRESS_NONE; +* local.family = TCS_AF_IP4; +* local.data.ip4.address = TCS_ADDRESS_ANY_IP4; +* local.data.ip4.port = 8080; * -* @return #TCS_SUCCESS if successful, otherwise the error code -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID -* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection -* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the address could not be resolved -* -* @see tcs_tcp_client() -* @see tcs_close() -*/ -TcsResult tcs_tcp_client_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port, int timeout_ms); - -/** -* @brief Creates a UDP socket bound to a local address structure for receiving datagrams. -* -* This function creates a UDP socket and binds it to the specified local address structure, -* allowing it to receive incoming UDP datagrams sent to that address/port combination. -* Use tcs_udp_receiver_str() if you want to specify the address as a hostname and port instead. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress local_address = TCS_ADDRESS_NONE; -* local_address.family = TCS_AF_IP4; -* local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; // Bind to all interfaces -* local_address.data.ip4.port = 8888; -* -* TcsSocket receiver = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_receiver(&receiver, &local_address); -* if (udp_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_udp(&socket, &local, NULL); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP receiver +* return -1; * } * -* // Receive data -* uint8_t buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress sender_address; -* -* TcsResult recv_res = tcs_receive_from(receiver, buffer, sizeof(buffer), -* TCS_FLAG_NONE, &sender_address, &bytes_received); -* if (recv_res == TCS_SUCCESS && bytes_received > 0) -* { -* // Process received data... -* } +* // Socket is now bound and ready to receive with tcs_receive_from() * -* tcs_close(&receiver); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address structure to bind to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address to bind to, or NULL to skip binding. +* @param[in] remote_address address to connect to, or NULL to skip connecting. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, if both addresses are NULL, or if both are provided with different address families. * -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender() -* @see tcs_udp_peer() -* @see tcs_receive_from() +* @see tcs_socket_udp_str() * @see tcs_close() */ -TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address); +TcsResult tcs_socket_udp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address); /** -* @brief Creates a UDP socket bound to a local address for receiving datagrams. +* @brief Create a UDP socket from string addresses, optionally bind and/or connect. * -* This function creates a UDP socket and binds it to the specified local address and port, -* allowing it to receive incoming UDP datagrams sent to that address/port combination. -* Use tcs_udp_receiver() if you want to specify the address as a TcsAddress structure. +* Parses the address strings with ::tcs_address_resolve() and delegates to ::tcs_socket_udp(). +* Addresses must include a port, e.g. "127.0.0.1:8080" or "[::1]:8080". +* At least one of @p local_address or @p remote_address must be non-NULL. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* TcsSocket receiver = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_receiver_str(&receiver, "0.0.0.0", 8888); -* if (udp_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP receiver -* } -* -* // Receive data -* uint8_t buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress remote_address; -* -* TcsResult recv_res = tcs_receive_from(receiver, buffer, sizeof(buffer), TCS_FLAG_NONE, &remote_address, &bytes_received); -* if (recv_res == TCS_SUCCESS) -* { -* // Process received data... -* } -* -* tcs_close(&receiver); -* tcs_lib_free(); -* return 0; -* } -* @endcode -* -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address to bind to. Use NULL or "0.0.0.0" to bind to all interfaces. -* @param[in] port is the local port to bind to. 0 will result in a random free port being assigned. -* -* @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the local address could not be resolved. -* -* @see tcs_udp_receiver() -* @see tcs_udp_sender_str() -* @see tcs_udp_peer_str() -* @see tcs_receive_from() -* @see tcs_close() -*/ -TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); - -/** -* @brief Creates a UDP socket configured to send datagrams to a specific remote address structure. +* tcs_lib_init(); * -* This function creates a UDP socket that's pre-configured to send datagrams to the specified -* remote address structure. The socket is not bound to a specific local address, so the OS -* will automatically assign a local address and port when sending data. -* Use tcs_udp_sender_str() if you want to specify the address as a hostname and port instead. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress remote_address = TCS_ADDRESS_NONE; -* remote_address.family = TCS_AF_IP4; -* remote_address.data.ip4.address = 0x7F000001; // 127.0.0.1 loopback -* remote_address.data.ip4.port = 8888; -* -* TcsSocket sender = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_sender(&sender, &remote_address); -* if (udp_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_udp_str(&socket, "0.0.0.0:8080", NULL); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP sender +* return -1; * } * -* // Send data -* uint8_t buffer[] = "Hello, UDP receiver!"; -* size_t bytes_sent = 0; -* -* TcsResult send_res = tcs_send(sender, buffer, sizeof(buffer)-1, TCS_FLAG_NONE, &bytes_sent); -* if (send_res == TCS_SUCCESS) -* { -* // Data sent successfully -* } +* // Socket is now bound and ready to receive with tcs_receive_from() * -* tcs_close(&sender); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] remote_address is the remote address structure to send to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address string to bind to, or NULL to skip binding. +* @param[in] remote_address address string to connect to, or NULL to skip connecting. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if both addresses are NULL. * -* @see tcs_udp_sender_str() -* @see tcs_udp_receiver() -* @see tcs_udp_peer() -* @see tcs_send() +* @see tcs_socket_udp() * @see tcs_close() */ -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address); +TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); /** -* @brief Creates a UDP socket configured to send datagrams to a specific remote address. +* @brief Create a packet socket bound to a network interface. * -* This function creates a UDP socket that's pre-configured to send datagrams to the specified -* remote address and port. The socket is not bound to a specific local address, so the OS -* will automatically assign a local address and port when sending data. -* Use tcs_udp_sender() if you want to specify the address as a TcsAddress structure. +* Creates an AF_PACKET socket for sending and receiving raw L2 frames. +* The socket is bound to the interface and protocol specified in @p bind_address. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket +* tcs_lib_init(); +* +* struct TcsAddress bind = TCS_ADDRESS_NONE; +* bind.family = TCS_AF_PACKET; +* bind.data.packet.interface_id = 1; // e.g. lo +* bind.data.packet.protocol = 0x22F0; // e.g. AVTP * -* TcsSocket sender = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_sender_str(&sender, "127.0.0.1", 8888); -* if (udp_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_packet(&socket, &bind, TCS_SOCK_DGRAM); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP sender +* return -1; * } * -* // Send data -* uint8_t buffer[] = "Hello, UDP receiver!"; -* size_t bytes_sent = 0; -* -* TcsResult send_res = tcs_send(sender, buffer, sizeof(buffer)-1, TCS_FLAG_NONE, &bytes_sent); -* if (send_res == TCS_SUCCESS) -* { -* // Data sent successfully -* } +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() * -* tcs_close(&sender); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] remote_address is the remote address to send to. Hostname or IP address string. -* @param[in] port is the remote port to send to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] bind_address address with family TCS_AF_PACKET specifying interface_id and protocol. +* @param[in] type socket type, either #TCS_SOCK_RAW for full L2 frames or #TCS_SOCK_DGRAM for frames without the L2 header. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the remote address could not be resolved. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if @p bind_address is NULL or not TCS_AF_PACKET. * -* @see tcs_udp_sender() -* @see tcs_udp_receiver_str() -* @see tcs_udp_peer_str() -* @see tcs_send() +* @see tcs_socket_packet_str() * @see tcs_close() */ -TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port); +TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type); /** -* @brief Creates a UDP socket bound to a local address structure that can send to a specific remote address structure. +* @brief Create a packet socket bound to a named network interface. * -* This function creates a UDP socket that's both bound to a local address structure for receiving -* datagrams and pre-configured to send datagrams to a specified remote address structure. This -* creates a complete bidirectional UDP communication channel in a single call. -* Use tcs_udp_peer_str() if you want to specify the addresses as hostname and port pairs instead. +* Looks up the interface by name using ::tcs_interface_list() and delegates to ::tcs_socket_packet(). +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress local_address = TCS_ADDRESS_NONE; -* local_address.family = TCS_AF_IP4; -* local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; // Bind to all interfaces -* local_address.data.ip4.port = 8888; -* -* struct TcsAddress remote_address = TCS_ADDRESS_NONE; -* remote_address.family = TCS_AF_IP4; -* remote_address.data.ip4.address = 0x7F000001; // 127.0.0.1 loopback -* remote_address.data.ip4.port = 9999; -* -* TcsSocket peer = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_peer(&peer, &local_address, &remote_address); -* if (udp_res != TCS_SUCCESS) +* tcs_lib_init(); +* +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_packet_str(&socket, "eth0", 0x22F0, TCS_SOCK_DGRAM); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP peer +* return -1; * } * -* // Send data - goes to the pre-configured remote address -* uint8_t send_buffer[] = "Hello, remote peer!"; -* size_t bytes_sent = 0; -* tcs_send(peer, send_buffer, sizeof(send_buffer)-1, TCS_FLAG_NONE, &bytes_sent); +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() * -* // Receive data from any sender -* uint8_t recv_buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress sender_address; -* tcs_receive_from(peer, recv_buffer, sizeof(recv_buffer), TCS_FLAG_NONE, -* &sender_address, &bytes_received); -* -* tcs_close(&peer); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address structure to bind to. -* @param[in] remote_address is the remote address structure to send to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] interface_name name of the network interface to bind to. +* @param[in] protocol EtherType protocol in host byte order, e.g. 0x22F0 for AVTP. +* @param[in] type socket type, either #TCS_SOCK_RAW for full L2 frames or #TCS_SOCK_DGRAM for frames without the L2 header. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if @p interface_name is NULL or not found. * -* @see tcs_udp_peer_str() -* @see tcs_udp_receiver() -* @see tcs_udp_sender() -* @see tcs_send() -* @see tcs_receive_from() +* @see tcs_socket_packet() * @see tcs_close() */ -TcsResult tcs_udp_peer(TcsSocket* socket_ctx, - const struct TcsAddress* local_address, - const struct TcsAddress* remote_address); +TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type); /** -* @brief Creates a UDP socket bound to a local address that can send to a specific remote address. -* -* This function creates a UDP socket that's both bound to a local address/port for receiving -* datagrams and pre-configured to send datagrams to a specified remote address/port. This -* creates a complete bidirectional UDP communication channel in a single call. -* Use tcs_udp_peer() if you want to specify the addresses as TcsAddress structures. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* TcsSocket peer = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_peer_str(&peer, "0.0.0.0", 8888, "192.168.1.100", 9999); -* if (udp_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP peer -* } -* -* // Send data - goes to the pre-configured remote address -* uint8_t send_buffer[] = "Hello, remote peer!"; -* size_t bytes_sent = 0; -* tcs_send(peer, send_buffer, sizeof(send_buffer)-1, TCS_FLAG_NONE, &bytes_sent); -* -* // Receive data from any sender -* uint8_t recv_buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress sender_address; -* tcs_receive_from(peer, recv_buffer, sizeof(recv_buffer), TCS_FLAG_NONE, -* &sender_address, &bytes_received); +* @brief Closes the socket, stop communication and free all resources for the socket. * -* tcs_close(&peer); -* tcs_lib_free(); -* return 0; -* } -* @endcode +* This will free all resources associated with the socket and set the socket value to #TCS_SOCKET_INVALID. * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address to bind to. Use NULL or "0.0.0.0" to bind to all interfaces. -* @param[in] local_port is the local port to bind to. 0 will result in a random free port being assigned. -* @param[in] remote_address is the remote address to send to. Hostname or IP address string. -* @param[in] remote_port is the remote port to send to. +* @param[in,out] socket_ctx is a pointer to your socket context you have previously created with ::tcs_socket() or one of the helper functions. Will be set to #TCS_SOCKET_INVALID on success. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the local or remote address could not be resolved. +* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a NULL pointer or a socket that is already #TCS_SOCKET_INVALID. +* @retval #TCS_ERROR_SOCKET_CLOSED if the socket is already closed. * -* @see tcs_udp_peer() -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender_str() -* @see tcs_send() -* @see tcs_receive_from() -* @see tcs_close() +* @see tcs_socket() +* @see tcs_socket_tcp() +* @see tcs_socket_udp() +* @see tcs_socket_packet() +* @see tcs_lib_init() +* @see tcs_lib_free() */ -TcsResult tcs_udp_peer_str(TcsSocket* socket_ctx, - const char* local_address, - uint16_t local_port, - const char* remote_address, - uint16_t remote_port); - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -/** - * @brief Open a raw L2 packet socket bound to a specific interface and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * The socket uses SOCK_RAW, giving full control over the Ethernet frame including VLAN tags. - * Use ::tcs_send_to() / ::tcs_receive_from() for communication. - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle that will be initialized on success. Must be set to - * #TCS_SOCKET_INVALID before the call. - * @param[in] bind_address Pointer to a ::TcsAddress with family TCS_AF_PACKET specifying interface_id and protocol. - * Use #TCS_ETH_P_ALL to capture all protocols. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) provided. - * - * @see tcs_raw_str() - */ -TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); - -/** - * @brief Open a raw L2 packet socket by interface name and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * Convenience wrapper around ::tcs_raw() that resolves the interface name - * to an interface ID via ::tcs_interface_list(). - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle that will be initialized on success. Must be set to - * #TCS_SOCKET_INVALID before the call. - * @param[in] interface_name Null-terminated name of the network interface (e.g. "eth0", "wlan0"). - * @param[in] protocol EtherType filter in host byte order. Use #TCS_ETH_P_ALL to capture all protocols. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) or interface not found. - * - * @see tcs_raw() - * @see tcs_interface_list() - */ -TcsResult tcs_raw_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -/** - * @brief Open an L2 packet DGRAM socket bound to a specific interface and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * The socket uses SOCK_DGRAM, where the kernel handles the Ethernet header. - * Use ::tcs_send_to() / ::tcs_receive_from() for communication. - * Note: connect() is not supported on AF_PACKET sockets (see packet(7)). - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle. Must be #TCS_SOCKET_INVALID before call. - * @param[in] bind_address Pointer to a ::TcsAddress with family TCS_AF_PACKET specifying interface_id and protocol. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) provided. - * - * @see tcs_packet_str() - */ -TcsResult tcs_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); - -/** - * @brief Open an L2 packet DGRAM socket by interface name and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * Convenience wrapper around ::tcs_packet() that resolves the interface name - * to an interface ID via ::tcs_interface_list(). - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle. Must be #TCS_SOCKET_INVALID before call. - * @param[in] interface_name Null-terminated name of the network interface (e.g. "eth0"). - * @param[in] protocol EtherType filter in host byte order. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) or interface not found. - * - * @see tcs_packet() - * @see tcs_interface_list() - */ -TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); +TcsResult tcs_close(TcsSocket* socket_ctx); /** * @brief Binds a socket to a local address. @@ -1320,7 +853,7 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint * return -1; * * TcsSocket server_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&server_socket, TCS_PRESET_TCP_IP4); + * TcsResult socket_res = tcs_socket(&server_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * if (socket_res != TCS_SUCCESS) * { * tcs_lib_free(); @@ -1349,7 +882,7 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint * } * @endcode * - * @param socket_ctx The socket to bind. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). + * @param socket_ctx The socket to bind. Must be a valid socket created with tcs_socket(). * @param local_address The local address structure to bind to. Use TCS_ADDRESS_ANY_IP4 for the address field to bind to all interfaces. * * @return #TCS_SUCCESS if successful, otherwise the error code. @@ -1358,8 +891,8 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint * @retval #TCS_ERROR_SYSTEM if the address is already in use or another system error occurred. * * @see tcs_listen() - * @see tcs_tcp_server_str() - * @see tcs_udp_receiver_str() + * @see tcs_socket_tcp() + * @see tcs_socket_udp() * @see tcs_address_socket_local() */ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address); @@ -1383,7 +916,7 @@ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address) * return -1; * * TcsSocket client_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4); + * TcsResult socket_res = tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * if (socket_res != TCS_SUCCESS) * { * tcs_lib_free(); @@ -1414,7 +947,7 @@ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address) * } * @endcode * - * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). + * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket(). * @param address The remote address structure to connect to. * * @return #TCS_SUCCESS if successful, otherwise the error code. @@ -1424,7 +957,7 @@ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address) * @retval #TCS_ERROR_SYSTEM if another system error occurred. * * @see tcs_connect_str() - * @see tcs_tcp_client() + * @see tcs_socket_tcp() * @see tcs_bind() * @see tcs_listen() */ @@ -1448,7 +981,7 @@ TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); * return -1; * * TcsSocket client_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4); + * TcsResult socket_res = tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * if (socket_res != TCS_SUCCESS) * { * tcs_lib_free(); @@ -1474,7 +1007,7 @@ TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); * } * @endcode * - * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). + * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket(). * @param remote_address The remote hostname or IP address to connect to. * @param port The remote port number to connect to. * @@ -1486,7 +1019,7 @@ TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); * @retval #TCS_ERROR_SYSTEM if another system error occurred. * * @see tcs_connect() - * @see tcs_tcp_client_str() + * @see tcs_socket_tcp_str() * @see tcs_bind() * @see tcs_listen() */ @@ -1513,7 +1046,7 @@ TcsResult tcs_listen(TcsSocket socket_ctx, int backlog); * Example usage: * @code * TcsSocket listen_socket = TCS_SOCKET_INVALID; - * tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4); + * tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * struct TcsAddress local_address = TCS_ADDRESS_NONE; * local_address.family = TCS_AF_IP4; * local_address.data.ip4.port = 1212; @@ -1704,8 +1237,8 @@ TcsResult tcs_receive_netstring(TcsSocket socket_ctx, uint8_t* buffer, size_t bu * tcs_lib_init(); * TcsSocket socket1 = TCS_SOCKET_INVALID; * TcsSocket socket2 = TCS_SOCKET_INVALID; -* tcs_socket_preset(&socket1, TCS_PRESET_UDP_IP4); -* tcs_socket_preset(&socket2, TCS_PRESET_UDP_IP4); +* tcs_socket(&socket1, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); +* tcs_socket(&socket2, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); * * struct TcsAddress addr1 = TCS_ADDRESS_NONE; * addr1.family = TCS_AF_IP4; @@ -3116,7 +2649,12 @@ TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, i return errno2retcode(errno); } -// tcs_socket_preset() is defined in tinycsocket_common.c +// tcs_socket_tcp() is defined in tinycsocket_common.c +// tcs_socket_tcp_str() is defined in tinycsocket_common.c +// tcs_socket_udp() is defined in tinycsocket_common.c +// tcs_socket_udp_str() is defined in tinycsocket_common.c +// tcs_socket_packet() is defined in tinycsocket_common.c +// tcs_socket_packet_str() is defined in tinycsocket_common.c TcsResult tcs_close(TcsSocket* socket_ctx) { @@ -3134,29 +2672,6 @@ TcsResult tcs_close(TcsSocket* socket_ctx) } } -// ######## High-level Socket Creation ######## - -// tcs_tcp_server_str() is defined in tinycsocket_common.c -// tcs_tcp_server() is defined in tinycsocket_common.c -// tcs_tcp_client_str() is defined in tinycsocket_common.c -// tcs_tcp_client() is defined in tinycsocket_common.c -// tcs_udp_receiver_str() is defined in tinycsocket_common.c -// tcs_udp_receiver() is defined in tinycsocket_common.c -// tcs_udp_sender_str() is defined in tinycsocket_common.c -// tcs_udp_sender() is defined in tinycsocket_common.c -// tcs_udp_peer_str() is defined in tinycsocket_common.c -// tcs_udp_peer() is defined in tinycsocket_common.c - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -// tcs_raw() is defined in tinycsocket_common.c -// tcs_raw_str() is defined in tinycsocket_common.c - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -// tcs_packet() is defined in tinycsocket_common.c -// tcs_packet_str() is defined in tinycsocket_common.c - // ######## Socket Operations ######## TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* address) @@ -3668,14 +3183,15 @@ TcsResult tcs_pool_poll(struct TcsPool* pool, events[filled].user_data = map->values[i]; events[filled].can_read = map->keys[i].revents & POLLIN; events[filled].can_write = map->keys[i].revents & POLLOUT; - if (map->keys[i].revents & POLLERR) + if (map->keys[i].revents & (POLLERR | POLLHUP)) { int so_error = 0; socklen_t so_error_size = sizeof(so_error); + TcsResult fallback = (map->keys[i].revents & POLLERR) ? TCS_ERROR_UNKNOWN : TCS_ERROR_SOCKET_CLOSED; if (getsockopt(map->keys[i].fd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_size) != 0) events[filled].error = errno2retcode(errno); else - events[filled].error = so_error != 0 ? errno2retcode(so_error) : TCS_ERROR_UNKNOWN; + events[filled].error = so_error != 0 ? errno2retcode(so_error) : fallback; } else { @@ -4982,7 +4498,12 @@ TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, i } } -// tcs_socket_preset() is defined in tinycsocket_common.c +// tcs_socket_tcp() is defined in tinycsocket_common.c +// tcs_socket_tcp_str() is defined in tinycsocket_common.c +// tcs_socket_udp() is defined in tinycsocket_common.c +// tcs_socket_udp_str() is defined in tinycsocket_common.c +// tcs_socket_packet() is defined in tinycsocket_common.c +// tcs_socket_packet_str() is defined in tinycsocket_common.c TcsResult tcs_close(TcsSocket* socket_ctx) { @@ -5001,29 +4522,6 @@ TcsResult tcs_close(TcsSocket* socket_ctx) } } -// ######## High-level Socket Creation ######## - -// tcs_tcp_server_str() is defined in tinycsocket_common.c -// tcs_tcp_server() is defined in tinycsocket_common.c -// tcs_tcp_client_str() is defined in tinycsocket_common.c -// tcs_tcp_client() is defined in tinycsocket_common.c -// tcs_udp_receiver_str() is defined in tinycsocket_common.c -// tcs_udp_receiver() is defined in tinycsocket_common.c -// tcs_udp_sender_str() is defined in tinycsocket_common.c -// tcs_udp_sender() is defined in tinycsocket_common.c -// tcs_udp_peer_str() is defined in tinycsocket_common.c -// tcs_udp_peer() is defined in tinycsocket_common.c - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -// tcs_raw() is defined in tinycsocket_common.c -// tcs_raw_str() is defined in tinycsocket_common.c - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -// tcs_packet() is defined in tinycsocket_common.c -// tcs_packet_str() is defined in tinycsocket_common.c - // ######## Socket Operations ######## TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* address) @@ -6512,6 +6010,7 @@ TcsResult tcs_address_socket_family(TcsSocket socket_ctx, TcsAddressFamily* out_ #include #include //sprintf +#include // malloc, free #include // memset // ######## Library Management ######## @@ -6523,455 +6022,254 @@ TcsResult tcs_address_socket_family(TcsSocket socket_ctx, TcsAddressFamily* out_ // tcs_socket() is defined in OS specific files -TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type) +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address, + int timeout_ms) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - - TcsAddressFamily family = TCS_AF_ANY; - int type = 0; - int protocol = 0; - - switch (socket_type) - { - case TCS_PRESET_TCP_IP4: - family = TCS_AF_IP4; - type = TCS_SOCK_STREAM; - protocol = TCS_PROTOCOL_IP_TCP; - break; - case TCS_PRESET_UDP_IP4: - family = TCS_AF_IP4; - type = TCS_SOCK_DGRAM; - protocol = TCS_PROTOCOL_IP_UDP; - break; - case TCS_PRESET_RAW: - family = TCS_AF_PACKET; - type = TCS_SOCK_RAW; - protocol = 0; // Block all traffic, unblock with tcs_bind_addr() - break; - case TCS_PRESET_PACKET: - family = TCS_AF_PACKET; - type = TCS_SOCK_DGRAM; - protocol = 0; // Block all traffic, unblock with tcs_bind_addr() - break; - case TCS_PRESET_TCP_IP6: - family = TCS_AF_IP6; - type = TCS_SOCK_STREAM; - protocol = TCS_PROTOCOL_IP_TCP; - break; - case TCS_PRESET_UDP_IP6: - family = TCS_AF_IP6; - type = TCS_SOCK_DGRAM; - protocol = TCS_PROTOCOL_IP_UDP; - break; - default: - return TCS_ERROR_NOT_IMPLEMENTED; - } - return tcs_socket(socket_ctx, family, type, protocol); -} - -// tcs_close() is defined in OS specific files - -// ######## High-level Socket Creation ######## - -TcsResult tcs_tcp_server(TcsSocket* socket_ctx, const struct TcsAddress* local_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) + return TCS_ERROR_INVALID_ARGUMENT; + if (timeout_ms < 0 && timeout_ms != TCS_WAIT_INF) return TCS_ERROR_INVALID_ARGUMENT; - TcsResult res = tcs_socket(socket_ctx, local_address->family, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); + TcsAddressFamily family = local_address != NULL ? local_address->family : remote_address->family; + + TcsResult res = tcs_socket(socket_ctx, family, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); if (res != TCS_SUCCESS) return res; - res = tcs_bind(*socket_ctx, local_address); - if (res != TCS_SUCCESS) + + if (local_address != NULL) { - tcs_close(socket_ctx); - return res; - } - res = tcs_listen(*socket_ctx, TCS_BACKLOG_MAX); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; -} - -TcsResult tcs_tcp_server_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - struct TcsAddress bind_address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(local_address, &bind_address); - if (res != TCS_SUCCESS) - return res; - if (bind_address.family != TCS_AF_IP4 && bind_address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - - uint16_t* parsed_port = NULL; - if (bind_address.family == TCS_AF_IP4) - { - parsed_port = &bind_address.data.ip4.port; - } - else if (bind_address.family == TCS_AF_IP6) - { - parsed_port = &bind_address.data.ip6.port; - } - else - { - return TCS_ERROR_UNKNOWN; - } - - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; // No port specified - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; // Port specified in both string and argument - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_tcp_server(socket_ctx, &bind_address); -} - -TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (timeout_ms < 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address->family != TCS_AF_IP4 && remote_address->family != TCS_AF_IP6) - return TCS_ERROR_NOT_IMPLEMENTED; - if (remote_address->family == TCS_AF_IP4 && remote_address->data.ip4.port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address->family == TCS_AF_IP6 && remote_address->data.ip6.port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, remote_address->family, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); - if (res != TCS_SUCCESS) - return res; - - struct TcsPool* timeout_pool = NULL; - res = tcs_pool_create(&timeout_pool); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - - res = tcs_opt_nonblocking_set(*socket_ctx, true); - if (res != TCS_SUCCESS) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res; - } - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_IN_PROGRESS && res != TCS_SUCCESS) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res; - } - - if (res == TCS_IN_PROGRESS) - { - res = tcs_pool_add(timeout_pool, *socket_ctx, NULL, false, true, true); + res = tcs_opt_reuse_address_set(*socket_ctx, true); if (res != TCS_SUCCESS) { - tcs_pool_destroy(&timeout_pool); tcs_close(socket_ctx); return res; } - - struct TcsPollEvent event = TCS_POOL_EVENT_EMPTY; - size_t events_populated = 0; - res = tcs_pool_poll(timeout_pool, &event, 1, &events_populated, timeout_ms); - if (res == TCS_ERROR_TIMED_OUT) + res = tcs_bind(*socket_ctx, local_address); + if (res != TCS_SUCCESS) { - tcs_pool_destroy(&timeout_pool); tcs_close(socket_ctx); - return TCS_ERROR_TIMED_OUT; + return res; } - if (res != TCS_SUCCESS || events_populated == 0) + } + + if (remote_address != NULL) + { + if (timeout_ms == TCS_WAIT_INF) { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res != TCS_SUCCESS ? res : TCS_ERROR_UNKNOWN; + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } } - if (event.error != TCS_SUCCESS) + else { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return TCS_ERROR_CONNECTION_REFUSED; + res = tcs_opt_nonblocking_set(*socket_ctx, true); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_IN_PROGRESS && res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + if (res == TCS_IN_PROGRESS) + { + struct TcsPool* pool = NULL; + res = tcs_pool_create(&pool); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + res = tcs_pool_add(pool, *socket_ctx, NULL, false, true, true); + if (res != TCS_SUCCESS) + { + tcs_pool_destroy(&pool); + tcs_close(socket_ctx); + return res; + } + struct TcsPollEvent event = TCS_POOL_EVENT_EMPTY; + size_t events_populated = 0; + res = tcs_pool_poll(pool, &event, 1, &events_populated, timeout_ms); + tcs_pool_destroy(&pool); + if (res == TCS_ERROR_TIMED_OUT) + { + tcs_close(socket_ctx); + return TCS_ERROR_TIMED_OUT; + } + if (res != TCS_SUCCESS || events_populated == 0) + { + tcs_close(socket_ctx); + return res != TCS_SUCCESS ? res : TCS_ERROR_UNKNOWN; + } + if (event.error != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return TCS_ERROR_CONNECTION_REFUSED; + } + } + res = tcs_opt_nonblocking_set(*socket_ctx, false); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } } } - tcs_pool_destroy(&timeout_pool); - - res = tcs_opt_nonblocking_set(*socket_ctx, false); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; } -TcsResult tcs_tcp_client_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port, int timeout_ms) +TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, + const char* local_address, + const char* remote_address, + int timeout_ms) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(remote_address, &address); - if (res != TCS_SUCCESS) - return res; - if (address.family != TCS_AF_IP4 && address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; + struct TcsAddress local_addr = TCS_ADDRESS_NONE; + struct TcsAddress remote_addr = TCS_ADDRESS_NONE; + TcsAddressFamily family = TCS_AF_ANY; - uint16_t* parsed_port = NULL; - if (address.family == TCS_AF_IP4) - { - parsed_port = &address.data.ip4.port; - } - else if (address.family == TCS_AF_IP6) + if (local_address != NULL) { - parsed_port = &address.data.ip6.port; + size_t count = 0; + TcsResult res = tcs_address_resolve(local_address, TCS_AF_ANY, &local_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; + family = local_addr.family; } - else + + if (remote_address != NULL) { - return TCS_ERROR_UNKNOWN; + size_t count = 0; + TcsResult res = tcs_address_resolve(remote_address, family, &remote_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; } - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_tcp_client(socket_ctx, &address, timeout_ms); + return tcs_socket_tcp(socket_ctx, + local_address != NULL ? &local_addr : NULL, + remote_address != NULL ? &remote_addr : NULL, + timeout_ms); } -TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address) +TcsResult tcs_socket_udp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, local_address->family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, local_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; -} - -TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress bind_address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(local_address, &bind_address); + TcsAddressFamily family = local_address != NULL ? local_address->family : remote_address->family; + + TcsResult res = tcs_socket(socket_ctx, family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); if (res != TCS_SUCCESS) return res; - if (bind_address.family != TCS_AF_IP4 && bind_address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - uint16_t* parsed_port = NULL; - if (bind_address.family == TCS_AF_IP4) - { - parsed_port = &bind_address.data.ip4.port; - } - else if (bind_address.family == TCS_AF_IP6) - { - parsed_port = &bind_address.data.ip6.port; - } - else + if (local_address != NULL) { - return TCS_ERROR_UNKNOWN; + res = tcs_opt_reuse_address_set(*socket_ctx, true); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + res = tcs_bind(*socket_ctx, local_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } } - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_udp_receiver(socket_ctx, &bind_address); -} - -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, remote_address->family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); - if (res != TCS_SUCCESS) - return res; - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_SUCCESS) + if (remote_address != NULL) { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; -} - -TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; + bool is_multicast = tcs_address_is_multicast(remote_address); - struct TcsAddress address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(remote_address, &address); - if (res != TCS_SUCCESS) - return res; - if (address.family != TCS_AF_IP4 && address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; + if (is_multicast) + { + res = tcs_opt_membership_add(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } - uint16_t* parsed_port = NULL; - if (address.family == TCS_AF_IP4) - { - parsed_port = &address.data.ip4.port; - } - else if (address.family == TCS_AF_IP6) - { - parsed_port = &address.data.ip6.port; - } - else - { - return TCS_ERROR_UNKNOWN; + if (!is_multicast || local_address == NULL) + { + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } } - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_udp_sender(socket_ctx, &address); -} - -TcsResult tcs_udp_peer(TcsSocket* socket_ctx, - const struct TcsAddress* local_address, - const struct TcsAddress* remote_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, local_address->family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, local_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } return TCS_SUCCESS; } -TcsResult tcs_udp_peer_str(TcsSocket* socket_ctx, - const char* local_address, - uint16_t local_port, - const char* remote_address, - uint16_t remote_port) +TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; struct TcsAddress local_addr = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(local_address, &local_addr); - if (res != TCS_SUCCESS) - return res; - if (local_addr.family != TCS_AF_IP4 && local_addr.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress remote_addr = TCS_ADDRESS_NONE; - res = tcs_address_parse(remote_address, &remote_addr); - if (res != TCS_SUCCESS) - return res; - if (remote_addr.family != TCS_AF_IP4 && remote_addr.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - - uint16_t* local_parsed_port = NULL; - if (local_addr.family == TCS_AF_IP4) - local_parsed_port = &local_addr.data.ip4.port; - else if (local_addr.family == TCS_AF_IP6) - local_parsed_port = &local_addr.data.ip6.port; - else - return TCS_ERROR_UNKNOWN; - - if (local_port == 0 && *local_parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_port != 0 && *local_parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_port != 0 && *local_parsed_port == 0) - *local_parsed_port = local_port; + TcsAddressFamily family = TCS_AF_ANY; - uint16_t* remote_parsed_port = NULL; - if (remote_addr.family == TCS_AF_IP4) - remote_parsed_port = &remote_addr.data.ip4.port; - else if (remote_addr.family == TCS_AF_IP6) - remote_parsed_port = &remote_addr.data.ip6.port; - else - return TCS_ERROR_UNKNOWN; + if (local_address != NULL) + { + size_t count = 0; + TcsResult res = tcs_address_resolve(local_address, TCS_AF_ANY, &local_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; + family = local_addr.family; + } - if (remote_port == 0 && *remote_parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_port != 0 && *remote_parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_port != 0 && *remote_parsed_port == 0) - *remote_parsed_port = remote_port; + if (remote_address != NULL) + { + size_t count = 0; + TcsResult res = tcs_address_resolve(remote_address, family, &remote_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; + } - return tcs_udp_peer(socket_ctx, &local_addr, &remote_addr); + return tcs_socket_udp( + socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); } -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address) +TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; @@ -6979,86 +6277,65 @@ TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address) return TCS_ERROR_INVALID_ARGUMENT; if (bind_address->family != TCS_AF_PACKET) return TCS_ERROR_INVALID_ARGUMENT; + if (type != TCS_SOCK_RAW && type != TCS_SOCK_DGRAM) + return TCS_ERROR_INVALID_ARGUMENT; - TcsResult res = tcs_socket(socket_ctx, TCS_AF_PACKET, TCS_SOCK_RAW, bind_address->data.packet.protocol); + TcsResult res = tcs_socket(socket_ctx, TCS_AF_PACKET, type, bind_address->data.packet.protocol); if (res != TCS_SUCCESS) return res; + res = tcs_bind(*socket_ctx, bind_address); if (res != TCS_SUCCESS) { tcs_close(socket_ctx); return res; } + return TCS_SUCCESS; } -TcsResult tcs_raw_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol) +TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; if (interface_name == NULL) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsInterface interfaces[16]; // TODO: use heap if more + struct TcsInterface stack_buf[16]; + struct TcsInterface* interfaces = stack_buf; size_t count = 0; - TcsResult res = tcs_interface_list(interfaces, 16, &count); + + TcsResult res = tcs_interface_list(stack_buf, 16, &count); if (res != TCS_SUCCESS) return res; - size_t iter_count = count < 16 ? count : 16; - for (size_t i = 0; i < iter_count; ++i) + size_t search_count = count < 16 ? count : 16; + for (size_t i = 0; i < search_count; ++i) { - if (strcmp(interfaces[i].name, interface_name) == 0) + if (strcmp(stack_buf[i].name, interface_name) == 0) { struct TcsAddress bind_address = TCS_ADDRESS_NONE; bind_address.family = TCS_AF_PACKET; - bind_address.data.packet.interface_id = interfaces[i].id; + bind_address.data.packet.interface_id = stack_buf[i].id; bind_address.data.packet.protocol = protocol; - return tcs_raw(socket_ctx, &bind_address); + return tcs_socket_packet(socket_ctx, &bind_address, type); } } - return TCS_ERROR_INVALID_ARGUMENT; -} - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -TcsResult tcs_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (bind_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (bind_address->family != TCS_AF_PACKET) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, TCS_AF_PACKET, TCS_SOCK_DGRAM, bind_address->data.packet.protocol); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, bind_address); - if (res != TCS_SUCCESS) + if (count > 16) { - tcs_close(socket_ctx); - return res; + interfaces = (struct TcsInterface*)malloc(count * sizeof(struct TcsInterface)); + if (interfaces == NULL) + return TCS_ERROR_MEMORY; + res = tcs_interface_list(interfaces, count, &count); + if (res != TCS_SUCCESS) + { + free(interfaces); + return res; + } } - return TCS_SUCCESS; -} - -TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (interface_name == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - struct TcsInterface interfaces[16]; - size_t count = 0; - TcsResult res = tcs_interface_list(interfaces, 16, &count); - if (res != TCS_SUCCESS) - return res; - - size_t iter_count = count < 16 ? count : 16; - for (size_t i = 0; i < iter_count; ++i) + for (size_t i = 0; i < count; ++i) { if (strcmp(interfaces[i].name, interface_name) == 0) { @@ -7066,13 +6343,19 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint bind_address.family = TCS_AF_PACKET; bind_address.data.packet.interface_id = interfaces[i].id; bind_address.data.packet.protocol = protocol; - return tcs_packet(socket_ctx, &bind_address); + if (interfaces != stack_buf) + free(interfaces); + return tcs_socket_packet(socket_ctx, &bind_address, type); } } + if (interfaces != stack_buf) + free(interfaces); return TCS_ERROR_INVALID_ARGUMENT; } +// tcs_close() is defined in OS specific files + // ######## Socket Operations ######## // tcs_bind() is defined in OS specific files diff --git a/src/tinycsocket_common.c b/src/tinycsocket_common.c index 94d0540..b1b9f3b 100644 --- a/src/tinycsocket_common.c +++ b/src/tinycsocket_common.c @@ -31,6 +31,7 @@ #include #include //sprintf +#include // malloc, free #include // memset // ######## Library Management ######## @@ -42,455 +43,254 @@ // tcs_socket() is defined in OS specific files -TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type) +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address, + int timeout_ms) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - - TcsAddressFamily family = TCS_AF_ANY; - int type = 0; - int protocol = 0; - - switch (socket_type) - { - case TCS_PRESET_TCP_IP4: - family = TCS_AF_IP4; - type = TCS_SOCK_STREAM; - protocol = TCS_PROTOCOL_IP_TCP; - break; - case TCS_PRESET_UDP_IP4: - family = TCS_AF_IP4; - type = TCS_SOCK_DGRAM; - protocol = TCS_PROTOCOL_IP_UDP; - break; - case TCS_PRESET_RAW: - family = TCS_AF_PACKET; - type = TCS_SOCK_RAW; - protocol = 0; // Block all traffic, unblock with tcs_bind_addr() - break; - case TCS_PRESET_PACKET: - family = TCS_AF_PACKET; - type = TCS_SOCK_DGRAM; - protocol = 0; // Block all traffic, unblock with tcs_bind_addr() - break; - case TCS_PRESET_TCP_IP6: - family = TCS_AF_IP6; - type = TCS_SOCK_STREAM; - protocol = TCS_PROTOCOL_IP_TCP; - break; - case TCS_PRESET_UDP_IP6: - family = TCS_AF_IP6; - type = TCS_SOCK_DGRAM; - protocol = TCS_PROTOCOL_IP_UDP; - break; - default: - return TCS_ERROR_NOT_IMPLEMENTED; - } - return tcs_socket(socket_ctx, family, type, protocol); -} - -// tcs_close() is defined in OS specific files - -// ######## High-level Socket Creation ######## - -TcsResult tcs_tcp_server(TcsSocket* socket_ctx, const struct TcsAddress* local_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, local_address->family, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, local_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - res = tcs_listen(*socket_ctx, TCS_BACKLOG_MAX); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; -} - -TcsResult tcs_tcp_server_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - struct TcsAddress bind_address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(local_address, &bind_address); - if (res != TCS_SUCCESS) - return res; - if (bind_address.family != TCS_AF_IP4 && bind_address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - - uint16_t* parsed_port = NULL; - if (bind_address.family == TCS_AF_IP4) - { - parsed_port = &bind_address.data.ip4.port; - } - else if (bind_address.family == TCS_AF_IP6) - { - parsed_port = &bind_address.data.ip6.port; - } - else - { - return TCS_ERROR_UNKNOWN; - } - - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; // No port specified - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; // Port specified in both string and argument - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_tcp_server(socket_ctx, &bind_address); -} - -TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (timeout_ms < 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address->family != TCS_AF_IP4 && remote_address->family != TCS_AF_IP6) - return TCS_ERROR_NOT_IMPLEMENTED; - if (remote_address->family == TCS_AF_IP4 && remote_address->data.ip4.port == 0) + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address->family == TCS_AF_IP6 && remote_address->data.ip6.port == 0) + if (timeout_ms < 0 && timeout_ms != TCS_WAIT_INF) return TCS_ERROR_INVALID_ARGUMENT; - TcsResult res = tcs_socket(socket_ctx, remote_address->family, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); - if (res != TCS_SUCCESS) - return res; + TcsAddressFamily family = local_address != NULL ? local_address->family : remote_address->family; - struct TcsPool* timeout_pool = NULL; - res = tcs_pool_create(&timeout_pool); + TcsResult res = tcs_socket(socket_ctx, family, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); return res; - } - res = tcs_opt_nonblocking_set(*socket_ctx, true); - if (res != TCS_SUCCESS) + if (local_address != NULL) { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res; - } - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_IN_PROGRESS && res != TCS_SUCCESS) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res; - } - - if (res == TCS_IN_PROGRESS) - { - res = tcs_pool_add(timeout_pool, *socket_ctx, NULL, false, true, true); + res = tcs_opt_reuse_address_set(*socket_ctx, true); if (res != TCS_SUCCESS) { - tcs_pool_destroy(&timeout_pool); tcs_close(socket_ctx); return res; } - - struct TcsPollEvent event = TCS_POOL_EVENT_EMPTY; - size_t events_populated = 0; - res = tcs_pool_poll(timeout_pool, &event, 1, &events_populated, timeout_ms); - if (res == TCS_ERROR_TIMED_OUT) + res = tcs_bind(*socket_ctx, local_address); + if (res != TCS_SUCCESS) { - tcs_pool_destroy(&timeout_pool); tcs_close(socket_ctx); - return TCS_ERROR_TIMED_OUT; + return res; } - if (res != TCS_SUCCESS || events_populated == 0) + } + + if (remote_address != NULL) + { + if (timeout_ms == TCS_WAIT_INF) { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res != TCS_SUCCESS ? res : TCS_ERROR_UNKNOWN; + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } } - if (event.error != TCS_SUCCESS) + else { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return TCS_ERROR_CONNECTION_REFUSED; + res = tcs_opt_nonblocking_set(*socket_ctx, true); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_IN_PROGRESS && res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + if (res == TCS_IN_PROGRESS) + { + struct TcsPool* pool = NULL; + res = tcs_pool_create(&pool); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + res = tcs_pool_add(pool, *socket_ctx, NULL, false, true, true); + if (res != TCS_SUCCESS) + { + tcs_pool_destroy(&pool); + tcs_close(socket_ctx); + return res; + } + struct TcsPollEvent event = TCS_POOL_EVENT_EMPTY; + size_t events_populated = 0; + res = tcs_pool_poll(pool, &event, 1, &events_populated, timeout_ms); + tcs_pool_destroy(&pool); + if (res == TCS_ERROR_TIMED_OUT) + { + tcs_close(socket_ctx); + return TCS_ERROR_TIMED_OUT; + } + if (res != TCS_SUCCESS || events_populated == 0) + { + tcs_close(socket_ctx); + return res != TCS_SUCCESS ? res : TCS_ERROR_UNKNOWN; + } + if (event.error != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return TCS_ERROR_CONNECTION_REFUSED; + } + } + res = tcs_opt_nonblocking_set(*socket_ctx, false); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } } } - tcs_pool_destroy(&timeout_pool); - - res = tcs_opt_nonblocking_set(*socket_ctx, false); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; } -TcsResult tcs_tcp_client_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port, int timeout_ms) +TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, + const char* local_address, + const char* remote_address, + int timeout_ms) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(remote_address, &address); - if (res != TCS_SUCCESS) - return res; - if (address.family != TCS_AF_IP4 && address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; + struct TcsAddress local_addr = TCS_ADDRESS_NONE; + struct TcsAddress remote_addr = TCS_ADDRESS_NONE; + TcsAddressFamily family = TCS_AF_ANY; - uint16_t* parsed_port = NULL; - if (address.family == TCS_AF_IP4) - { - parsed_port = &address.data.ip4.port; - } - else if (address.family == TCS_AF_IP6) + if (local_address != NULL) { - parsed_port = &address.data.ip6.port; + size_t count = 0; + TcsResult res = tcs_address_resolve(local_address, TCS_AF_ANY, &local_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; + family = local_addr.family; } - else + + if (remote_address != NULL) { - return TCS_ERROR_UNKNOWN; + size_t count = 0; + TcsResult res = tcs_address_resolve(remote_address, family, &remote_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; } - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_tcp_client(socket_ctx, &address, timeout_ms); + return tcs_socket_tcp(socket_ctx, + local_address != NULL ? &local_addr : NULL, + remote_address != NULL ? &remote_addr : NULL, + timeout_ms); } -TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address) +TcsResult tcs_socket_udp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, local_address->family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, local_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; -} - -TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress bind_address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(local_address, &bind_address); + TcsAddressFamily family = local_address != NULL ? local_address->family : remote_address->family; + + TcsResult res = tcs_socket(socket_ctx, family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); if (res != TCS_SUCCESS) return res; - if (bind_address.family != TCS_AF_IP4 && bind_address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - uint16_t* parsed_port = NULL; - if (bind_address.family == TCS_AF_IP4) + if (local_address != NULL) { - parsed_port = &bind_address.data.ip4.port; - } - else if (bind_address.family == TCS_AF_IP6) - { - parsed_port = &bind_address.data.ip6.port; - } - else - { - return TCS_ERROR_UNKNOWN; + res = tcs_opt_reuse_address_set(*socket_ctx, true); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + res = tcs_bind(*socket_ctx, local_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } } - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_udp_receiver(socket_ctx, &bind_address); -} - -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, remote_address->family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); - if (res != TCS_SUCCESS) - return res; - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_SUCCESS) + if (remote_address != NULL) { - tcs_close(socket_ctx); - return res; - } - return TCS_SUCCESS; -} + bool is_multicast = tcs_address_is_multicast(remote_address); -TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - struct TcsAddress address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(remote_address, &address); - if (res != TCS_SUCCESS) - return res; - if (address.family != TCS_AF_IP4 && address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; + if (is_multicast) + { + res = tcs_opt_membership_add(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } - uint16_t* parsed_port = NULL; - if (address.family == TCS_AF_IP4) - { - parsed_port = &address.data.ip4.port; - } - else if (address.family == TCS_AF_IP6) - { - parsed_port = &address.data.ip6.port; - } - else - { - return TCS_ERROR_UNKNOWN; + if (!is_multicast || local_address == NULL) + { + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } } - if (port == 0 && *parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (port != 0 && *parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - - if (port != 0 && *parsed_port == 0) - *parsed_port = port; - - return tcs_udp_sender(socket_ctx, &address); -} - -TcsResult tcs_udp_peer(TcsSocket* socket_ctx, - const struct TcsAddress* local_address, - const struct TcsAddress* remote_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, local_address->family, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, local_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } return TCS_SUCCESS; } -TcsResult tcs_udp_peer_str(TcsSocket* socket_ctx, - const char* local_address, - uint16_t local_port, - const char* remote_address, - uint16_t remote_port) +TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; - if (local_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; struct TcsAddress local_addr = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(local_address, &local_addr); - if (res != TCS_SUCCESS) - return res; - if (local_addr.family != TCS_AF_IP4 && local_addr.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress remote_addr = TCS_ADDRESS_NONE; - res = tcs_address_parse(remote_address, &remote_addr); - if (res != TCS_SUCCESS) - return res; - if (remote_addr.family != TCS_AF_IP4 && remote_addr.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - - uint16_t* local_parsed_port = NULL; - if (local_addr.family == TCS_AF_IP4) - local_parsed_port = &local_addr.data.ip4.port; - else if (local_addr.family == TCS_AF_IP6) - local_parsed_port = &local_addr.data.ip6.port; - else - return TCS_ERROR_UNKNOWN; + TcsAddressFamily family = TCS_AF_ANY; - if (local_port == 0 && *local_parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_port != 0 && *local_parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_port != 0 && *local_parsed_port == 0) - *local_parsed_port = local_port; - - uint16_t* remote_parsed_port = NULL; - if (remote_addr.family == TCS_AF_IP4) - remote_parsed_port = &remote_addr.data.ip4.port; - else if (remote_addr.family == TCS_AF_IP6) - remote_parsed_port = &remote_addr.data.ip6.port; - else - return TCS_ERROR_UNKNOWN; + if (local_address != NULL) + { + size_t count = 0; + TcsResult res = tcs_address_resolve(local_address, TCS_AF_ANY, &local_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; + family = local_addr.family; + } - if (remote_port == 0 && *remote_parsed_port == 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_port != 0 && *remote_parsed_port != 0) - return TCS_ERROR_INVALID_ARGUMENT; - if (remote_port != 0 && *remote_parsed_port == 0) - *remote_parsed_port = remote_port; + if (remote_address != NULL) + { + size_t count = 0; + TcsResult res = tcs_address_resolve(remote_address, family, &remote_addr, 1, &count); + if (res != TCS_SUCCESS) + return res; + if (count == 0) + return TCS_ERROR_ADDRESS_LOOKUP_FAILED; + } - return tcs_udp_peer(socket_ctx, &local_addr, &remote_addr); + return tcs_socket_udp( + socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); } -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address) +TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; @@ -498,86 +298,65 @@ TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address) return TCS_ERROR_INVALID_ARGUMENT; if (bind_address->family != TCS_AF_PACKET) return TCS_ERROR_INVALID_ARGUMENT; + if (type != TCS_SOCK_RAW && type != TCS_SOCK_DGRAM) + return TCS_ERROR_INVALID_ARGUMENT; - TcsResult res = tcs_socket(socket_ctx, TCS_AF_PACKET, TCS_SOCK_RAW, bind_address->data.packet.protocol); + TcsResult res = tcs_socket(socket_ctx, TCS_AF_PACKET, type, bind_address->data.packet.protocol); if (res != TCS_SUCCESS) return res; + res = tcs_bind(*socket_ctx, bind_address); if (res != TCS_SUCCESS) { tcs_close(socket_ctx); return res; } + return TCS_SUCCESS; } -TcsResult tcs_raw_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol) +TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type) { if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) return TCS_ERROR_INVALID_ARGUMENT; if (interface_name == NULL) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsInterface interfaces[16]; // TODO: use heap if more + struct TcsInterface stack_buf[16]; + struct TcsInterface* interfaces = stack_buf; size_t count = 0; - TcsResult res = tcs_interface_list(interfaces, 16, &count); + + TcsResult res = tcs_interface_list(stack_buf, 16, &count); if (res != TCS_SUCCESS) return res; - size_t iter_count = count < 16 ? count : 16; - for (size_t i = 0; i < iter_count; ++i) + size_t search_count = count < 16 ? count : 16; + for (size_t i = 0; i < search_count; ++i) { - if (strcmp(interfaces[i].name, interface_name) == 0) + if (strcmp(stack_buf[i].name, interface_name) == 0) { struct TcsAddress bind_address = TCS_ADDRESS_NONE; bind_address.family = TCS_AF_PACKET; - bind_address.data.packet.interface_id = interfaces[i].id; + bind_address.data.packet.interface_id = stack_buf[i].id; bind_address.data.packet.protocol = protocol; - return tcs_raw(socket_ctx, &bind_address); + return tcs_socket_packet(socket_ctx, &bind_address, type); } } - return TCS_ERROR_INVALID_ARGUMENT; -} - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -TcsResult tcs_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (bind_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (bind_address->family != TCS_AF_PACKET) - return TCS_ERROR_INVALID_ARGUMENT; - - TcsResult res = tcs_socket(socket_ctx, TCS_AF_PACKET, TCS_SOCK_DGRAM, bind_address->data.packet.protocol); - if (res != TCS_SUCCESS) - return res; - res = tcs_bind(*socket_ctx, bind_address); - if (res != TCS_SUCCESS) + if (count > 16) { - tcs_close(socket_ctx); - return res; + interfaces = (struct TcsInterface*)malloc(count * sizeof(struct TcsInterface)); + if (interfaces == NULL) + return TCS_ERROR_MEMORY; + res = tcs_interface_list(interfaces, count, &count); + if (res != TCS_SUCCESS) + { + free(interfaces); + return res; + } } - return TCS_SUCCESS; -} - -TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol) -{ - if (socket_ctx == NULL || *socket_ctx != TCS_SOCKET_INVALID) - return TCS_ERROR_INVALID_ARGUMENT; - if (interface_name == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - struct TcsInterface interfaces[16]; - size_t count = 0; - TcsResult res = tcs_interface_list(interfaces, 16, &count); - if (res != TCS_SUCCESS) - return res; - - size_t iter_count = count < 16 ? count : 16; - for (size_t i = 0; i < iter_count; ++i) + for (size_t i = 0; i < count; ++i) { if (strcmp(interfaces[i].name, interface_name) == 0) { @@ -585,13 +364,19 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint bind_address.family = TCS_AF_PACKET; bind_address.data.packet.interface_id = interfaces[i].id; bind_address.data.packet.protocol = protocol; - return tcs_packet(socket_ctx, &bind_address); + if (interfaces != stack_buf) + free(interfaces); + return tcs_socket_packet(socket_ctx, &bind_address, type); } } + if (interfaces != stack_buf) + free(interfaces); return TCS_ERROR_INVALID_ARGUMENT; } +// tcs_close() is defined in OS specific files + // ######## Socket Operations ######## // tcs_bind() is defined in OS specific files diff --git a/src/tinycsocket_internal.h b/src/tinycsocket_internal.h index a2a6045..c192ffd 100644 --- a/src/tinycsocket_internal.h +++ b/src/tinycsocket_internal.h @@ -23,7 +23,7 @@ #ifndef TINYCSOCKET_INTERNAL_H_ #define TINYCSOCKET_INTERNAL_H_ -static const char* const TCS_VERSION_TXT = "v0.3.69"; +static const char* const TCS_VERSION_TXT = "v0.3.70"; static const char* const TCS_LICENSE_TXT = "Copyright 2018 Markus Lindelöw\n" "\n" @@ -57,27 +57,14 @@ static const char* const TCS_LICENSE_TXT = * * Socket Creation: * - TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, int protocol); -* - TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); +* - TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address, int timeout_ms); +* - TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address, int timeout_ms); +* - TcsResult tcs_socket_udp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address); +* - TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); +* - TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type); +* - TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type); * - TcsResult tcs_close(TcsSocket* socket_ctx); * -* High-level Socket Creation: -* - TcsResult tcs_tcp_server(TcsSocket* socket_ctx, const struct TcsAddress* local_address); -* - TcsResult tcs_tcp_server_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); -* - TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms); -* - TcsResult tcs_tcp_client_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port, int timeout_ms); -* - TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address); -* - TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); -* - TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address); -* - TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port); -* - TcsResult tcs_udp_peer(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address); -* - TcsResult tcs_udp_peer_str(TcsSocket* socket_ctx, const char* local_address, uint16_t local_port, const char* remote_address, uint16_t remote_port); -* -* High-level Raw L2-Packet Sockets (Experimental): -* - TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); -* - TcsResult tcs_raw_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); -* - TcsResult tcs_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); -* - TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); -* * Socket Operations: * - TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address); * - TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); @@ -95,7 +82,6 @@ static const char* const TCS_LICENSE_TXT = * - TcsResult tcs_receive_from(TcsSocket socket_ctx, uint8_t* buffer, size_t buffer_size, uint32_t flags, struct TcsAddress* source_address, size_t* bytes_received); * - TcsResult tcs_receive_line(TcsSocket socket_ctx, uint8_t* buffer, size_t buffer_size, size_t* bytes_received, uint8_t delimiter); * - TcsResult tcs_receive_netstring(TcsSocket socket_ctx, uint8_t* buffer, size_t buffer_size, size_t* bytes_received); - * * Socket Pooling: * - TcsResult tcs_pool_create(struct TcsPool** pool); @@ -139,7 +125,6 @@ static const char* const TCS_LICENSE_TXT = * - TcsResult tcs_opt_multicast_interface_set(TcsSocket socket_ctx, const struct TcsAddress* local_address); * - TcsResult tcs_opt_multicast_loop_set(TcsSocket socket_ctx, bool do_loopback); * - TcsResult tcs_opt_multicast_loop_get(TcsSocket socket_ctx, bool* is_loopback); - * * Address and Interface Utilities: * - TcsResult tcs_interface_list(struct TcsInterface interfaces[], size_t capacity, size_t* out_count); @@ -338,17 +323,6 @@ extern const uint16_t TCS_PROTOCOL_IP_UDP; /**< Use UDP protocol (use with TCS_S // Ethernet protocols (host byte order) static const uint16_t TCS_ETH_P_ALL = 0x0003; /**< Receive all protocols. Use with TCS_AF_PACKET for capture. */ -// Simple socket creation -typedef enum -{ - TCS_PRESET_TCP_IP4, - TCS_PRESET_UDP_IP4, - TCS_PRESET_TCP_IP6, - TCS_PRESET_UDP_IP6, - TCS_PRESET_RAW, // Layer 2 raw, CAP_NET_RAW permission may be needed - TCS_PRESET_PACKET, // Layer 2 dgram, CAP_NET_RAW permission may be needed -} TcsPreset; - // Flags extern const uint32_t TCS_AI_PASSIVE; /**< Use this flag for pure listening sockets */ @@ -501,11 +475,10 @@ TcsResult tcs_lib_free(void); * @brief Create a new socket (BSD-style) * * This is a thin wrapper around the native socket function. - * You may want to use ::tcs_socket_preset() instead, or one of the helper functions to create and setup a socket directly: - * - ::tcs_tcp_server_str() - * - ::tcs_tcp_client_str() - * - ::tcs_udp_receiver_str() - * - ::tcs_udp_sender_str() + * You may want to use one of the helper functions to create and setup a socket directly: + * - ::tcs_socket_tcp_str() + * - ::tcs_socket_udp_str() + * - ::tcs_socket_packet_str() * * Call ::tcs_close() to stop communication and free all resources for the socket. * @@ -545,11 +518,9 @@ TcsResult tcs_lib_free(void); * @retval #TCS_ERROR_NOT_IMPLEMENTED if you have provided an address family that is not supported on this platform. * @retval #TCS_ERROR_PERMISSION_DENIED if you do not have permission to create the socket. E.g. raw sockets often require elevated permissions. * - * @see tcs_socket_preset() - * @see tcs_tcp_server_str() - * @see tcs_tcp_client_str() - * @see tcs_udp_receiver_str() - * @see tcs_udp_sender_str() + * @see tcs_socket_tcp_str() + * @see tcs_socket_udp_str() + * @see tcs_socket_packet_str() * @see tcs_close() * @see tcs_lib_init() * @see tcs_lib_free() @@ -557,745 +528,307 @@ TcsResult tcs_lib_free(void); TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, int protocol); /** - * @brief Creates a new socket with simplified options. - * - * This is a simple wrapper around tcs_socket() to make it easier to create common socket types. - * Consider using one of the helper functions instead to create and setup a socket directly: - * - ::tcs_tcp_server_str() - * - ::tcs_tcp_client_str() - * - ::tcs_udp_receiver_str() - * - ::tcs_udp_sender_str() - * - * @code - * #include "tinycsocket.h" - * - * int main() - * { - * TcsResult tcs_init_res = tcs_lib_init(); - * if (tcs_init_res != TCS_SUCCESS) - * return -1; // Failed to initialize tinycsocket - * - * TcsSocket my_socket = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID. - * TcsResult tcs_socket_res = tcs_socket_preset(&my_socket, TCS_PRESET_TCP_IP4); - * if (tcs_socket_res != TCS_SUCCESS) - * { - * tcs_lib_free(); - * return -2; // Failed to create socket - * } - * - * // Do stuff with my_socket here. See examples in the documentation. - * - * tcs_close(&my_socket); // Safe to call even if my_socket is TCS_SOCKET_INVALID - * tcs_lib_free(); - * } - * @endcode - * - * @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. - * @param[in] socket_type Socket family / transport combination. See ::TcsPreset for supported values. - * - * @return #TCS_SUCCESS if successful, otherwise the error code. - * - * @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a socket value that is not #TCS_SOCKET_INVALID. - * - * @see tcs_socket() - * @see tcs_tcp_server_str() - * @see tcs_tcp_client_str() - * @see tcs_udp_receiver_str() - * @see tcs_udp_sender_str() - * @see tcs_close() - * @see tcs_lib_init() - * @see tcs_lib_free() - */ -TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); - -/** -* @brief Closes the socket, stop communication and free all resources for the socket. +* @brief Create a TCP socket, optionally bind to a local address and/or connect to a remote address. * -* This will free all resources associated with the socket and set the socket value to #TCS_SOCKET_INVALID. -* -* @param[in,out] socket_ctx is a pointer to your socket context you have previously created with ::tcs_socket() or one of the helper functions. Will be set to #TCS_SOCKET_INVALID on success. -* -* @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a NULL pointer or a socket that is already #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_SOCKET_CLOSED if the socket is already closed. -* -* @see tcs_socket() -* @see tcs_socket_preset() -* @see tcs_tcp_server_str() -* @see tcs_tcp_client_str() -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender_str() -* @see tcs_lib_init() -* @see tcs_lib_free() -*/ -TcsResult tcs_close(TcsSocket* socket_ctx); - -// ######## High-level Socket Creation ######## - -/** -* @brief Setup a socket to listen for incoming tcp connections given an address struct. +* Creates an IPv4 or IPv6 TCP socket based on the address family of the provided address(es). +* If @p local_address is not NULL, SO_REUSEADDR is set and the socket is bound to it. +* If @p remote_address is not NULL, the socket connects to it. +* At least one of @p local_address or @p remote_address must be non-NULL. +* If both are provided, they must have the same address family. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress local_address = TCS_ADDRESS_NONE; -* local_address.family = TCS_AF_IP4; -* local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; // Bind to all IPv4 interfaces -* local_address.data.ip4.port = 1212; -* -* TcsSocket server_socket = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID. -* TcsResult tcs_server_res = tcs_tcp_server(&server_socket, &local_address); -* if (tcs_server_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create server socket -* } +* tcs_lib_init(); * -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult accept_result = tcs_accept(&server_socket, &client_socket, NULL); // Accept incoming connections -* if (accept_result != TCS_SUCCESS) -* { -* tcs_close(&server_socket); -* tcs_lib_free(); -* return -3; // Failed to accept incoming connection -* } -* -* // Do stuff with client_socket here. See examples in the examples folder. -* -* tcs_shutdown(&client_socket, TCS_SD_BOTH); // Shutdown the accepted socket -* tcs_close(&client_socket); // Close the accepted socket -* tcs_close(&server_socket); // Safe to call even if server_socket is TCS_SOCKET_INVALID -* tcs_lib_free(); -* } -* @endcode -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address -* -* @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a socket value that is not #TCS_SOCKET_INVALID. -* -* @see tcs_socket_preset() - */ -TcsResult tcs_tcp_server(TcsSocket* socket_ctx, const struct TcsAddress* local_address); - -/** -* @brief Setup a socket to listen for incoming tcp connections. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket +* struct TcsAddress local = TCS_ADDRESS_NONE; +* local.family = TCS_AF_IP4; +* local.data.ip4.address = TCS_ADDRESS_ANY_IP4; +* local.data.ip4.port = 8080; * -* TcsSocket server_socket = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID. -* TcsResult tcs_server_res = tcs_tcp_server_str(&server_socket, "0.0.0.0", 1212); -* if (tcs_server_res != TCS_SUCCESS) +* TcsSocket server = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_tcp(&server, &local, NULL, 0); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create server socket -* } -* -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult accept_result = tcs_accept(&server_socket, &client_socket, NULL); // Accept incoming connections -* if (accept_result != TCS_SUCCESS) -* { -* tcs_close(&server_socket); -* tcs_lib_free(); -* return -3; // Failed to accept incoming connection +* return -1; * } * -* // Do stuff with server_socket here. See examples in the documentation. +* // Socket is now bound, call tcs_listen() and tcs_accept() to accept connections * -* tcs_shutdown(&client_socket, TCS_SD_BOTH); // Shutdown the accepted socket -* tcs_close(&client_socket); // Close the accepted socket -* tcs_close(&server_socket); // Safe to call even if server_socket is TCS_SOCKET_INVALID +* tcs_close(&server); * tcs_lib_free(); * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address to bind to. Use NULL or "0.0.0.0" to bind to all interfaces. Port number in string will result in invalid argument error. -* @param[in] port is the local port to bind to. 0 will result in a random free port being assigned. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address to bind to, or NULL to skip binding. +* @param[in] remote_address address to connect to, or NULL to skip connecting. +* @param[in] timeout_ms maximum time in milliseconds to wait for connection, or #TCS_WAIT_INF for OS default timeout. Ignored if @p remote_address is NULL. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a socket value that is not #TCS_SOCKET_INVALID. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, if both addresses are NULL, or if both are provided with different address families. +* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. +* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out. * -* @see tcs_socket() -* @see tcs_socket_preset() -* @see tcs_tcp_client_str() +* @see tcs_connect() +* @see tcs_listen() * @see tcs_close() -* @see tcs_lib_init() -* @see tcs_lib_free() */ -TcsResult tcs_tcp_server_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address, + int timeout_ms); /** -* @brief Setup a socket to connect to a remote TCP server using a specific address structure. +* @brief Create a TCP socket from string addresses, optionally bind and/or connect. * -* This will create a TCP client socket and attempt to connect to the specified remote address. -* The function blocks until connected or until the specified timeout elapses. -* Use tcs_tcp_client_str() if you want to specify the address as a hostname and port instead. +* Parses the address strings with ::tcs_address_parse() and delegates to ::tcs_socket_tcp(). +* Addresses must include a port, e.g. "127.0.0.1:8080" or "[::1]:8080". +* At least one of @p local_address or @p remote_address must be non-NULL. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; -* -* struct TcsAddress remote_addr = {0}; -* remote_addr.family = TCS_AF_IP4; -* remote_addr.data.ip4.address = 0x7F000001; // 127.0.0.1 loopback -* remote_addr.data.ip4.port = 8080; +* tcs_lib_init(); * -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult connect_res = tcs_tcp_client(&client_socket, &remote_addr, 1000); // 1000 milliseconds timeout -* if (connect_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_tcp_str(&socket, NULL, "127.0.0.1:8080", 5000); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; +* return -1; * } * -* // Use the connected socket -* uint8_t buffer[] = "Hello, server!"; -* size_t bytes_sent = 0; -* tcs_send(client_socket, buffer, sizeof(buffer)-1, TCS_MSG_SENDALL, &bytes_sent); +* // Socket is now connected and ready for communication * -* tcs_close(&client_socket); +* tcs_close(&socket); * tcs_lib_free(); * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use -* @param[in] remote_address is the remote address structure to connect to -* @param[in] timeout_ms is the maximum time in milliseconds to wait until connected, use #TCS_WAIT_INF to wait indefinitely +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address string to bind to, or NULL to skip binding. +* @param[in] remote_address address string to connect to, or NULL to skip connecting. +* @param[in] timeout_ms maximum time in milliseconds to wait for connection, or #TCS_WAIT_INF for OS default timeout. Ignored if @p remote_address is NULL. * -* @return #TCS_SUCCESS if successful, otherwise the error code -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID -* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection -* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the address could not be resolved +* @return #TCS_SUCCESS if successful, otherwise the error code. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if both addresses are NULL. +* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. +* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out. * -* @see tcs_tcp_client_str() +* @see tcs_socket_tcp() * @see tcs_close() */ -TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms); +TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, + const char* local_address, + const char* remote_address, + int timeout_ms); /** -* @brief Setup a socket and connect to a remote TCP server. +* @brief Create a UDP socket, optionally bind to a local address and/or connect to a remote address. * -* This will create a TCP client socket and attempt to connect to the specified remote address. -* The function blocks until connected or until the specified timeout elapses. -* Use tcs_tcp_client() if you want to specify the address as a struct TcsAddress instead. +* Creates an IPv4 or IPv6 UDP socket based on the address family of the provided address(es). +* If @p local_address is not NULL, SO_REUSEADDR is set and the socket is bound to it. +* If @p remote_address is not NULL and is a unicast address, the socket is connected to it, +* allowing use of ::tcs_send() instead of ::tcs_send_to(). +* If @p remote_address is a multicast address, the socket joins the multicast group. +* When only @p remote_address is given (no @p local_address), the socket is also connected, +* allowing use of ::tcs_send(). When both are given, the socket is not connected to avoid +* filtering out multicast traffic — use ::tcs_send_to() instead. +* At least one of @p local_address or @p remote_address must be non-NULL. +* If both are provided, they must have the same address family. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; +* tcs_lib_init(); * -* TcsSocket client_socket = TCS_SOCKET_INVALID; -* TcsResult connect_res = tcs_tcp_client_str(&client_socket, "127.0.0.1", 8080, 1000); // 1000 milliseconds timeout -* if (connect_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; -* } -* -* // Use the connected socket -* uint8_t buffer[] = "Hello, server!"; -* size_t bytes_sent = 0; -* tcs_send(client_socket, buffer, sizeof(buffer)-1, TCS_MSG_SENDALL, &bytes_sent); +* struct TcsAddress local = TCS_ADDRESS_NONE; +* local.family = TCS_AF_IP4; +* local.data.ip4.address = TCS_ADDRESS_ANY_IP4; +* local.data.ip4.port = 8080; * -* tcs_close(&client_socket); -* tcs_lib_free(); -* return 0; -* } -* @endcode -* -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use -* @param[in] remote_address is the remote address to connect to. Hostname or IP address string. -* @param[in] port is the remote port to connect to -* @param[in] timeout_ms is the maximum time in milliseconds to wait until connected, use #TCS_WAIT_INF to wait indefinitely -* -* @return #TCS_SUCCESS if successful, otherwise the error code -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID -* @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection -* @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the address could not be resolved -* -* @see tcs_tcp_client() -* @see tcs_close() -*/ -TcsResult tcs_tcp_client_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port, int timeout_ms); - -/** -* @brief Creates a UDP socket bound to a local address structure for receiving datagrams. -* -* This function creates a UDP socket and binds it to the specified local address structure, -* allowing it to receive incoming UDP datagrams sent to that address/port combination. -* Use tcs_udp_receiver_str() if you want to specify the address as a hostname and port instead. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress local_address = TCS_ADDRESS_NONE; -* local_address.family = TCS_AF_IP4; -* local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; // Bind to all interfaces -* local_address.data.ip4.port = 8888; -* -* TcsSocket receiver = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_receiver(&receiver, &local_address); -* if (udp_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_udp(&socket, &local, NULL); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP receiver +* return -1; * } * -* // Receive data -* uint8_t buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress sender_address; -* -* TcsResult recv_res = tcs_receive_from(receiver, buffer, sizeof(buffer), -* TCS_FLAG_NONE, &sender_address, &bytes_received); -* if (recv_res == TCS_SUCCESS && bytes_received > 0) -* { -* // Process received data... -* } +* // Socket is now bound and ready to receive with tcs_receive_from() * -* tcs_close(&receiver); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address structure to bind to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address to bind to, or NULL to skip binding. +* @param[in] remote_address address to connect to, or NULL to skip connecting. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, if both addresses are NULL, or if both are provided with different address families. * -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender() -* @see tcs_udp_peer() -* @see tcs_receive_from() +* @see tcs_socket_udp_str() * @see tcs_close() */ -TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address); +TcsResult tcs_socket_udp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address); /** -* @brief Creates a UDP socket bound to a local address for receiving datagrams. +* @brief Create a UDP socket from string addresses, optionally bind and/or connect. * -* This function creates a UDP socket and binds it to the specified local address and port, -* allowing it to receive incoming UDP datagrams sent to that address/port combination. -* Use tcs_udp_receiver() if you want to specify the address as a TcsAddress structure. +* Parses the address strings with ::tcs_address_resolve() and delegates to ::tcs_socket_udp(). +* Addresses must include a port, e.g. "127.0.0.1:8080" or "[::1]:8080". +* At least one of @p local_address or @p remote_address must be non-NULL. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket +* tcs_lib_init(); * -* TcsSocket receiver = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_receiver_str(&receiver, "0.0.0.0", 8888); -* if (udp_res != TCS_SUCCESS) +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_udp_str(&socket, "0.0.0.0:8080", NULL); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP receiver -* } -* -* // Receive data -* uint8_t buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress remote_address; -* -* TcsResult recv_res = tcs_receive_from(receiver, buffer, sizeof(buffer), TCS_FLAG_NONE, &remote_address, &bytes_received); -* if (recv_res == TCS_SUCCESS) -* { -* // Process received data... -* } -* -* tcs_close(&receiver); -* tcs_lib_free(); -* return 0; -* } -* @endcode -* -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address to bind to. Use NULL or "0.0.0.0" to bind to all interfaces. -* @param[in] port is the local port to bind to. 0 will result in a random free port being assigned. -* -* @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the local address could not be resolved. -* -* @see tcs_udp_receiver() -* @see tcs_udp_sender_str() -* @see tcs_udp_peer_str() -* @see tcs_receive_from() -* @see tcs_close() -*/ -TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port); - -/** -* @brief Creates a UDP socket configured to send datagrams to a specific remote address structure. -* -* This function creates a UDP socket that's pre-configured to send datagrams to the specified -* remote address structure. The socket is not bound to a specific local address, so the OS -* will automatically assign a local address and port when sending data. -* Use tcs_udp_sender_str() if you want to specify the address as a hostname and port instead. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress remote_address = TCS_ADDRESS_NONE; -* remote_address.family = TCS_AF_IP4; -* remote_address.data.ip4.address = 0x7F000001; // 127.0.0.1 loopback -* remote_address.data.ip4.port = 8888; -* -* TcsSocket sender = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_sender(&sender, &remote_address); -* if (udp_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP sender +* return -1; * } * -* // Send data -* uint8_t buffer[] = "Hello, UDP receiver!"; -* size_t bytes_sent = 0; -* -* TcsResult send_res = tcs_send(sender, buffer, sizeof(buffer)-1, TCS_FLAG_NONE, &bytes_sent); -* if (send_res == TCS_SUCCESS) -* { -* // Data sent successfully -* } +* // Socket is now bound and ready to receive with tcs_receive_from() * -* tcs_close(&sender); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] remote_address is the remote address structure to send to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] local_address address string to bind to, or NULL to skip binding. +* @param[in] remote_address address string to connect to, or NULL to skip connecting. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if both addresses are NULL. * -* @see tcs_udp_sender_str() -* @see tcs_udp_receiver() -* @see tcs_udp_peer() -* @see tcs_send() +* @see tcs_socket_udp() * @see tcs_close() */ -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address); +TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); /** -* @brief Creates a UDP socket configured to send datagrams to a specific remote address. +* @brief Create a packet socket bound to a network interface. * -* This function creates a UDP socket that's pre-configured to send datagrams to the specified -* remote address and port. The socket is not bound to a specific local address, so the OS -* will automatically assign a local address and port when sending data. -* Use tcs_udp_sender() if you want to specify the address as a TcsAddress structure. +* Creates an AF_PACKET socket for sending and receiving raw L2 frames. +* The socket is bound to the interface and protocol specified in @p bind_address. +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket +* tcs_lib_init(); * -* TcsSocket sender = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_sender_str(&sender, "127.0.0.1", 8888); -* if (udp_res != TCS_SUCCESS) +* struct TcsAddress bind = TCS_ADDRESS_NONE; +* bind.family = TCS_AF_PACKET; +* bind.data.packet.interface_id = 1; // e.g. lo +* bind.data.packet.protocol = 0x22F0; // e.g. AVTP +* +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_packet(&socket, &bind, TCS_SOCK_DGRAM); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP sender +* return -1; * } * -* // Send data -* uint8_t buffer[] = "Hello, UDP receiver!"; -* size_t bytes_sent = 0; -* -* TcsResult send_res = tcs_send(sender, buffer, sizeof(buffer)-1, TCS_FLAG_NONE, &bytes_sent); -* if (send_res == TCS_SUCCESS) -* { -* // Data sent successfully -* } +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() * -* tcs_close(&sender); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] remote_address is the remote address to send to. Hostname or IP address string. -* @param[in] port is the remote port to send to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] bind_address address with family TCS_AF_PACKET specifying interface_id and protocol. +* @param[in] type socket type, either #TCS_SOCK_RAW for full L2 frames or #TCS_SOCK_DGRAM for frames without the L2 header. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the remote address could not be resolved. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if @p bind_address is NULL or not TCS_AF_PACKET. * -* @see tcs_udp_sender() -* @see tcs_udp_receiver_str() -* @see tcs_udp_peer_str() -* @see tcs_send() +* @see tcs_socket_packet_str() * @see tcs_close() */ -TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port); +TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type); /** -* @brief Creates a UDP socket bound to a local address structure that can send to a specific remote address structure. +* @brief Create a packet socket bound to a named network interface. * -* This function creates a UDP socket that's both bound to a local address structure for receiving -* datagrams and pre-configured to send datagrams to a specified remote address structure. This -* creates a complete bidirectional UDP communication channel in a single call. -* Use tcs_udp_peer_str() if you want to specify the addresses as hostname and port pairs instead. +* Looks up the interface by name using ::tcs_interface_list() and delegates to ::tcs_socket_packet(). +* On failure, *socket_ctx is always set back to #TCS_SOCKET_INVALID. * * @code * #include "tinycsocket.h" * int main() * { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* struct TcsAddress local_address = TCS_ADDRESS_NONE; -* local_address.family = TCS_AF_IP4; -* local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; // Bind to all interfaces -* local_address.data.ip4.port = 8888; -* -* struct TcsAddress remote_address = TCS_ADDRESS_NONE; -* remote_address.family = TCS_AF_IP4; -* remote_address.data.ip4.address = 0x7F000001; // 127.0.0.1 loopback -* remote_address.data.ip4.port = 9999; -* -* TcsSocket peer = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_peer(&peer, &local_address, &remote_address); -* if (udp_res != TCS_SUCCESS) +* tcs_lib_init(); +* +* TcsSocket socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_packet_str(&socket, "eth0", 0x22F0, TCS_SOCK_DGRAM); +* if (res != TCS_SUCCESS) * { * tcs_lib_free(); -* return -2; // Failed to create UDP peer +* return -1; * } * -* // Send data - goes to the pre-configured remote address -* uint8_t send_buffer[] = "Hello, remote peer!"; -* size_t bytes_sent = 0; -* tcs_send(peer, send_buffer, sizeof(send_buffer)-1, TCS_FLAG_NONE, &bytes_sent); +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() * -* // Receive data from any sender -* uint8_t recv_buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress sender_address; -* tcs_receive_from(peer, recv_buffer, sizeof(recv_buffer), TCS_FLAG_NONE, -* &sender_address, &bytes_received); -* -* tcs_close(&peer); +* tcs_close(&socket); * tcs_lib_free(); -* return 0; * } * @endcode * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address structure to bind to. -* @param[in] remote_address is the remote address structure to send to. +* @param[out] socket_ctx pointer to socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. +* @param[in] interface_name name of the network interface to bind to. +* @param[in] protocol EtherType protocol in host byte order, e.g. 0x22F0 for AVTP. +* @param[in] type socket type, either #TCS_SOCK_RAW for full L2 frames or #TCS_SOCK_DGRAM for frames without the L2 header. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. +* @retval #TCS_ERROR_INVALID_ARGUMENT if @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if @p interface_name is NULL or not found. * -* @see tcs_udp_peer_str() -* @see tcs_udp_receiver() -* @see tcs_udp_sender() -* @see tcs_send() -* @see tcs_receive_from() +* @see tcs_socket_packet() * @see tcs_close() */ -TcsResult tcs_udp_peer(TcsSocket* socket_ctx, - const struct TcsAddress* local_address, - const struct TcsAddress* remote_address); +TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type); /** -* @brief Creates a UDP socket bound to a local address that can send to a specific remote address. -* -* This function creates a UDP socket that's both bound to a local address/port for receiving -* datagrams and pre-configured to send datagrams to a specified remote address/port. This -* creates a complete bidirectional UDP communication channel in a single call. -* Use tcs_udp_peer() if you want to specify the addresses as TcsAddress structures. -* -* @code -* #include "tinycsocket.h" -* int main() -* { -* TcsResult tcs_init_res = tcs_lib_init(); -* if (tcs_init_res != TCS_SUCCESS) -* return -1; // Failed to initialize tinycsocket -* -* TcsSocket peer = TCS_SOCKET_INVALID; // Always initialize TcsSocket to TCS_SOCKET_INVALID -* TcsResult udp_res = tcs_udp_peer_str(&peer, "0.0.0.0", 8888, "192.168.1.100", 9999); -* if (udp_res != TCS_SUCCESS) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP peer -* } -* -* // Send data - goes to the pre-configured remote address -* uint8_t send_buffer[] = "Hello, remote peer!"; -* size_t bytes_sent = 0; -* tcs_send(peer, send_buffer, sizeof(send_buffer)-1, TCS_FLAG_NONE, &bytes_sent); -* -* // Receive data from any sender -* uint8_t recv_buffer[2048]; -* size_t bytes_received = 0; -* struct TcsAddress sender_address; -* tcs_receive_from(peer, recv_buffer, sizeof(recv_buffer), TCS_FLAG_NONE, -* &sender_address, &bytes_received); +* @brief Closes the socket, stop communication and free all resources for the socket. * -* tcs_close(&peer); -* tcs_lib_free(); -* return 0; -* } -* @endcode +* This will free all resources associated with the socket and set the socket value to #TCS_SOCKET_INVALID. * -* @param[out] socket_ctx is a pointer to your socket context to be created, which must have been initialized to #TCS_SOCKET_INVALID before use. -* @param[in] local_address is the local address to bind to. Use NULL or "0.0.0.0" to bind to all interfaces. -* @param[in] local_port is the local port to bind to. 0 will result in a random free port being assigned. -* @param[in] remote_address is the remote address to send to. Hostname or IP address string. -* @param[in] remote_port is the remote port to send to. +* @param[in,out] socket_ctx is a pointer to your socket context you have previously created with ::tcs_socket() or one of the helper functions. Will be set to #TCS_SOCKET_INVALID on success. * * @return #TCS_SUCCESS if successful, otherwise the error code. -* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument, such as a socket value that is not #TCS_SOCKET_INVALID. -* @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. -* @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the local or remote address could not be resolved. +* @retval #TCS_ERROR_INVALID_ARGUMENT if you have provided an invalid argument. Such as a NULL pointer or a socket that is already #TCS_SOCKET_INVALID. +* @retval #TCS_ERROR_SOCKET_CLOSED if the socket is already closed. * -* @see tcs_udp_peer() -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender_str() -* @see tcs_send() -* @see tcs_receive_from() -* @see tcs_close() +* @see tcs_socket() +* @see tcs_socket_tcp() +* @see tcs_socket_udp() +* @see tcs_socket_packet() +* @see tcs_lib_init() +* @see tcs_lib_free() */ -TcsResult tcs_udp_peer_str(TcsSocket* socket_ctx, - const char* local_address, - uint16_t local_port, - const char* remote_address, - uint16_t remote_port); - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -/** - * @brief Open a raw L2 packet socket bound to a specific interface and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * The socket uses SOCK_RAW, giving full control over the Ethernet frame including VLAN tags. - * Use ::tcs_send_to() / ::tcs_receive_from() for communication. - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle that will be initialized on success. Must be set to - * #TCS_SOCKET_INVALID before the call. - * @param[in] bind_address Pointer to a ::TcsAddress with family TCS_AF_PACKET specifying interface_id and protocol. - * Use #TCS_ETH_P_ALL to capture all protocols. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) provided. - * - * @see tcs_raw_str() - */ -TcsResult tcs_raw(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); - -/** - * @brief Open a raw L2 packet socket by interface name and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * Convenience wrapper around ::tcs_raw() that resolves the interface name - * to an interface ID via ::tcs_interface_list(). - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle that will be initialized on success. Must be set to - * #TCS_SOCKET_INVALID before the call. - * @param[in] interface_name Null-terminated name of the network interface (e.g. "eth0", "wlan0"). - * @param[in] protocol EtherType filter in host byte order. Use #TCS_ETH_P_ALL to capture all protocols. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) or interface not found. - * - * @see tcs_raw() - * @see tcs_interface_list() - */ -TcsResult tcs_raw_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -/** - * @brief Open an L2 packet DGRAM socket bound to a specific interface and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * The socket uses SOCK_DGRAM, where the kernel handles the Ethernet header. - * Use ::tcs_send_to() / ::tcs_receive_from() for communication. - * Note: connect() is not supported on AF_PACKET sockets (see packet(7)). - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle. Must be #TCS_SOCKET_INVALID before call. - * @param[in] bind_address Pointer to a ::TcsAddress with family TCS_AF_PACKET specifying interface_id and protocol. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) provided. - * - * @see tcs_packet_str() - */ -TcsResult tcs_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address); - -/** - * @brief Open an L2 packet DGRAM socket by interface name and protocol filter. - * - * @warning This API is **experimental** and not recommended for production use. - * - * Convenience wrapper around ::tcs_packet() that resolves the interface name - * to an interface ID via ::tcs_interface_list(). - * - * @param[out] socket_ctx Pointer to a #TcsSocket handle. Must be #TCS_SOCKET_INVALID before call. - * @param[in] interface_name Null-terminated name of the network interface (e.g. "eth0"). - * @param[in] protocol EtherType filter in host byte order. - * - * @retval TCS_SUCCESS Socket created successfully. - * @retval TCS_ERROR_PERMISSION_DENIED Operation not permitted (may require CAP_NET_RAW). - * @retval TCS_ERROR_INVALID_ARGUMENT Invalid argument(s) or interface not found. - * - * @see tcs_packet() - * @see tcs_interface_list() - */ -TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol); +TcsResult tcs_close(TcsSocket* socket_ctx); /** * @brief Binds a socket to a local address. @@ -1314,7 +847,7 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint * return -1; * * TcsSocket server_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&server_socket, TCS_PRESET_TCP_IP4); + * TcsResult socket_res = tcs_socket(&server_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * if (socket_res != TCS_SUCCESS) * { * tcs_lib_free(); @@ -1343,7 +876,7 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint * } * @endcode * - * @param socket_ctx The socket to bind. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). + * @param socket_ctx The socket to bind. Must be a valid socket created with tcs_socket(). * @param local_address The local address structure to bind to. Use TCS_ADDRESS_ANY_IP4 for the address field to bind to all interfaces. * * @return #TCS_SUCCESS if successful, otherwise the error code. @@ -1352,8 +885,8 @@ TcsResult tcs_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint * @retval #TCS_ERROR_SYSTEM if the address is already in use or another system error occurred. * * @see tcs_listen() - * @see tcs_tcp_server_str() - * @see tcs_udp_receiver_str() + * @see tcs_socket_tcp() + * @see tcs_socket_udp() * @see tcs_address_socket_local() */ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address); @@ -1377,7 +910,7 @@ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address) * return -1; * * TcsSocket client_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4); + * TcsResult socket_res = tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * if (socket_res != TCS_SUCCESS) * { * tcs_lib_free(); @@ -1408,7 +941,7 @@ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address) * } * @endcode * - * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). + * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket(). * @param address The remote address structure to connect to. * * @return #TCS_SUCCESS if successful, otherwise the error code. @@ -1418,7 +951,7 @@ TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address) * @retval #TCS_ERROR_SYSTEM if another system error occurred. * * @see tcs_connect_str() - * @see tcs_tcp_client() + * @see tcs_socket_tcp() * @see tcs_bind() * @see tcs_listen() */ @@ -1442,7 +975,7 @@ TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); * return -1; * * TcsSocket client_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4); + * TcsResult socket_res = tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * if (socket_res != TCS_SUCCESS) * { * tcs_lib_free(); @@ -1468,7 +1001,7 @@ TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); * } * @endcode * - * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). + * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket(). * @param remote_address The remote hostname or IP address to connect to. * @param port The remote port number to connect to. * @@ -1480,7 +1013,7 @@ TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); * @retval #TCS_ERROR_SYSTEM if another system error occurred. * * @see tcs_connect() - * @see tcs_tcp_client_str() + * @see tcs_socket_tcp_str() * @see tcs_bind() * @see tcs_listen() */ @@ -1507,7 +1040,7 @@ TcsResult tcs_listen(TcsSocket socket_ctx, int backlog); * Example usage: * @code * TcsSocket listen_socket = TCS_SOCKET_INVALID; - * tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4); + * tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); * struct TcsAddress local_address = TCS_ADDRESS_NONE; * local_address.family = TCS_AF_IP4; * local_address.data.ip4.port = 1212; @@ -1698,8 +1231,8 @@ TcsResult tcs_receive_netstring(TcsSocket socket_ctx, uint8_t* buffer, size_t bu * tcs_lib_init(); * TcsSocket socket1 = TCS_SOCKET_INVALID; * TcsSocket socket2 = TCS_SOCKET_INVALID; -* tcs_socket_preset(&socket1, TCS_PRESET_UDP_IP4); -* tcs_socket_preset(&socket2, TCS_PRESET_UDP_IP4); +* tcs_socket(&socket1, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); +* tcs_socket(&socket2, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); * * struct TcsAddress addr1 = TCS_ADDRESS_NONE; * addr1.family = TCS_AF_IP4; diff --git a/src/tinycsocket_posix.c b/src/tinycsocket_posix.c index 317386b..fc20abd 100644 --- a/src/tinycsocket_posix.c +++ b/src/tinycsocket_posix.c @@ -428,7 +428,12 @@ TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, i return errno2retcode(errno); } -// tcs_socket_preset() is defined in tinycsocket_common.c +// tcs_socket_tcp() is defined in tinycsocket_common.c +// tcs_socket_tcp_str() is defined in tinycsocket_common.c +// tcs_socket_udp() is defined in tinycsocket_common.c +// tcs_socket_udp_str() is defined in tinycsocket_common.c +// tcs_socket_packet() is defined in tinycsocket_common.c +// tcs_socket_packet_str() is defined in tinycsocket_common.c TcsResult tcs_close(TcsSocket* socket_ctx) { @@ -446,29 +451,6 @@ TcsResult tcs_close(TcsSocket* socket_ctx) } } -// ######## High-level Socket Creation ######## - -// tcs_tcp_server_str() is defined in tinycsocket_common.c -// tcs_tcp_server() is defined in tinycsocket_common.c -// tcs_tcp_client_str() is defined in tinycsocket_common.c -// tcs_tcp_client() is defined in tinycsocket_common.c -// tcs_udp_receiver_str() is defined in tinycsocket_common.c -// tcs_udp_receiver() is defined in tinycsocket_common.c -// tcs_udp_sender_str() is defined in tinycsocket_common.c -// tcs_udp_sender() is defined in tinycsocket_common.c -// tcs_udp_peer_str() is defined in tinycsocket_common.c -// tcs_udp_peer() is defined in tinycsocket_common.c - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -// tcs_raw() is defined in tinycsocket_common.c -// tcs_raw_str() is defined in tinycsocket_common.c - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -// tcs_packet() is defined in tinycsocket_common.c -// tcs_packet_str() is defined in tinycsocket_common.c - // ######## Socket Operations ######## TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* address) @@ -980,14 +962,15 @@ TcsResult tcs_pool_poll(struct TcsPool* pool, events[filled].user_data = map->values[i]; events[filled].can_read = map->keys[i].revents & POLLIN; events[filled].can_write = map->keys[i].revents & POLLOUT; - if (map->keys[i].revents & POLLERR) + if (map->keys[i].revents & (POLLERR | POLLHUP)) { int so_error = 0; socklen_t so_error_size = sizeof(so_error); + TcsResult fallback = (map->keys[i].revents & POLLERR) ? TCS_ERROR_UNKNOWN : TCS_ERROR_SOCKET_CLOSED; if (getsockopt(map->keys[i].fd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_size) != 0) events[filled].error = errno2retcode(errno); else - events[filled].error = so_error != 0 ? errno2retcode(so_error) : TCS_ERROR_UNKNOWN; + events[filled].error = so_error != 0 ? errno2retcode(so_error) : fallback; } else { diff --git a/src/tinycsocket_win32.c b/src/tinycsocket_win32.c index 2b52151..a3d256c 100644 --- a/src/tinycsocket_win32.c +++ b/src/tinycsocket_win32.c @@ -357,7 +357,12 @@ TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, i } } -// tcs_socket_preset() is defined in tinycsocket_common.c +// tcs_socket_tcp() is defined in tinycsocket_common.c +// tcs_socket_tcp_str() is defined in tinycsocket_common.c +// tcs_socket_udp() is defined in tinycsocket_common.c +// tcs_socket_udp_str() is defined in tinycsocket_common.c +// tcs_socket_packet() is defined in tinycsocket_common.c +// tcs_socket_packet_str() is defined in tinycsocket_common.c TcsResult tcs_close(TcsSocket* socket_ctx) { @@ -376,29 +381,6 @@ TcsResult tcs_close(TcsSocket* socket_ctx) } } -// ######## High-level Socket Creation ######## - -// tcs_tcp_server_str() is defined in tinycsocket_common.c -// tcs_tcp_server() is defined in tinycsocket_common.c -// tcs_tcp_client_str() is defined in tinycsocket_common.c -// tcs_tcp_client() is defined in tinycsocket_common.c -// tcs_udp_receiver_str() is defined in tinycsocket_common.c -// tcs_udp_receiver() is defined in tinycsocket_common.c -// tcs_udp_sender_str() is defined in tinycsocket_common.c -// tcs_udp_sender() is defined in tinycsocket_common.c -// tcs_udp_peer_str() is defined in tinycsocket_common.c -// tcs_udp_peer() is defined in tinycsocket_common.c - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -// tcs_raw() is defined in tinycsocket_common.c -// tcs_raw_str() is defined in tinycsocket_common.c - -// ######## High-level L2-Packet DGRAM Sockets (Experimental) ######## - -// tcs_packet() is defined in tinycsocket_common.c -// tcs_packet_str() is defined in tinycsocket_common.c - // ######## Socket Operations ######## TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* address) diff --git a/tests/tests.cpp b/tests/tests.cpp index 4d7e673..d6bbc57 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -100,7 +100,7 @@ TEST_CASE("Example from README") REQUIRE(tcs_lib_init() == TCS_SUCCESS); TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); REQUIRE(tcs_connect_str(client_socket, "example.com", 80) == TCS_SUCCESS); uint8_t send_buffer[] = "GET / HTTP/1.1\nHost: example.com\n\n"; @@ -139,7 +139,7 @@ TEST_CASE("Create socket") int pre_mem_diff = TCS_MEM_DIFF(); // When - TcsResult sts = tcs_socket_preset(&socket, TCS_PRESET_UDP_IP4); + TcsResult sts = tcs_socket(&socket, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP); // Then CHECK(sts == TCS_SUCCESS); @@ -204,7 +204,7 @@ TEST_CASE("Bind UDP") // Given TcsSocket socket = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket, TCS_PRESET_UDP_IP4) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); // When struct TcsAddress local_address = TCS_ADDRESS_NONE; @@ -228,7 +228,7 @@ TEST_CASE("Non-blocking") // Given TcsSocket socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); // When TcsResult sts = tcs_opt_nonblocking_set(socket, true); @@ -257,8 +257,8 @@ TEST_CASE("Simple TCP Test") TcsSocket accept_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); struct TcsAddress local_address = TCS_ADDRESS_NONE; @@ -297,8 +297,8 @@ TEST_CASE("Simple 2 msg tcs_receive_line") TcsSocket server_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); struct TcsAddress local_address = TCS_ADDRESS_NONE; @@ -357,8 +357,8 @@ TEST_CASE("Partial msg tcs_receive_line") TcsSocket server_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); TcsAddress local_address = TCS_ADDRESS_NONE; @@ -405,8 +405,8 @@ TEST_CASE("sendv") TcsSocket accept_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); TcsAddress local_address = TCS_ADDRESS_NONE; @@ -453,8 +453,8 @@ TEST_CASE("Simple TCP Netstring Test") TcsSocket accept_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); TcsAddress local_address = TCS_ADDRESS_NONE; @@ -493,8 +493,8 @@ TEST_CASE("Netstring with multi-digit length") TcsSocket accept_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); TcsAddress local_address = TCS_ADDRESS_NONE; @@ -534,8 +534,8 @@ TEST_CASE("Netstring with overflowing length is rejected") TcsSocket accept_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); TcsAddress local_address = TCS_ADDRESS_NONE; @@ -572,8 +572,8 @@ TEST_CASE("shutdown") TcsSocket peer1 = TCS_NULLSOCKET; TcsSocket peer2 = TCS_NULLSOCKET; - CHECK(tcs_create(&peer1, TCS_PRESET_UDP_IP4) == TCS_SUCCESS); - CHECK(tcs_create(&peer2, TCS_PRESET_UDP_IP4) == TCS_SUCCESS); + CHECK(tcs_socket(&peer1, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); + CHECK(tcs_socket(&peer2, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); CHECK(tcs_bind(peer1, 5678) == TCS_SUCCESS); CHECK(tcs_bind(peer2, 5679) == TCS_SUCCESS); @@ -602,6 +602,238 @@ TEST_CASE("shutdown") } #endif +TEST_CASE("tcs_socket_tcp bind and connect") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + TcsSocket listen_socket = TCS_SOCKET_INVALID; + TcsSocket accept_socket = TCS_SOCKET_INVALID; + TcsSocket client_socket = TCS_SOCKET_INVALID; + + struct TcsAddress local_address = TCS_ADDRESS_NONE; + local_address.family = TCS_AF_IP4; + local_address.data.ip4.address = TCS_ADDRESS_LOOPBACK_IP4; + local_address.data.ip4.port = 1470; + + CHECK(tcs_socket_tcp(&listen_socket, &local_address, NULL, 0) == TCS_SUCCESS); + REQUIRE(tcs_listen(listen_socket, TCS_BACKLOG_MAX) == TCS_SUCCESS); + + struct TcsAddress remote_address = TCS_ADDRESS_NONE; + remote_address.family = TCS_AF_IP4; + remote_address.data.ip4.address = TCS_ADDRESS_LOOPBACK_IP4; + remote_address.data.ip4.port = 1470; + + CHECK(tcs_socket_tcp(&client_socket, NULL, &remote_address, 5000) == TCS_SUCCESS); + CHECK(tcs_accept(listen_socket, &accept_socket, NULL) == TCS_SUCCESS); + + // When + const uint8_t* send_buffer = (const uint8_t*)"hello"; + uint8_t recv_buffer[8] = {0}; + CHECK(tcs_send(client_socket, send_buffer, 5, TCS_MSG_SENDALL, NULL) == TCS_SUCCESS); + CHECK(tcs_receive(accept_socket, recv_buffer, 5, TCS_MSG_WAITALL, NULL) == TCS_SUCCESS); + + // Then + CHECK(memcmp(recv_buffer, send_buffer, 5) == 0); + + // Clean up + CHECK(tcs_close(&client_socket) == TCS_SUCCESS); + CHECK(tcs_close(&accept_socket) == TCS_SUCCESS); + CHECK(tcs_close(&listen_socket) == TCS_SUCCESS); + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_tcp invalid arguments") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Both NULL + TcsSocket socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_tcp(&socket, NULL, NULL, 0) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Family mismatch + struct TcsAddress local4 = TCS_ADDRESS_NONE; + local4.family = TCS_AF_IP4; + local4.data.ip4.address = TCS_ADDRESS_LOOPBACK_IP4; + local4.data.ip4.port = 1470; + + struct TcsAddress remote6 = TCS_ADDRESS_NONE; + remote6.family = TCS_AF_IP6; + remote6.data.ip6.port = 1470; + + socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_tcp(&socket, &local4, &remote6, 0) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_tcp connect timeout") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Connect to a non-listening port should time out + struct TcsAddress remote_address = TCS_ADDRESS_NONE; + remote_address.family = TCS_AF_IP4; + remote_address.data.ip4.address = TCS_ADDRESS_LOOPBACK_IP4; + remote_address.data.ip4.port = 1471; + + TcsSocket socket = TCS_SOCKET_INVALID; + TcsResult res = tcs_socket_tcp(&socket, NULL, &remote_address, 100); + + if (res != TCS_ERROR_TIMED_OUT && res != TCS_ERROR_CONNECTION_REFUSED) + { + WARN(res == TCS_ERROR_TIMED_OUT); + WARN(res == TCS_ERROR_CONNECTION_REFUSED); + CHECK((res == TCS_ERROR_TIMED_OUT || res == TCS_ERROR_CONNECTION_REFUSED)); + } + CHECK(socket == TCS_SOCKET_INVALID); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_tcp_str bind and connect") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + TcsSocket listen_socket = TCS_SOCKET_INVALID; + TcsSocket accept_socket = TCS_SOCKET_INVALID; + TcsSocket client_socket = TCS_SOCKET_INVALID; + + CHECK(tcs_socket_tcp_str(&listen_socket, "127.0.0.1:1472", NULL, 0) == TCS_SUCCESS); + REQUIRE(tcs_listen(listen_socket, TCS_BACKLOG_MAX) == TCS_SUCCESS); + + CHECK(tcs_socket_tcp_str(&client_socket, NULL, "127.0.0.1:1472", 5000) == TCS_SUCCESS); + CHECK(tcs_accept(listen_socket, &accept_socket, NULL) == TCS_SUCCESS); + + // When + const uint8_t* send_buffer = (const uint8_t*)"world"; + uint8_t recv_buffer[8] = {0}; + CHECK(tcs_send(client_socket, send_buffer, 5, TCS_MSG_SENDALL, NULL) == TCS_SUCCESS); + CHECK(tcs_receive(accept_socket, recv_buffer, 5, TCS_MSG_WAITALL, NULL) == TCS_SUCCESS); + + // Then + CHECK(memcmp(recv_buffer, send_buffer, 5) == 0); + + // Clean up + CHECK(tcs_close(&client_socket) == TCS_SUCCESS); + CHECK(tcs_close(&accept_socket) == TCS_SUCCESS); + CHECK(tcs_close(&listen_socket) == TCS_SUCCESS); + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_udp bind and send_to") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + TcsSocket socket_recv = TCS_SOCKET_INVALID; + TcsSocket socket_send = TCS_SOCKET_INVALID; + + struct TcsAddress local_address = TCS_ADDRESS_NONE; + local_address.family = TCS_AF_IP4; + local_address.data.ip4.address = TCS_ADDRESS_ANY_IP4; + local_address.data.ip4.port = 1473; + + CHECK(tcs_socket_udp(&socket_recv, &local_address, NULL) == TCS_SUCCESS); + CHECK(tcs_opt_receive_timeout_set(socket_recv, 5000) == TCS_SUCCESS); + + struct TcsAddress remote_address = TCS_ADDRESS_NONE; + remote_address.family = TCS_AF_IP4; + remote_address.data.ip4.address = TCS_ADDRESS_LOOPBACK_IP4; + remote_address.data.ip4.port = 1473; + + CHECK(tcs_socket_udp(&socket_send, NULL, &remote_address) == TCS_SUCCESS); + + // When + const uint8_t* send_buffer = (const uint8_t*)"hello"; + uint8_t recv_buffer[8] = {0}; + size_t sent = 0; + size_t received = 0; + CHECK(tcs_send(socket_send, send_buffer, 5, TCS_FLAG_NONE, &sent) == TCS_SUCCESS); + CHECK(tcs_receive(socket_recv, recv_buffer, 5, TCS_FLAG_NONE, &received) == TCS_SUCCESS); + + // Then + CHECK(sent == 5); + CHECK(received == 5); + CHECK(memcmp(recv_buffer, send_buffer, 5) == 0); + + // Clean up + CHECK(tcs_close(&socket_send) == TCS_SUCCESS); + CHECK(tcs_close(&socket_recv) == TCS_SUCCESS); + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_udp invalid arguments") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Both NULL + TcsSocket socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_udp(&socket, NULL, NULL) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Family mismatch + struct TcsAddress local4 = TCS_ADDRESS_NONE; + local4.family = TCS_AF_IP4; + local4.data.ip4.address = TCS_ADDRESS_LOOPBACK_IP4; + local4.data.ip4.port = 1473; + + struct TcsAddress remote6 = TCS_ADDRESS_NONE; + remote6.family = TCS_AF_IP6; + remote6.data.ip6.port = 1473; + + socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_udp(&socket, &local4, &remote6) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_udp_str bind and send_to") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + TcsSocket socket_recv = TCS_SOCKET_INVALID; + TcsSocket socket_send = TCS_SOCKET_INVALID; + + CHECK(tcs_socket_udp_str(&socket_recv, "0.0.0.0:1474", NULL) == TCS_SUCCESS); + CHECK(tcs_opt_receive_timeout_set(socket_recv, 5000) == TCS_SUCCESS); + + CHECK(tcs_socket_udp_str(&socket_send, NULL, "127.0.0.1:1474") == TCS_SUCCESS); + + // When + const uint8_t* send_buffer = (const uint8_t*)"world"; + uint8_t recv_buffer[8] = {0}; + size_t sent = 0; + size_t received = 0; + CHECK(tcs_send(socket_send, send_buffer, 5, TCS_FLAG_NONE, &sent) == TCS_SUCCESS); + CHECK(tcs_receive(socket_recv, recv_buffer, 5, TCS_FLAG_NONE, &received) == TCS_SUCCESS); + + // Then + CHECK(sent == 5); + CHECK(received == 5); + CHECK(memcmp(recv_buffer, send_buffer, 5) == 0); + + // Clean up + CHECK(tcs_close(&socket_send) == TCS_SUCCESS); + CHECK(tcs_close(&socket_recv) == TCS_SUCCESS); + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + TEST_CASE("tcs_pool simple memory check") { // Setup @@ -631,7 +863,7 @@ TEST_CASE("tcs_pool_poll simple write") int pre_mem_diff = TCS_MEM_DIFF(); TcsSocket socket = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket, TCS_PRESET_UDP_IP4) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); TcsAddress local_address = TCS_ADDRESS_NONE; local_address.family = TCS_AF_IP4; @@ -672,7 +904,7 @@ TEST_CASE("tcs_pool_poll simple read") // Given TcsSocket socket = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket, TCS_PRESET_UDP_IP4) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); int allocation_diff_before = TCS_MEM_DIFF(); struct TcsAddress local_address = TCS_ADDRESS_NONE; local_address.family = TCS_AF_IP4; @@ -729,7 +961,7 @@ TEST_CASE("tcs_pool_poll partial") for (int i = 0; i < SOCKET_COUNT; ++i) { socket[i] = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket[i], TCS_PRESET_UDP_IP4) == TCS_SUCCESS); + CHECK(tcs_socket(&socket[i], TCS_AF_IP4, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); struct TcsAddress local_address = TCS_ADDRESS_NONE; local_address.family = TCS_AF_IP4; @@ -1348,7 +1580,7 @@ TEST_CASE("Simple AVTP talker") REQUIRE(tcs_lib_init() == TCS_SUCCESS); TcsSocket socket = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket, TCS_PRESET_RAW) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_PACKET, TCS_SOCK_RAW, TCS_ETH_P_ALL) == TCS_SUCCESS); TcsAddress address = TCS_ADDRESS_NONE; tcs_address_parse("", &address); @@ -1587,7 +1819,7 @@ TEST_CASE("Create packet socket") TcsSocket socket = TCS_SOCKET_INVALID; // When - CHECK(tcs_socket_preset(&socket, TCS_PRESET_RAW) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_PACKET, TCS_SOCK_RAW, TCS_ETH_P_ALL) == TCS_SUCCESS); // Then CHECK(socket != TCS_SOCKET_INVALID); @@ -1660,7 +1892,7 @@ TEST_CASE("TSN Create talker socket bind") TcsSocket socket = TCS_SOCKET_INVALID; // When - CHECK(tcs_socket_preset(&socket, TCS_PRESET_RAW) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_PACKET, TCS_SOCK_RAW, TCS_ETH_P_ALL) == TCS_SUCCESS); CHECK(tcs_opt_priority_set(socket, 6) == TCS_SUCCESS); // Set priority to 6 (VLAN priority 6) struct TcsInterfaceAddress addr[20]; @@ -1704,7 +1936,7 @@ TEST_CASE("TSN Create listener") TcsSocket socket = TCS_SOCKET_INVALID; // When - CHECK(tcs_socket_preset(&socket, TCS_PRESET_RAW) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_PACKET, TCS_SOCK_RAW, TCS_ETH_P_ALL) == TCS_SUCCESS); struct TcsInterfaceAddress addr[20]; size_t addresses_found = 0; @@ -1738,7 +1970,7 @@ TEST_CASE("Create DGRAM packet socket with preset") TcsSocket socket = TCS_SOCKET_INVALID; // When - CHECK(tcs_socket_preset(&socket, TCS_PRESET_PACKET) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_PACKET, TCS_SOCK_DGRAM, TCS_ETH_P_ALL) == TCS_SUCCESS); // Then CHECK(socket != TCS_SOCKET_INVALID); @@ -1748,7 +1980,7 @@ TEST_CASE("Create DGRAM packet socket with preset") REQUIRE(tcs_lib_free() == TCS_SUCCESS); } -TEST_CASE("tcs_packet bind") +TEST_CASE("tcs_socket_packet bind") { // Setup REQUIRE(tcs_lib_init() == TCS_SUCCESS); @@ -1766,7 +1998,7 @@ TEST_CASE("tcs_packet bind") bind_address.data.packet.protocol = 0x22F0; // When - CHECK(tcs_packet(&socket, &bind_address) == TCS_SUCCESS); + CHECK(tcs_socket_packet(&socket, &bind_address, TCS_SOCK_DGRAM) == TCS_SUCCESS); // Then CHECK(socket != TCS_SOCKET_INVALID); @@ -1776,7 +2008,7 @@ TEST_CASE("tcs_packet bind") REQUIRE(tcs_lib_free() == TCS_SUCCESS); } -TEST_CASE("tcs_packet sendto") +TEST_CASE("tcs_socket_packet sendto") { // Setup REQUIRE(tcs_lib_init() == TCS_SUCCESS); @@ -1793,7 +2025,7 @@ TEST_CASE("tcs_packet sendto") bind_address.data.packet.interface_id = addrs[0].iface.id; bind_address.data.packet.protocol = 0x22F0; - CHECK(tcs_packet(&socket, &bind_address) == TCS_SUCCESS); + CHECK(tcs_socket_packet(&socket, &bind_address, TCS_SOCK_DGRAM) == TCS_SUCCESS); // When - sendto with explicit destination struct TcsAddress dest_address = TCS_ADDRESS_NONE; @@ -1814,7 +2046,7 @@ TEST_CASE("tcs_packet sendto") REQUIRE(tcs_lib_free() == TCS_SUCCESS); } -TEST_CASE("tcs_packet_str bind") +TEST_CASE("tcs_socket_packet_str bind") { // Setup REQUIRE(tcs_lib_init() == TCS_SUCCESS); @@ -1842,7 +2074,7 @@ TEST_CASE("tcs_packet_str bind") } // When - CHECK(tcs_packet_str(&socket, iface_name, 0x22F0) == TCS_SUCCESS); + CHECK(tcs_socket_packet_str(&socket, iface_name, 0x22F0, TCS_SOCK_DGRAM) == TCS_SUCCESS); // Then CHECK(socket != TCS_SOCKET_INVALID); @@ -1853,7 +2085,7 @@ TEST_CASE("tcs_packet_str bind") } #endif -TEST_CASE("tcs_packet invalid arguments") +TEST_CASE("tcs_socket_packet invalid arguments old") { // Setup REQUIRE(tcs_lib_init() == TCS_SUCCESS); @@ -1862,10 +2094,125 @@ TEST_CASE("tcs_packet invalid arguments") TcsSocket socket = TCS_SOCKET_INVALID; // When/Then - NULL address should fail - CHECK(tcs_packet(&socket, NULL) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(tcs_socket_packet(&socket, NULL, TCS_SOCK_DGRAM) == TCS_ERROR_INVALID_ARGUMENT); // When/Then - NULL socket should fail - CHECK(tcs_packet(NULL, NULL) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(tcs_socket_packet(NULL, NULL, TCS_SOCK_DGRAM) == TCS_ERROR_INVALID_ARGUMENT); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +#if defined(__linux__) +TEST_CASE("tcs_socket_packet DGRAM") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + struct TcsAddress bind = TCS_ADDRESS_NONE; + bind.family = TCS_AF_PACKET; + bind.data.packet.interface_id = 1; // lo + bind.data.packet.protocol = 0x22F0; + + TcsSocket socket = TCS_SOCKET_INVALID; + + // When + CHECK(tcs_socket_packet(&socket, &bind, TCS_SOCK_DGRAM) == TCS_SUCCESS); + + // Then + CHECK(socket != TCS_SOCKET_INVALID); + CHECK(tcs_close(&socket) == TCS_SUCCESS); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_packet RAW") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + struct TcsAddress bind = TCS_ADDRESS_NONE; + bind.family = TCS_AF_PACKET; + bind.data.packet.interface_id = 1; // lo + bind.data.packet.protocol = 0x22F0; + + TcsSocket socket = TCS_SOCKET_INVALID; + + // When + CHECK(tcs_socket_packet(&socket, &bind, TCS_SOCK_RAW) == TCS_SUCCESS); + + // Then + CHECK(socket != TCS_SOCKET_INVALID); + CHECK(tcs_close(&socket) == TCS_SUCCESS); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_packet_str DGRAM") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + TcsSocket socket = TCS_SOCKET_INVALID; + + // When + CHECK(tcs_socket_packet_str(&socket, "lo", 0x22F0, TCS_SOCK_DGRAM) == TCS_SUCCESS); + + // Then + CHECK(socket != TCS_SOCKET_INVALID); + CHECK(tcs_close(&socket) == TCS_SUCCESS); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} +#endif + +TEST_CASE("tcs_socket_packet invalid arguments") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // NULL bind_address + TcsSocket socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_packet(&socket, NULL, TCS_SOCK_DGRAM) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Wrong family + struct TcsAddress bad = TCS_ADDRESS_NONE; + bad.family = TCS_AF_IP4; + socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_packet(&socket, &bad, TCS_SOCK_DGRAM) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Invalid type + struct TcsAddress bind = TCS_ADDRESS_NONE; + bind.family = TCS_AF_PACKET; + bind.data.packet.interface_id = 1; + bind.data.packet.protocol = 0x22F0; + socket = TCS_SOCKET_INVALID; + CHECK(tcs_socket_packet(&socket, &bind, 9999) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); + + // Clean up + REQUIRE(tcs_lib_free() == TCS_SUCCESS); +} + +TEST_CASE("tcs_socket_packet_str invalid interface") +{ + // Setup + REQUIRE(tcs_lib_init() == TCS_SUCCESS); + + // Given + TcsSocket socket = TCS_SOCKET_INVALID; + + // When/Then + CHECK(tcs_socket_packet_str(&socket, "nonexistent99", 0x22F0, TCS_SOCK_DGRAM) == TCS_ERROR_INVALID_ARGUMENT); + CHECK(socket == TCS_SOCKET_INVALID); // Clean up REQUIRE(tcs_lib_free() == TCS_SUCCESS); @@ -1878,8 +2225,8 @@ TEST_CASE("tcs_address_socket_local on TCP") TcsSocket listen_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); struct TcsAddress bind_address = TCS_ADDRESS_NONE; @@ -1916,8 +2263,8 @@ TEST_CASE("tcs_address_socket_remote on TCP") TcsSocket listen_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); struct TcsAddress bind_address = TCS_ADDRESS_NONE; @@ -1956,7 +2303,7 @@ TEST_CASE("tcs_address_socket_local and remote with invalid args") CHECK(tcs_address_socket_remote(TCS_SOCKET_INVALID, &addr) == TCS_ERROR_INVALID_ARGUMENT); TcsSocket socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&socket, TCS_PRESET_TCP_IP4) == TCS_SUCCESS); + REQUIRE(tcs_socket(&socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_address_socket_local(socket, NULL) == TCS_ERROR_INVALID_ARGUMENT); CHECK(tcs_address_socket_remote(socket, NULL) == TCS_ERROR_INVALID_ARGUMENT); @@ -1971,7 +2318,7 @@ TEST_CASE("IPv6 socket preset TCP") REQUIRE(tcs_lib_init() == TCS_SUCCESS); TcsSocket socket = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket, TCS_PRESET_TCP_IP6) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_IP6, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(socket != TCS_SOCKET_INVALID); CHECK(tcs_close(&socket) == TCS_SUCCESS); @@ -1983,7 +2330,7 @@ TEST_CASE("IPv6 socket preset UDP") REQUIRE(tcs_lib_init() == TCS_SUCCESS); TcsSocket socket = TCS_SOCKET_INVALID; - CHECK(tcs_socket_preset(&socket, TCS_PRESET_UDP_IP6) == TCS_SUCCESS); + CHECK(tcs_socket(&socket, TCS_AF_IP6, TCS_SOCK_DGRAM, TCS_PROTOCOL_IP_UDP) == TCS_SUCCESS); CHECK(socket != TCS_SOCKET_INVALID); CHECK(tcs_close(&socket) == TCS_SUCCESS); @@ -1998,8 +2345,8 @@ TEST_CASE("IPv6 Simple TCP Test") TcsSocket accept_socket = TCS_SOCKET_INVALID; TcsSocket client_socket = TCS_SOCKET_INVALID; - REQUIRE(tcs_socket_preset(&listen_socket, TCS_PRESET_TCP_IP6) == TCS_SUCCESS); - REQUIRE(tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP6) == TCS_SUCCESS); + REQUIRE(tcs_socket(&listen_socket, TCS_AF_IP6, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); + REQUIRE(tcs_socket(&client_socket, TCS_AF_IP6, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP) == TCS_SUCCESS); CHECK(tcs_opt_reuse_address_set(listen_socket, true) == TCS_SUCCESS); struct TcsAddress local_address = TCS_ADDRESS_NONE;