Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 34 additions & 17 deletions include/tinycsocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#ifndef TINYCSOCKET_INTERNAL_H_
#define TINYCSOCKET_INTERNAL_H_

static const char* const TCS_VERSION_TXT = "v0.3.78";
static const char* const TCS_VERSION_TXT = "v0.3.79";
extern const char* const TCS_LICENSE_TXT;

/*
Expand Down Expand Up @@ -124,7 +124,7 @@ extern const char* const TCS_LICENSE_TXT;
* - TcsResult tcs_address_socket_remote(TcsSocket socket, struct TcsAddress* out_remote_address);
* - TcsResult tcs_address_socket_family(TcsSocket socket, TcsFamily* out_family);
* - TcsResult tcs_address_parse(const char str[], struct TcsAddress* out_address);
* - TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[70]);
* - TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[], size_t str_length, size_t* out_length);
* - bool tcs_address_is_equal(const struct TcsAddress* l, const struct TcsAddress* r);
* - bool tcs_address_is_any(const struct TcsAddress* addr);
* - bool tcs_address_is_link_local(const struct TcsAddress* addr);
Expand Down Expand Up @@ -1901,14 +1901,17 @@ TcsResult tcs_address_parse(const char str[], struct TcsAddress* out_address);

/**
* @brief Convert an address to a string.
*
*
* This will make a verbose string representation of the address.
*
* Pass @c out_str=NULL and @c str_length=0 with @c out_length set to query the required size.
*
* @param[in] address the address to convert.
* @param[out] out_str buffer of at least 70 bytes to receive the string representation.
* @return #TCS_SUCCESS if successful, otherwise the error code.
* @param[out] out_str buffer to receive the null-terminated string representation.
* @param[in] str_length capacity of @c out_str in bytes. A buffer of 70 bytes is always sufficient for any supported address family.
* @param[out] out_length receives the length of the written string excluding the null terminator. May be NULL.
* @return #TCS_SUCCESS if successful, #TCS_ERROR_MEMORY if @c out_str is too small, otherwise the error code.
*/
TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[70]);
TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[], size_t str_length, size_t* out_length);

/** @brief Check if two addresses are equal. Returns false for NULL, mismatched, unknown, or unsupported address families. */
bool tcs_address_is_equal(const struct TcsAddress* l, const struct TcsAddress* r);
Expand Down Expand Up @@ -7473,12 +7476,17 @@ TcsResult tcs_address_parse(const char str[], struct TcsAddress* out_address)
return TCS_SUCCESS;
}

TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[], size_t str_length, size_t* out_length)
{
if (address == NULL || str == NULL)
if (address == NULL)
return TCS_ERROR_INVALID_ARGUMENT;
if (out_str == NULL && str_length != 0)
return TCS_ERROR_INVALID_ARGUMENT;
if (out_str == NULL && out_length == NULL)
return TCS_ERROR_INVALID_ARGUMENT;

memset(str, 0, 70);
char str[70];
memset(str, 0, sizeof str);
if (address->family.native == TCS_FAMILY_IPV4.native)
{
uint32_t d = address->data.ipv4.address;
Expand All @@ -7488,9 +7496,9 @@ TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
uint8_t b3 = (uint8_t)((d >> 16) & 0xFF);
uint8_t b4 = (uint8_t)((d >> 24) & 0xFF);
if (p == 0)
snprintf(str, 70, "%i.%i.%i.%i", b4, b3, b2, b1);
snprintf(str, sizeof str, "%i.%i.%i.%i", b4, b3, b2, b1);
else
snprintf(str, 70, "%i.%i.%i.%i:%i", b4, b3, b2, b1, p);
snprintf(str, sizeof str, "%i.%i.%i.%i:%i", b4, b3, b2, b1, p);
}
else if (address->family.native == TCS_FAMILY_IPV6.native)
{
Expand Down Expand Up @@ -7545,18 +7553,18 @@ TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
uint16_t p = address->data.ipv6.port;
TcsInterfaceId sc = address->data.ipv6.scope_id;
if (p != 0 && sc != 0)
snprintf(str, 70, "[%s%%%u]:%u", addr_str, (unsigned int)sc, (unsigned int)p);
snprintf(str, sizeof str, "[%s%%%u]:%u", addr_str, (unsigned int)sc, (unsigned int)p);
else if (p != 0)
snprintf(str, 70, "[%s]:%u", addr_str, (unsigned int)p);
snprintf(str, sizeof str, "[%s]:%u", addr_str, (unsigned int)p);
else if (sc != 0)
snprintf(str, 70, "%s%%%u", addr_str, (unsigned int)sc);
snprintf(str, sizeof str, "%s%%%u", addr_str, (unsigned int)sc);
else
snprintf(str, 70, "%s", addr_str);
snprintf(str, sizeof str, "%s", addr_str);
}
else if (address->family.native == TCS_FAMILY_PACKET.native)
{
snprintf(str,
70,
sizeof str,
"%02X:%02X:%02X:%02X:%02X:%02X",
address->data.packet.mac[0],
address->data.packet.mac[1],
Expand All @@ -7570,6 +7578,15 @@ TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
return TCS_ERROR_NOT_IMPLEMENTED;
}

size_t written = strlen(str);
if (out_length != NULL)
*out_length = written;
if (out_str == NULL)
return TCS_SUCCESS;
if (str_length <= written)
return TCS_ERROR_MEMORY;
memcpy(out_str, str, written + 1);

return TCS_SUCCESS;
}

Expand Down
34 changes: 24 additions & 10 deletions src/tinycsocket_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1311,12 +1311,17 @@ TcsResult tcs_address_parse(const char str[], struct TcsAddress* out_address)
return TCS_SUCCESS;
}

TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[], size_t str_length, size_t* out_length)
{
if (address == NULL || str == NULL)
if (address == NULL)
return TCS_ERROR_INVALID_ARGUMENT;
if (out_str == NULL && str_length != 0)
return TCS_ERROR_INVALID_ARGUMENT;
if (out_str == NULL && out_length == NULL)
return TCS_ERROR_INVALID_ARGUMENT;

memset(str, 0, 70);
char str[70];
memset(str, 0, sizeof str);
if (address->family.native == TCS_FAMILY_IPV4.native)
{
uint32_t d = address->data.ipv4.address;
Expand All @@ -1326,9 +1331,9 @@ TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
uint8_t b3 = (uint8_t)((d >> 16) & 0xFF);
uint8_t b4 = (uint8_t)((d >> 24) & 0xFF);
if (p == 0)
snprintf(str, 70, "%i.%i.%i.%i", b4, b3, b2, b1);
snprintf(str, sizeof str, "%i.%i.%i.%i", b4, b3, b2, b1);
else
snprintf(str, 70, "%i.%i.%i.%i:%i", b4, b3, b2, b1, p);
snprintf(str, sizeof str, "%i.%i.%i.%i:%i", b4, b3, b2, b1, p);
}
else if (address->family.native == TCS_FAMILY_IPV6.native)
{
Expand Down Expand Up @@ -1383,18 +1388,18 @@ TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
uint16_t p = address->data.ipv6.port;
TcsInterfaceId sc = address->data.ipv6.scope_id;
if (p != 0 && sc != 0)
snprintf(str, 70, "[%s%%%u]:%u", addr_str, (unsigned int)sc, (unsigned int)p);
snprintf(str, sizeof str, "[%s%%%u]:%u", addr_str, (unsigned int)sc, (unsigned int)p);
else if (p != 0)
snprintf(str, 70, "[%s]:%u", addr_str, (unsigned int)p);
snprintf(str, sizeof str, "[%s]:%u", addr_str, (unsigned int)p);
else if (sc != 0)
snprintf(str, 70, "%s%%%u", addr_str, (unsigned int)sc);
snprintf(str, sizeof str, "%s%%%u", addr_str, (unsigned int)sc);
else
snprintf(str, 70, "%s", addr_str);
snprintf(str, sizeof str, "%s", addr_str);
}
else if (address->family.native == TCS_FAMILY_PACKET.native)
{
snprintf(str,
70,
sizeof str,
"%02X:%02X:%02X:%02X:%02X:%02X",
address->data.packet.mac[0],
address->data.packet.mac[1],
Expand All @@ -1408,6 +1413,15 @@ TcsResult tcs_address_to_str(const struct TcsAddress* address, char str[70])
return TCS_ERROR_NOT_IMPLEMENTED;
}

size_t written = strlen(str);
if (out_length != NULL)
*out_length = written;
if (out_str == NULL)
return TCS_SUCCESS;
if (str_length <= written)
return TCS_ERROR_MEMORY;
memcpy(out_str, str, written + 1);

return TCS_SUCCESS;
}

Expand Down
17 changes: 10 additions & 7 deletions src/tinycsocket_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#ifndef TINYCSOCKET_INTERNAL_H_
#define TINYCSOCKET_INTERNAL_H_

static const char* const TCS_VERSION_TXT = "v0.3.78";
static const char* const TCS_VERSION_TXT = "v0.3.79";
extern const char* const TCS_LICENSE_TXT;

/*
Expand Down Expand Up @@ -118,7 +118,7 @@ extern const char* const TCS_LICENSE_TXT;
* - TcsResult tcs_address_socket_remote(TcsSocket socket, struct TcsAddress* out_remote_address);
* - TcsResult tcs_address_socket_family(TcsSocket socket, TcsFamily* out_family);
* - TcsResult tcs_address_parse(const char str[], struct TcsAddress* out_address);
* - TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[70]);
* - TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[], size_t str_length, size_t* out_length);
* - bool tcs_address_is_equal(const struct TcsAddress* l, const struct TcsAddress* r);
* - bool tcs_address_is_any(const struct TcsAddress* addr);
* - bool tcs_address_is_link_local(const struct TcsAddress* addr);
Expand Down Expand Up @@ -1895,14 +1895,17 @@ TcsResult tcs_address_parse(const char str[], struct TcsAddress* out_address);

/**
* @brief Convert an address to a string.
*
*
* This will make a verbose string representation of the address.
*
* Pass @c out_str=NULL and @c str_length=0 with @c out_length set to query the required size.
*
* @param[in] address the address to convert.
* @param[out] out_str buffer of at least 70 bytes to receive the string representation.
* @return #TCS_SUCCESS if successful, otherwise the error code.
* @param[out] out_str buffer to receive the null-terminated string representation.
* @param[in] str_length capacity of @c out_str in bytes. A buffer of 70 bytes is always sufficient for any supported address family.
* @param[out] out_length receives the length of the written string excluding the null terminator. May be NULL.
* @return #TCS_SUCCESS if successful, #TCS_ERROR_MEMORY if @c out_str is too small, otherwise the error code.
*/
TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[70]);
TcsResult tcs_address_to_str(const struct TcsAddress* address, char out_str[], size_t str_length, size_t* out_length);

