]> git.seodisparate.com - UDPConnection/commitdiff
Replace ipv4 with ipv6
authorStephen Seo <seo.disparate@gmail.com>
Sat, 7 Sep 2019 07:36:11 +0000 (16:36 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Sat, 7 Sep 2019 07:36:11 +0000 (16:36 +0900)
ipv6 also supports sending to ipv4.

cpp_impl/src/UDPC_Defines.hpp
cpp_impl/src/UDPConnection.cpp
cpp_impl/src/UDPConnection.h
cpp_impl/src/test/TestUDPC.cpp

index c65088cceb3573cd12f34b85249d6fe08f2082a5..48f1af9f39967d74dcfa2175d1fa93af6ae9bab3 100644 (file)
@@ -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 <atomic>
@@ -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<UDPC_PacketInfo> sentPkts;
     TSQueue<UDPC_PacketInfo> 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<UDPC_ConnectionId, ConnectionData, ConnectionIdHasher> conMap;
-    // ipv4 address to all connected UDPC_ConnectionId
-    std::unordered_map<uint32_t, std::unordered_set<UDPC_ConnectionId, ConnectionIdHasher> > addrConMap;
-    // id to ipv4 address and port (as UDPC_ConnectionId)
+    // ipv6 address to all connected UDPC_ConnectionId
+    std::unordered_map<struct in6_addr, std::unordered_set<UDPC_ConnectionId, ConnectionIdHasher>, IPV6_Hasher> addrConMap;
+    // id to ipv6 address and port (as UDPC_ConnectionId)
     std::unordered_map<uint32_t, UDPC_ConnectionId> 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
index b0d51c9a63dd9fa0a012bea7fde21b595f98d311..0a2f775f848848c20904caa2cdd76cfdf7474f03 100644 (file)
@@ -8,6 +8,9 @@
 #include <vector>
 #include <functional>
 #include <type_traits>
+#include <string>
+#include <sstream>
+#include <ios>
 
 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<uint32_t>()(key.addr) ^ (std::hash<uint16_t>()(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<std::string>()(value);
+}
+
+std::size_t UDPC::IPV6_Hasher::operator()(const struct in6_addr& addr) const {
+    return std::hash<std::string>()(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<UDPC_ConnectionId, UDPC::ConnectionIdHasher>{}
             ));
         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;
-
-        if(temp >= 100) {
-            c->atostrBuf[index++] = '0' + temp / 100;
-        }
-        if(temp >= 10) {
-            c->atostrBuf[index++] = '0' + ((temp / 10) % 10);
+    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(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++] = ':';
+            }
         }
-        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++));
+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;
+        }
+        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 {
-                addr |= (temp << (8 * index++));
+                prevColon = true;
             }
-            temp = 0;
         } else {
-            return 0;
+            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;
         }
-        ++addrStr;
+        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;
 }
index ee9bb775d8ae0a7058150a2784614679db75343c..9533b2a81e817be78f957bfde0eb3d755c0b895c 100644 (file)
@@ -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
 }
index bec109478d234e75e567196e42a38a47e852d009..e85c5d0eb1f0c21ed897b844d895eff20b42b046 100644 (file)
 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");
+
+    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");
+
+    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");
 
-    resultBuf = UDPC_atostr((UDPC_HContext)&context, 0xFF08000A);
-    EXPECT_EQ(std::strcmp(resultBuf, "10.0.8.255"), 0);
+    conId.addr.s6_addr[15] = 0;
 
-    resultBuf = UDPC_atostr((UDPC_HContext)&context, 0x0201A8C0);
-    EXPECT_EQ(std::strcmp(resultBuf, "192.168.1.2"), 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<void> futures[64];
-    const char* ptrs[64];
+    std::future<void> 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);
 }