diff --git a/api/oc_collection.c b/api/oc_collection.c index fcd922a9ec..afd255b33b 100644 --- a/api/oc_collection.c +++ b/api/oc_collection.c @@ -20,6 +20,7 @@ #if defined(OC_COLLECTIONS) && defined(OC_SERVER) #include "api/oc_collection_internal.h" +#include "api/oc_endpoint_internal.h" #include "api/oc_helpers_internal.h" #include "api/oc_link_internal.h" #include "api/oc_ri_internal.h" @@ -715,10 +716,9 @@ collection_encode_links(const oc_collection_t *collection, continue; } oc_rep_object_array_start_item(eps); - oc_string_t ep; - if (oc_endpoint_to_string(eps, &ep) == 0) { + oc_string64_t ep; + if (oc_endpoint_to_string64(eps, &ep)) { oc_rep_set_text_string_v1(eps, ep, oc_string(ep), oc_string_len(ep)); - oc_free_string(&ep); } oc_rep_object_array_end_item(eps); } @@ -856,10 +856,9 @@ oc_handle_collection_linked_list_request(oc_request_t *request) continue; } oc_rep_object_array_start_item(eps); - oc_string_t ep; - if (oc_endpoint_to_string(eps, &ep) == 0) { + oc_string64_t ep; + if (oc_endpoint_to_string64(eps, &ep)) { oc_rep_set_text_string_v1(eps, ep, oc_string(ep), oc_string_len(ep)); - oc_free_string(&ep); } oc_rep_object_array_end_item(eps); } diff --git a/api/oc_discovery.c b/api/oc_discovery.c index 66fdd4b443..55d80cf125 100644 --- a/api/oc_discovery.c +++ b/api/oc_discovery.c @@ -18,6 +18,7 @@ #include "api/oc_core_res_internal.h" #include "api/oc_discovery_internal.h" +#include "api/oc_endpoint_internal.h" #include "api/oc_resource_internal.h" #include "api/oc_ri_internal.h" #include "api/oc_server_api_internal.h" @@ -122,12 +123,11 @@ discovery_encode_endpoint(CborEncoder *eps, const oc_endpoint_t *ep, int latency) { oc_rep_begin_object(eps, ep); - oc_string_t ep_str; - if (oc_endpoint_to_string(ep, &ep_str) == 0) { + oc_string64_t ep_str; + if (oc_endpoint_to_string64(ep, &ep_str)) { g_err |= oc_rep_encode_text_string(&ep_map, "ep", OC_CHAR_ARRAY_LEN("ep")); g_err |= oc_rep_encode_text_string(&ep_map, oc_string(ep_str), oc_string_len(ep_str)); - oc_free_string(&ep_str); } if (latency > 0) { g_err |= @@ -1058,14 +1058,15 @@ oc_wkcore_discovery_handler(oc_request_t *request, oc_string_t uri; memset(&uri, 0, sizeof(oc_string_t)); while (eps != NULL) { - if (eps->flags & SECURED) { - oc_string_t ep; - if (oc_endpoint_to_string(eps, &ep) == 0) { - length = clf_add_str_to_buffer(oc_string(ep), oc_string_len(ep)); - response_length += length; - oc_free_string(&ep); - break; - } + if ((eps->flags & SECURED) == 0) { + eps = eps->next; + continue; + } + oc_string64_t ep; + if (oc_endpoint_to_string64(eps, &ep)) { + length = clf_add_str_to_buffer(oc_string(ep), oc_string_len(ep)); + response_length += length; + break; } eps = eps->next; } diff --git a/api/oc_endpoint.c b/api/oc_endpoint.c index d58ebd53d1..7dedf2cf5e 100644 --- a/api/oc_endpoint.c +++ b/api/oc_endpoint.c @@ -16,8 +16,9 @@ * ****************************************************************************/ +#include "api/oc_endpoint_internal.h" +#include "api/oc_helpers_internal.h" #include "oc_endpoint.h" -#include "oc_endpoint_internal.h" #include "oc_core_res.h" #include "port/common/oc_ip.h" #include "port/oc_connectivity.h" @@ -66,26 +67,41 @@ oc_endpoint_set_di(oc_endpoint_t *endpoint, const oc_uuid_t *di) memcpy(endpoint->di.id, di->id, sizeof(di->id)); } -const char * -oc_endpoint_flags_to_scheme(unsigned flags) +static oc_string_view_t +endpoint_flags_to_scheme(unsigned flags) { #ifdef OC_TCP if ((flags & TCP) != 0) { if ((flags & SECURED) != 0) { - return OC_SCHEME_COAPS_TCP; + return OC_STRING_VIEW(OC_SCHEME_COAPS_TCP); } - return OC_SCHEME_COAP_TCP; + return OC_STRING_VIEW(OC_SCHEME_COAP_TCP); } -#endif +#endif /* OC_TCP */ if ((flags & SECURED) != 0) { - return OC_SCHEME_COAPS; + return OC_STRING_VIEW(OC_SCHEME_COAPS); } - return OC_SCHEME_COAP; + return OC_STRING_VIEW(OC_SCHEME_COAP); +} + +int +oc_endpoint_flags_to_scheme(unsigned flags, char *buffer, size_t buffer_size) +{ + oc_string_view_t scheme = endpoint_flags_to_scheme(flags); + if (buffer == NULL) { + return (int)scheme.length; + } + if (scheme.length < buffer_size) { + memcpy(buffer, scheme.data, scheme.length); + buffer[scheme.length] = '\0'; + return (int)scheme.length; + } + return -1; } int oc_endpoint_host(const oc_endpoint_t *endpoint, char *buffer, - uint32_t buffer_size) + size_t buffer_size) { #ifdef OC_IPV4 if ((endpoint->flags & IPV4) != 0) { @@ -99,8 +115,8 @@ oc_endpoint_host(const oc_endpoint_t *endpoint, char *buffer, } int -oc_endpoint_to_cstring(const oc_endpoint_t *endpoint, char *buffer, - uint32_t buffer_size) +oc_endpoint_address_and_port_to_cstring(const oc_endpoint_t *endpoint, + char *buffer, size_t buffer_size) { #ifdef OC_IPV4 if ((endpoint->flags & IPV4) != 0) { @@ -115,6 +131,26 @@ oc_endpoint_to_cstring(const oc_endpoint_t *endpoint, char *buffer, return -1; } +int +oc_endpoint_to_cstring(const oc_endpoint_t *endpoint, char *buffer, + size_t buffer_size) +{ + int written = + oc_endpoint_flags_to_scheme(endpoint->flags, buffer, buffer_size); + if (written < 0) { + return -1; + } + int len = written; + buffer += written; + buffer_size -= (size_t)written; + written = + oc_endpoint_address_and_port_to_cstring(endpoint, buffer, buffer_size); + if (written < 0) { + return -1; + } + return len + written; +} + int oc_endpoint_to_string(const oc_endpoint_t *endpoint, oc_string_t *endpoint_str) { @@ -122,15 +158,31 @@ oc_endpoint_to_string(const oc_endpoint_t *endpoint, oc_string_t *endpoint_str) return -1; } - char ip[OC_IPV6_MAXSTRLEN] = { 0 }; - if (oc_endpoint_to_cstring(endpoint, ip, OC_ARRAY_SIZE(ip)) != 0) { + oc_string64_t ep_str; + if (!oc_endpoint_to_string64(endpoint, &ep_str)) { return -1; } - oc_concat_strings(endpoint_str, oc_endpoint_flags_to_scheme(endpoint->flags), - ip); + oc_new_string(endpoint_str, oc_string(ep_str), oc_string_len(ep_str)); return 0; } +bool +oc_endpoint_to_string64(const oc_endpoint_t *endpoint, + oc_string64_t *endpoint_str) +{ + if (!endpoint || !endpoint_str) { + return false; + } + memset(endpoint_str, 0, sizeof(oc_string64_t)); + int written = oc_endpoint_to_cstring(endpoint, oc_string(*endpoint_str), + OC_ARRAY_SIZE(endpoint_str->ptr)); + if (written < 0) { + return false; + } + endpoint_str->size = (size_t)written + 1; + return true; +} + int oc_endpoint_port(const oc_endpoint_t *endpoint) { @@ -353,26 +405,8 @@ parse_endpoint_uri(const oc_string_t *endpoint_str, return false; } - const char *address = NULL; - switch (flags) { -#ifdef OC_TCP - case TCP | SECURED: - address = ep + OC_CHAR_ARRAY_LEN(OC_SCHEME_COAPS_TCP); - break; - case TCP: - address = ep + OC_CHAR_ARRAY_LEN(OC_SCHEME_COAP_TCP); - break; -#endif /* OC_TCP */ - case SECURED: - address = ep + OC_CHAR_ARRAY_LEN(OC_SCHEME_COAPS); - break; - case 0: - address = ep + OC_CHAR_ARRAY_LEN(OC_SCHEME_COAP); - break; - default: - OC_ERR("invalid endpoint(%s) uri scheme: %d", ep != NULL ? ep : "", flags); - return false; - } + oc_string_view_t scheme = endpoint_flags_to_scheme(flags); + const char *address = ep + scheme.length; size_t ep_len = oc_string_len(*endpoint_str); size_t address_len = ep_len - (address - ep); @@ -701,12 +735,7 @@ oc_endpoint_list_copy(oc_endpoint_t **dst, const oc_endpoint_t *src) return count; oc_endpoint_list_copy_err: - ep = head; - while (ep != NULL) { - oc_endpoint_t *next = ep->next; - oc_free_endpoint(ep); - ep = next; - } + oc_endpoint_list_free(head); return -1; } diff --git a/api/oc_endpoint_internal.h b/api/oc_endpoint_internal.h index 18c3624e68..a9f74e4a38 100644 --- a/api/oc_endpoint_internal.h +++ b/api/oc_endpoint_internal.h @@ -21,8 +21,11 @@ #include "oc_endpoint.h" #include "util/oc_compiler.h" +#include "util/oc_macros_internal.h" #include +#include +#include #ifdef __cplusplus extern "C" { @@ -40,28 +43,60 @@ extern "C" { #define OC_SCHEME_OCF "ocf://" -/** @brief Get scheme string for transport flags */ -const char *oc_endpoint_flags_to_scheme(unsigned flags) OC_RETURNS_NONNULL; +/** + * @brief Write the scheme string (including NUL terminator) for given transport + * flags to buffer + * + * @param flags transport flags of an endpoint + * @param buffer output buffer (if NULL the function returns the number of bytes + * that would have been written, excluding the NUL terminator) + * @param buffer_size size of output buffer + * @return return number of written bytes (excluding the NUL terminator) + * @return -1 for error + */ +int oc_endpoint_flags_to_scheme(unsigned flags, char *buffer, + size_t buffer_size); /** * @brief Convert the endpoint to a human readable string (e.g. - * "coaps://[fe::22]:/") + * "[fe::22]:1234") * - * @param endpoint the endpoint - * @param buffer output buffer + * @param endpoint the endpoint (cannot be NULL) + * @param buffer output buffer (cannot be NULL) * @param buffer_size size of output buffer - * @return int 0 success + * @return number of written bytes, -1 for error */ -int oc_endpoint_to_cstring(const oc_endpoint_t *endpoint, char *buffer, - uint32_t buffer_size) OC_NONNULL(); +int oc_endpoint_address_and_port_to_cstring(const oc_endpoint_t *endpoint, + char *buffer, size_t buffer_size) + OC_NONNULL(); /** @brief Get host of the endpoint as string */ int oc_endpoint_host(const oc_endpoint_t *endpoint, char *buffer, - uint32_t buffer_size) OC_NONNULL(); + size_t buffer_size) OC_NONNULL(); /** @brief Get port of the endpoint */ int oc_endpoint_port(const oc_endpoint_t *endpoint) OC_NONNULL(); +typedef struct oc_string64_s +{ + size_t size; + char ptr[64]; +} oc_string64_t; + +#define oc_string64_cap(ocstring) \ + (OC_ARRAY_SIZE((ocstring).ptr) - (ocstring).size) + +/** + * @brief convert the endpoint to a human readable string (e.g. + * "coaps://[fe::22]:1234"). + * + * @param endpoint the endpoint + * @param endpoint_str endpoint as human readable string + * @return true for success + */ +bool oc_endpoint_to_string64(const oc_endpoint_t *endpoint, + oc_string64_t *endpoint_str); + #ifdef __cplusplus } #endif diff --git a/api/oc_introspection.c b/api/oc_introspection.c index 06e3e814e5..89b69aeef5 100644 --- a/api/oc_introspection.c +++ b/api/oc_introspection.c @@ -21,6 +21,7 @@ #ifdef OC_INTROSPECTION #include "api/oc_core_res_internal.h" +#include "api/oc_endpoint_internal.h" #include "api/oc_introspection_internal.h" #include "api/oc_ri_internal.h" #include "api/oc_server_api_internal.h" @@ -126,10 +127,9 @@ oc_introspection_wk_get_uri(size_t device, int interface_index, if ((interface_index == -1 || eps->interface_index == (unsigned)interface_index) && (eps->flags == flags)) { - oc_string_t ep; - if (oc_endpoint_to_string(eps, &ep) == 0) { + oc_string64_t ep; + if (oc_endpoint_to_string64(eps, &ep)) { oc_concat_strings(uri, oc_string(ep), OC_INTROSPECTION_DATA_URI); - oc_free_string(&ep); return true; } } diff --git a/api/oc_push.c b/api/oc_push.c index d6d37446df..1ac8e2383a 100644 --- a/api/oc_push.c +++ b/api/oc_push.c @@ -28,6 +28,7 @@ #include "api/oc_helpers_internal.h" #include "api/oc_rep_internal.h" +#include "api/oc_endpoint_internal.h" #include "oc_api.h" #include "oc_core_res.h" #include "oc_core_res_internal.h" @@ -572,9 +573,9 @@ get_ns_properties(const oc_resource_t *resource, oc_interface_mask_t iface_mask, /* * pushtarget */ - oc_string_t ep; + oc_string64_t ep; oc_string_t full_uri; - if (oc_endpoint_to_string(&ns_instance->pushtarget_ep, &ep) < 0) { + if (!oc_endpoint_to_string64(&ns_instance->pushtarget_ep, &ep)) { /* handle NULL pushtarget... */ #if 0 char ipv6addrstr[50], ipv4addrstr[50]; @@ -594,8 +595,6 @@ get_ns_properties(const oc_resource_t *resource, oc_interface_mask_t iface_mask, oc_string(ns_instance->targetpath)); else oc_new_string(&full_uri, oc_string(ep), oc_string_len(ep)); - - oc_free_string(&ep); } oc_rep_set_text_string(root, pushtarget, oc_string(full_uri)); @@ -2428,9 +2427,10 @@ push_update(oc_ns_t *ns_instance) return false; } #ifdef OC_PUSHDEBUG - oc_string_t ep, full_uri; + oc_string64_t ep; + oc_string_t full_uri; - oc_endpoint_to_string(&ns_instance->pushtarget_ep, &ep); + oc_endpoint_to_string64(&ns_instance->pushtarget_ep, &ep); if (oc_string_len(ns_instance->targetpath)) { oc_concat_strings(&full_uri, oc_string(ep), oc_string(ns_instance->targetpath)); @@ -2440,7 +2440,6 @@ push_update(oc_ns_t *ns_instance) OC_PUSH_DBG("push \"%s\" ====> \"%s\"", oc_string(src_rsc->uri), oc_string(full_uri)); - oc_free_string(&ep); oc_free_string(&full_uri); #endif OC_PUSH_DBG("state of Push Proxy (\"%s\") is changed (%s => %s)", diff --git a/api/unittest/eptest.cpp b/api/unittest/eptest.cpp index a985a8b2b9..8415888136 100644 --- a/api/unittest/eptest.cpp +++ b/api/unittest/eptest.cpp @@ -22,13 +22,16 @@ #include "oc_helpers.h" #include "oc_uuid.h" #include "port/common/oc_ip.h" +#include "port/oc_connectivity.h" #include "port/oc_random.h" +#include "tests/gtest/Device.h" #include "tests/gtest/Endpoint.h" #include #include #include #include +#include #include #ifdef _WIN32 @@ -94,6 +97,56 @@ TEST_F(TestEndpoint, SetDeviceID) oc_random_destroy(); } +TEST_F(TestEndpoint, EndpointFlagsToScheme) +{ + std::array buf; + buf.fill(0); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAP), + oc_endpoint_flags_to_scheme(0, buf.data(), buf.size())); + EXPECT_STREQ(OC_SCHEME_COAP, buf.data()); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAP), + oc_endpoint_flags_to_scheme(0, nullptr, 0)); + EXPECT_EQ(-1, oc_endpoint_flags_to_scheme(0, buf.data(), 0)); + + buf.fill(0); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAPS), + oc_endpoint_flags_to_scheme(SECURED, buf.data(), buf.size())); + EXPECT_STREQ(OC_SCHEME_COAPS, buf.data()); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAPS), + oc_endpoint_flags_to_scheme(SECURED, nullptr, 0)); + EXPECT_EQ(-1, oc_endpoint_flags_to_scheme(SECURED, buf.data(), 0)); + +#ifdef OC_TCP + buf.fill(0); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAP_TCP), + oc_endpoint_flags_to_scheme(TCP, buf.data(), buf.size())); + EXPECT_STREQ(OC_SCHEME_COAP_TCP, buf.data()); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAP_TCP), + oc_endpoint_flags_to_scheme(TCP, nullptr, 0)); + EXPECT_EQ(-1, oc_endpoint_flags_to_scheme(TCP, buf.data(), 0)); + + buf.fill(0); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAPS_TCP), + oc_endpoint_flags_to_scheme(SECURED | TCP, buf.data(), buf.size())); + EXPECT_STREQ(OC_SCHEME_COAPS_TCP, buf.data()); + EXPECT_EQ(OC_CHAR_ARRAY_LEN(OC_SCHEME_COAPS_TCP), + oc_endpoint_flags_to_scheme(SECURED | TCP, nullptr, 0)); + EXPECT_EQ(-1, oc_endpoint_flags_to_scheme(SECURED | TCP, buf.data(), 0)); +#endif /* OC_TCP */ +} + +TEST_F(TestEndpoint, EndpointToCStringInvalid) +{ + oc_endpoint_t ep = oc::endpoint::FromString("coap://[::1]:42"); + // cannot fit scheme + std::array too_small{}; + EXPECT_EQ(-1, oc_endpoint_to_cstring(&ep, &too_small[0], too_small.size())); + + // can fit scheme but not address + std::array too_small2{}; + EXPECT_EQ(-1, oc_endpoint_to_cstring(&ep, &too_small2[0], too_small2.size())); +} + TEST_F(TestEndpoint, EndpointToStringInvalid) { EXPECT_EQ(-1, oc_endpoint_to_string(nullptr, nullptr)); @@ -102,6 +155,8 @@ TEST_F(TestEndpoint, EndpointToStringInvalid) EXPECT_EQ(-1, oc_endpoint_to_string(nullptr, &ep_str)); oc_endpoint_t ep{}; + EXPECT_EQ(-1, oc_endpoint_to_string(&ep, nullptr)); + EXPECT_EQ(-1, oc_endpoint_to_string(&ep, &ep_str)); } @@ -137,9 +192,9 @@ TEST_F(TestEndpoint, StringToEndpointInvalid) TEST_F(TestEndpoint, IPv6AddressToStringFail) { -#define ENDPOINT_ADDR "[fe80:123::1]:42" + constexpr std::string_view ENDPOINT_ADDR = "[fe80:123::1]:42"; + std::string ep_str = "coap://" + std::string(ENDPOINT_ADDR); - std::string ep_str = "coap://" ENDPOINT_ADDR; oc_endpoint_t ep = oc::endpoint::FromString(ep_str); std::array too_small{}; EXPECT_EQ(-1, oc_ipv6_address_and_port_to_string( @@ -152,33 +207,57 @@ TEST_F(TestEndpoint, IPv6AddressToStringFail) std::array too_small3{}; EXPECT_EQ(-1, oc_ipv6_address_and_port_to_string( &ep.addr.ipv6, too_small3.data(), too_small3.size())); -#undef ENDPOINT_ADDR } TEST_F(TestEndpoint, IPv6AddressToString) { -#define ENDPOINT_ADDR "[::1]:42" - std::string ep_str = "coap://" ENDPOINT_ADDR; + constexpr std::string_view ENDPOINT_ADDR = "[::1]:42"; + std::string ep_str = "coap://" + std::string(ENDPOINT_ADDR); oc_endpoint_t ep = oc::endpoint::FromString(ep_str); std::array exact{}; - EXPECT_EQ(0, oc_ipv6_address_and_port_to_string(&ep.addr.ipv6, exact.data(), + EXPECT_EQ(8, oc_ipv6_address_and_port_to_string(&ep.addr.ipv6, exact.data(), exact.size())); - EXPECT_STREQ(ENDPOINT_ADDR, exact.data()); + EXPECT_STREQ(ENDPOINT_ADDR.data(), exact.data()); std::array larger{}; - EXPECT_EQ(0, oc_ipv6_address_and_port_to_string(&ep.addr.ipv6, larger.data(), + EXPECT_EQ(8, oc_ipv6_address_and_port_to_string(&ep.addr.ipv6, larger.data(), larger.size())); - EXPECT_STREQ(ENDPOINT_ADDR, larger.data()); -#undef ENDPOINT_ADDR + EXPECT_STREQ(ENDPOINT_ADDR.data(), larger.data()); +} + +TEST_F(TestEndpoint, EndpointToString64Invalid) +{ + EXPECT_FALSE(oc_endpoint_to_string64(nullptr, nullptr)); + + oc_string64_t ep_str{}; + EXPECT_FALSE(oc_endpoint_to_string64(nullptr, &ep_str)); + + oc_endpoint_t ep{}; + EXPECT_FALSE(oc_endpoint_to_string64(&ep, nullptr)); + + EXPECT_FALSE(oc_endpoint_to_string64(&ep, &ep_str)); +} + +TEST_F(TestEndpoint, IPv6EndpointToString64) +{ + constexpr std::string_view ENDPOINT_ADDR = "[::1]:42"; + std::string ep_str = "coap://" + std::string(ENDPOINT_ADDR); + oc_endpoint_t ep = oc::endpoint::FromString(ep_str); + + oc_string64_t ep_str64{}; + oc_endpoint_to_string64(&ep, &ep_str64); + EXPECT_EQ(15, oc_string_len(ep_str64)); + EXPECT_EQ(15, strlen(oc_string(ep_str64))); + EXPECT_STREQ(ep_str.c_str(), oc_string(ep_str64)); } #ifdef OC_IPV4 TEST_F(TestEndpoint, IPv4AddressToStringFail) { -#define ENDPOINT_ADDR "127.0.0.1:80" - std::string ep_str = "coap://" ENDPOINT_ADDR; + constexpr std::string_view ENDPOINT_ADDR = "127.0.0.1:80"; + std::string ep_str = "coap://" + std::string(ENDPOINT_ADDR); oc_endpoint_t ep = oc::endpoint::FromString(ep_str); std::array too_small{}; EXPECT_EQ(-1, oc_ipv4_address_and_port_to_string( @@ -187,24 +266,37 @@ TEST_F(TestEndpoint, IPv4AddressToStringFail) std::array too_small2{}; EXPECT_EQ(-1, oc_ipv4_address_and_port_to_string( &ep.addr.ipv4, too_small2.data(), too_small2.size())); -#undef ENDPOINT_ADDR } TEST_F(TestEndpoint, IPv4AddressToString) { -#define ENDPOINT_ADDR "127.0.0.1:80" - std::string ep_str = "coap://" ENDPOINT_ADDR; + constexpr std::string_view ENDPOINT_ADDR = "127.0.0.1:80"; + std::string ep_str = "coap://" + std::string(ENDPOINT_ADDR); oc_endpoint_t ep = oc::endpoint::FromString(ep_str); std::array exact{}; - EXPECT_EQ(0, oc_ipv4_address_and_port_to_string(&ep.addr.ipv4, exact.data(), - exact.size())); - EXPECT_STREQ(ENDPOINT_ADDR, exact.data()); + EXPECT_EQ(12, oc_ipv4_address_and_port_to_string(&ep.addr.ipv4, exact.data(), + exact.size())); + EXPECT_STREQ(ENDPOINT_ADDR.data(), exact.data()); std::array larger{}; - EXPECT_EQ(0, oc_ipv4_address_and_port_to_string(&ep.addr.ipv4, larger.data(), - larger.size())); - EXPECT_STREQ(ENDPOINT_ADDR, larger.data()); -#undef ENDPOINT_ADDR + EXPECT_EQ(12, oc_ipv4_address_and_port_to_string(&ep.addr.ipv4, larger.data(), + larger.size())); + EXPECT_STREQ(ENDPOINT_ADDR.data(), larger.data()); +} + +TEST_F(TestEndpoint, IPv4EndpointToString64) +{ + constexpr std::string_view ENDPOINT_ADDR = "127.0.0.1:80"; + std::string ep_str = "coap://" + std::string(ENDPOINT_ADDR); + oc_endpoint_t ep = oc::endpoint::FromString(ep_str); + + EXPECT_FALSE(oc_endpoint_to_string64(&ep, nullptr)); + + oc_string64_t ep_str64{}; + EXPECT_TRUE(oc_endpoint_to_string64(&ep, &ep_str64)); + EXPECT_EQ(19, oc_string_len(ep_str64)); + EXPECT_EQ(19, strlen(oc_string(ep_str64))); + EXPECT_STREQ(ep_str.c_str(), oc_string(ep_str64)); } #endif /* OC_IPV4 */ @@ -226,10 +318,20 @@ TEST_F(TestEndpoint, StringToEndpoint) for (size_t i = 0; i < spu0.size(); ++i) { oc_endpoint_t ep = oc::endpoint::FromString(spu0[i]); + oc_string_t ep_str{}; EXPECT_EQ(0, oc_endpoint_to_string(&ep, &ep_str)); EXPECT_STREQ(exp[i].c_str(), oc_string(ep_str)); oc_free_string(&ep_str); + + oc_string64_t ep_str64{}; + EXPECT_TRUE(oc_endpoint_to_string64(&ep, &ep_str64)); + EXPECT_STREQ(exp[i].c_str(), oc_string(ep_str64)); + + std::array ep_buf{}; + EXPECT_EQ(exp[i].length(), + oc_endpoint_to_cstring(&ep, &ep_buf[0], ep_buf.size())); + EXPECT_STREQ(exp[i].c_str(), ep_buf.data()); } } @@ -806,3 +908,48 @@ TEST_F(TestEndpoint, EndpointHost) } #endif /* OC_IPV4 */ } + +#ifdef OC_CLIENT + +static constexpr size_t kDeviceID{ 0 }; + +class TestEndpointWithServer : public testing::Test { +public: + static void SetUpTestCase() { ASSERT_TRUE(oc::TestDevice::StartServer()); } + + static void TearDownTestCase() { oc::TestDevice::StopServer(); } +}; + +TEST_F(TestEndpointWithServer, SetLocalAddressFail) +{ + oc_endpoint_t ep{}; + oc_endpoint_set_local_address(&ep, UINT32_MAX); + EXPECT_TRUE(oc_endpoint_is_empty(&ep)); +} + +TEST_F(TestEndpointWithServer, SetLocalAddress) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + std::array addr_empty{}; + ASSERT_EQ(0, memcmp(&ep.addr_local, &addr_empty[0], sizeof(ep.addr_local))); + + // oc_endpoint_set_local_address should modify only the output parameter, + // which is a local copy in this test case, so the global endpoints shouldn't + // be modified + auto checkEndpoints = [](size_t device) { + oc_endpoint_t *eps = oc_connectivity_get_endpoints(device); + while (eps != nullptr) { + EXPECT_NE(0, memcmp(&eps->addr, &eps->addr_local, sizeof(eps->addr))); + eps = eps->next; + } + }; + + oc_endpoint_set_local_address(&ep, ep.interface_index); + EXPECT_NE(0, memcmp(&ep.addr_local, &addr_empty[0], sizeof(ep.addr_local))); + checkEndpoints(kDeviceID); +} + +#endif /* OC_CLIENT */ diff --git a/api/unittest/plgdtimetest.cpp b/api/unittest/plgdtimetest.cpp index d3c5335437..f2d2e5fea1 100644 --- a/api/unittest/plgdtimetest.cpp +++ b/api/unittest/plgdtimetest.cpp @@ -594,8 +594,11 @@ TEST_F(TestPlgdTimeWithServer, FetchTimeFail) ep_flags |= TCP; #endif /* OC_TCP */ - std::string ep_str = - std::string(oc_endpoint_flags_to_scheme(ep_flags)) + "[ff02::158]:12345"; + std::array scheme{}; + ASSERT_NE(-1, + oc_endpoint_flags_to_scheme(ep_flags, &scheme[0], scheme.size())); + + std::string ep_str = std::string(scheme.data()) + "[ff02::158]:12345"; oc_endpoint_t ep = oc::endpoint::FromString(ep_str); auto fetch_handler = [](oc_status_t code, oc_clock_time_t, void *data) { diff --git a/include/oc_endpoint.h b/include/oc_endpoint.h index 7b84df403a..d60dcbd07b 100644 --- a/include/oc_endpoint.h +++ b/include/oc_endpoint.h @@ -27,6 +27,7 @@ #ifdef OC_OSCORE #include "messaging/coap/oscore_constants.h" #endif /* OC_OSCORE */ +#include "util/oc_compiler.h" #include @@ -139,11 +140,12 @@ void oc_free_endpoint(oc_endpoint_t *endpoint); * @param di device identifier (cannot be NULL) */ OC_API -void oc_endpoint_set_di(oc_endpoint_t *endpoint, const oc_uuid_t *di); +void oc_endpoint_set_di(oc_endpoint_t *endpoint, const oc_uuid_t *di) + OC_NONNULL(); /** * @brief convert the endpoint to a human readable string (e.g. - * "coaps://[fe::22]:/") + * "coaps://[fe::22]:1234") * * @param endpoint the endpoint * @param endpoint_str endpoint as human readable string @@ -153,6 +155,19 @@ OC_API int oc_endpoint_to_string(const oc_endpoint_t *endpoint, oc_string_t *endpoint_str); +/** + * @brief convert the endpoint to a human readable string (e.g. + * "coaps://[fe::22]:1234") + * + * @param endpoint the endpoint (cannot be NULL) + * @param buffer output buffer (cannot be NULL) + * @param buffer_size size of output buffer + * @return number of written bytes, -1 for error + */ +OC_API +int oc_endpoint_to_cstring(const oc_endpoint_t *endpoint, char *buffer, + size_t buffer_size) OC_NONNULL(); + /** * @brief string to endpoint * @@ -168,7 +183,7 @@ int oc_string_to_endpoint(const oc_string_t *endpoint_str, /** * @brief parse path component (ie. the part after the first '/') of a uri * - * @param[in] endpoint_str uri to parse + * @param endpoint_str uri to parse * @param[out] path output variable * @return 0 on success * @return -1 on failure @@ -215,16 +230,18 @@ int oc_endpoint_compare_address(const oc_endpoint_t *ep1, * @return false otherwise */ OC_API -bool oc_endpoint_is_empty(const oc_endpoint_t *endpoint); +bool oc_endpoint_is_empty(const oc_endpoint_t *endpoint) OC_NONNULL(); /** - * @brief set interface index on the endpoint + * @brief set local address on endpoint from the first device endpoint with + * matching flags and interface index * - * @param ep the endpoint (cannot be NULL) + * @param[in,out] ep the endpoint (cannot be NULL) * @param interface_index the interface index */ OC_API -void oc_endpoint_set_local_address(oc_endpoint_t *ep, unsigned interface_index); +void oc_endpoint_set_local_address(oc_endpoint_t *ep, unsigned interface_index) + OC_NONNULL(); /** * @brief copy endpoint @@ -233,16 +250,21 @@ void oc_endpoint_set_local_address(oc_endpoint_t *ep, unsigned interface_index); * @param src source endpoint (cannot be NULL) */ OC_API -void oc_endpoint_copy(oc_endpoint_t *dst, const oc_endpoint_t *src); +void oc_endpoint_copy(oc_endpoint_t *dst, const oc_endpoint_t *src) + OC_NONNULL(); /** * @brief copy list of endpoints * * @param dst destination list of endpoints (cannot be NULL) * @param src source list of endpoints + * + * @return 0 on success + * @return -1 on failure */ OC_API -int oc_endpoint_list_copy(oc_endpoint_t **dst, const oc_endpoint_t *src); +int oc_endpoint_list_copy(oc_endpoint_t **dst, const oc_endpoint_t *src) + OC_NONNULL(1); /** * @brief deallocate a linked list of endpoints diff --git a/include/oc_helpers.h b/include/oc_helpers.h index 2b38a3879c..ce054d0c2e 100644 --- a/include/oc_helpers.h +++ b/include/oc_helpers.h @@ -45,7 +45,7 @@ typedef struct oc_mmem oc_handle_t, oc_string_t, oc_array_t, oc_string_array_t, * @brief cast oc_string to string * */ -#define oc_string(ocstring) (oc_cast(ocstring, char)) +#define oc_string(ocstring) ((char *)(ocstring).ptr) #ifdef OC_MEMORY_TRACE #define oc_alloc_string(ocstring, size) \ diff --git a/messaging/coap/observe.c b/messaging/coap/observe.c index 22ff1cde2d..82472821c6 100644 --- a/messaging/coap/observe.c +++ b/messaging/coap/observe.c @@ -52,6 +52,7 @@ #ifdef OC_SERVER #include "api/oc_buffer_internal.h" +#include "api/oc_endpoint_internal.h" #include "api/oc_helpers_internal.h" #include "api/oc_query_internal.h" #include "api/oc_ri_internal.h" @@ -1064,15 +1065,13 @@ coap_iterate_observers(oc_resource_t *resource, oc_response_t *response, } if (prepare_response) { #if OC_DBG_IS_ENABLED - oc_string_t ep_str; - memset(&ep_str, 0, sizeof(oc_string_t)); + oc_string64_t ep_str; const char *ep_cstr = ""; - if (oc_endpoint_to_string(&obs->endpoint, &ep_str) == 0) { + if (oc_endpoint_to_string64(&obs->endpoint, &ep_str)) { ep_cstr = oc_string(ep_str); } COAP_DBG("prepare GET request to resource(%s) for endpoint %s", oc_string(resource->uri), ep_cstr); - oc_free_string(&ep_str); #endif /* OC_DBG_IS_ENABLED */ if (!coap_fill_response(response, resource, &obs->endpoint, iface_mask, true)) { diff --git a/messaging/coap/transactions.c b/messaging/coap/transactions.c index c7dcfd49aa..a00e58dc61 100644 --- a/messaging/coap/transactions.c +++ b/messaging/coap/transactions.c @@ -276,9 +276,9 @@ coap_free_transactions_by_endpoint(const oc_endpoint_t *endpoint, oc_status_t code) { #if OC_DBG_IS_ENABLED - char ep_addr[64] = { 0 }; - oc_endpoint_to_cstring(endpoint, ep_addr, OC_ARRAY_SIZE(ep_addr)); - COAP_DBG("free transactions for endpoint(%s)", ep_addr); + oc_string64_t ep_str; + oc_endpoint_to_string64(endpoint, &ep_str); + COAP_DBG("free transactions for endpoint(%s)", oc_string(ep_str)); #endif /* OC_DBG_IS_ENABLED */ #ifndef OC_CLIENT (void)code; diff --git a/port/common/oc_ip.c b/port/common/oc_ip.c index 088495b3f0..d47a0ad714 100644 --- a/port/common/oc_ip.c +++ b/port/common/oc_ip.c @@ -41,12 +41,13 @@ int oc_ipv6_address_to_string(const oc_ipv6_addr_t *ipv6, char *buffer, - uint32_t buffer_size) + size_t buffer_size) { assert(ipv6 != NULL); assert(buffer != NULL); - if (inet_ntop(AF_INET6, ipv6->address, buffer, buffer_size) == NULL) { + if (inet_ntop(AF_INET6, ipv6->address, buffer, (socklen_t)buffer_size) == + NULL) { return -1; } // safe: maximal IPv6 length is 45 @@ -55,42 +56,42 @@ oc_ipv6_address_to_string(const oc_ipv6_addr_t *ipv6, char *buffer, int oc_ipv6_address_and_port_to_string(const oc_ipv6_addr_t *ipv6, char *buffer, - uint32_t buffer_size) + size_t buffer_size) { assert(ipv6 != NULL); assert(buffer != NULL); - // shortest valid ipv6 address with a port + // shortest valid IPv6 address with a port if (buffer_size < sizeof("[::1]:X")) { return -1; } - uint32_t start = 0; + size_t start = 0; buffer[start++] = '['; int written = oc_ipv6_address_to_string(ipv6, &buffer[start], buffer_size - start); if (written < 0) { return -1; } - start += (uint32_t)written; - - written = snprintf(&buffer[start], buffer_size - start, "]:%u", ipv6->port); - if ((written < 0) || start + (uint32_t)written >= buffer_size) { + start += (size_t)written; + size_t remaining_space = buffer_size - start; + written = snprintf(&buffer[start], remaining_space, "]:%u", ipv6->port); + if (written < 0 || (size_t)written >= remaining_space) { return -1; } - return 0; + return (int)(start + written); } #ifdef OC_IPV4 int oc_ipv4_address_to_string(const oc_ipv4_addr_t *ipv4, char *buffer, - uint32_t buffer_size) + size_t buffer_size) { assert(buffer != NULL); const uint8_t *addr = ipv4->address; int written = snprintf(buffer, buffer_size, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); - if ((written < 0) || (uint32_t)written >= buffer_size) { + if ((written < 0) || (size_t)written >= buffer_size) { return -1; } return written; @@ -98,7 +99,7 @@ oc_ipv4_address_to_string(const oc_ipv4_addr_t *ipv4, char *buffer, int oc_ipv4_address_and_port_to_string(const oc_ipv4_addr_t *ipv4, char *buffer, - uint32_t buffer_size) + size_t buffer_size) { assert(ipv4 != NULL); assert(buffer != NULL); @@ -107,13 +108,14 @@ oc_ipv4_address_and_port_to_string(const oc_ipv4_addr_t *ipv4, char *buffer, if (written < 0) { return -1; } + int ret = written; buffer_size -= written; written = snprintf(&buffer[written], buffer_size, ":%u", ipv4->port); - if ((written < 0) || (uint32_t)written >= buffer_size) { + if ((written < 0) || (size_t)written >= buffer_size) { return -1; } - return 0; + return ret + written; } #endif /* OC_IPV4 */ diff --git a/port/common/oc_ip.h b/port/common/oc_ip.h index c1419361bb..7b8dfbc2e1 100644 --- a/port/common/oc_ip.h +++ b/port/common/oc_ip.h @@ -31,21 +31,21 @@ extern "C" { /** @brief Convert an IPv6 address to a human readable string */ int oc_ipv6_address_to_string(const oc_ipv6_addr_t *ipv6, char *buffer, - uint32_t buffer_size) OC_NONNULL(); + size_t buffer_size) OC_NONNULL(); /** Convert an IPv6 address with port to a human readable string */ int oc_ipv6_address_and_port_to_string(const oc_ipv6_addr_t *ipv6, char *buffer, - uint32_t buffer_size) OC_NONNULL(); + size_t buffer_size) OC_NONNULL(); #ifdef OC_IPV4 /** @brief Convert an IPv4 address to a human readable string */ int oc_ipv4_address_to_string(const oc_ipv4_addr_t *ipv4, char *buffer, - uint32_t buffer_size) OC_NONNULL(); + size_t buffer_size) OC_NONNULL(); /** Convert an IPv4 address with port to a human readable string */ int oc_ipv4_address_and_port_to_string(const oc_ipv4_addr_t *ipv4, char *buffer, - uint32_t buffer_size) OC_NONNULL(); + size_t buffer_size) OC_NONNULL(); #endif /* OC_IPV4 */ diff --git a/port/linux/tcpsession.c b/port/linux/tcpsession.c index f27b427899..9a76c4366d 100644 --- a/port/linux/tcpsession.c +++ b/port/linux/tcpsession.c @@ -19,6 +19,7 @@ #define __USE_GNU #include "api/oc_buffer_internal.h" +#include "api/oc_endpoint_internal.h" #include "api/oc_network_events_internal.h" #include "api/oc_session_events_internal.h" #include "api/oc_tcp_internal.h" @@ -199,15 +200,14 @@ get_interface_index(int sock) static void log_new_session(oc_endpoint_t *endpoint, int sock, bool is_connected) { - oc_string_t ep; + oc_string64_t ep; const char *addr = ""; - if (oc_endpoint_to_string(endpoint, &ep) == 0) { + if (oc_endpoint_to_string64(endpoint, &ep)) { addr = oc_string(ep); } OC_DBG("new TCP session endpoint: %s, endpoint interface: %d, sock: %d, " "connected: %d", addr, endpoint->interface_index, sock, (int)is_connected); - oc_free_string(&ep); } #endif /* OC_DBG_IS_ENABLED */ diff --git a/security/oc_tls.c b/security/oc_tls.c index 6619328bf7..e5c1c83e05 100644 --- a/security/oc_tls.c +++ b/security/oc_tls.c @@ -20,6 +20,7 @@ #include "oc_tls_internal.h" #include "api/oc_buffer_internal.h" +#include "api/oc_endpoint_internal.h" #include "api/oc_events_internal.h" #include "api/oc_network_events_internal.h" #include "api/oc_session_events_internal.h" @@ -398,13 +399,12 @@ process_drop_event_for_removed_endpoint(oc_process_event_t ev, oc_message_t *message = (oc_message_t *)data; if (oc_endpoint_compare(&message->endpoint, endpoint) == 0) { #if OC_DBG_IS_ENABLED - oc_string_t endpoint_str; - oc_endpoint_to_string(&message->endpoint, &endpoint_str); + oc_string64_t endpoint_str; + oc_endpoint_to_string64(&message->endpoint, &endpoint_str); OC_DBG("oc_tls: dropping %s message for removed endpoint(%s)", (ev == oc_event_to_oc_process_event(RI_TO_TLS_EVENT)) ? "sent" : "received", oc_string(endpoint_str)); - oc_free_string(&endpoint_str); #endif /* OC_DBG_IS_ENABLED */ oc_message_unref(message); return true; @@ -416,12 +416,11 @@ static void oc_tls_free_peer(oc_tls_peer_t *peer, bool inactivity_cb, bool from_reset) { #if OC_DBG_IS_ENABLED - oc_string_t endpoint_str; - oc_endpoint_to_string(&peer->endpoint, &endpoint_str); + oc_string64_t endpoint_str; + oc_endpoint_to_string64(&peer->endpoint, &endpoint_str); OC_DBG("oc_tls: freeing peer(%p): endpoint(%s), role(%s)", (void *)peer, oc_string(endpoint_str), peer->role == MBEDTLS_SSL_IS_SERVER ? "server" : "client"); - oc_free_string(&endpoint_str); #endif /* OC_DBG_IS_ENABLED */ #ifdef OC_PKI if (peer->user_data.free != NULL) { @@ -2110,12 +2109,11 @@ oc_tls_add_new_peer(oc_tls_new_peer_params_t params) oc_list_add(g_tls_peers, peer); #if OC_DBG_IS_ENABLED - oc_string_t endpoint_str; - oc_endpoint_to_string(&peer->endpoint, &endpoint_str); + oc_string64_t endpoint_str; + oc_endpoint_to_string64(&peer->endpoint, &endpoint_str); OC_DBG("oc_tls: new peer(%p) added: endpoint(%s), role(%s)", (void *)peer, oc_string(endpoint_str), peer->role == MBEDTLS_SSL_IS_SERVER ? "server" : "client"); - oc_free_string(&endpoint_str); #endif /* OC_DBG_IS_ENABLED */ return peer; @@ -2939,13 +2937,12 @@ oc_tls_recv_message(oc_message_t *message) peer = oc_tls_get_peer(&message->endpoint); if (peer != NULL && peer->role != MBEDTLS_SSL_IS_CLIENT) { #if OC_ERR_IS_ENABLED - oc_string_t endpoint_str; - oc_endpoint_to_string(&message->endpoint, &endpoint_str); + oc_string64_t endpoint_str; + oc_endpoint_to_string64(&message->endpoint, &endpoint_str); // The peer is not a client, so it is not possible to receive a message. OC_ERR("oc_tls: TCP-TLS peer %p with endpoint(%s) is not in role as " "client but as server", (void *)peer, oc_string(endpoint_str)); - oc_free_string(&endpoint_str); #endif /* OC_ERR_IS_ENABLED */ peer = NULL; } diff --git a/tests/gtest/Device.cpp b/tests/gtest/Device.cpp index 9b6016d07a..7f07746ace 100644 --- a/tests/gtest/Device.cpp +++ b/tests/gtest/Device.cpp @@ -422,8 +422,9 @@ TestDevice::ClearDynamicResources() #endif /* OC_SERVER */ -std::optional -TestDevice::GetEndpoint(size_t device, unsigned flags, unsigned exclude_flags) +oc_endpoint_t * +TestDevice::GetEndpointPtr(size_t device, unsigned flags, + unsigned exclude_flags) { oc_endpoint_t *ep = oc_connectivity_get_endpoints(device); auto has_matching_flags = [](const oc_endpoint_t *ep, unsigned flags, @@ -446,10 +447,20 @@ TestDevice::GetEndpoint(size_t device, unsigned flags, unsigned exclude_flags) while (ep != nullptr) { if (has_matching_flags(ep, flags, exclude_flags) && has_matching_device(ep, device)) { - return *ep; + return ep; } ep = ep->next; } + return nullptr; +} + +std::optional +TestDevice::GetEndpoint(size_t device, unsigned flags, unsigned exclude_flags) +{ + oc_endpoint_t *ep = GetEndpointPtr(device, flags, exclude_flags); + if (ep != nullptr) { + return *ep; + } return std::nullopt; } diff --git a/tests/gtest/Device.h b/tests/gtest/Device.h index 2333d33dd5..abd3c1ab67 100644 --- a/tests/gtest/Device.h +++ b/tests/gtest/Device.h @@ -223,6 +223,15 @@ class TestDevice { size_t device, unsigned flags = defaultEndpointIncludeFlags(), unsigned exclude_flags = kDefaultEndpointExcludeFlags); + /** @brief Get pointer to a matching endpoint of a device + * + * @warning list of endpoints might get refreshed by each call of the method, + * so you should not store the pointer for later use + */ + static oc_endpoint_t *GetEndpointPtr( + size_t device, unsigned flags = defaultEndpointIncludeFlags(), + unsigned exclude_flags = kDefaultEndpointExcludeFlags); + private: static int SetSystemTime(oc_clock_time_t time, void *user_data); diff --git a/tests/gtest/Endpoint.cpp b/tests/gtest/Endpoint.cpp index 04cc8fb061..eae276db60 100644 --- a/tests/gtest/Endpoint.cpp +++ b/tests/gtest/Endpoint.cpp @@ -51,10 +51,9 @@ FromString(const std::string &addr, oc_endpoint_t *ep, oc_string_t *uri) std::string ToAddress(const oc_endpoint_t &ep) { - oc_string_t ep_str{}; - oc_endpoint_to_string(&ep, &ep_str); + oc_string64_t ep_str{}; + oc_endpoint_to_string64(&ep, &ep_str); std::string s(oc_string(ep_str)); - oc_free_string(&ep_str); return s; }