From 8a9bbf3595415042383c2253c144aaa2a6339406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Lindel=C3=B6w?= Date: Thu, 16 Apr 2026 10:45:33 +0200 Subject: [PATCH 1/6] Add tcs_socket_tcp --- src/tinycsocket_common.c | 150 +++++++++++++++++++++++++++++++++++++ src/tinycsocket_internal.h | 98 ++++++++++++++++++++++++ tests/tests.cpp | 122 ++++++++++++++++++++++++++++++ 3 files changed, 370 insertions(+) diff --git a/src/tinycsocket_common.c b/src/tinycsocket_common.c index 94d0540..6bbc7e6 100644 --- a/src/tinycsocket_common.c +++ b/src/tinycsocket_common.c @@ -89,6 +89,156 @@ TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type) return tcs_socket(socket_ctx, family, type, protocol); } +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; + if (local_address == NULL && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + 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; + + 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; + + if (local_address != NULL) + { + 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 (remote_address != NULL) + { + if (timeout_ms == TCS_WAIT_INF) + { + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } + else + { + 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; + } + } + } + + return TCS_SUCCESS; +} + +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 (local_address == NULL && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + + struct TcsAddress local_addr = TCS_ADDRESS_NONE; + struct TcsAddress remote_addr = TCS_ADDRESS_NONE; + TcsAddressFamily family = TCS_AF_ANY; + + 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_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_socket_tcp(socket_ctx, + local_address != NULL ? &local_addr : NULL, + remote_address != NULL ? &remote_addr : NULL, + timeout_ms); +} + // tcs_close() is defined in OS specific files // ######## High-level Socket Creation ######## diff --git a/src/tinycsocket_internal.h b/src/tinycsocket_internal.h index a2a6045..ac3ee1c 100644 --- a/src/tinycsocket_internal.h +++ b/src/tinycsocket_internal.h @@ -58,6 +58,8 @@ 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_close(TcsSocket* socket_ctx); * * High-level Socket Creation: @@ -608,6 +610,102 @@ TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, i */ TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); +/** +* @brief Create a TCP socket, optionally bind to a local address and/or connect to a remote address. +* +* 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() +* { +* tcs_lib_init(); +* +* 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 = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_tcp(&server, &local, NULL, 0); +* if (res != TCS_SUCCESS) +* { +* tcs_lib_free(); +* return -1; +* } +* +* // Socket is now bound, call tcs_listen() and tcs_accept() to accept connections +* +* tcs_close(&server); +* 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] 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 @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_connect() +* @see tcs_listen() +* @see tcs_close() +*/ +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address, int timeout_ms); + +/** +* @brief Create a TCP socket from string addresses, optionally bind and/or connect. +* +* 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() +* { +* tcs_lib_init(); +* +* 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 -1; +* } +* +* // Socket is now connected and ready for communication +* +* tcs_close(&socket); +* 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] 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 @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_socket_tcp() +* @see tcs_close() +*/ +TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address, int timeout_ms); + /** * @brief Closes the socket, stop communication and free all resources for the socket. * diff --git a/tests/tests.cpp b/tests/tests.cpp index 4d7e673..1fa1197 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -602,6 +602,128 @@ 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); + 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_pool simple memory check") { // Setup From 1bb147c083076e9e9b68bd57073d48e4a192be27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Lindel=C3=B6w?= Date: Thu, 16 Apr 2026 11:27:15 +0200 Subject: [PATCH 2/6] Add tcs_socket_udp --- include/tinycsocket.h | 451 ++++++++++++++++++++++++++++++++++++- src/tinycsocket_common.c | 97 ++++++++ src/tinycsocket_internal.h | 110 ++++++++- tests/tests.cpp | 104 +++++++++ 4 files changed, 758 insertions(+), 4 deletions(-) diff --git a/include/tinycsocket.h b/include/tinycsocket.h index fb3d844..8833c5d 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" @@ -64,6 +64,10 @@ 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_close(TcsSocket* socket_ctx); * * High-level Socket Creation: @@ -614,6 +618,204 @@ TcsResult tcs_socket(TcsSocket* socket_ctx, TcsAddressFamily family, int type, i */ TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); +/** +* @brief Create a TCP socket, optionally bind to a local address and/or connect to a remote address. +* +* 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() +* { +* tcs_lib_init(); +* +* 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 = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_tcp(&server, &local, NULL, 0); +* if (res != TCS_SUCCESS) +* { +* tcs_lib_free(); +* return -1; +* } +* +* // Socket is now bound, call tcs_listen() and tcs_accept() to accept connections +* +* tcs_close(&server); +* 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] 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 @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_connect() +* @see tcs_listen() +* @see tcs_close() +*/ +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address, + int timeout_ms); + +/** +* @brief Create a TCP socket from string addresses, optionally bind and/or connect. +* +* 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() +* { +* tcs_lib_init(); +* +* 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 -1; +* } +* +* // Socket is now connected and ready for communication +* +* tcs_close(&socket); +* 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] 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 @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_socket_tcp() +* @see tcs_close() +*/ +TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, + const char* local_address, + const char* remote_address, + int timeout_ms); + +/** +* @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() +* { +* tcs_lib_init(); +* +* 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 socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_udp(&socket, &local, NULL); +* if (res != TCS_SUCCESS) +* { +* tcs_lib_free(); +* return -1; +* } +* +* // Socket is now bound and ready to receive with tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @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_socket_udp_str() +* @see tcs_close() +*/ +TcsResult tcs_socket_udp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address); + +/** +* @brief Create a UDP socket from string addresses, optionally bind and/or connect. +* +* 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() +* { +* tcs_lib_init(); +* +* 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 -1; +* } +* +* // Socket is now bound and ready to receive with tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if both addresses are NULL. +* +* @see tcs_socket_udp() +* @see tcs_close() +*/ +TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); + /** * @brief Closes the socket, stop communication and free all resources for the socket. * @@ -6570,6 +6772,253 @@ TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type) return tcs_socket(socket_ctx, family, type, protocol); } +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; + if (local_address == NULL && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + 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; + + 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; + + if (local_address != NULL) + { + 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 (remote_address != NULL) + { + if (timeout_ms == TCS_WAIT_INF) + { + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } + else + { + 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; + } + } + } + + return TCS_SUCCESS; +} + +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 (local_address == NULL && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + + struct TcsAddress local_addr = TCS_ADDRESS_NONE; + struct TcsAddress remote_addr = TCS_ADDRESS_NONE; + TcsAddressFamily family = TCS_AF_ANY; + + 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_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_socket_tcp(socket_ctx, + local_address != NULL ? &local_addr : NULL, + remote_address != NULL ? &remote_addr : NULL, + timeout_ms); +} + +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 && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) + return TCS_ERROR_INVALID_ARGUMENT; + + 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 (local_address != NULL) + { + 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 (remote_address != NULL) + { + bool is_multicast = tcs_address_is_multicast(remote_address); + + if (is_multicast) + { + res = tcs_opt_membership_add(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } + + if (!is_multicast || local_address == NULL) + { + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } + } + + return TCS_SUCCESS; +} + +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 && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + + struct TcsAddress local_addr = TCS_ADDRESS_NONE; + struct TcsAddress remote_addr = TCS_ADDRESS_NONE; + TcsAddressFamily family = TCS_AF_ANY; + + 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_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_socket_udp( + socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); +} + // tcs_close() is defined in OS specific files // ######## High-level Socket Creation ######## diff --git a/src/tinycsocket_common.c b/src/tinycsocket_common.c index 6bbc7e6..c1a7532 100644 --- a/src/tinycsocket_common.c +++ b/src/tinycsocket_common.c @@ -239,6 +239,103 @@ TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, timeout_ms); } +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 && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) + return TCS_ERROR_INVALID_ARGUMENT; + + 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 (local_address != NULL) + { + 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 (remote_address != NULL) + { + bool is_multicast = tcs_address_is_multicast(remote_address); + + if (is_multicast) + { + res = tcs_opt_membership_add(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } + + if (!is_multicast || local_address == NULL) + { + res = tcs_connect(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } + } + + return TCS_SUCCESS; +} + +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 && remote_address == NULL) + return TCS_ERROR_INVALID_ARGUMENT; + + struct TcsAddress local_addr = TCS_ADDRESS_NONE; + struct TcsAddress remote_addr = TCS_ADDRESS_NONE; + TcsAddressFamily family = TCS_AF_ANY; + + 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_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_socket_udp( + socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); +} + // tcs_close() is defined in OS specific files // ######## High-level Socket Creation ######## diff --git a/src/tinycsocket_internal.h b/src/tinycsocket_internal.h index ac3ee1c..59b51d2 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" @@ -60,6 +60,8 @@ static const char* const TCS_LICENSE_TXT = * - 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_close(TcsSocket* socket_ctx); * * High-level Socket Creation: @@ -660,7 +662,10 @@ TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type); * @see tcs_listen() * @see tcs_close() */ -TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address, int timeout_ms); +TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address, + int timeout_ms); /** * @brief Create a TCP socket from string addresses, optionally bind and/or connect. @@ -704,7 +709,106 @@ TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_a * @see tcs_socket_tcp() * @see tcs_close() */ -TcsResult tcs_socket_tcp_str(TcsSocket* socket_ctx, const char* local_address, const char* 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 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() +* { +* tcs_lib_init(); +* +* 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 socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_udp(&socket, &local, NULL); +* if (res != TCS_SUCCESS) +* { +* tcs_lib_free(); +* return -1; +* } +* +* // Socket is now bound and ready to receive with tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @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_socket_udp_str() +* @see tcs_close() +*/ +TcsResult tcs_socket_udp(TcsSocket* socket_ctx, + const struct TcsAddress* local_address, + const struct TcsAddress* remote_address); + +/** +* @brief Create a UDP socket from string addresses, optionally bind and/or connect. +* +* 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() +* { +* tcs_lib_init(); +* +* 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 -1; +* } +* +* // Socket is now bound and ready to receive with tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @p socket_ctx is NULL, if *socket_ctx is not #TCS_SOCKET_INVALID, or if both addresses are NULL. +* +* @see tcs_socket_udp() +* @see tcs_close() +*/ +TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); /** * @brief Closes the socket, stop communication and free all resources for the socket. diff --git a/tests/tests.cpp b/tests/tests.cpp index 1fa1197..3900db2 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -724,6 +724,110 @@ TEST_CASE("tcs_socket_tcp_str bind and connect") 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 From 395ad39e5d01f0c87327c47dadfe430f51ce975b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Lindel=C3=B6w?= Date: Thu, 16 Apr 2026 11:49:28 +0200 Subject: [PATCH 3/6] Add tcs_socket_packet --- include/tinycsocket.h | 175 ++++++++++++++++++++++++++++++++++++- src/tinycsocket_common.c | 86 ++++++++++++++++++ src/tinycsocket_internal.h | 89 ++++++++++++++++++- tests/tests.cpp | 113 ++++++++++++++++++++++++ 4 files changed, 459 insertions(+), 4 deletions(-) diff --git a/include/tinycsocket.h b/include/tinycsocket.h index 8833c5d..374e676 100644 --- a/include/tinycsocket.h +++ b/include/tinycsocket.h @@ -68,6 +68,8 @@ static const char* const TCS_LICENSE_TXT = * - 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: @@ -105,7 +107,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); @@ -149,7 +150,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); @@ -816,6 +816,91 @@ TcsResult tcs_socket_udp(TcsSocket* socket_ctx, */ TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); +/** +* @brief Create a packet socket bound to a network interface. +* +* 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() +* { +* 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 socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_packet(&socket, &bind, TCS_SOCK_DGRAM); +* if (res != TCS_SUCCESS) +* { +* tcs_lib_free(); +* return -1; +* } +* +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @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_socket_packet_str() +* @see tcs_close() +*/ +TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type); + +/** +* @brief Create a packet socket bound to a named network interface. +* +* 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() +* { +* 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 -1; +* } +* +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @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_socket_packet() +* @see tcs_close() +*/ +TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type); + /** * @brief Closes the socket, stop communication and free all resources for the socket. * @@ -6714,6 +6799,7 @@ TcsResult tcs_address_socket_family(TcsSocket socket_ctx, TcsAddressFamily* out_ #include #include //sprintf +#include // malloc, free #include // memset // ######## Library Management ######## @@ -7019,6 +7105,91 @@ TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, c socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); } +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; + if (bind_address == NULL) + 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, 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_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 stack_buf[16]; + struct TcsInterface* interfaces = stack_buf; + size_t count = 0; + + TcsResult res = tcs_interface_list(stack_buf, 16, &count); + if (res != TCS_SUCCESS) + return res; + + size_t search_count = count < 16 ? count : 16; + for (size_t i = 0; i < search_count; ++i) + { + 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 = stack_buf[i].id; + bind_address.data.packet.protocol = protocol; + return tcs_socket_packet(socket_ctx, &bind_address, type); + } + } + + if (count > 16) + { + 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; + } + } + + for (size_t i = 0; i < count; ++i) + { + if (strcmp(interfaces[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.protocol = protocol; + 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 // ######## High-level Socket Creation ######## diff --git a/src/tinycsocket_common.c b/src/tinycsocket_common.c index c1a7532..24f2b17 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 ######## @@ -336,6 +337,91 @@ TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, c socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); } +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; + if (bind_address == NULL) + 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, 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_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 stack_buf[16]; + struct TcsInterface* interfaces = stack_buf; + size_t count = 0; + + TcsResult res = tcs_interface_list(stack_buf, 16, &count); + if (res != TCS_SUCCESS) + return res; + + size_t search_count = count < 16 ? count : 16; + for (size_t i = 0; i < search_count; ++i) + { + 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 = stack_buf[i].id; + bind_address.data.packet.protocol = protocol; + return tcs_socket_packet(socket_ctx, &bind_address, type); + } + } + + if (count > 16) + { + 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; + } + } + + for (size_t i = 0; i < count; ++i) + { + if (strcmp(interfaces[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.protocol = protocol; + 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 // ######## High-level Socket Creation ######## diff --git a/src/tinycsocket_internal.h b/src/tinycsocket_internal.h index 59b51d2..b74be5b 100644 --- a/src/tinycsocket_internal.h +++ b/src/tinycsocket_internal.h @@ -62,6 +62,8 @@ static const char* const TCS_LICENSE_TXT = * - 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: @@ -99,7 +101,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); @@ -143,7 +144,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); @@ -810,6 +810,91 @@ TcsResult tcs_socket_udp(TcsSocket* socket_ctx, */ TcsResult tcs_socket_udp_str(TcsSocket* socket_ctx, const char* local_address, const char* remote_address); +/** +* @brief Create a packet socket bound to a network interface. +* +* 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() +* { +* 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 socket = TCS_SOCKET_INVALID; +* TcsResult res = tcs_socket_packet(&socket, &bind, TCS_SOCK_DGRAM); +* if (res != TCS_SUCCESS) +* { +* tcs_lib_free(); +* return -1; +* } +* +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @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_socket_packet_str() +* @see tcs_close() +*/ +TcsResult tcs_socket_packet(TcsSocket* socket_ctx, const struct TcsAddress* bind_address, int type); + +/** +* @brief Create a packet socket bound to a named network interface. +* +* 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() +* { +* 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 -1; +* } +* +* // Socket is now bound and ready for tcs_send_to() / tcs_receive_from() +* +* tcs_close(&socket); +* 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] 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 @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_socket_packet() +* @see tcs_close() +*/ +TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_name, uint16_t protocol, int type); + /** * @brief Closes the socket, stop communication and free all resources for the socket. * diff --git a/tests/tests.cpp b/tests/tests.cpp index 3900db2..6e61186 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -2097,6 +2097,119 @@ TEST_CASE("tcs_packet invalid arguments") REQUIRE(tcs_lib_free() == TCS_SUCCESS); } +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 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 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); +} + +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); +} + TEST_CASE("tcs_address_socket_local on TCP") { REQUIRE(tcs_lib_init() == TCS_SUCCESS); From d1421ad63f85cdf175b6b8698ab2a6333e9b5b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Lindel=C3=B6w?= Date: Thu, 16 Apr 2026 12:36:05 +0200 Subject: [PATCH 4/6] Remove old high level socket functions --- README.md | 2 +- examples/tcp_client.c | 2 +- examples/tcp_server.c | 5 +- examples/udp_client.c | 10 +- examples/udp_server.c | 2 +- include/tinycsocket.h | 1944 ++++++------------------------------ src/tinycsocket_common.c | 548 ---------- src/tinycsocket_internal.h | 800 +-------------- src/tinycsocket_posix.c | 30 +- src/tinycsocket_win32.c | 30 +- tests/tests.cpp | 92 +- 11 files changed, 394 insertions(+), 3071 deletions(-) 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 374e676..46a67b9 100644 --- a/include/tinycsocket.h +++ b/include/tinycsocket.h @@ -63,7 +63,6 @@ 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); @@ -72,24 +71,6 @@ static const char* const TCS_LICENSE_TXT = * - 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); @@ -348,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 */ @@ -511,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. * @@ -555,69 +524,15 @@ 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() */ 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 Create a TCP socket, optionally bind to a local address and/or connect to a remote address. * @@ -913,751 +828,147 @@ TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_nam * @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_socket_tcp() +* @see tcs_socket_udp() +* @see tcs_socket_packet() * @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 -* } -* -* // 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() + * @brief Binds a socket to a local address. + * + * This function associates a socket with a specific local address and port, allowing it to + * receive incoming connections or datagrams on that address. For TCP server sockets, you + * must call this before tcs_listen(). For UDP sockets, binding determines which address + * and port the socket will receive datagrams on. + * + * @code + * #include "tinycsocket.h" + * int main() + * { + * TcsResult tcs_init_res = tcs_lib_init(); + * if (tcs_init_res != TCS_SUCCESS) + * return -1; + * + * TcsSocket server_socket = TCS_SOCKET_INVALID; + * 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(); + * return -2; + * } + * + * 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 = 8080; + * + * TcsResult bind_res = tcs_bind(server_socket, &local_address); + * if (bind_res != TCS_SUCCESS) + * { + * tcs_close(&server_socket); + * tcs_lib_free(); + * return -3; // Failed to bind to address + * } + * + * // For TCP: now call tcs_listen() + * // For UDP: socket is ready to receive datagrams + * + * tcs_close(&server_socket); + * tcs_lib_free(); + * return 0; + * } + * @endcode + * + * @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. + * @retval #TCS_ERROR_INVALID_ARGUMENT if socket_ctx is invalid or local_address is NULL. + * @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. + * @retval #TCS_ERROR_SYSTEM if the address is already in use or another system error occurred. + * + * @see tcs_listen() + * @see tcs_socket_tcp() + * @see tcs_socket_udp() + * @see tcs_address_socket_local() */ -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 -* -* 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 -* } -* -* 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 server_socket here. See examples in the documentation. -* -* 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 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. -* -* @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_socket_preset() -* @see tcs_tcp_client_str() -* @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); - -/** -* @brief Setup a socket to connect to a remote TCP server using a specific address structure. -* -* 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. -* -* @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; -* -* 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) -* { -* 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(); -* } -* @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 -* -* @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_str() -* @see tcs_close() -*/ -TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* remote_address, int timeout_ms); +TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address); /** -* @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. -* -* @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 -* -* @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 Connect a socket to a remote address structure. + * + * This function establishes a connection to the specified remote address structure. + * For TCP sockets, this initiates a three-way handshake. For UDP sockets, this + * associates the socket with the remote address for subsequent send operations. + * The function blocks indefinitely until the connection is established or fails. + * Timeout for this function is set by OS defaults. Use TcsPool or + * ::tcs_opt_nonblocking_set() for non-blocking behavior. + * + * @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 socket_res = tcs_socket(&client_socket, TCS_AF_IP4, TCS_SOCK_STREAM, TCS_PROTOCOL_IP_TCP); + * if (socket_res != TCS_SUCCESS) + * { + * tcs_lib_free(); + * return -2; + * } + * + * 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 = 8080; + * + * TcsResult connect_res = tcs_connect(client_socket, &remote_address); + * if (connect_res != TCS_SUCCESS) + * { + * tcs_close(&client_socket); + * tcs_lib_free(); + * return -3; // Failed to connect + * } + * + * // Socket is now connected and ready for communication + * 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 + * + * @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. + * @retval #TCS_ERROR_INVALID_ARGUMENT if socket_ctx is invalid or address is NULL. + * @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. + * @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out (can take 3+ minutes for unreachable hosts). + * @retval #TCS_ERROR_SYSTEM if another system error occurred. + * + * @see tcs_connect_str() + * @see tcs_socket_tcp() + * @see tcs_bind() + * @see tcs_listen() + */ +TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); /** -* @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) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP receiver -* } -* -* // 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... -* } -* -* 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 structure to bind to. -* -* @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. -* -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender() -* @see tcs_udp_peer() -* @see tcs_receive_from() -* @see tcs_close() -*/ -TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address); - -/** -* @brief Creates a UDP socket bound to a local address for receiving datagrams. -* -* 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. -* -* @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. -* -* 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 -* } -* -* // 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 -* } -* -* tcs_close(&sender); -* 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. -* -* @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_udp_sender_str() -* @see tcs_udp_receiver() -* @see tcs_udp_peer() -* @see tcs_send() -* @see tcs_close() -*/ -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address); - -/** -* @brief Creates a UDP socket configured to send datagrams to a specific remote address. -* -* 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. -* -* @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 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) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP sender -* } -* -* // 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 -* } -* -* tcs_close(&sender); -* 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. -* -* @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. -* -* @see tcs_udp_sender() -* @see tcs_udp_receiver_str() -* @see tcs_udp_peer_str() -* @see tcs_send() -* @see tcs_close() -*/ -TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port); - -/** -* @brief Creates a UDP socket bound to a local address structure that can send to a specific remote address structure. -* -* 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. -* -* @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_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); -* -* tcs_close(&peer); -* 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. -* -* @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. -* -* @see tcs_udp_peer_str() -* @see tcs_udp_receiver() -* @see tcs_udp_sender() -* @see tcs_send() -* @see tcs_receive_from() -* @see tcs_close() -*/ -TcsResult tcs_udp_peer(TcsSocket* socket_ctx, - const struct TcsAddress* local_address, - const struct TcsAddress* remote_address); - -/** -* @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); -* -* tcs_close(&peer); -* 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] 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. -* -* @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. -* -* @see tcs_udp_peer() -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender_str() -* @see tcs_send() -* @see tcs_receive_from() -* @see tcs_close() -*/ -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); - -/** - * @brief Binds a socket to a local address. - * - * This function associates a socket with a specific local address and port, allowing it to - * receive incoming connections or datagrams on that address. For TCP server sockets, you - * must call this before tcs_listen(). For UDP sockets, binding determines which address - * and port the socket will receive datagrams on. - * - * @code - * #include "tinycsocket.h" - * int main() - * { - * TcsResult tcs_init_res = tcs_lib_init(); - * if (tcs_init_res != TCS_SUCCESS) - * return -1; - * - * TcsSocket server_socket = TCS_SOCKET_INVALID; - * TcsResult socket_res = tcs_socket_preset(&server_socket, TCS_PRESET_TCP_IP4); - * if (socket_res != TCS_SUCCESS) - * { - * tcs_lib_free(); - * return -2; - * } - * - * 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 = 8080; - * - * TcsResult bind_res = tcs_bind(server_socket, &local_address); - * if (bind_res != TCS_SUCCESS) - * { - * tcs_close(&server_socket); - * tcs_lib_free(); - * return -3; // Failed to bind to address - * } - * - * // For TCP: now call tcs_listen() - * // For UDP: socket is ready to receive datagrams - * - * tcs_close(&server_socket); - * tcs_lib_free(); - * return 0; - * } - * @endcode - * - * @param socket_ctx The socket to bind. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). - * @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. - * @retval #TCS_ERROR_INVALID_ARGUMENT if socket_ctx is invalid or local_address is NULL. - * @retval #TCS_ERROR_PERMISSION_DENIED if binding to the specified address/port requires elevated privileges. - * @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_address_socket_local() - */ -TcsResult tcs_bind(TcsSocket socket_ctx, const struct TcsAddress* local_address); - -/** - * @brief Connect a socket to a remote address structure. + * @brief Connect a socket to a remote hostname and port. * - * This function establishes a connection to the specified remote address structure. + * This function establishes a connection to the specified remote address and port. * For TCP sockets, this initiates a three-way handshake. For UDP sockets, this * associates the socket with the remote address for subsequent send operations. - * The function blocks indefinitely until the connection is established or fails. * Timeout for this function is set by OS defaults. Use TcsPool or * ::tcs_opt_nonblocking_set() for non-blocking behavior. * @@ -1670,19 +981,14 @@ 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(); * return -2; * } * - * 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 = 8080; - * - * TcsResult connect_res = tcs_connect(client_socket, &remote_address); + * TcsResult connect_res = tcs_connect_str(client_socket, "192.168.1.100", 8080); * if (connect_res != TCS_SUCCESS) * { * tcs_close(&client_socket); @@ -1701,79 +1007,19 @@ 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 address The remote address structure to connect to. + * @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. * * @return #TCS_SUCCESS if successful, otherwise the error code. - * @retval #TCS_ERROR_INVALID_ARGUMENT if socket_ctx is invalid or address is NULL. + * @retval #TCS_ERROR_INVALID_ARGUMENT if socket_ctx is invalid or remote_address is NULL. * @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. - * @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out (can take 3+ minutes for unreachable hosts). - * @retval #TCS_ERROR_SYSTEM if another system error occurred. - * - * @see tcs_connect_str() - * @see tcs_tcp_client() - * @see tcs_bind() - * @see tcs_listen() - */ -TcsResult tcs_connect(TcsSocket socket_ctx, const struct TcsAddress* address); - -/** - * @brief Connect a socket to a remote hostname and port. - * - * This function establishes a connection to the specified remote address and port. - * For TCP sockets, this initiates a three-way handshake. For UDP sockets, this - * associates the socket with the remote address for subsequent send operations. - * Timeout for this function is set by OS defaults. Use TcsPool or - * ::tcs_opt_nonblocking_set() for non-blocking behavior. - * - * @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 socket_res = tcs_socket_preset(&client_socket, TCS_PRESET_TCP_IP4); - * if (socket_res != TCS_SUCCESS) - * { - * tcs_lib_free(); - * return -2; - * } - * - * TcsResult connect_res = tcs_connect_str(client_socket, "192.168.1.100", 8080); - * if (connect_res != TCS_SUCCESS) - * { - * tcs_close(&client_socket); - * tcs_lib_free(); - * return -3; // Failed to connect - * } - * - * // Socket is now connected and ready for communication - * 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 - * - * @param socket_ctx The socket to connect. Must be a valid socket created with tcs_socket() or tcs_socket_preset(). - * @param remote_address The remote hostname or IP address to connect to. - * @param port The remote port number to connect to. - * - * @return #TCS_SUCCESS if successful, otherwise the error code. - * @retval #TCS_ERROR_INVALID_ARGUMENT if socket_ctx is invalid or remote_address is NULL. - * @retval #TCS_ERROR_CONNECTION_REFUSED if the remote server refused the connection. - * @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the hostname could not be resolved. - * @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out. + * @retval #TCS_ERROR_ADDRESS_LOOKUP_FAILED if the hostname could not be resolved. + * @retval #TCS_ERROR_TIMED_OUT if the connection attempt timed out. * @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() */ @@ -1800,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; @@ -1991,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; @@ -3403,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) { @@ -3421,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) @@ -5269,7 +4497,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) { @@ -5288,29 +4521,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) @@ -6811,53 +6021,6 @@ 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) -{ - 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); -} - TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address, @@ -6947,651 +6110,165 @@ TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, 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; - } - } - } - - return TCS_SUCCESS; -} - -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 (local_address == NULL && remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - struct TcsAddress local_addr = TCS_ADDRESS_NONE; - struct TcsAddress remote_addr = TCS_ADDRESS_NONE; - TcsAddressFamily family = TCS_AF_ANY; - - 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_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_socket_tcp(socket_ctx, - local_address != NULL ? &local_addr : NULL, - remote_address != NULL ? &remote_addr : NULL, - timeout_ms); -} - -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 && remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) - return TCS_ERROR_INVALID_ARGUMENT; - - 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 (local_address != NULL) - { - 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 (remote_address != NULL) - { - bool is_multicast = tcs_address_is_multicast(remote_address); - - if (is_multicast) - { - res = tcs_opt_membership_add(*socket_ctx, remote_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - } - - if (!is_multicast || local_address == NULL) - { - res = tcs_connect(*socket_ctx, remote_address); - if (res != TCS_SUCCESS) - { - tcs_close(socket_ctx); - return res; - } - } - } - - return TCS_SUCCESS; -} - -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 && remote_address == NULL) - return TCS_ERROR_INVALID_ARGUMENT; - - struct TcsAddress local_addr = TCS_ADDRESS_NONE; - struct TcsAddress remote_addr = TCS_ADDRESS_NONE; - TcsAddressFamily family = TCS_AF_ANY; - - 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_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_socket_udp( - socket_ctx, local_address != NULL ? &local_addr : NULL, remote_address != NULL ? &remote_addr : NULL); -} - -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; - if (bind_address == NULL) - 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, 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_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 stack_buf[16]; - struct TcsInterface* interfaces = stack_buf; - size_t count = 0; - - TcsResult res = tcs_interface_list(stack_buf, 16, &count); - if (res != TCS_SUCCESS) - return res; - - size_t search_count = count < 16 ? count : 16; - for (size_t i = 0; i < search_count; ++i) - { - 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 = stack_buf[i].id; - bind_address.data.packet.protocol = protocol; - return tcs_socket_packet(socket_ctx, &bind_address, type); - } - } - - if (count > 16) - { - 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; - } - } - - for (size_t i = 0; i < count; ++i) - { - if (strcmp(interfaces[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.protocol = protocol; - 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 - -// ######## 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) - 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); - 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) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return TCS_ERROR_TIMED_OUT; - } - if (res != TCS_SUCCESS || events_populated == 0) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res != TCS_SUCCESS ? res : TCS_ERROR_UNKNOWN; - } - if (event.error != TCS_SUCCESS) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return TCS_ERROR_CONNECTION_REFUSED; - } - } - - 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) -{ - 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; - - 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 (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); -} - -TcsResult tcs_udp_receiver(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_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 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; + } + } } + return TCS_SUCCESS; } -TcsResult tcs_udp_receiver_str(TcsSocket* socket_ctx, const char* local_address, uint16_t port) +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 (local_address == NULL) + if (local_address == NULL && remote_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; + 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 (bind_address.family == TCS_AF_IP4) - { - parsed_port = &bind_address.data.ip4.port; - } - else if (bind_address.family == TCS_AF_IP6) + if (local_address != NULL) { - parsed_port = &bind_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_udp_receiver(socket_ctx, &bind_address); + return tcs_socket_tcp(socket_ctx, + local_address != NULL ? &local_addr : NULL, + remote_address != NULL ? &remote_addr : NULL, + timeout_ms); } -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_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 (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) - { - 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) + if (local_address == NULL && remote_address == NULL) return TCS_ERROR_INVALID_ARGUMENT; - if (remote_address == NULL) + if (local_address != NULL && remote_address != NULL && local_address->family != remote_address->family) return TCS_ERROR_INVALID_ARGUMENT; - struct TcsAddress address = TCS_ADDRESS_NONE; - TcsResult res = tcs_address_parse(remote_address, &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 (address.family != TCS_AF_IP4 && address.family != TCS_AF_IP6) - return TCS_ERROR_INVALID_ARGUMENT; - 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 + 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_sender(socket_ctx, &address); -} + if (remote_address != NULL) + { + bool is_multicast = tcs_address_is_multicast(remote_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; + if (is_multicast) + { + res = tcs_opt_membership_add(*socket_ctx, remote_address); + if (res != TCS_SUCCESS) + { + tcs_close(socket_ctx); + return res; + } + } - 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; + if (!is_multicast || local_address == NULL) + { + 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; @@ -7599,86 +6276,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) { @@ -7686,13 +6342,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 24f2b17..b1b9f3b 100644 --- a/src/tinycsocket_common.c +++ b/src/tinycsocket_common.c @@ -43,53 +43,6 @@ // tcs_socket() is defined in OS specific files -TcsResult tcs_socket_preset(TcsSocket* socket_ctx, TcsPreset socket_type) -{ - 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); -} - TcsResult tcs_socket_tcp(TcsSocket* socket_ctx, const struct TcsAddress* local_address, const struct TcsAddress* remote_address, @@ -424,507 +377,6 @@ TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_nam // 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) - 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); - 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) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return TCS_ERROR_TIMED_OUT; - } - if (res != TCS_SUCCESS || events_populated == 0) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return res != TCS_SUCCESS ? res : TCS_ERROR_UNKNOWN; - } - if (event.error != TCS_SUCCESS) - { - tcs_pool_destroy(&timeout_pool); - tcs_close(socket_ctx); - return TCS_ERROR_CONNECTION_REFUSED; - } - } - - 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) -{ - 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; - - 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 (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); -} - -TcsResult tcs_udp_receiver(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_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) - 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; - 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) - { - 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; - - 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; - - 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 (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) -{ - 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; - - 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; - - 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 (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; - - return tcs_udp_peer(socket_ctx, &local_addr, &remote_addr); -} - -// ######## High-level Raw L2-Packet Sockets (Experimental) ######## - -TcsResult tcs_raw(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_RAW, 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) -{ - 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 - 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) - { - if (strcmp(interfaces[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.protocol = protocol; - return tcs_raw(socket_ctx, &bind_address); - } - } - - 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) - { - tcs_close(socket_ctx); - 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) - { - if (strcmp(interfaces[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.protocol = protocol; - return tcs_packet(socket_ctx, &bind_address); - } - } - - return TCS_ERROR_INVALID_ARGUMENT; -} - // ######## Socket Operations ######## // tcs_bind() is defined in OS specific files diff --git a/src/tinycsocket_internal.h b/src/tinycsocket_internal.h index b74be5b..c192ffd 100644 --- a/src/tinycsocket_internal.h +++ b/src/tinycsocket_internal.h @@ -57,7 +57,6 @@ 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); @@ -66,24 +65,6 @@ static const char* const TCS_LICENSE_TXT = * - 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); @@ -342,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 */ @@ -505,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. * @@ -549,69 +518,15 @@ 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() */ 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 Create a TCP socket, optionally bind to a local address and/or connect to a remote address. * @@ -907,683 +822,14 @@ TcsResult tcs_socket_packet_str(TcsSocket* socket_ctx, const char* interface_nam * @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_socket_tcp() +* @see tcs_socket_udp() +* @see tcs_socket_packet() * @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 -* } -* -* // 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 -* -* 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 -* } -* -* 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 server_socket here. See examples in the documentation. -* -* 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 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. -* -* @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_socket_preset() -* @see tcs_tcp_client_str() -* @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); - -/** -* @brief Setup a socket to connect to a remote TCP server using a specific address structure. -* -* 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. -* -* @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; -* -* 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) -* { -* 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(); -* } -* @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 -* -* @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_str() -* @see tcs_close() -*/ -TcsResult tcs_tcp_client(TcsSocket* socket_ctx, const struct TcsAddress* 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. -* -* @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 -* -* @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) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP receiver -* } -* -* // 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... -* } -* -* 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 structure to bind to. -* -* @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. -* -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender() -* @see tcs_udp_peer() -* @see tcs_receive_from() -* @see tcs_close() -*/ -TcsResult tcs_udp_receiver(TcsSocket* socket_ctx, const struct TcsAddress* local_address); - -/** -* @brief Creates a UDP socket bound to a local address for receiving datagrams. -* -* 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. -* -* @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. -* -* 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 -* } -* -* // 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 -* } -* -* tcs_close(&sender); -* 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. -* -* @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_udp_sender_str() -* @see tcs_udp_receiver() -* @see tcs_udp_peer() -* @see tcs_send() -* @see tcs_close() -*/ -TcsResult tcs_udp_sender(TcsSocket* socket_ctx, const struct TcsAddress* remote_address); - -/** -* @brief Creates a UDP socket configured to send datagrams to a specific remote address. -* -* 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. -* -* @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 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) -* { -* tcs_lib_free(); -* return -2; // Failed to create UDP sender -* } -* -* // 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 -* } -* -* tcs_close(&sender); -* 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. -* -* @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. -* -* @see tcs_udp_sender() -* @see tcs_udp_receiver_str() -* @see tcs_udp_peer_str() -* @see tcs_send() -* @see tcs_close() -*/ -TcsResult tcs_udp_sender_str(TcsSocket* socket_ctx, const char* remote_address, uint16_t port); - -/** -* @brief Creates a UDP socket bound to a local address structure that can send to a specific remote address structure. -* -* 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. -* -* @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_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); -* -* tcs_close(&peer); -* 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. -* -* @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. -* -* @see tcs_udp_peer_str() -* @see tcs_udp_receiver() -* @see tcs_udp_sender() -* @see tcs_send() -* @see tcs_receive_from() -* @see tcs_close() -*/ -TcsResult tcs_udp_peer(TcsSocket* socket_ctx, - const struct TcsAddress* local_address, - const struct TcsAddress* remote_address); - -/** -* @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); -* -* tcs_close(&peer); -* 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] 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. -* -* @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. -* -* @see tcs_udp_peer() -* @see tcs_udp_receiver_str() -* @see tcs_udp_sender_str() -* @see tcs_send() -* @see tcs_receive_from() -* @see tcs_close() -*/ -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); - /** * @brief Binds a socket to a local address. * @@ -1601,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(); @@ -1630,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. @@ -1639,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); @@ -1664,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(); @@ -1695,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. @@ -1705,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() */ @@ -1729,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(); @@ -1755,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. * @@ -1767,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() */ @@ -1794,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; @@ -1985,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..3a3a22c 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) 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 6e61186..61670af 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); @@ -857,7 +857,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; @@ -898,7 +898,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; @@ -955,7 +955,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; @@ -1574,7 +1574,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); @@ -1813,7 +1813,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); @@ -1886,7 +1886,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]; @@ -1930,7 +1930,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; @@ -1964,7 +1964,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); @@ -1974,7 +1974,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); @@ -1992,7 +1992,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); @@ -2002,7 +2002,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); @@ -2019,7 +2019,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; @@ -2040,7 +2040,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); @@ -2068,7 +2068,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); @@ -2079,7 +2079,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); @@ -2088,10 +2088,10 @@ 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); @@ -2217,8 +2217,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; @@ -2255,8 +2255,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; @@ -2295,7 +2295,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); @@ -2310,7 +2310,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); @@ -2322,7 +2322,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); @@ -2337,8 +2337,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; From fe4f4a9b33f4ece0460766d932cfbbacef42da43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Lindel=C3=B6w?= Date: Thu, 16 Apr 2026 12:59:57 +0200 Subject: [PATCH 5/6] Fix af_packet tests --- tests/tests.cpp | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/tests/tests.cpp b/tests/tests.cpp index 61670af..d6bbc57 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -685,7 +685,13 @@ TEST_CASE("tcs_socket_tcp connect timeout") TcsSocket socket = TCS_SOCKET_INVALID; TcsResult res = tcs_socket_tcp(&socket, NULL, &remote_address, 100); - CHECK((res == TCS_ERROR_TIMED_OUT || res == TCS_ERROR_CONNECTION_REFUSED)); + + 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 @@ -2097,6 +2103,7 @@ TEST_CASE("tcs_socket_packet invalid arguments old") REQUIRE(tcs_lib_free() == TCS_SUCCESS); } +#if defined(__linux__) TEST_CASE("tcs_socket_packet DGRAM") { // Setup @@ -2145,6 +2152,26 @@ TEST_CASE("tcs_socket_packet RAW") 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 @@ -2175,25 +2202,6 @@ TEST_CASE("tcs_socket_packet invalid arguments") 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); -} - TEST_CASE("tcs_socket_packet_str invalid interface") { // Setup From afb19445c4eded0ce99a077457911f80d5a8380d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Lindel=C3=B6w?= Date: Thu, 16 Apr 2026 13:22:45 +0200 Subject: [PATCH 6/6] Fix POLLHUP --- include/tinycsocket.h | 5 +++-- src/tinycsocket_posix.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/tinycsocket.h b/include/tinycsocket.h index 46a67b9..7700ead 100644 --- a/include/tinycsocket.h +++ b/include/tinycsocket.h @@ -3183,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 { diff --git a/src/tinycsocket_posix.c b/src/tinycsocket_posix.c index 3a3a22c..fc20abd 100644 --- a/src/tinycsocket_posix.c +++ b/src/tinycsocket_posix.c @@ -962,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 {