/** @brief Check if two addresses are equal. Returns false for NULL, mismatched, unknown, or unsupported address families. */
bool tcs_address_is_equal(const struct TcsAddress* l, const struct TcsAddress* r);
Expand Down
26 changes: 13 additions & 13 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ TEST_CASE("Interface list")
for (size_t j = 0; j < addr_display; ++j)
{
char addr_str[70];
tcs_address_to_str(&addresses[j].address, addr_str);
tcs_address_to_str(&addresses[j].address, addr_str, sizeof addr_str, NULL);
printf(" Address %u,%zu: %s\n", interfaces[i].id, j, addr_str);
}
}
Expand Down Expand Up @@ -1158,7 +1158,7 @@ TEST_CASE("Get loopback address")
ifaddrs[i].address.data.ipv4.address == TCS_ADDRESS_IPV4_LOOPBACK)
{
char out_str[70];
tcs_address_to_str(&ifaddrs[i].address, out_str);
tcs_address_to_str(&ifaddrs[i].address, out_str, sizeof out_str, NULL);
printf("Interface loopback: %u; %s; %s\n", ifaddrs[i].iface.id, ifaddrs[i].iface.name, out_str);
found_loopback = true;
}
Expand All @@ -1183,7 +1183,7 @@ TEST_CASE("tcs_util_address_to_string with only IPv4")

// When
char address_str[70];
tcs_address_to_str(&addr, address_str);
tcs_address_to_str(&addr, address_str, sizeof address_str, NULL);

// Then
CHECK_EQ(address_str, "127.0.0.1");
Expand All @@ -1199,7 +1199,7 @@ TEST_CASE("tcs_util_address_to_string with IPv4 and port")

// When
char address_str[70];
tcs_address_to_str(&addr, address_str);
tcs_address_to_str(&addr, address_str, sizeof address_str, NULL);

// Then
CHECK_EQ(address_str, "127.0.0.1:1234");
Expand Down Expand Up @@ -2770,7 +2770,7 @@ TEST_CASE("IPv6 address to string loopback")
addr.family = TCS_FAMILY_IPV6;
addr.data.ipv6.address = TCS_ADDRESS_IPV6_LOOPBACK;
char str[70];
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "::1");
}

Expand All @@ -2780,7 +2780,7 @@ TEST_CASE("IPv6 address to string all-zeros")
addr.family = TCS_FAMILY_IPV6;
addr.data.ipv6.address = TCS_ADDRESS_IPV6_ANY;
char str[70];
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "::");
}

Expand All @@ -2791,7 +2791,7 @@ TEST_CASE("IPv6 address to string with port")
addr.data.ipv6.address = TCS_ADDRESS_IPV6_LOOPBACK;
addr.data.ipv6.port = 8080;
char str[70];
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "[::1]:8080");
}

Expand All @@ -2802,27 +2802,27 @@ TEST_CASE("IPv6 address to string RFC 5952 compliance")

// Leading zeros suppressed
CHECK(tcs_address_parse("2001:0db8::0001", &addr) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "2001:db8::1");

// Single zero group NOT compressed to ::
CHECK(tcs_address_parse("2001:db8:0:1:1:1:1:1", &addr) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "2001:db8:0:1:1:1:1:1");

// Longest zero run compressed, first run wins on tie
CHECK(tcs_address_parse("1:0:0:2:0:0:0:3", &addr) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "1:0:0:2::3");

// Equal length runs: first wins
CHECK(tcs_address_parse("1:0:0:2:3:0:0:4", &addr) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "1::2:3:0:0:4");

// Lowercase hex
CHECK(tcs_address_parse("ABCD:EF01::1", &addr) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, "abcd:ef01::1");
}

Expand All @@ -2834,7 +2834,7 @@ TEST_CASE("IPv6 address roundtrip")
TcsAddress addr;
CHECK(tcs_address_parse(inputs[i], &addr) == TCS_SUCCESS);
char str[70];
CHECK(tcs_address_to_str(&addr, str) == TCS_SUCCESS);
CHECK(tcs_address_to_str(&addr, str, sizeof str, NULL) == TCS_SUCCESS);
CHECK_EQ(str, inputs[i]);
}
}
Expand Down
Loading