#include "io/socket/Socket.h" #include "io/socket/Endpoint.h" #include "system/System.h" #include "ehs/Encryption.h" #if defined(EHS_OS_WINDOWS) #include #include #elif defined(EHS_OS_LINUX) #include #include #include #include #include #include #include #include #endif namespace ehc { const Version Socket::ver(1, 0, 0); const UInt_64 Socket::internalSys = Str_8::Hash_64("Internal"); const UInt_64 Socket::connectOp = Str_8::Hash_64("Connect"); const UInt_64 Socket::connectedOp = Str_8::Hash_64("Connected"); const UInt_64 Socket::rejectedOp = Str_8::Hash_64("Rejected"); const UInt_64 Socket::disconnectOp = Str_8::Hash_64("Disconnect"); const UInt_64 Socket::disconnectedOp = Str_8::Hash_64("Disconnected"); const UInt_64 Socket::statusUpdateOp = Str_8::Hash_64("StatusUpdate"); const UInt_64 Socket::pingOp = Str_8::Hash_64("Ping"); const UInt_64 Socket::pongOp = Str_8::Hash_64("Pong"); const UInt_64 Socket::latencyOp = Str_8::Hash_64("Latency"); const UInt_64 Socket::receivedOp = Str_8::Hash_64("Received"); Socket::~Socket() { UnInitialize(); } Socket::Socket() : hdl(EHS_INVALID_SOCKET), type(IP::V4), port(0), bound(false), appVer(0, 0, 0), disposition(Disposition::UNKNOWN), dropPackets(false), hashId(0), buffer(nullptr), bufferSize(0), maxEndpoints(0), lastTSC(0), delta(0.0f), maxTimeout(5.0f), resendRate(0.5f), connectedCb(nullptr), activeCb(nullptr), disconnectedCb(nullptr) { AddType("Socket"); } Socket::Socket(const Version& ver, const Disposition disposition, const Str_8& id, const UInt_64 maxEndpoints) : hdl(EHS_INVALID_SOCKET), type(IP::V4), port(0), bound(false), appVer(ver), disposition(disposition), dropPackets(false), id(id), hashId(id.Hash_32()), buffer(nullptr), bufferSize(0), maxEndpoints(maxEndpoints), lastTSC(CPU::GetTSC()), delta(0.0f), maxTimeout(5.0f), resendRate(0.5f), connectedCb(nullptr), activeCb(nullptr), disconnectedCb(nullptr) { } Socket::Socket(const Socket& sock) : BaseObj(sock), hdl(EHS_INVALID_SOCKET), type(sock.type), address(sock.address), port(sock.port), bound(false), appVer(sock.appVer), disposition(sock.disposition), dropPackets(sock.dropPackets), id(sock.id), hashId(sock.hashId), buffer(nullptr), bufferSize(0), maxEndpoints(sock.maxEndpoints), lastTSC(CPU::GetTSC()), delta(0.0f), maxTimeout(sock.maxTimeout), resendRate(sock.resendRate), connectedCb(nullptr), activeCb(nullptr), disconnectedCb(nullptr) { AddType("Socket"); } Socket& Socket::operator=(const Socket& sock) { if (this == &sock) return *this; BaseObj::operator=(sock); hdl = EHS_INVALID_SOCKET; type = sock.type; address = sock.address; port = sock.port; bound = false; appVer = sock.appVer; disposition = sock.disposition; dropPackets = sock.dropPackets; id = sock.id; hashId = sock.hashId; buffer = nullptr; bufferSize = 0; systems = Array(); endpoints = Vector(); maxEndpoints = sock.maxEndpoints; lastTSC = 0; delta = 0.0f; maxTimeout = sock.maxTimeout; resendRate = sock.resendRate; connectedCb = nullptr; activeCb = nullptr; disconnectedCb = nullptr; return *this; } void Socket::Initialize() { if (hdl != EHS_INVALID_SOCKET) return; #if defined(EHS_OS_WINDOWS) WSADATA data = {}; int wsaCode = WSAStartup(MAKEWORD(2, 2), &data); if (wsaCode) { EHS_LOG_INT(LogType::ERR, 0, "WSAStartup failed with the error #" + Str_8::FromNum(wsaCode) + "."); return; } #endif if (type == IP::V6) hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); else if (type == IP::V4) hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); else return; if (hdl == EHS_INVALID_SOCKET) { UInt_32 code = 0; #if defined(EHS_OS_WINDOWS) code = WSAGetLastError(); #elif defined(EHS_OS_LINUX) code = errno; #endif EHS_LOG_INT(LogType::ERR, 1, "Failed to create socket with error #" + Str_8::FromNum(code) + "."); #if defined(EHS_OS_WINDOWS) if (WSACleanup() == SOCKET_ERROR) EHS_LOG_INT(LogType::ERR, 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); #endif return; } if (type == IP::V4) { buffer = new Byte[EHS_IPV4_UDP_PAYLOAD]; bufferSize = EHS_IPV4_UDP_PAYLOAD; } else if (type == IP::V6) { buffer = new Byte[EHS_IPV6_UDP_PAYLOAD]; bufferSize = EHS_IPV6_UDP_PAYLOAD; } } void Socket::UnInitialize() { if (hdl == EHS_INVALID_SOCKET) return; delete[] buffer; buffer = nullptr; bufferSize = 0; Serializer payload(Endianness::LE); payload.Write(0); for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetStatus() != Status::PENDING) endpoints[i]->Send(false, true, false, internalSys, disconnectOp, payload); delete endpoints[i]; } endpoints.Clear(); for (UInt_64 i = 0; i < systems.Size(); ++i) delete systems[i]; systems.Clear(); Int_32 code = 0; #if defined(EHS_OS_WINDOWS) code = closesocket(hdl); if (code == SOCKET_ERROR) EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + "."); #elif defined(EHS_OS_LINUX) code = close(hdl); if (code == -1) EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + "."); #endif hdl = EHS_INVALID_SOCKET; #if defined(EHS_OS_WINDOWS) if (WSACleanup() == SOCKET_ERROR) EHS_LOG_INT(LogType::ERR, 1, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); #endif bound = false; } void Socket::Bind(const Str_8& newAddress, const UInt_16 newPort) { if (hdl == EHS_INVALID_SOCKET || bound) return; if (type == IP::V6) Bind_v6(newAddress, newPort); else if (type == IP::V4) Bind_v4(newAddress, newPort); address = newAddress; port = newPort; bound = true; } void Socket::Connect(const Str_8& address, const UInt_16 port) { if (hdl == EHS_INVALID_SOCKET) return; Serializer payload(Endianness::LE); payload.Write(CPU::GetArchitecture()); payload.WriteStr(id); payload.WriteVersion(ver); payload.WriteVersion(appVer); Endpoint* end = new Endpoint(hdl, type, address, port); end->SetParent(this); end->Send(false, true, false, "Internal", "Connect", payload); endpoints.Push(end); } bool Socket::Disconnect(const Disposition disposition, const UInt_64 hashId, const Str_8& msg) { if (hdl == EHS_INVALID_SOCKET) return false; Endpoint* end = GetEndpoint(disposition, hashId); if (!end) return false; Str_8 dcMsg = end->GetId() + " has been disconnected."; if (msg.Size()) dcMsg += " Reason: " + msg; EHS_LOG_INT(LogType::INFO, 0, dcMsg); Serializer<> payload(Endianness::LE); payload.WriteStr(msg); end->Send(false, true, false, internalSys, disconnectOp, payload); return true; } bool Socket::Disconnect(const Disposition disposition, const Str_8& id, const Str_8& msg) { return Disconnect(disposition, id.Hash_32(), msg); } void Socket::Broadcast(const Disposition disposition, const Status status, const bool deltaLocked, const bool encrypted, const bool ensure, const UInt_64 sysHashId, const UInt_64 opHashId, const Serializer<>& payload) { if (hdl == EHS_INVALID_SOCKET) return; for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetStatus() == status) endpoints[i]->Send(deltaLocked, encrypted, ensure, sysHashId, opHashId, payload); } } void Socket::Broadcast(const Disposition disposition, const Status status, const bool deltaLocked, const bool encrypted, const bool ensure, const Str_8& sysId, const Str_8& opId, const Serializer<>& payload) { Broadcast(disposition, status, deltaLocked, encrypted, ensure, sysId.Hash_64(), opId.Hash_64(), payload); } void Socket::Poll() { if (hdl == EHS_INVALID_SOCKET) return; UInt_64 newTSC = CPU::GetTSC(); delta = (float)(newTSC - lastTSC) / (float)CPU::GetTSC_Freq(); lastTSC = newTSC; Str_8 rAddress; UInt_16 rPort = 0; UInt_16 received = 0; while ((received = Receive(&rAddress, &rPort, buffer, bufferSize))) { Serializer<> payload(Endianness::LE, buffer, received); bool encrypted = payload.Read(); if (encrypted) Encryption::Encrypt_64(payload.Size() - payload.GetOffset(), &payload[payload.GetOffset()]); payload.SetOffset(0); Header header = payload.Read
(); if (!header.ensure && header.endpointId && header.system == internalSys && header.op == connectOp) { Architecture rArch = payload.Read(); Str_8 rId = payload.ReadStr(); Endpoint* end = new Endpoint(hdl, header.disposition, rArch, rId, type, rAddress, rPort); end->SetStatus(Status::PENDING); end->SetParent(this); Serializer sPayload(Endianness::LE); Version rVer = payload.ReadVersion(); if (rVer != ver) { sPayload.WriteStr("Your Event Horizon Socket Layer version " + Str_8::FromNum(rVer.major) + "." + Str_8::FromNum(rVer.minor) + "." + Str_8::FromNum(rVer.patch) + " does not match remote endpoint version " + Str_8::FromNum(ver.major) + "." + Str_8::FromNum(ver.minor) + "." + Str_8::FromNum(ver.patch) + ". Connection rejected."); end->Send(false, true, false, internalSys, rejectedOp, sPayload); continue; } Version rAppVer = payload.ReadVersion(); if (rAppVer != appVer) { sPayload.WriteStr("Your application version " + Str_8::FromNum(rAppVer.major) + "." + Str_8::FromNum(rAppVer.minor) + "." + Str_8::FromNum(rAppVer.patch) + " does not match remote endpoint application version " + Str_8::FromNum(appVer.major) + "." + Str_8::FromNum(appVer.minor) + "." + Str_8::FromNum(appVer.patch) + ". Connection rejected."); end->Send(false, true, false, internalSys, rejectedOp, sPayload); continue; } if (HasEndpoint(header.disposition, header.endpointId)) { if (header.disposition == Disposition::SERVICE) { sPayload.WriteStr( "The service, \"" + end->GetId() + "\", is taken. Connection rejected."); end->Send(false, true, false, internalSys, rejectedOp, sPayload); continue; } else { sPayload.WriteStr( "The username, \"" + end->GetId() + "\", is taken. Connection rejected."); end->Send(false, true, false, internalSys, rejectedOp, sPayload); continue; } } if (connectedCb && !connectedCb(this, end)) { sPayload.WriteStr("Connection rejected."); end->Send(false, true, false, internalSys, rejectedOp, sPayload); continue; } endpoints.Push(end); sPayload.Write(CPU::GetArchitecture()); sPayload.WriteStr(id); UInt_64 active = GetEndpointsCount(Disposition::ENDPOINT, Status::ACTIVE); if (maxEndpoints && active >= maxEndpoints) { end->SetStatus(Status::IN_LOCAL_QUEUE); UpdateQueue(active); sPayload.Write(Status::IN_REMOTE_QUEUE); sPayload.Write(end->GetQueueSlot()); EHS_LOG_INT(LogType::INFO, 1, end->GetId() + " connected and is in queue slot " + end->GetQueueSlot() + "."); } else { end->SetStatus(Status::ACTIVE); if (activeCb) activeCb(this, end); sPayload.Write(Status::ACTIVE); sPayload.Write(0); EHS_LOG_INT(LogType::INFO, 1, end->GetId() + " connected."); } end->Send(false, true, false, internalSys, connectedOp, sPayload); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == connectedOp) { Endpoint* end = GetEndpoint(rAddress, rPort); if (!end || end->GetStatus() != Status::PENDING) continue; Architecture arch = payload.Read(); Str_8 id = payload.ReadStr(); *end = Endpoint(hdl, header.disposition, arch, id, type, rAddress, rPort); end->SetStatus(payload.Read()); end->SetQueueSlot(payload.Read()); end->SetParent(this); if (connectedCb) connectedCb(this, end); Str_8 msg = "Successfully connected to " + end->GetId(); if (end->GetStatus() == Status::IN_REMOTE_QUEUE) msg += " and in queue slot " + Str_8::FromNum(end->GetQueueSlot()) + "."; else msg += "."; EHS_LOG_INT(LogType::INFO, 2, msg); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == rejectedOp) { if (!RemoveEndpoint(rAddress, rPort)) continue; Str_8 msg = payload.ReadStr(); if (msg.Size()) EHS_LOG_INT(LogType::INFO, 3, msg); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == disconnectOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; end->Send(false, true, false, internalSys, disconnectedOp, {}); if (disconnectedCb) disconnectedCb(this, end); Str_8 dcMsg; if (header.disposition == Disposition::SERVICE) dcMsg = "You have been disconnected from, \"" + end->GetId() + "\"."; else dcMsg = end->GetId() + " has disconnected."; Str_8 msg = payload.ReadStr(); if (msg.Size()) dcMsg += " Reason: " + msg; EHS_LOG_INT(LogType::INFO, 4, dcMsg); RemoveEndpoint(header.disposition, end->GetHashId()); UpdateQueue(); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == disconnectedOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; if (disconnectedCb) disconnectedCb(this, end); RemoveEndpoint(end); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == statusUpdateOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; Status newStatus = payload.Read(); UInt_64 newSlot = payload.Read(); if (end->GetStatus() == Status::ACTIVE) { if (activeCb) activeCb(this, end); EHS_LOG_INT(LogType::INFO, 5, "Your connection status to " + end->GetId() + " has now become active."); } else if (end->GetStatus() == Status::IN_REMOTE_QUEUE && newStatus == Status::IN_REMOTE_QUEUE) { EHS_LOG_INT(LogType::INFO, 5, "Your queue slot for " + end->GetId() + " is now " + newSlot + "."); } end->SetStatus(newStatus); end->SetQueueSlot(newSlot); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == pingOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; end->SetDeltaRate(payload.Read()); end->Pong(delta); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == pongOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; end->SetDeltaRate(payload.Read()); end->SendLatency(); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == latencyOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; end->SetLatency(payload.Read()); } else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == receivedOp) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; UInt_64 msgId = payload.Read(); UInt_64 fragment = payload.Read(); end->RemoveInsurance(msgId, fragment); } else if (header.endpointId) { Endpoint* end = GetEndpoint(header.disposition, header.endpointId); if (!end) continue; if (dropPackets && !header.ensure && header.id < end->GetNextRecvId()) { EHS_LOG_INT(LogType::INFO, 6, "Old packet intentionally dropped."); continue; } if (header.ensure) { Serializer sPayload(Endianness::LE); sPayload.Write(header.id); sPayload.Write(header.fragment); end->Send(false, true, false, internalSys, receivedOp, sPayload); } end->AddReceived( header, Serializer<>(Endianness::LE, &payload[payload.GetOffset()], payload.Size() - payload.GetOffset()) ); } else { EHS_LOG_INT(LogType::INFO, 7, "Corrupted packet."); } } PollEndpoints(endpoints); } bool Socket::IsInitialized() const { return hdl != EHS_INVALID_SOCKET; } void Socket::SetAddressType(const IP newType) { if (hdl != EHS_INVALID_SOCKET) return; type = newType; } IP Socket::GetAddressType() const { return type; } Str_8 Socket::GetAddress() const { return address; } UInt_16 Socket::GetPort() const { return port; } bool Socket::IsBound() const { return bound; } Version Socket::GetVersion() const { return ver; } Version Socket::GetAppVersion() const { return appVer; } Disposition Socket::GetDisposition() const { return disposition; } void Socket::EnableDropPackets(const bool enable) { dropPackets = enable; } bool Socket::IsDropPacketsEnabled() const { return dropPackets; } Str_8 Socket::GetId() const { return id; } UInt_64 Socket::GetHashId() const { return hashId; } bool Socket::HasSystem(const UInt_64 hashId) const { if (internalSys == hashId) return true; for (UInt_64 i = 0; i < systems.Size(); ++i) if (systems[i]->GetHashId() == hashId) return true; return false; } bool Socket::HasSystem(const Str_8& id) const { return HasSystem(id.Hash_64()); } bool Socket::AddSystem(System* sys) { if (HasSystem(sys->GetHashId())) return false; systems.Push(sys); return true; } System* Socket::GetSystem(const UInt_64 hashId) { for (UInt_64 i = 0; i < systems.Size(); ++i) if (systems[i]->GetHashId() == hashId) return systems[i]; return nullptr; } System* Socket::GetSystem(const Str_8& id) { return GetSystem(id.Hash_32()); } bool Socket::HasEndpoint(const Disposition disposition, const Status status, const UInt_64 hashId) const { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetStatus() != status) continue; if (endpoints[i]->GetHashId() == hashId) return true; } return false; } bool Socket::HasEndpoint(const Disposition disposition, const Status status, const Str_8& id) const { return HasEndpoint(disposition, status, id.Hash_32()); } bool Socket::HasEndpoint(const Disposition disposition, const UInt_64 hashId) const { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetHashId() == hashId) return true; } return false; } bool Socket::HasEndpoint(const Disposition disposition, const Str_8& id) const { return HasEndpoint(disposition, id.Hash_64()); } bool Socket::HasEndpoint(const Str_8& address, const UInt_16 port) const { for (UInt_64 i = 0; i < endpoints.Size(); ++i) if (endpoints[i]->GetAddress() == address && endpoints[i]->GetPort() == port) return true; return false; } Endpoint* Socket::GetEndpoint(const Disposition disposition, const Status status, const UInt_64 hashId) { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetStatus() != status) continue; if (endpoints[i]->GetHashId() == hashId) return endpoints[i]; } return nullptr; } Endpoint* Socket::GetEndpoint(const Disposition disposition, const Status status, const Str_8& id) { return GetEndpoint(disposition, status, id.Hash_32()); } Endpoint* Socket::GetEndpoint(const Disposition disposition, const UInt_64 hashId) { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetHashId() == hashId) return endpoints[i]; } return nullptr; } Endpoint* Socket::GetEndpoint(const Disposition disposition, const Str_8& id) { return GetEndpoint(disposition, id.Hash_32()); } Endpoint* Socket::GetEndpoint(const Str_8& address, const UInt_16 port) { for (UInt_64 i = 0; i < endpoints.Size(); ++i) if (endpoints[i]->GetAddress() == address && endpoints[i]->GetPort() == port) return endpoints[i]; return nullptr; } Array Socket::GetEndpoints(const Disposition disposition, const Status status) { Array result(endpoints.Size()); UInt_64 count = 0; for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetStatus() == status) result[count++] = endpoints[i]; } result.Resize(count); return result; } Array Socket::GetEndpoints(const Disposition disposition) { Array result(endpoints.Size()); UInt_64 count = 0; for (UInt_64 i = 0; i < endpoints.Size(); ++i) if (endpoints[i]->GetDisposition() == disposition) result[count++] = endpoints[i]; result.Resize(count); return result; } UInt_64 Socket::GetEndpointsCount(const Disposition disposition, const Status status) { UInt_64 count = 0; for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetStatus() == status) ++count; } return count; } UInt_64 Socket::GetEndpointsCount(const Disposition disposition) { UInt_64 count = 0; for (UInt_64 i = 0; i < endpoints.Size(); ++i) if (endpoints[i]->GetDisposition() == disposition) ++count; return count; } UInt_64 Socket::GetMaxEndpoints() const { return maxEndpoints; } void Socket::SetBlocking(const bool blocking) { if (hdl == EHS_INVALID_SOCKET) { EHS_LOG_INT(LogType::ERR, 0, "Attempted to toggle blocking while socket is not initialized."); return; } #if defined(EHS_OS_WINDOWS) u_long r = (u_long)!blocking; int result = ioctlsocket(hdl, FIONBIO, &r); if (result != NO_ERROR) EHS_LOG_INT(LogType::ERR, 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(result) + "."); #elif defined(EHS_OS_LINUX) if (fcntl(hdl, F_SETFL, O_NONBLOCK, blocking) == -1) EHS_LOG_INT(LogType::ERR, 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + "."); #endif } bool Socket::IsBlocking() const { #if defined(EHS_OS_WINDOWS) u_long r = 0; if (ioctlsocket(hdl, FIONREAD, &r) == SOCKET_ERROR) EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve socket info."); return (bool)r; #elif defined(EHS_OS_LINUX) return (bool)fcntl(hdl, F_GETFL, O_NONBLOCK); #else return true; #endif } void Socket::SetMaxTimeout(const float seconds) { maxTimeout = seconds; } float Socket::GetMaxTimeout() const { return maxTimeout; } void Socket::SetResendRate(const float seconds) { resendRate = seconds; } float Socket::GetResendRate() const { return resendRate; } void Socket::SetConnectedCb(bool (*newCb)(Socket*, Endpoint*)) { connectedCb = newCb; } void Socket::SetActiveCb(void (*newCb)(Socket*, Endpoint*)) { activeCb = newCb; } void Socket::SetDisconnectedCb(void (*newCb)(Socket*, Endpoint*)) { disconnectedCb = newCb; } void Socket::UpdateQueue(UInt_64 active) { UInt_64 slot = 0; for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetStatus() == Status::IN_LOCAL_QUEUE) { if (active < maxEndpoints) { endpoints[i]->SetStatus(Status::ACTIVE); endpoints[i]->SetQueueSlot(0); Serializer payload(Endianness::LE); payload.Write(Status::ACTIVE); payload.Write(0); endpoints[i]->Send(false, true, false, internalSys, statusUpdateOp, payload); if (activeCb) activeCb(this, endpoints[i]); ++active; } else { if (endpoints[i]->GetQueueSlot() != slot) { Serializer payload(Endianness::LE); payload.Write(Status::IN_REMOTE_QUEUE); payload.Write(slot); endpoints[i]->Send(false, true, false, internalSys, statusUpdateOp, payload); endpoints[i]->SetQueueSlot(slot++); } else { ++slot; } } } } } void Socket::UpdateQueue() { UpdateQueue(GetEndpointsCount(Disposition::ENDPOINT, Status::ACTIVE)); } bool Socket::RemoveEndpoint(const Disposition disposition, const UInt_64 hashId) { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetDisposition() != disposition) continue; if (endpoints[i]->GetHashId() == hashId) { delete endpoints[i]; if (i != endpoints.Size() - 1) endpoints.Swap(i, endpoints.End()); endpoints.Pop(); return true; } } return false; } bool Socket::RemoveEndpoint(const Str_8& address, const UInt_16 port) { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i]->GetAddress() == address && endpoints[i]->GetPort() == port) { delete endpoints[i]; if (i != endpoints.Size() - 1) endpoints.Swap(i, endpoints.End()); endpoints.Pop(); return true; } } return false; } bool Socket::RemoveEndpoint(const Endpoint* const end) { for (UInt_64 i = 0; i < endpoints.Size(); ++i) { if (endpoints[i] == end) { delete endpoints[i]; if (i != endpoints.Size() - 1) endpoints.Swap(i, endpoints.End()); endpoints.Pop(); return true; } } return false; } void Socket::PollEndpoints(Vector& endpoints) { UInt_64 i = 0; while (i < endpoints.Size()) { endpoints[i]->Poll(delta); if (endpoints[i]->GetStatus() == Status::PENDING) { if (endpoints[i]->GetTimeout() >= maxTimeout) { EHS_LOG_INT(LogType::INFO, 0, "Failed to connect to, \"" + endpoints[i]->GetAddress() + ":" + Str_8::FromNum(endpoints[i]->GetPort()) + "\"."); delete endpoints[i]; if (i != endpoints.Size() - 1) endpoints.Swap(i, endpoints.End()); endpoints.Pop(); continue; } } else { if (endpoints[i]->GetTimeout() >= maxTimeout) { EHS_LOG_INT(LogType::INFO, 6, endpoints[i]->GetId() + " timed out."); if (disconnectedCb) disconnectedCb(this, endpoints[i]); delete endpoints[i]; if (i != endpoints.Size() - 1) endpoints.Swap(i, endpoints.End()); endpoints.Pop(); UpdateQueue(); continue; } Vector* frags = endpoints[i]->GetReceived(); UInt_64 f = 0; while (f < frags->Size()) { if (!(*frags)[f].IsComplete()) { ++f; continue; } Packet packet = (*frags)[f].Combine(); System* sys = GetSystem(packet.header.system); if (!sys) { ++f; continue; } sys->Execute(this, endpoints[i], packet.header.op, packet.payload); frags->Swap(f, frags->End()); frags->Pop(); } } ++i; } } void Socket::Bind_v6(const Str_8& address, const UInt_16 port) { Int_32 code = 0; sockaddr_in6 result = {}; result.sin6_family = AF_INET6; result.sin6_port = htons(port); if (address.Size()) { Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr); if (!code) { EHS_LOG_INT(LogType::ERR, 0, "The given address, \"" + address + "\" is not valid."); return; } else if (code == -1) { Int_32 dCode = 0; #if defined(EHS_OS_WINDOWS) dCode = WSAGetLastError(); #elif defined(EHS_OS_LINUX) dCode = errno; #endif EHS_LOG_INT(LogType::ERR, 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); return; } } else { result.sin6_addr = in6addr_any; } code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); #if defined(EHS_OS_WINDOWS) if (code == SOCKET_ERROR) { EHS_LOG_INT(LogType::ERR, 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); return; } #elif defined(EHS_OS_LINUX) if (code == -1) { EHS_LOG_INT(LogType::ERR, 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); return; } #endif } void Socket::Bind_v4(const Str_8& address, const UInt_16 port) { Int_32 code = 0; sockaddr_in result = {}; result.sin_family = AF_INET; result.sin_port = htons(port); if (address.Size()) { code = inet_pton(AF_INET, address, &result.sin_addr); if (!code) { EHS_LOG_INT(LogType::ERR, 0, "The given address, \"" + address + "\" is not valid."); return; } else if (code == -1) { Int_32 dCode = 0; #if defined(EHS_OS_WINDOWS) dCode = WSAGetLastError(); #elif defined(EHS_OS_LINUX) dCode = errno; #endif EHS_LOG_INT(LogType::ERR, 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); return; } } else { result.sin_addr.S_un.S_addr = htonl(INADDR_ANY); } code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); #if defined(EHS_OS_WINDOWS) if (code == SOCKET_ERROR) { EHS_LOG_INT(LogType::ERR, 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); return; } #elif defined(EHS_OS_LINUX) if (code == -1) { EHS_LOG_INT(LogType::ERR, 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); return; } #endif } UInt_16 Socket::Receive(Str_8* address, UInt_16* port, Byte* const data, const UInt_16 size) { if (hdl == EHS_INVALID_SOCKET) { EHS_LOG_INT(LogType::ERR, 0, "Attempted to receive while socket is not initialized."); return 0; } if (type == IP::V4 && size > EHS_IPV4_UDP_PAYLOAD) { EHS_LOG_INT(LogType::ERR, 1, "Attempted to receive with a buffer size of, \"" + Str_8::FromNum(size) + "\", that exceeds, \"" + Str_8::FromNum(EHS_IPV4_UDP_PAYLOAD) + "."); } sockaddr_in6 remote = {}; UInt_32 addrLen = type == IP::V6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in); SInt_64 received = 0; #if defined(EHS_OS_WINDOWS) received = recvfrom(hdl, (char*)data, (int)size, 0, (sockaddr*)&remote, (int*)&addrLen); if (received == SOCKET_ERROR) { int code = WSAGetLastError(); if (code == WSAEMSGSIZE) { UnInitialize(); EHS_LOG_INT(LogType::ERR, 2, "The buffer size, \"" + Str_8::FromNum(size) + "\" is too small."); } else if (code != WSAECONNRESET && code != WSAEWOULDBLOCK) { UnInitialize(); EHS_LOG_INT(LogType::ERR, 3, "Failed to receive with error #" + Str_8::FromNum(code) + "."); } return 0; } #elif defined(EHS_OS_LINUX) received = recvfrom(hdl, data, size, 0, (sockaddr*)&remote, &addrLen); if (received == -1) { int code = errno; if (code != ECONNRESET && code != EWOULDBLOCK) { UnInitialize(); EHS_LOG_INT(LogType::ERR, 2, "Failed to receive with error #" + Str_8::FromNum(code) + "."); } return 0; } #endif if (addrLen == sizeof(sockaddr_in6)) { char tmpAddr[INET6_ADDRSTRLEN]; if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) { Int_32 code = 0; #if defined(EHS_OS_WINDOWS) code = WSAGetLastError(); #elif defined(EHS_OS_LINUX) code = errno; #endif EHS_LOG_INT(LogType::ERR, 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + "."); return received; } *address = tmpAddr; *port = ntohs(remote.sin6_port); } else if (addrLen == sizeof(sockaddr_in)) { char tmpAddr[INET_ADDRSTRLEN]; if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN)) { Int_32 code = 0; #if defined(EHS_OS_WINDOWS) code = WSAGetLastError(); #elif defined(EHS_OS_LINUX) code = errno; #endif EHS_LOG_INT(LogType::ERR, 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + "."); return (UInt_16)received; } *address = tmpAddr; *port = ntohs(((sockaddr_in*)&remote)->sin_port); } return (UInt_16)received; } }