* be considered undefined behavior.
*
* It may be easier to use UDPC_atostr_cid().
+ *
+ * UDPC internally uses UDPC_atostr() for logging. This means that while UDPC
+ * is running, a string created with UDPC_atostr() may be overwritten
+ * eventually by UDPC. To avoid this situation, UDPC_atostr_unsafe() or
+ * UDPC_atostr_unsafe_cid() may be used as the ptr returned by it will not be
+ * overwritten by UDPC as time passes.
*/
UDPC_EXPORT const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr);
+/*!
+ * \brief Similar to UPDC_atostr(), but the returned ptr must be free'd.
+ *
+ * \warning The returned pointer must be freed with free(), or
+ * UDPC_atostr_unsafe_free(), or UDPC_atostr_unsafe_free_ptr().
+ *
+ * This function is thread-safe.
+ *
+ * UDPC internally uses UDPC_atostr() for logging. This means that while UDPC
+ * is running, a string created with UDPC_atostr() may be overwritten
+ * eventually by UDPC. To avoid this situation, UDPC_atostr_unsafe() or
+ * UDPC_atostr_unsafe_cid() may be used as the ptr returned by it will not be
+ * overwritten by UDPC as time passes.
+ *
+ * It may be easier to use UDPC_atostr_unsafe_cid().
+ */
+UDPC_EXPORT const char *UDPC_atostr_unsafe(UDPC_IPV6_ADDR_TYPE addr);
+
+/*!
+ * \brief Similar to UPDC_atostr(), but the returned ptr must be free'd.
+ *
+ * \warning The returned pointer must be freed with free(), or
+ * UDPC_atostr_unsafe_free(), or UDPC_atostr_unsafe_free_ptr().
+ *
+ * This function is thread-safe.
+ *
+ * UDPC internally uses UDPC_atostr() for logging. This means that while UDPC
+ * is running, a string created with UDPC_atostr() may be overwritten
+ * eventually by UDPC. To avoid this situation, UDPC_atostr_unsafe() or
+ * UDPC_atostr_unsafe_cid() may be used as the ptr returned by it will not be
+ * overwritten by UDPC as time passes.
+ */
+UDPC_EXPORT const char *UDPC_atostr_unsafe_cid(UDPC_ConnectionId cid);
+
+/*!
+ * \brief Free an addr string created with UDPC_atostr_unsafe().
+ *
+ * This function is thread-safe.
+ */
+UDPC_EXPORT void UDPC_atostr_unsafe_free(const char *addrBuf);
+
+/*!
+ * \brief Free an addr string created with UDPC_atostr_unsafe() and zeroes the
+ * pointer.
+ *
+ * This function is thread-safe.
+ *
+ * \code{.c}
+ * UDPC_ConnectionId aConnectionId = ...;
+ * const char *addrString = UDPC_atostr_unsafe_cid(aConnectionId);
+ * // Use addrString somewhere...
+ * UDPC_atostr_unsafe_free_ptr(&addrString);
+ * \endcode
+ */
+UDPC_EXPORT void UDPC_atostr_unsafe_free_ptr(const char **addrBuf);
+
// =============================================================================
// Helpers
return UDPC_atostr(ctx, connectionId.addr);
}
-const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
- UDPC::Context *c = UDPC::verifyContext(ctx);
- if(!c) {
- return nullptr;
- }
- uint32_t headIndex;
- {
- // Use a lock to ensure that "atostrBufIndex" is always a valid value.
- std::lock_guard<std::mutex> indexLock(c->atostrBufIndexMutex);
- c->atostrBufIndex = (c->atostrBufIndex + UDPC_ATOSTR_BUFSIZE) % UDPC_ATOSTR_SIZE;
- headIndex = c->atostrBufIndex;
- }
+void UDPC_Helper_atostr(char *buffer,
+ UDPC_IPV6_ADDR_TYPE addr,
+ const uint32_t headIndex) {
uint32_t index = headIndex;
bool usedDouble = false;
for(unsigned int i = 0; i < 16; ++i) {
if(i != 0 && i % 2 == 0) {
- if(headIndex - index > 1 && c->atostrBuf[index - 1] == ':') {
+ if(headIndex - index > 1 && buffer[index - 1] == ':') {
if(usedDouble) {
- if(c->atostrBuf[index - 2] != ':') {
- c->atostrBuf[index++] = '0';
- c->atostrBuf[index++] = ':';
+ if(buffer[index - 2] != ':') {
+ buffer[index++] = '0';
+ buffer[index++] = ':';
} else {
// continue use of double :, do nothing here
}
} else {
usedDouble = true;
- c->atostrBuf[index++] = ':';
+ buffer[index++] = ':';
}
} else {
- c->atostrBuf[index++] = ':';
+ buffer[index++] = ':';
}
}
if(UDPC_IPV6_ADDR_SUB(addr)[i] == 0
- && (headIndex - index <= 1 || c->atostrBuf[index] == ':')) {
+ && (headIndex - index <= 1 || buffer[index] == ':')) {
continue;
} else {
std::stringstream sstream;
<< std::hex << (unsigned int) UDPC_IPV6_ADDR_SUB(addr)[i];
std::string out(sstream.str());
unsigned int outOffset = 0;
- if(headIndex - index <= 1 || c->atostrBuf[index - 1] == ':') {
+ if(headIndex - index <= 1 || buffer[index - 1] == ':') {
if(out[0] == '0') {
if(out[1] == '0') {
outOffset = 2;
continue;
} else if(outOffset == 1) {
if(out[outOffset] != '0') {
- std::memcpy(c->atostrBuf + index, out.c_str() + outOffset, 1);
+ std::memcpy(buffer + index, out.c_str() + outOffset, 1);
++index;
}
} else {
- std::memcpy(c->atostrBuf + index, out.c_str() + outOffset, 2);
+ std::memcpy(buffer + index, out.c_str() + outOffset, 2);
index += 2;
}
}
}
- if(c->atostrBuf[index - 1] == ':'
- && (headIndex - index <= 2 || c->atostrBuf[index - 2] != ':')) {
- c->atostrBuf[index++] = '0';
+ if(buffer[index - 1] == ':'
+ && (headIndex - index <= 2 || buffer[index - 2] != ':')) {
+ buffer[index++] = '0';
}
- c->atostrBuf[index] = 0;
+ buffer[index] = 0;
+}
+
+const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
+ UDPC::Context *c = UDPC::verifyContext(ctx);
+ if(!c) {
+ return nullptr;
+ }
+ uint32_t headIndex;
+ {
+ // Use a lock to ensure that "atostrBufIndex" is always a valid value.
+ std::lock_guard<std::mutex> indexLock(c->atostrBufIndexMutex);
+ c->atostrBufIndex = (c->atostrBufIndex + UDPC_ATOSTR_BUFSIZE) % UDPC_ATOSTR_SIZE;
+ headIndex = c->atostrBufIndex;
+ }
+
+ UDPC_Helper_atostr(c->atostrBuf, addr, headIndex);
return c->atostrBuf + headIndex;
}
+const char *UDPC_atostr_unsafe(UDPC_IPV6_ADDR_TYPE addr) {
+ char *buf = (char*)std::malloc(40);
+ std::memset(buf, 0, 40);
+
+ UDPC_Helper_atostr(buf, addr, 0);
+
+ return buf;
+}
+
+const char *UDPC_atostr_unsafe_cid(UDPC_ConnectionId cid) {
+ return UDPC_atostr_unsafe(cid.addr);
+}
+
+void UDPC_atostr_unsafe_free(const char *addrBuf) {
+ if (addrBuf) {
+ std::free((void*)addrBuf);
+ }
+}
+
+void UDPC_atostr_unsafe_free_ptr(const char **addrBuf) {
+ if (addrBuf && *addrBuf) {
+ std::free((void*)*addrBuf);
+ *addrBuf = nullptr;
+ }
+}
+
UDPC_IPV6_ADDR_TYPE UDPC_strtoa(const char *addrStr) {
UDPC_IPV6_ADDR_TYPE result = in6addr_loopback;
std::cmatch matchResults;
}
}
+TEST(UDPC, atostr_unsafe) {
+ const char* results[64] = {
+ "::1111:1",
+ "::1111:2",
+ "::1111:3",
+ "::1111:4",
+ "::1111:5",
+ "::1111:6",
+ "::1111:7",
+ "::1111:8",
+ "::1111:9",
+ "::1111:a",
+ "::1111:b",
+ "::1111:c",
+ "::1111:d",
+ "::1111:e",
+ "::1111:f",
+ "::1111:10",
+ "::1111:11",
+ "::1111:12",
+ "::1111:13",
+ "::1111:14",
+ "::1111:15",
+ "::1111:16",
+ "::1111:17",
+ "::1111:18",
+ "::1111:19",
+ "::1111:1a",
+ "::1111:1b",
+ "::1111:1c",
+ "::1111:1d",
+ "::1111:1e",
+ "::1111:1f",
+ "::1111:20",
+ "::1111:21",
+ "::1111:22",
+ "::1111:23",
+ "::1111:24",
+ "::1111:25",
+ "::1111:26",
+ "::1111:27",
+ "::1111:28",
+ "::1111:29",
+ "::1111:2a",
+ "::1111:2b",
+ "::1111:2c",
+ "::1111:2d",
+ "::1111:2e",
+ "::1111:2f",
+ "::1111:30",
+ "::1111:31",
+ "::1111:32",
+ "::1111:33",
+ "::1111:34",
+ "::1111:35",
+ "::1111:36",
+ "::1111:37",
+ "::1111:38",
+ "::1111:39",
+ "::1111:3a",
+ "::1111:3b",
+ "::1111:3c",
+ "::1111:3d",
+ "::1111:3e",
+ "::1111:3f",
+ "::1111:40"
+ };
+ std::future<void> futures[32];
+ const char* ptrs[32];
+ for(unsigned int i = 0; i < 2; ++i) {
+ for(unsigned int j = 0; j < 32; ++j) {
+ futures[j] = std::async(std::launch::async, [] (unsigned int id, const char** ptr) {
+ UDPC_ConnectionId conId = {
+ {0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0x11, 0x11, 0x0, (unsigned char)(id + 1)},
+ 0,
+ 0
+ };
+ ptr[id] = UDPC_atostr_unsafe(conId.addr);
+ }, j, ptrs);
+ }
+ for(unsigned int j = 0; j < 32; ++j) {
+ ASSERT_TRUE(futures[j].valid());
+ futures[j].wait();
+ }
+ for(unsigned int j = 0; j < 32; ++j) {
+ EXPECT_STREQ(ptrs[j], results[j]);
+ UDPC_atostr_unsafe_free_ptr(ptrs + j);
+ UDPC_atostr_unsafe_free_ptr(ptrs + j);
+ }
+ }
+}
+
TEST(UDPC, strtoa) {
struct in6_addr addr;