From 255930db9a195cf74623eb795f8f96e8aa143971 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Sat, 7 Sep 2019 16:36:11 +0900 Subject: [PATCH] Replace ipv4 with ipv6 ipv6 also supports sending to ipv4. --- cpp_impl/src/UDPC_Defines.hpp | 25 +- cpp_impl/src/UDPConnection.cpp | 536 ++++++++++++++++++++++----------- cpp_impl/src/UDPConnection.h | 34 +-- cpp_impl/src/test/TestUDPC.cpp | 219 +++++++++----- 4 files changed, 529 insertions(+), 285 deletions(-) diff --git a/cpp_impl/src/UDPC_Defines.hpp b/cpp_impl/src/UDPC_Defines.hpp index c65088c..48f1af9 100644 --- a/cpp_impl/src/UDPC_Defines.hpp +++ b/cpp_impl/src/UDPC_Defines.hpp @@ -12,8 +12,8 @@ #define UDPC_ID_NO_REC_CHK 0x20000000 #define UDPC_ID_RESENDING 0x10000000 -#define UDPC_ATOSTR_BUFCOUNT 64 -#define UDPC_ATOSTR_BUFSIZE 16 +#define UDPC_ATOSTR_BUFCOUNT 32 +#define UDPC_ATOSTR_BUFSIZE 40 #define UDPC_ATOSTR_SIZE (UDPC_ATOSTR_BUFCOUNT * UDPC_ATOSTR_BUFSIZE) #include @@ -38,7 +38,6 @@ static const auto ONE_SECOND = std::chrono::seconds(1); static const auto TEN_SECONDS = std::chrono::seconds(10); static const auto THIRTY_SECONDS = std::chrono::seconds(30); -static uint32_t LOCAL_ADDR = 0; static const auto INIT_PKT_INTERVAL_DT = std::chrono::seconds(5); static const auto HEARTBEAT_PKT_INTERVAL_DT = std::chrono::milliseconds(150); static const auto PACKET_TIMEOUT_TIME = ONE_SECOND; @@ -63,6 +62,10 @@ struct ConnectionIdHasher { std::size_t operator()(const UDPC_ConnectionId& key) const; }; +struct IPV6_Hasher { + std::size_t operator()(const struct in6_addr& addr) const; +}; + struct ConnectionData { ConnectionData(); ConnectionData(bool isServer, Context *ctx); @@ -93,7 +96,7 @@ struct ConnectionData { std::chrono::steady_clock::duration toggleT; std::chrono::steady_clock::duration toggleTimer; std::chrono::steady_clock::duration toggledTimer; - uint32_t addr; // in network order + struct in6_addr addr; // in network order uint16_t port; // in native order std::deque sentPkts; TSQueue sendPkts; @@ -237,14 +240,14 @@ public: char atostrBuf[UDPC_ATOSTR_SIZE]; int socketHandle; - struct sockaddr_in socketInfo; + struct sockaddr_in6 socketInfo; std::chrono::steady_clock::time_point lastUpdated; - // ipv4 address and port (as UDPC_ConnectionId) to ConnectionData + // ipv6 address and port (as UDPC_ConnectionId) to ConnectionData std::unordered_map conMap; - // ipv4 address to all connected UDPC_ConnectionId - std::unordered_map > addrConMap; - // id to ipv4 address and port (as UDPC_ConnectionId) + // ipv6 address to all connected UDPC_ConnectionId + std::unordered_map, IPV6_Hasher> addrConMap; + // id to ipv6 address and port (as UDPC_ConnectionId) std::unordered_map idMap; std::default_random_engine rng_engine; @@ -273,8 +276,12 @@ float timePointsToFSec( const std::chrono::steady_clock::time_point& older, const std::chrono::steady_clock::time_point& newer); +UDPC_PacketInfo get_empty_pinfo(); + } // namespace UDPC bool operator ==(const UDPC_ConnectionId& a, const UDPC_ConnectionId& b); +bool operator ==(const struct in6_addr& a, const struct in6_addr& b); + #endif diff --git a/cpp_impl/src/UDPConnection.cpp b/cpp_impl/src/UDPConnection.cpp index b0d51c9..0a2f775 100644 --- a/cpp_impl/src/UDPConnection.cpp +++ b/cpp_impl/src/UDPConnection.cpp @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include UDPC::SentPktInfo::SentPktInfo() : id(0), @@ -15,13 +18,29 @@ sentTime(std::chrono::steady_clock::now()) {} std::size_t UDPC::ConnectionIdHasher::operator()(const UDPC_ConnectionId& key) const { - return std::hash()(key.addr) ^ (std::hash()(key.port) << 1); + std::string value((const char*)key.addr.s6_addr, 16); + value.push_back((char)((key.port >> 8) & 0xFF)); + value.push_back((char)(key.port & 0xFF)); + return std::hash()(value); +} + +std::size_t UDPC::IPV6_Hasher::operator()(const struct in6_addr& addr) const { + return std::hash()(std::string((const char*)addr.s6_addr, 16)); } bool operator ==(const UDPC_ConnectionId& a, const UDPC_ConnectionId& b) { return a.addr == b.addr && a.port == b.port; } +bool operator ==(const struct in6_addr& a, const struct in6_addr& b) { + for(unsigned int i = 0; i < 16; ++i) { + if(a.s6_addr[i] != b.s6_addr[i]) { + return false; + } + } + return true; +} + UDPC::ConnectionData::ConnectionData() : flags(), id(0), @@ -86,14 +105,6 @@ rng_engine() flags.reset(0); } - if(UDPC::LOCAL_ADDR == 0) { - if(UDPC::isBigEndian()) { - UDPC::LOCAL_ADDR = 0x7F000001; - } else { - UDPC::LOCAL_ADDR = 0x0100007F; - } - } - rng_engine.seed(std::chrono::system_clock::now().time_since_epoch().count()); } @@ -173,29 +184,54 @@ float UDPC::timePointsToFSec( * (float)decltype(dt)::period::num / (float)decltype(dt)::period::den; } -UDPC_ConnectionId UDPC_create_id(uint32_t addr, uint16_t port) { +UDPC_PacketInfo UDPC::get_empty_pinfo() { + return UDPC_PacketInfo { + {0}, // data (array) + 0, // flags + 0, // dataSize + { // sender + {0}, // ipv6 addr + 0 // port + }, + { // receiver + {0}, // ipv6 addr + 0 // port + }, + }; +} + +UDPC_ConnectionId UDPC_create_id(struct in6_addr addr, uint16_t port) { return UDPC_ConnectionId{addr, port}; } -UDPC_HContext UDPC_init(uint16_t listenPort, uint32_t listenAddr, int isClient) { +UDPC_ConnectionId UDPC_create_id_anyaddr(uint16_t port) { + return UDPC_ConnectionId{in6addr_any, port}; +} + +UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient) { UDPC::Context *ctx = new UDPC::Context(false); ctx->flags.set(1, isClient); // create socket - ctx->socketHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ctx->socketHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if(ctx->socketHandle <= 0) { // TODO maybe different way of handling init fail delete ctx; return nullptr; } + // allow ipv4 connections on ipv6 socket + { + int no = 0; + setsockopt(ctx->socketHandle, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)); + } + // bind socket - ctx->socketInfo.sin_family = AF_INET; - ctx->socketInfo.sin_addr.s_addr = - (listenAddr == 0 ? INADDR_ANY : listenAddr); - ctx->socketInfo.sin_port = htons(listenPort); + ctx->socketInfo.sin6_family = AF_INET6; + ctx->socketInfo.sin6_addr = listenId.addr; + ctx->socketInfo.sin6_port = htons(listenId.port); if(bind(ctx->socketHandle, (const struct sockaddr *)&ctx->socketInfo, - sizeof(struct sockaddr_in)) < 0) { + sizeof(struct sockaddr_in6)) < 0) { // TODO maybe different way of handling init fail CleanupSocket(ctx->socketHandle); delete ctx; @@ -203,11 +239,11 @@ UDPC_HContext UDPC_init(uint16_t listenPort, uint32_t listenAddr, int isClient) } // TODO verify this is necessary to get the listen port - if(ctx->socketInfo.sin_port == 0) { - struct sockaddr_in getInfo; - socklen_t size = sizeof(struct sockaddr_in); + if(ctx->socketInfo.sin6_port == 0) { + struct sockaddr_in6 getInfo; + socklen_t size = sizeof(struct sockaddr_in6); if(getsockname(ctx->socketHandle, (struct sockaddr *)&getInfo, &size) == 0) { - ctx->socketInfo.sin_port = getInfo.sin_port; + ctx->socketInfo.sin6_port = getInfo.sin6_port; } } @@ -230,10 +266,9 @@ UDPC_HContext UDPC_init(uint16_t listenPort, uint32_t listenAddr, int isClient) return (UDPC_HContext) ctx; } -UDPC_HContext UDPC_init_threaded_update(uint16_t listenPort, uint32_t listenAddr, +UDPC_HContext UDPC_init_threaded_update(UDPC_ConnectionId listenId, int isClient) { - UDPC::Context *ctx = - (UDPC::Context *)UDPC_init(listenPort, listenAddr, isClient); + UDPC::Context *ctx = (UDPC::Context *)UDPC_init(listenId, isClient); if(!ctx) { return nullptr; } @@ -270,7 +305,7 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::VERBOSE, "Timed out connection with ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); continue; @@ -284,7 +319,7 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::INFO, "Switching to bad mode in connection with ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); iter->second.flags.reset(1); @@ -310,7 +345,7 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::INFO, "Switching to good mode in connection with ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); iter->second.flags.set(1); @@ -379,22 +414,22 @@ void UDPC_update(UDPC_HContext ctx) { nullptr, 0x1); - struct sockaddr_in destinationInfo; - destinationInfo.sin_family = AF_INET; - destinationInfo.sin_addr.s_addr = iter->second.addr; - destinationInfo.sin_port = htons(iter->second.port); + struct sockaddr_in6 destinationInfo; + destinationInfo.sin6_family = AF_INET6; + std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); + destinationInfo.sin6_port = htons(iter->second.port); long int sentBytes = sendto( c->socketHandle, buf.get(), 20, 0, (struct sockaddr*) &destinationInfo, - sizeof(struct sockaddr_in)); + sizeof(struct sockaddr_in6)); if(sentBytes != 20) { c->log( UDPC_LoggingType::ERROR, "Failed to send packet to initiate connection to ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); continue; @@ -414,22 +449,22 @@ void UDPC_update(UDPC_HContext ctx) { &iter->second.lseq, 0x1); - struct sockaddr_in destinationInfo; - destinationInfo.sin_family = AF_INET; - destinationInfo.sin_addr.s_addr = iter->second.addr; - destinationInfo.sin_port = htons(iter->second.port); + struct sockaddr_in6 destinationInfo; + destinationInfo.sin6_family = AF_INET6; + std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); + destinationInfo.sin6_port = htons(iter->second.port); long int sentBytes = sendto( c->socketHandle, buf.get(), 20, 0, (struct sockaddr*) &destinationInfo, - sizeof(struct sockaddr_in)); + sizeof(struct sockaddr_in6)); if(sentBytes != 20) { c->log( UDPC_LoggingType::ERROR, "Failed to send packet to initiate connection to ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); continue; @@ -456,31 +491,31 @@ void UDPC_update(UDPC_HContext ctx) { &iter->second.lseq, 0); - struct sockaddr_in destinationInfo; - destinationInfo.sin_family = AF_INET; - destinationInfo.sin_addr.s_addr = iter->second.addr; - destinationInfo.sin_port = htons(iter->second.port); + struct sockaddr_in6 destinationInfo; + destinationInfo.sin6_family = AF_INET6; + std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); + destinationInfo.sin6_port = htons(iter->second.port); long int sentBytes = sendto( c->socketHandle, buf.get(), 20, 0, (struct sockaddr*) &destinationInfo, - sizeof(struct sockaddr_in)); + sizeof(struct sockaddr_in6)); if(sentBytes != 20) { c->log( UDPC_LoggingType::ERROR, "Failed to send heartbeat packet to ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); continue; } - UDPC_PacketInfo pInfo{{0}, 0, 0, 0, 0, 0, 0}; - pInfo.sender.addr = UDPC::LOCAL_ADDR; + UDPC_PacketInfo pInfo = UDPC::get_empty_pinfo(); + pInfo.sender.addr = in6addr_loopback; pInfo.receiver.addr = iter->first.addr; - pInfo.sender.port = c->socketInfo.sin_port; + pInfo.sender.port = c->socketInfo.sin6_port; pInfo.receiver.port = iter->second.port; *((uint32_t*)(pInfo.data + 8)) = iter->second.lseq - 1; @@ -493,7 +528,7 @@ void UDPC_update(UDPC_HContext ctx) { iter->second.sentInfoMap.insert(std::make_pair(sentPktInfo->id, sentPktInfo)); } else { // sendPkts or priorityPkts not empty - UDPC_PacketInfo pInfo; + UDPC_PacketInfo pInfo = UDPC::get_empty_pinfo(); bool isResending = false; if(!iter->second.priorityPkts.empty()) { // TODO verify getting struct copy is valid @@ -515,22 +550,22 @@ void UDPC_update(UDPC_HContext ctx) { (pInfo.flags & 0x4) | (isResending ? 0x8 : 0)); std::memcpy(buf.get() + 20, pInfo.data, pInfo.dataSize); - struct sockaddr_in destinationInfo; - destinationInfo.sin_family = AF_INET; - destinationInfo.sin_addr.s_addr = iter->second.addr; - destinationInfo.sin_port = htons(iter->second.port); + struct sockaddr_in6 destinationInfo; + destinationInfo.sin6_family = AF_INET6; + std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); + destinationInfo.sin6_port = htons(iter->second.port); long int sentBytes = sendto( c->socketHandle, buf.get(), pInfo.dataSize + 20, 0, (struct sockaddr*) &destinationInfo, - sizeof(struct sockaddr_in)); + sizeof(struct sockaddr_in6)); if(sentBytes != 20 + pInfo.dataSize) { c->log( UDPC_LoggingType::ERROR, "Failed to send packet to ", - UDPC_atostr(ctx, iter->second.addr), + UDPC_atostr(ctx, iter->first), ":", iter->second.port); continue; @@ -538,25 +573,25 @@ void UDPC_update(UDPC_HContext ctx) { if((pInfo.flags & 0x4) == 0) { // is check-received, store data in case packet gets lost - UDPC_PacketInfo sentPInfo; + UDPC_PacketInfo sentPInfo = UDPC::get_empty_pinfo(); std::memcpy(sentPInfo.data, buf.get(), 20 + pInfo.dataSize); sentPInfo.flags = 0; sentPInfo.dataSize = 20 + pInfo.dataSize; - sentPInfo.sender.addr = UDPC::LOCAL_ADDR; + sentPInfo.sender.addr = in6addr_loopback; sentPInfo.receiver.addr = iter->first.addr; - sentPInfo.sender.port = c->socketInfo.sin_port; + sentPInfo.sender.port = c->socketInfo.sin6_port; sentPInfo.receiver.port = iter->second.port; iter->second.sentPkts.push_back(std::move(pInfo)); iter->second.cleanupSentPkts(); } else { // is not check-received, only id stored in data array - UDPC_PacketInfo sentPInfo; + UDPC_PacketInfo sentPInfo = UDPC::get_empty_pinfo(); sentPInfo.flags = 0x4; sentPInfo.dataSize = 0; - sentPInfo.sender.addr = UDPC::LOCAL_ADDR; + sentPInfo.sender.addr = in6addr_loopback; sentPInfo.receiver.addr = iter->first.addr; - sentPInfo.sender.port = c->socketInfo.sin_port; + sentPInfo.sender.port = c->socketInfo.sin6_port; sentPInfo.receiver.port = iter->second.port; *((uint32_t*)(sentPInfo.data + 8)) = iter->second.lseq - 1; @@ -575,7 +610,7 @@ void UDPC_update(UDPC_HContext ctx) { #if UDPC_PLATFORM == UDPC_PLATFORM_WINDOWS typedef int socklen_t; #endif - struct sockaddr_in receivedData; + struct sockaddr_in6 receivedData; socklen_t receivedDataSize = sizeof(receivedData); int bytes = recvfrom( c->socketHandle, @@ -593,9 +628,9 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::INFO, "Received packet is smaller than header, ignoring packet from ", - UDPC_atostr(ctx, receivedData.sin_addr.s_addr), + UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}), ":", - receivedData.sin_port); + receivedData.sin6_port); return; } @@ -605,9 +640,9 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::INFO, "Received packet has invalid protocol id, ignoring packet from ", - UDPC_atostr(ctx, receivedData.sin_addr.s_addr), + UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}), ":", - receivedData.sin_port); + receivedData.sin6_port); return; } @@ -622,7 +657,7 @@ void UDPC_update(UDPC_HContext ctx) { bool isResending = conID & UDPC_ID_RESENDING; conID &= 0x0FFFFFFF; - UDPC_ConnectionId identifier{receivedData.sin_addr.s_addr, ntohs(receivedData.sin_port)}; + UDPC_ConnectionId identifier{receivedData.sin6_addr, ntohs(receivedData.sin6_port)}; if(isConnect && c->flags.test(2)) { // is connect packet and is accepting new connections @@ -632,12 +667,12 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::VERBOSE, "Establishing connection with client ", - UDPC_atostr(ctx, receivedData.sin_addr.s_addr), + UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}), ":", - receivedData.sin_port); + receivedData.sin6_port); UDPC::ConnectionData newConnection(true, c); - newConnection.addr = receivedData.sin_addr.s_addr; - newConnection.port = ntohs(receivedData.sin_port); + newConnection.addr = receivedData.sin6_addr; + newConnection.port = ntohs(receivedData.sin6_port); c->idMap.insert(std::make_pair(newConnection.id, identifier)); c->conMap.insert(std::make_pair(identifier, std::move(newConnection))); @@ -666,9 +701,9 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::VERBOSE, "Established connection with server ", - UDPC_atostr(ctx, receivedData.sin_addr.s_addr), + UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}), ":", - receivedData.sin_port); + receivedData.sin6_port); // TODO trigger event client established connection with server } return; @@ -687,9 +722,9 @@ void UDPC_update(UDPC_HContext ctx) { c->log( UDPC_LoggingType::INFO, "Received valid packet from ", - UDPC_atostr(ctx, receivedData.sin_addr.s_addr), + UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}), ":", - receivedData.sin_port); + receivedData.sin6_port); // update rtt for(auto sentIter = iter->second.sentPkts.rbegin(); sentIter != iter->second.sentPkts.rend(); ++sentIter) { @@ -747,7 +782,7 @@ void UDPC_update(UDPC_HContext ctx) { break; } - UDPC_PacketInfo resendingData; + UDPC_PacketInfo resendingData = UDPC::get_empty_pinfo(); resendingData.dataSize = sentIter->dataSize - 20; std::memcpy(resendingData.data, sentIter->data + 20, resendingData.dataSize); resendingData.flags = 0; @@ -816,7 +851,7 @@ void UDPC_update(UDPC_HContext ctx) { } if(bytes > 20) { - UDPC_PacketInfo recPktInfo; + UDPC_PacketInfo recPktInfo = UDPC::get_empty_pinfo(); std::memcpy(recPktInfo.data, c->recvBuf, bytes); recPktInfo.dataSize = bytes; recPktInfo.flags = @@ -824,10 +859,10 @@ void UDPC_update(UDPC_HContext ctx) { | (isPing ? 0x2 : 0) | (isNotRecChecked ? 0x4 : 0) | (isResending ? 0x8 : 0); - recPktInfo.sender.addr = receivedData.sin_addr.s_addr; - recPktInfo.receiver.addr = UDPC::LOCAL_ADDR; - recPktInfo.sender.port = receivedData.sin_port; - recPktInfo.receiver.port = c->socketInfo.sin_port; + recPktInfo.sender.addr = receivedData.sin6_addr; + recPktInfo.receiver.addr = in6addr_loopback; + recPktInfo.sender.port = receivedData.sin6_port; + recPktInfo.receiver.port = c->socketInfo.sin6_port; if(iter->second.receivedPkts.size() == iter->second.receivedPkts.capacity()) { c->log( @@ -844,38 +879,35 @@ void UDPC_update(UDPC_HContext ctx) { } } -void UDPC_client_initiate_connection(UDPC_HContext ctx, uint32_t addr, uint16_t port) { +void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { UDPC::Context *c = UDPC::verifyContext(ctx); if(!c || !c->flags.test(1)) { return; } UDPC::ConnectionData newCon(false, c); - UDPC_ConnectionId identifier{addr, port}; // TODO make thread safe by using mutex - c->conMap.insert(std::make_pair(identifier, std::move(newCon))); - auto addrConIter = c->addrConMap.find(addr); + c->conMap.insert(std::make_pair(connectionId, std::move(newCon))); + auto addrConIter = c->addrConMap.find(connectionId.addr); if(addrConIter == c->addrConMap.end()) { auto insertResult = c->addrConMap.insert(std::make_pair( - addr, + connectionId.addr, std::unordered_set{} )); assert(insertResult.second); addrConIter = insertResult.first; } - addrConIter->second.insert(identifier); + addrConIter->second.insert(connectionId); } -int UDPC_get_queue_send_available(UDPC_HContext ctx, uint32_t addr, uint16_t port) { +int UDPC_get_queue_send_available(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { UDPC::Context *c = UDPC::verifyContext(ctx); if(!c) { return 0; } - UDPC_ConnectionId identifier{addr, port}; - - auto iter = c->conMap.find(identifier); + auto iter = c->conMap.find(connectionId); if(iter != c->conMap.end()) { return iter->second.sendPkts.capacity() - iter->second.sendPkts.size(); } else { @@ -883,7 +915,7 @@ int UDPC_get_queue_send_available(UDPC_HContext ctx, uint32_t addr, uint16_t por } } -void UDPC_queue_send(UDPC_HContext ctx, uint32_t destAddr, uint16_t destPort, +void UDPC_queue_send(UDPC_HContext ctx, UDPC_ConnectionId destinationId, uint32_t isChecked, void *data, uint32_t size) { if(size == 0 || !data) { return; @@ -894,9 +926,7 @@ void UDPC_queue_send(UDPC_HContext ctx, uint32_t destAddr, uint16_t destPort, return; } - UDPC_ConnectionId identifier{destAddr, destPort}; - - auto iter = c->conMap.find(identifier); + auto iter = c->conMap.find(destinationId); if(iter == c->conMap.end()) { c->log( UDPC_LoggingType::ERROR, @@ -905,12 +935,12 @@ void UDPC_queue_send(UDPC_HContext ctx, uint32_t destAddr, uint16_t destPort, return; } - UDPC_PacketInfo sendInfo; + UDPC_PacketInfo sendInfo = UDPC::get_empty_pinfo(); std::memcpy(sendInfo.data, data, size); sendInfo.dataSize = size; - sendInfo.sender.addr = UDPC::LOCAL_ADDR; - sendInfo.sender.port = c->socketInfo.sin_port; - sendInfo.receiver.addr = destAddr; + sendInfo.sender.addr = in6addr_loopback; + sendInfo.sender.port = c->socketInfo.sin6_port; + sendInfo.receiver.addr = destinationId.addr; sendInfo.receiver.port = iter->second.port; sendInfo.flags = (isChecked ? 0x0 : 0x4); @@ -925,53 +955,44 @@ int UDPC_set_accept_new_connections(UDPC_HContext ctx, int isAccepting) { return c->isAcceptNewConnections.exchange(isAccepting == 0 ? false : true); } -int UDPC_drop_connection(UDPC_HContext ctx, uint32_t addr, uint16_t port) { +int UDPC_drop_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId, bool dropAllWithAddr) { UDPC::Context *c = UDPC::verifyContext(ctx); if(!c) { return 0; } - UDPC_ConnectionId identifier{addr, port}; - - auto iter = c->conMap.find(identifier); - if(iter != c->conMap.end()) { - if(iter->second.flags.test(4)) { - c->idMap.erase(iter->second.id); - } - auto addrConIter = c->addrConMap.find(addr); + if(dropAllWithAddr) { + auto addrConIter = c->addrConMap.find(connectionId.addr); if(addrConIter != c->addrConMap.end()) { - addrConIter->second.erase(identifier); - if(addrConIter->second.empty()) { - c->addrConMap.erase(addrConIter); + for(auto identIter = addrConIter->second.begin(); + identIter != addrConIter->second.end(); + ++identIter) { + auto conIter = c->conMap.find(*identIter); + assert(conIter != c->conMap.end()); + if(conIter->second.flags.test(4)) { + c->idMap.erase(conIter->second.id); + } + c->conMap.erase(conIter); } + c->addrConMap.erase(addrConIter); + return 1; } - c->conMap.erase(iter); - return 1; - } - - return 0; -} - -int UDPC_drop_connection_addr(UDPC_HContext ctx, uint32_t addr) { - UDPC::Context *c = UDPC::verifyContext(ctx); - if(!c) { - return 0; - } - - auto addrConIter = c->addrConMap.find(addr); - if(addrConIter != c->addrConMap.end()) { - for(auto identIter = addrConIter->second.begin(); - identIter != addrConIter->second.end(); - ++identIter) { - auto conIter = c->conMap.find(*identIter); - assert(conIter != c->conMap.end()); - if(conIter->second.flags.test(4)) { - c->idMap.erase(conIter->second.id); + } else { + auto iter = c->conMap.find(connectionId); + if(iter != c->conMap.end()) { + if(iter->second.flags.test(4)) { + c->idMap.erase(iter->second.id); } - c->conMap.erase(conIter); + auto addrConIter = c->addrConMap.find(connectionId.addr); + if(addrConIter != c->addrConMap.end()) { + addrConIter->second.erase(connectionId); + if(addrConIter->second.empty()) { + c->addrConMap.erase(addrConIter); + } + } + c->conMap.erase(iter); + return 1; } - c->addrConMap.erase(addrConIter); - return 1; } return 0; @@ -996,13 +1017,13 @@ UDPC_LoggingType set_logging_type(UDPC_HContext ctx, UDPC_LoggingType loggingTyp UDPC_PacketInfo UDPC_get_received(UDPC_HContext ctx) { UDPC::Context *c = UDPC::verifyContext(ctx); if(!c) { - return UDPC_PacketInfo{{0}, 0, 0, 0, 0, 0, 0}; + return UDPC::get_empty_pinfo(); } // TODO impl - return UDPC_PacketInfo{{0}, 0, 0, 0, 0, 0, 0}; + return UDPC::get_empty_pinfo(); } -const char *UDPC_atostr(UDPC_HContext ctx, uint32_t addr) { +const char *UDPC_atostr(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { UDPC::Context *c = UDPC::verifyContext(ctx); if(!c) { return nullptr; @@ -1010,55 +1031,220 @@ const char *UDPC_atostr(UDPC_HContext ctx, uint32_t addr) { const uint32_t headIndex = c->atostrBufIndex.fetch_add(UDPC_ATOSTR_BUFSIZE) % UDPC_ATOSTR_SIZE; uint32_t index = headIndex; - for(int x = 0; x < 4; ++x) { - unsigned char temp = (addr >> (x * 8)) & 0xFF; + bool usedDouble = false; - if(temp >= 100) { - c->atostrBuf[index++] = '0' + temp / 100; + for(unsigned int i = 0; i < 16; ++i) { + if(i != 0 && i % 2 == 0) { + if(headIndex - index > 1 && c->atostrBuf[index - 1] == ':') { + if(usedDouble) { + if(c->atostrBuf[index - 2] != ':') { + c->atostrBuf[index++] = '0'; + c->atostrBuf[index++] = ':'; + } else { + // continue use of double :, do nothing here + } + } else { + usedDouble = true; + c->atostrBuf[index++] = ':'; + } + } else { + c->atostrBuf[index++] = ':'; + } } - if(temp >= 10) { - c->atostrBuf[index++] = '0' + ((temp / 10) % 10); - } - c->atostrBuf[index++] = '0' + temp % 10; - if(x < 3) { - c->atostrBuf[index++] = '.'; + if(connectionId.addr.s6_addr[i] == 0) { + continue; + } else { + std::stringstream sstream; + sstream << std::hex << (unsigned int) connectionId.addr.s6_addr[i]; + std::string out(sstream.str()); + if(out.size() == 1) { + if(out[0] != '0') { + std::memcpy(c->atostrBuf + index, out.c_str(), 1); + ++index; + } + } else { + std::memcpy(c->atostrBuf + index, out.c_str(), 2); + index += 2; + } } } + if(c->atostrBuf[index - 1] == ':' + && (headIndex - index <= 2 || c->atostrBuf[index - 2] != ':')) { + c->atostrBuf[index++] = '0'; + } + c->atostrBuf[index] = 0; return c->atostrBuf + headIndex; } -uint32_t UDPC_strtoa(const char *addrStr) { - uint32_t addr = 0; - uint32_t temp = 0; - uint32_t index = 0; - while(*addrStr != 0) { - if(*addrStr >= '0' && *addrStr <= '9') { - temp *= 10; - temp += *addrStr - '0'; - } else if(*addrStr == '.' && temp <= 0xFF && index < 3) { - if(UDPC::isBigEndian()) { - addr |= (temp << (24 - 8 * index++)); - } else { - addr |= (temp << (8 * index++)); - } - temp = 0; - } else { - return 0; +struct in6_addr UDPC_strtoa(const char *addrStr) { + struct in6_addr result{{0}}; + unsigned int index = 0; + unsigned int strIndex = 0; + int doubleColonIndex = -1; + unsigned char bytes[2] = {0, 0}; + unsigned char bytesState = 0; + bool prevColon = false; + + const auto checkInc = [&result, &index, &bytes] () -> bool { + if(index < 15) { + result.s6_addr[index++] = bytes[0]; + result.s6_addr[index++] = bytes[1]; + bytes[0] = 0; + bytes[1] = 0; + return false; } - ++addrStr; + return true; + }; + + while(addrStr[strIndex] != '\0') { + if(addrStr[strIndex] >= '0' && addrStr[strIndex] <= '9') { + switch(bytesState) { + case 0: + bytes[0] = (addrStr[strIndex] - '0'); + bytesState = 1; + break; + case 1: + bytes[0] = (bytes[0] << 4) | (addrStr[strIndex] - '0'); + bytesState = 2; + break; + case 2: + bytes[1] = (addrStr[strIndex] - '0'); + bytesState = 3; + break; + case 3: + bytes[1] = (bytes[1] << 4) | (addrStr[strIndex] - '0'); + bytesState = 0; + if(checkInc()) { + return in6addr_loopback; + } + break; + default: + return in6addr_loopback; + } + prevColon = false; + } else if(addrStr[strIndex] >= 'a' && addrStr[strIndex] <= 'f') { + switch(bytesState) { + case 0: + bytes[0] = (addrStr[strIndex] - 'a' + 10); + bytesState = 1; + break; + case 1: + bytes[0] = (bytes[0] << 4) | (addrStr[strIndex] - 'a' + 10); + bytesState = 2; + break; + case 2: + bytes[1] = (addrStr[strIndex] - 'a' + 10); + bytesState = 3; + break; + case 3: + bytes[1] = (bytes[1] << 4) | (addrStr[strIndex] - 'a' + 10); + bytesState = 0; + if(checkInc()) { + return in6addr_loopback; + } + break; + default: + return in6addr_loopback; + } + prevColon = false; + } else if(addrStr[strIndex] >= 'A' && addrStr[strIndex] <= 'F') { + switch(bytesState) { + case 0: + bytes[0] = (addrStr[strIndex] - 'A' + 10); + bytesState = 1; + break; + case 1: + bytes[0] = (bytes[0] << 4) | (addrStr[strIndex] - 'A' + 10); + bytesState = 2; + break; + case 2: + bytes[1] = (addrStr[strIndex] - 'A' + 10); + bytesState = 3; + break; + case 3: + bytes[1] = (bytes[1] << 4) | (addrStr[strIndex] - 'A' + 10); + bytesState = 0; + if(checkInc()) { + return in6addr_loopback; + } + break; + default: + return in6addr_loopback; + } + prevColon = false; + } else if(addrStr[strIndex] == ':') { + switch(bytesState) { + case 1: + case 2: + bytes[1] = bytes[0]; + bytes[0] = 0; + if(checkInc()) { + return in6addr_loopback; + } + break; + case 3: + bytes[1] |= (bytes[0] & 0xF) << 4; + bytes[0] = bytes[0] >> 4; + if(checkInc()) { + return in6addr_loopback; + } + break; + case 0: + break; + default: + return in6addr_loopback; + } + bytesState = 0; + if(prevColon) { + if(doubleColonIndex >= 0) { + return in6addr_loopback; + } else { + doubleColonIndex = index; + } + } else { + prevColon = true; + } + } else { + return in6addr_loopback; + } + + ++strIndex; + } + switch(bytesState) { + case 1: + case 2: + bytes[1] = bytes[0]; + bytes[0] = 0; + if(checkInc()) { + return in6addr_loopback; + } + break; + case 3: + bytes[1] |= (bytes[0] & 0xF) << 4; + bytes[0] = bytes[0] >> 4; + if(checkInc()) { + return in6addr_loopback; + } + break; + case 0: + break; + default: + return in6addr_loopback; } - if(index == 3 && temp <= 0xFF) { - if(UDPC::isBigEndian()) { - addr |= temp; - } else { - addr |= temp << 24; + if(doubleColonIndex >= 0) { + strIndex = 16 - index; + if(strIndex < 2) { + return in6addr_loopback; + } + for(unsigned int i = 16; i-- > (unsigned int)doubleColonIndex + strIndex; ) { + result.s6_addr[i] = result.s6_addr[i - strIndex]; + result.s6_addr[i - strIndex] = 0; } - return addr; - } else { - return 0; } + + return result; } diff --git a/cpp_impl/src/UDPConnection.h b/cpp_impl/src/UDPConnection.h index ee9bb77..9533b2a 100644 --- a/cpp_impl/src/UDPConnection.h +++ b/cpp_impl/src/UDPConnection.h @@ -51,7 +51,7 @@ typedef struct UDPC_Context *UDPC_HContext; typedef enum { SILENT, ERROR, WARNING, VERBOSE, INFO } UDPC_LoggingType; typedef struct { - uint32_t addr; + struct in6_addr addr; uint16_t port; } UDPC_ConnectionId; @@ -70,35 +70,29 @@ typedef struct { UDPC_ConnectionId receiver; } UDPC_PacketInfo; -UDPC_ConnectionId UDPC_create_id(uint32_t addr, uint16_t port); +/// port should be in native byte order (not network/big-endian) +UDPC_ConnectionId UDPC_create_id(struct in6_addr addr, uint16_t port); -/// listenPort must be in native byte order, listenAddr must be in network byte order (big-endian) -UDPC_HContext UDPC_init(uint16_t listenPort, uint32_t listenAddr, int isClient); -/// listenPort must be in native byte order, listenAddr must be in network byte order (big-endian) -UDPC_HContext UDPC_init_threaded_update(uint16_t listenPort, uint32_t listenAddr, +UDPC_ConnectionId UDPC_create_id_anyaddr(uint16_t port); + +UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient); +UDPC_HContext UDPC_init_threaded_update(UDPC_ConnectionId listenId, int isClient); void UDPC_destroy(UDPC_HContext ctx); void UDPC_update(UDPC_HContext ctx); -/// addr must be in network byte order (big-endian), port must be in native byte order -void UDPC_client_initiate_connection(UDPC_HContext ctx, uint32_t addr, uint16_t port); +void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId); -/// addr must be in network byte order (big-endian), port must be in native byte order -int UDPC_get_queue_send_available(UDPC_HContext ctx, uint32_t addr, uint16_t port); +int UDPC_get_queue_send_available(UDPC_HContext ctx, UDPC_ConnectionId connectionId); -/// destAddr must be in network byte order (big-endian), destPort must be in native byte order -void UDPC_queue_send(UDPC_HContext ctx, uint32_t destAddr, uint16_t destPort, +void UDPC_queue_send(UDPC_HContext ctx, UDPC_ConnectionId destinationId, uint32_t isChecked, void *data, uint32_t size); int UDPC_set_accept_new_connections(UDPC_HContext ctx, int isAccepting); -/// addr must be in network byte order (big-endian), port must be in native byte order -int UDPC_drop_connection(UDPC_HContext ctx, uint32_t addr, uint16_t port); - -/// addr must be in network byte order, drops all connections to specified addr -int UDPC_drop_connection_addr(UDPC_HContext ctx, uint32_t addr); +int UDPC_drop_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId, bool dropAllWithAddr); uint32_t UDPC_set_protocol_id(UDPC_HContext ctx, uint32_t id); @@ -106,11 +100,9 @@ UDPC_LoggingType set_logging_type(UDPC_HContext ctx, UDPC_LoggingType loggingTyp UDPC_PacketInfo UDPC_get_received(UDPC_HContext ctx); -/// addr must be in network byte order -const char *UDPC_atostr(UDPC_HContext ctx, uint32_t addr); +const char *UDPC_atostr(UDPC_HContext ctx, UDPC_ConnectionId connectionId); -/// returns a 4 byte unsigned integer address in network byte order -uint32_t UDPC_strtoa(const char *addrStr); +struct in6_addr UDPC_strtoa(const char *addrStr); #ifdef __cplusplus } diff --git a/cpp_impl/src/test/TestUDPC.cpp b/cpp_impl/src/test/TestUDPC.cpp index bec1094..e85c5d0 100644 --- a/cpp_impl/src/test/TestUDPC.cpp +++ b/cpp_impl/src/test/TestUDPC.cpp @@ -9,108 +9,167 @@ TEST(UDPC, atostr) { UDPC::Context context(false); + UDPC_ConnectionId conId; const char* resultBuf; - resultBuf = UDPC_atostr((UDPC_HContext)&context, 0x0100007F); - EXPECT_EQ(std::strcmp(resultBuf, "127.0.0.1"), 0); + for(unsigned int i = 0; i < 16; ++i) { + conId.addr.s6_addr[i] = (i % 3 == 0 ? 0xFF : (i % 3 == 1 ? 0x12 : 0x56)); + } + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + EXPECT_STREQ(resultBuf, "ff12:56ff:1256:ff12:56ff:1256:ff12:56ff"); - resultBuf = UDPC_atostr((UDPC_HContext)&context, 0xFF08000A); - EXPECT_EQ(std::strcmp(resultBuf, "10.0.8.255"), 0); + for(unsigned int i = 0; i < 8; ++i) { + conId.addr.s6_addr[i] = 0; + } + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + EXPECT_STREQ(resultBuf, "::56ff:1256:ff12:56ff"); - resultBuf = UDPC_atostr((UDPC_HContext)&context, 0x0201A8C0); - EXPECT_EQ(std::strcmp(resultBuf, "192.168.1.2"), 0); + conId.addr.s6_addr[0] = 1; + conId.addr.s6_addr[1] = 2; + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + EXPECT_STREQ(resultBuf, "12::56ff:1256:ff12:56ff"); + + conId.addr.s6_addr[14] = 0; + conId.addr.s6_addr[15] = 0; + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + EXPECT_STREQ(resultBuf, "12::56ff:1256:ff12:0"); + + for(unsigned int i = 0; i < 15; ++i) { + conId.addr.s6_addr[i] = 0; + } + conId.addr.s6_addr[15] = 1; + + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + EXPECT_STREQ(resultBuf, "::1"); + + conId.addr.s6_addr[15] = 0; + + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + EXPECT_STREQ(resultBuf, "::"); } TEST(UDPC, atostr_concurrent) { UDPC::Context context(false); const char* results[64] = { - "0.0.0.0", - "1.1.1.1", - "2.2.2.2", - "3.3.3.3", - "4.4.4.4", - "5.5.5.5", - "6.6.6.6", - "7.7.7.7", - "8.8.8.8", - "9.9.9.9", - "10.10.10.10", - "11.11.11.11", - "12.12.12.12", - "13.13.13.13", - "14.14.14.14", - "15.15.15.15", - "16.16.16.16", - "17.17.17.17", - "18.18.18.18", - "19.19.19.19", - "20.20.20.20", - "21.21.21.21", - "22.22.22.22", - "23.23.23.23", - "24.24.24.24", - "25.25.25.25", - "26.26.26.26", - "27.27.27.27", - "28.28.28.28", - "29.29.29.29", - "30.30.30.30", - "31.31.31.31", - "32.32.32.32", - "33.33.33.33", - "34.34.34.34", - "35.35.35.35", - "36.36.36.36", - "37.37.37.37", - "38.38.38.38", - "39.39.39.39", - "40.40.40.40", - "41.41.41.41", - "42.42.42.42", - "43.43.43.43", - "44.44.44.44", - "45.45.45.45", - "46.46.46.46", - "47.47.47.47", - "48.48.48.48", - "49.49.49.49", - "50.50.50.50", - "51.51.51.51", - "52.52.52.52", - "53.53.53.53", - "54.54.54.54", - "55.55.55.55", - "56.56.56.56", - "57.57.57.57", - "58.58.58.58", - "59.59.59.59", - "60.60.60.60", - "61.61.61.61", - "62.62.62.62", - "63.63.63.63" + "::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 futures[64]; - const char* ptrs[64]; + std::future futures[32]; + const char* ptrs[32]; for(unsigned int i = 0; i < 2; ++i) { - for(unsigned int j = 0; j < 64; ++j) { + for(unsigned int j = 0; j < 32; ++j) { futures[j] = std::async(std::launch::async, [] (unsigned int id, const char** ptr, UDPC::Context* c) { - ptr[id] = UDPC_atostr((UDPC_HContext)c, id | (id << 8) | (id << 16) | (id << 24)); + UDPC_ConnectionId conId = { + {0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x11, 0x11, 0x0, (unsigned char)(id + 1)}, + 0 + }; + ptr[id] = UDPC_atostr((UDPC_HContext)c, conId); }, j, ptrs, &context); } - for(unsigned int j = 0; j < 64; ++j) { + for(unsigned int j = 0; j < 32; ++j) { ASSERT_TRUE(futures[j].valid()); futures[j].wait(); } - for(unsigned int j = 0; j < 64; ++j) { - EXPECT_EQ(std::strcmp(ptrs[j], results[j]), 0); + for(unsigned int j = 0; j < 32; ++j) { + EXPECT_STREQ(ptrs[j], results[j]); } } } TEST(UDPC, strtoa) { - EXPECT_EQ(UDPC_strtoa("127.0.0.1"), 0x0100007F); - EXPECT_EQ(UDPC_strtoa("10.0.8.255"), 0xFF08000A); - EXPECT_EQ(UDPC_strtoa("192.168.1.2"), 0x0201A8C0); + struct in6_addr addr; + + for(unsigned int i = 0; i < 16; ++i) { + addr.s6_addr[i] = 0; + } + addr.s6_addr[15] = 1; + + EXPECT_EQ(UDPC_strtoa("::1"), addr); + + // check invalid + EXPECT_EQ(UDPC_strtoa("1:1::1:1::1"), addr); + EXPECT_EQ(UDPC_strtoa("derpadoodle"), addr); + + addr = { + 0xF0, 0xF, 0x0, 0x1, + 0x56, 0x78, 0x9A, 0xBC, + 0xDE, 0xFF, 0x1, 0x2, + 0x3, 0x4, 0x5, 0x6 + }; + EXPECT_EQ(UDPC_strtoa("F00F:1:5678:9abc:deff:102:304:506"), addr); + + addr = { + 0x0, 0xFF, 0x1, 0x0, + 0x0, 0x1, 0x10, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x12, 0x34, 0xab, 0xcd + }; + EXPECT_EQ(UDPC_strtoa("ff:100:1:1000::1234:abcd"), addr); }