Replace ipv4 with ipv6

ipv6 also supports sending to ipv4.
This commit is contained in:
Stephen Seo 2019-09-07 16:36:11 +09:00
parent 38eb06f105
commit 255930db9a
4 changed files with 529 additions and 285 deletions

View 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

View 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;
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;
}

View 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
}

View file

@ -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<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);
}