Impl "unsafe" versions of UDPC_atostr(...)

These "unsafe" versions are guaranteed to not have the returned address
strings be overwritten by UDPC, but they must be manually free'd later
(as mentioned in the documentation).
This commit is contained in:
Stephen Seo 2023-06-22 13:25:26 +09:00
parent 4abd9fddcb
commit 77d69cabbc
3 changed files with 216 additions and 26 deletions

View file

@ -846,9 +846,71 @@ UDPC_EXPORT const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId con
* be considered undefined behavior. * be considered undefined behavior.
* *
* It may be easier to use UDPC_atostr_cid(). * 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); 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 // Helpers

View file

@ -2666,42 +2666,33 @@ const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId connectionId) {
return UDPC_atostr(ctx, connectionId.addr); return UDPC_atostr(ctx, connectionId.addr);
} }
const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) { void UDPC_Helper_atostr(char *buffer,
UDPC::Context *c = UDPC::verifyContext(ctx); UDPC_IPV6_ADDR_TYPE addr,
if(!c) { const uint32_t headIndex) {
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;
}
uint32_t index = headIndex; uint32_t index = headIndex;
bool usedDouble = false; bool usedDouble = false;
for(unsigned int i = 0; i < 16; ++i) { for(unsigned int i = 0; i < 16; ++i) {
if(i != 0 && i % 2 == 0) { if(i != 0 && i % 2 == 0) {
if(headIndex - index > 1 && c->atostrBuf[index - 1] == ':') { if(headIndex - index > 1 && buffer[index - 1] == ':') {
if(usedDouble) { if(usedDouble) {
if(c->atostrBuf[index - 2] != ':') { if(buffer[index - 2] != ':') {
c->atostrBuf[index++] = '0'; buffer[index++] = '0';
c->atostrBuf[index++] = ':'; buffer[index++] = ':';
} else { } else {
// continue use of double :, do nothing here // continue use of double :, do nothing here
} }
} else { } else {
usedDouble = true; usedDouble = true;
c->atostrBuf[index++] = ':'; buffer[index++] = ':';
} }
} else { } else {
c->atostrBuf[index++] = ':'; buffer[index++] = ':';
} }
} }
if(UDPC_IPV6_ADDR_SUB(addr)[i] == 0 if(UDPC_IPV6_ADDR_SUB(addr)[i] == 0
&& (headIndex - index <= 1 || c->atostrBuf[index] == ':')) { && (headIndex - index <= 1 || buffer[index] == ':')) {
continue; continue;
} else { } else {
std::stringstream sstream; std::stringstream sstream;
@ -2709,7 +2700,7 @@ const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
<< std::hex << (unsigned int) UDPC_IPV6_ADDR_SUB(addr)[i]; << std::hex << (unsigned int) UDPC_IPV6_ADDR_SUB(addr)[i];
std::string out(sstream.str()); std::string out(sstream.str());
unsigned int outOffset = 0; 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[0] == '0') {
if(out[1] == '0') { if(out[1] == '0') {
outOffset = 2; outOffset = 2;
@ -2722,25 +2713,67 @@ const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
continue; continue;
} else if(outOffset == 1) { } else if(outOffset == 1) {
if(out[outOffset] != '0') { if(out[outOffset] != '0') {
std::memcpy(c->atostrBuf + index, out.c_str() + outOffset, 1); std::memcpy(buffer + index, out.c_str() + outOffset, 1);
++index; ++index;
} }
} else { } else {
std::memcpy(c->atostrBuf + index, out.c_str() + outOffset, 2); std::memcpy(buffer + index, out.c_str() + outOffset, 2);
index += 2; index += 2;
} }
} }
} }
if(c->atostrBuf[index - 1] == ':' if(buffer[index - 1] == ':'
&& (headIndex - index <= 2 || c->atostrBuf[index - 2] != ':')) { && (headIndex - index <= 2 || buffer[index - 2] != ':')) {
c->atostrBuf[index++] = '0'; 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; 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 UDPC_strtoa(const char *addrStr) {
UDPC_IPV6_ADDR_TYPE result = in6addr_loopback; UDPC_IPV6_ADDR_TYPE result = in6addr_loopback;
std::cmatch matchResults; std::cmatch matchResults;

View file

@ -155,6 +155,101 @@ TEST(UDPC, atostr_concurrent) {
} }
} }
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) { TEST(UDPC, strtoa) {
struct in6_addr addr; struct in6_addr addr;