diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index 74537a3..6062a6d 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -22,10 +22,9 @@ jobs: - name: Building/Compiling/Installing Project run: | cd ${{ gitea.workspace }} - vcpkg install mkdir build cd build - cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake .. + cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" .. cmake --build . --config Release cmake --install . @@ -56,6 +55,7 @@ jobs: files: |- ehs-windows-amd64.zip api_key: '${{secrets.RELEASE_TOKEN}}' + pre_release: false Linux-AMD64-Build: runs-on: linux-x86_64 @@ -101,6 +101,7 @@ jobs: files: |- ehs-linux-amd64.zip api_key: '${{secrets.RELEASE_TOKEN}}' + pre_release: false Linux-AARCH64-Build: runs-on: linux-aarch64 @@ -144,4 +145,5 @@ jobs: with: files: |- ehs-linux-aarch64.zip - api_key: '${{secrets.RELEASE_TOKEN}}' \ No newline at end of file + api_key: '${{secrets.RELEASE_TOKEN}}' + pre_release: false \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 930f78b..e3eda96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,12 +132,12 @@ set(EHS_SOURCES include/ehs/io/socket/TCP.h src/io/socket/SSL.cpp include/ehs/io/socket/SSL.h - include/ehs/io/socket/ehc/NetUtils.h - src/io/socket/EHC.cpp include/ehs/io/socket/EHC.h - src/io/socket/ehc/NetFrag.cpp include/ehs/io/socket/ehc/NetFrag.h - src/io/socket/ehc/NetEnd.cpp include/ehs/io/socket/ehc/NetEnd.h - src/io/socket/ehc/NetSys.cpp include/ehs/io/socket/ehc/NetSys.h - src/io/socket/ehc/NetOp.cpp include/ehs/io/socket/ehc/NetOp.h + include/ehs/io/socket/ehc/NetUtils.h + src/io/socket/EHC.cpp include/ehs/io/socket/EHC.h + src/io/socket/ehc/NetFrag.cpp include/ehs/io/socket/ehc/NetFrag.h + src/io/socket/ehc/NetEnd.cpp include/ehs/io/socket/ehc/NetEnd.h + src/io/socket/ehc/NetSys.cpp include/ehs/io/socket/ehc/NetSys.h + src/io/socket/ehc/NetOp.cpp include/ehs/io/socket/ehc/NetOp.h src/io/socket/rest/Twitch.cpp include/ehs/io/socket/rest/Twitch.h src/io/socket/rest/TwitchChat.cpp include/ehs/io/socket/rest/TwitchChat.h @@ -193,12 +193,9 @@ set(EHS_SOURCES include/ehs/io/socket/ehc/NetClientCh.h src/io/socket/ehc/NetClientCh.cpp src/io/socket/ehc/NetServerCh.cpp - src/io/socket/ehc/NetUtils.cpp - src/io/socket/BaseICMP.cpp - include/ehs/io/socket/ICMP_LNX.h - src/io/socket/ICMP_LNX.cpp - include/ehs/io/socket/ICMP.h - src/io/socket/ICMP_LNX.cpp + src/io/socket/ehc/NetUtils.cpp + include/ehs/io/socket/BaseICMP.h src/io/socket/BaseICMP.cpp + include/ehs/io/socket/ICMP.h ) if (IS_OS_WINDOWS) @@ -217,7 +214,9 @@ if (IS_OS_WINDOWS) src/io/COM.cpp include/ehs/io/COM.h src/system/CPU_MSVC_AMD64.asm src/HRNG_MSVC.asm src/Math_MSVC_AMD64.asm src/io/Directory_W32.cpp include/ehs/io/Directory_W32.h + include/ehs/io/socket/ICMP_W32.h src/io/socket/ICMP_W32.cpp ) + elseif (IS_OS_LINUX) list(APPEND EHS_SOURCES src/io/socket/UDP_BSD.cpp include/ehs/io/socket/UDP_BSD.h @@ -234,6 +233,7 @@ elseif (IS_OS_LINUX) src/system/User.cpp include/ehs/system/User.h src/io/Directory_LNX.cpp include/ehs/io/Directory_LNX.h src/io/Usb_LNX.cpp include/ehs/io/Usb_LNX.h + include/ehs/io/socket/ICMP_LNX.h src/io/socket/ICMP_LNX.cpp ) #set(LINUX_WINDOW_SYSTEM "Wayland" CACHE STRING "Linux Window System") @@ -262,8 +262,7 @@ endif() #message("${CMAKE_CXX_FLAGS}") add_library(EHS_Stc STATIC ${EHS_SOURCES}) -add_library(EHS_Dyn SHARED ${EHS_SOURCES} - include/ehs/io/socket/BaseICMP.h) +add_library(EHS_Dyn SHARED ${EHS_SOURCES}) add_executable(StrToHash src/StrToHash.cpp) target_compile_definitions(EHS_Dyn PRIVATE EHS_LIB_EXPORT) @@ -313,7 +312,7 @@ target_link_libraries(EHS_Dyn OpenSSL::SSL OpenSSL::Crypto ZLIB::ZLIB) target_link_libraries(StrToHash OpenSSL::SSL OpenSSL::Crypto ZLIB::ZLIB) if (IS_OS_WINDOWS) - target_link_libraries(EHS_Dyn avrt ws2_32) + target_link_libraries(EHS_Dyn avrt ws2_32 IPHLPAPI) target_link_libraries(StrToHash ws2_32 avrt EHS_Stc) elseif (IS_OS_LINUX) if (LINUX_WINDOW_SYSTEM STREQUAL "Wayland") diff --git a/README.md b/README.md index b2a5fa6..29c74fc 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This project does not fully follow the C++ standard. - Semaphores - CPU information and features at runtime - HTTP(S) Socket Layer +- ICMP & ICMPv6 Socket - TCP Socket - UDP Socket - COM (Serial) IO diff --git a/include/ehs/io/socket/BaseICMP.h b/include/ehs/io/socket/BaseICMP.h index ab5acdd..42d3720 100644 --- a/include/ehs/io/socket/BaseICMP.h +++ b/include/ehs/io/socket/BaseICMP.h @@ -25,6 +25,8 @@ namespace ehs IP version; public: + virtual ~BaseICMP() = default; + BaseICMP(); BaseICMP(IP version); @@ -37,17 +39,30 @@ namespace ehs BaseICMP &operator=(const BaseICMP &icmp); - virtual UInt_64 Send(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size); + virtual void Release(); - virtual UInt_64 Receive(Str_8 &address, ICMP_Header header, Serializer &data); + UInt_64 Send(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size); + + UInt_64 Receive(Str_8 &address, ICMP_Header &header, Serializer &data); void SendEchoRequest(const Str_8 &address, ICMP_EchoRequest er, const Byte *data, UInt_64 size); virtual void SetReceiveTimeout(UInt_64 timeout); + IP GetVersion() const; + virtual bool IsValid() const; protected: - static UInt_16 ComputeChecksum(UInt_16 *buffer, Size length); + static UInt_16 ComputeChecksumV4(UInt_16 *buffer, Size length); + + private: + virtual UInt_64 SendV6(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size); + + virtual UInt_64 SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size); + + virtual UInt_64 ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const; + + virtual UInt_64 ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const; }; } diff --git a/include/ehs/io/socket/ICMP.h b/include/ehs/io/socket/ICMP.h index a6ed5cb..ec18023 100644 --- a/include/ehs/io/socket/ICMP.h +++ b/include/ehs/io/socket/ICMP.h @@ -1,7 +1,9 @@ #pragma once +#include "ehs/system/OS.h" + #ifdef EHS_OS_WINDOWS -#include "BaseICMP.h" + #include "ICMP_W32.h" #else -#include "ICMP_LNX.h" + #include "ICMP_LNX.h" #endif \ No newline at end of file diff --git a/include/ehs/io/socket/ICMP_LNX.h b/include/ehs/io/socket/ICMP_LNX.h index eb255fc..83525f1 100644 --- a/include/ehs/io/socket/ICMP_LNX.h +++ b/include/ehs/io/socket/ICMP_LNX.h @@ -2,14 +2,26 @@ #include "BaseICMP.h" +#include + namespace ehs { - class ICMP : public BaseICMP + struct PseudoICMPv6_Header + { + sockaddr_in6 src; + sockaddr_in6 dst; + UInt_32 length; + }; + + class ICMP final : public BaseICMP { private: Int_32 hdl; + sockaddr_in6 src; public: + ~ICMP() override; + ICMP(); ICMP(IP version); @@ -22,12 +34,27 @@ namespace ehs ICMP &operator=(const ICMP &icmp); - UInt_64 Send(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) override; - - UInt_64 Receive(Str_8 &address, ICMP_Header header, Serializer &data) override; + void Release() override; void SetReceiveTimeout(UInt_64 timeout) override; bool IsValid() const override; + + private: + static bool IsLinkLocal(const in6_addr &addr); + + static sockaddr_in6 RetrieveSrcAddress(); + + static UInt_32 CalculatePseudoHeaderChecksum(const PseudoICMPv6_Header &header); + + UInt_16 ComputeChecksumV6(UInt_16* buffer, Size length, const sockaddr_in6& dst); + + UInt_64 SendV6(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) override; + + UInt_64 SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) override; + + UInt_64 ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const override; + + UInt_64 ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const override; }; } \ No newline at end of file diff --git a/include/ehs/io/socket/ICMP_W32.h b/include/ehs/io/socket/ICMP_W32.h new file mode 100644 index 0000000..15cc295 --- /dev/null +++ b/include/ehs/io/socket/ICMP_W32.h @@ -0,0 +1,63 @@ +#pragma once + +#include "BaseICMP.h" +#include "ehs/System/OS.h" + +#include +#include +#include + +namespace ehs +{ + struct PseudoICMPv6_Header + { + sockaddr_in6 src; + sockaddr_in6 dst; + UInt_32 length; + }; + + class ICMP : public BaseICMP + { + private: + Socket hdl; + sockaddr_in6 src; + + public: + ~ICMP() override; + + ICMP(); + + ICMP(IP version); + + ICMP(ICMP &&icmp) noexcept; + + ICMP(const ICMP &icmp); + + ICMP &operator=(ICMP &&icmp) noexcept; + + ICMP &operator=(const ICMP &icmp); + + void Release() override; + + void SetReceiveTimeout(UInt_64 timeout) override; + + bool IsValid() const override; + + private: + static bool IsLinkLocal(const in6_addr &addr); + + static sockaddr_in6 RetrieveSrcAddress(); + + static UInt_32 CalculatePseudoHeaderChecksum(const PseudoICMPv6_Header &header); + + UInt_16 ComputeChecksumV6(UInt_16* buffer, Size length, const sockaddr_in6& dst); + + UInt_64 SendV6(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) override; + + UInt_64 SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) override; + + UInt_64 ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const override; + + UInt_64 ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const override; + }; +} \ No newline at end of file diff --git a/src/io/socket/BaseICMP.cpp b/src/io/socket/BaseICMP.cpp index 4559e99..55273ca 100644 --- a/src/io/socket/BaseICMP.cpp +++ b/src/io/socket/BaseICMP.cpp @@ -1,8 +1,6 @@ #include "ehs/io/socket/BaseICMP.h" #include "ehs/Serializer.h" -#include - namespace ehs { @@ -47,14 +45,24 @@ namespace ehs return *this; } - UInt_64 BaseICMP::Send(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) + void BaseICMP::Release() { - return 0; } - UInt_64 BaseICMP::Receive(Str_8 &address, ICMP_Header header, Serializer &data) + UInt_64 BaseICMP::Send(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) { - return 0; + if (GetVersion() == IP::V6) + return SendV6(address, header, data, size); + + return SendV4(address, header, data, size); + } + + UInt_64 BaseICMP::Receive(Str_8 &address, ICMP_Header &header, Serializer &data) + { + if (GetVersion() == IP::V6) + return ReceiveV6(address, header, data); + + return ReceiveV4(address, header, data); } void BaseICMP::SendEchoRequest(const Str_8 &address, ICMP_EchoRequest er, const Byte *data, UInt_64 size) @@ -67,7 +75,7 @@ namespace ehs } ICMP_Header header = { - 8, + version == IP::V6 ? (UInt_8)128 : (UInt_8)8, 0, 0 }; @@ -87,12 +95,17 @@ namespace ehs { } + IP BaseICMP::GetVersion() const + { + return version; + } + bool BaseICMP::IsValid() const { return false; } - UInt_16 BaseICMP::ComputeChecksum(UInt_16 *buffer, Size length) + UInt_16 BaseICMP::ComputeChecksumV4(UInt_16 *buffer, Size length) { Size sum = 0; while (length > 1) @@ -109,4 +122,24 @@ namespace ehs return (UInt_16)~sum; } + + UInt_64 BaseICMP::SendV6(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) + { + return 0; + } + + UInt_64 BaseICMP::SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) + { + return 0; + } + + UInt_64 BaseICMP::ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const + { + return 0; + } + + UInt_64 BaseICMP::ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const + { + return 0; + } } diff --git a/src/io/socket/ICMP_LNX.cpp b/src/io/socket/ICMP_LNX.cpp index 5e426fd..c2cdf6f 100644 --- a/src/io/socket/ICMP_LNX.cpp +++ b/src/io/socket/ICMP_LNX.cpp @@ -5,18 +5,30 @@ #include #include #include +#include +#include namespace ehs { + ICMP::~ICMP() + { + if (close(hdl) == -1) + EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); + } + ICMP::ICMP() - : hdl(EHS_INVALID_SOCKET) + : hdl(EHS_INVALID_SOCKET), src{} { } ICMP::ICMP(const IP version) - : BaseICMP(version) + : BaseICMP(version), src{} { - hdl = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (version == IP::V6) + hdl = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + else + hdl = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (hdl < 0) { EHS_LOG_INT(LogType::ERR, 0, "Failed to create ICMP socket with error #" + Str_8::FromNum(errno) + "."); @@ -27,13 +39,14 @@ namespace ehs } ICMP::ICMP(ICMP &&icmp) noexcept - : BaseICMP((BaseICMP &&)icmp), hdl(icmp.hdl) + : BaseICMP((BaseICMP &&)icmp), hdl(icmp.hdl), src(icmp.src) { icmp.hdl = EHS_INVALID_SOCKET; + icmp.src = {}; } ICMP::ICMP(const ICMP &icmp) - : BaseICMP(icmp), hdl(icmp.hdl) + : BaseICMP(icmp), hdl(icmp.hdl), src{} { } @@ -45,8 +58,10 @@ namespace ehs BaseICMP::operator=((BaseICMP &&)icmp); hdl = icmp.hdl; + src = icmp.src; icmp.hdl = EHS_INVALID_SOCKET; + icmp.src = {}; return *this; } @@ -59,11 +74,136 @@ namespace ehs BaseICMP::operator=(icmp); hdl = icmp.hdl; + src = {}; return *this; } - UInt_64 ICMP::Send(const Str_8 &address, ICMP_Header header, const Byte *data, const UInt_64 size) + void ICMP::Release() + { + if (close(hdl) == -1) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); + + return; + } + + hdl = EHS_INVALID_SOCKET; + + EHS_LOG_SUCCESS(); + } + + void ICMP::SetReceiveTimeout(const UInt_64 timeout) + { + timeval result = {}; + result.tv_sec = (long)timeout; + result.tv_usec = 0; + + if (setsockopt(hdl, SOL_SOCKET, SO_RCVTIMEO, &result, sizeof(result)) < 0) + { + EHS_LOG_INT(LogType::WARN, 0, "Failed to set receive timeout with error #" + Str_8::FromNum(errno) + "."); + + return; + } + + EHS_LOG_SUCCESS(); + } + + bool ICMP::IsValid() const + { + return hdl != EHS_INVALID_SOCKET; + } + + bool ICMP::IsLinkLocal(const in6_addr &addr) + { + return addr.s6_addr[0] == 0xfe && (addr.s6_addr[1] & 0xc0) == 0x80; + } + + sockaddr_in6 ICMP::RetrieveSrcAddress() + { + ifaddrs *ifaddr; + sockaddr_in6 addr = {}; + + if (getifaddrs(&ifaddr) == -1) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve public address with error #" + Str_8::FromNum(errno) + "."); + + return addr; + } + + for (ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_INET6) + continue; + + addr = *(sockaddr_in6 *)ifa->ifa_addr; + if (!addr.sin6_addr.s6_addr32[0] || IsLinkLocal(addr.sin6_addr)) + continue; + + break; + } + + freeifaddrs(ifaddr); + + EHS_LOG_SUCCESS(); + + return addr; + } + + UInt_32 ICMP::CalculatePseudoHeaderChecksum(const PseudoICMPv6_Header &header) + { + UInt_32 checksum = 0; + + for (UInt_8 i = 0; i < 16; ++i) + checksum += header.src.sin6_addr.s6_addr[i]; + + for (UInt_8 i = 0; i < 16; ++i) + checksum += header.dst.sin6_addr.s6_addr[i]; + + checksum += 58; + + checksum += htons(header.length); + + checksum = (checksum >> 16) + (checksum & 0xFFFF); + checksum += (checksum >> 16); + + return checksum; + } + + UInt_16 ICMP::ComputeChecksumV6(UInt_16 *buffer, Size length, const sockaddr_in6 &dst) + { + UInt_32 checksum = 0; + + if (!src.sin6_addr.s6_addr32[0]) + { + src = RetrieveSrcAddress(); + if (!src.sin6_addr.s6_addr32[0]) + { + EHS_LOG_INT(LogType::ERR, 0, "Could not retrieve a suitable global address."); + return checksum; + } + } + + checksum += CalculatePseudoHeaderChecksum({src, dst, (UInt_32)length}); + + while (length > 1) + { + checksum += *buffer++; + length -= sizeof(UInt_16); + } + + if (length == 1) + checksum += *(UInt_8 *)buffer; + + // Carry over any overflow from the 16-bit result + checksum = (checksum >> 16) + (checksum & 0xFFFF); + checksum += (checksum >> 16); + + // Return the 16-bit complement + return ~checksum; + } + + UInt_64 ICMP::SendV6(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) { if (!IsValid()) { @@ -82,17 +222,17 @@ namespace ehs payload.SetOffset(payload.GetOffset() + size); - header.checksum = ComputeChecksum((UInt_16 *)&payload[0], payload.Size()); + sockaddr_in6 dst = {}; + dst.sin6_family = AF_INET6; + inet_pton(AF_INET6, address, &(dst.sin6_addr)); + + header.checksum = ComputeChecksumV6((UInt_16 *)&payload[0], payload.Size(), dst); payload.SetOffset(0); payload.Write(header); payload.SetOffset(payload.Size()); - sockaddr_in dst_addr = {}; - dst_addr.sin_family = AF_INET; - inet_pton(AF_INET, address, &(dst_addr.sin_addr)); - - SInt_64 sent = sendto(hdl, payload, payload.Size(), 0, (sockaddr *)&dst_addr, sizeof(dst_addr)); + SInt_64 sent = sendto(hdl, payload, payload.Size(), 0, (sockaddr *)&dst, sizeof(sockaddr_in6)); if (sent < 0) { EHS_LOG_INT(LogType::ERR, 0, "Failed to send packet with error #" + Str_8::FromNum(errno) + "."); @@ -105,7 +245,96 @@ namespace ehs return sent; } - UInt_64 ICMP::Receive(Str_8 &address, ICMP_Header header, Serializer &data) + UInt_64 ICMP::SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT(LogType::WARN, 0, "Socket is not initialized."); + + return 0; + } + + header.checksum = 0; + + Serializer payload(Endianness::LE); + payload.Write(header); + payload.Resize(payload.Size() + size); + + Util::Copy(&payload[payload.GetOffset()], data, size); + + payload.SetOffset(payload.GetOffset() + size); + + header.checksum = ComputeChecksumV4((UInt_16 *)&payload[0], payload.Size()); + + payload.SetOffset(0); + payload.Write(header); + payload.SetOffset(payload.Size()); + + sockaddr_in dst = {}; + dst.sin_family = AF_INET; + inet_pton(AF_INET, address, &(dst.sin_addr)); + + SInt_64 sent = sendto(hdl, payload, payload.Size(), 0, (sockaddr *)&dst, sizeof(sockaddr_in)); + if (sent < 0) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to send packet with error #" + Str_8::FromNum(errno) + "."); + + return 0; + } + + EHS_LOG_SUCCESS(); + + return sent; + } + + UInt_64 ICMP::ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const + { + if (!IsValid()) + { + EHS_LOG_INT(LogType::WARN, 0, "Socket is not initialized."); + + return 0; + } + + Serializer payload(Endianness::LE); + payload.Resize(1500); + + sockaddr_in6 remote = {}; + socklen_t from_len = sizeof(remote); + + SInt_64 recv = recvfrom(hdl, payload, 1500, 0, (sockaddr *)&remote, &from_len); + if (recv < 0) + { + int code = errno; + if (code == EAGAIN) + EHS_LOG_SUCCESS(); + else + EHS_LOG_INT(LogType::ERR, 0, "Failed to receive packet with error #" + Str_8::FromNum(code) + "."); + + return 0; + } + + payload.Resize(recv); + + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + EHS_LOG_INT(LogType::ERR, 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(errno) + "."); + + return recv; + } + + address = tmpAddr; + header = payload.Read(); + data = Serializer(payload.GetEndianness(), &payload[payload.GetOffset()], payload.Size() - payload.GetOffset()); + + EHS_LOG_SUCCESS(); + + return recv; + } + + UInt_64 ICMP::ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const { if (!IsValid()) { @@ -152,25 +381,4 @@ namespace ehs return recv; } - - void ICMP::SetReceiveTimeout(UInt_64 timeout) - { - timeval result = {}; - result.tv_sec = (long)timeout; - result.tv_usec = 0; - - if (setsockopt(hdl, SOL_SOCKET, SO_RCVTIMEO, &result, sizeof(result)) < 0) - { - EHS_LOG_INT(LogType::WARN, 0, "Failed to set receive timeout with error #" + Str_8::FromNum(errno) + "."); - - return; - } - - EHS_LOG_SUCCESS(); - } - - bool ICMP::IsValid() const - { - return hdl != EHS_INVALID_SOCKET; - } } diff --git a/src/io/socket/ICMP_W32.cpp b/src/io/socket/ICMP_W32.cpp new file mode 100644 index 0000000..16ac1a5 --- /dev/null +++ b/src/io/socket/ICMP_W32.cpp @@ -0,0 +1,391 @@ +#include "ehs/io/socket/ICMP_W32.h" + +struct iphdr +{ + u_char ip_hl:4, ip_v:4; + u_char ip_tos; + u_short ip_len; + u_short ip_id; + u_short ip_off; + u_char ip_ttl; + u_char ip_p; + u_short ip_sum; + in_addr ip_src, ip_dst; +}; + +namespace ehs +{ + ICMP::~ICMP() + { + if (closesocket(hdl) == -1) + EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); + } + + ICMP::ICMP() + : hdl(EHS_INVALID_SOCKET) + { + } + + ICMP::ICMP(const IP version) + : BaseICMP(version) + { + hdl = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (hdl < 0) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to create ICMP socket with error #" + Str_8::FromNum(errno) + "."); + return; + } + + EHS_LOG_SUCCESS(); + } + + ICMP::ICMP(ICMP &&icmp) noexcept + : BaseICMP((BaseICMP &&)icmp), hdl(icmp.hdl) + { + icmp.hdl = EHS_INVALID_SOCKET; + } + + ICMP::ICMP(const ICMP &icmp) + : BaseICMP(icmp), hdl(icmp.hdl) + { + } + + ICMP & ICMP::operator=(ICMP &&icmp) noexcept + { + if (this == &icmp) + return *this; + + BaseICMP::operator=((BaseICMP &&)icmp); + + hdl = icmp.hdl; + + icmp.hdl = EHS_INVALID_SOCKET; + + return *this; + } + + ICMP & ICMP::operator=(const ICMP &icmp) + { + if (this == &icmp) + return *this; + + BaseICMP::operator=(icmp); + + hdl = icmp.hdl; + + return *this; + } + + void ICMP::Release() + { + if (closesocket(hdl) == -1) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); + + return; + } + + hdl = EHS_INVALID_SOCKET; + + EHS_LOG_SUCCESS(); + } + + void ICMP::SetReceiveTimeout(UInt_64 timeout) + { + timeval result = {}; + result.tv_sec = (long)timeout; + result.tv_usec = 0; + + if (setsockopt(hdl, SOL_SOCKET, SO_RCVTIMEO, (const char *)&result, sizeof(result)) < 0) + { + EHS_LOG_INT(LogType::WARN, 0, "Failed to set receive timeout with error #" + Str_8::FromNum(errno) + "."); + + return; + } + + EHS_LOG_SUCCESS(); + } + + bool ICMP::IsValid() const + { + return hdl != EHS_INVALID_SOCKET; + } + + bool ICMP::IsLinkLocal(const in6_addr &addr) + { + return addr.s6_addr[0] == 0xfe && (addr.s6_addr[1] & 0xc0) == 0x80; + } + + sockaddr_in6 ICMP::RetrieveSrcAddress() + { + sockaddr_in6 addr = {}; + UInt_32 outBufLen = 15000; + Array buffer(outBufLen); + PIP_ADAPTER_ADDRESSES pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)&buffer[0]; + + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAdapterAddresses, (PULONG)&outBufLen) == ERROR_BUFFER_OVERFLOW) + { + buffer.Resize(outBufLen); + pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)&buffer[0]; + } + + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAdapterAddresses, (PULONG)&outBufLen) == NO_ERROR) + { + for (PIP_ADAPTER_ADDRESSES adapter = pAdapterAddresses; adapter != nullptr; adapter = adapter->Next) + { + for (PIP_ADAPTER_UNICAST_ADDRESS addrInfo = adapter->FirstUnicastAddress; addrInfo != nullptr; addrInfo = addrInfo->Next) + { + SOCKADDR *sa = addrInfo->Address.lpSockaddr; + if (sa->sa_family != AF_INET6) + continue; + + sockaddr_in6 *ipv6Addr = (sockaddr_in6 *)sa; + + // Skip link-local addresses + if (IN6_IS_ADDR_LINKLOCAL(&ipv6Addr->sin6_addr)) + continue; + + addr = *ipv6Addr; + + return addr; // Return the first suitable address + } + } + } + + EHS_LOG_SUCCESS(); + + return addr; // Return an empty sockaddr_in6 if no valid address was found + } + + UInt_32 ICMP::CalculatePseudoHeaderChecksum(const PseudoICMPv6_Header &header) + { + UInt_32 checksum = 0; + + for (UInt_8 i = 0; i < 16; ++i) + checksum += header.src.sin6_addr.s6_addr[i]; + + for (UInt_8 i = 0; i < 16; ++i) + checksum += header.dst.sin6_addr.s6_addr[i]; + + checksum += 58; + + checksum += htons(header.length); + + checksum = (checksum >> 16) + (checksum & 0xFFFF); + checksum += (checksum >> 16); + + return checksum; + } + + UInt_16 ICMP::ComputeChecksumV6(UInt_16 *buffer, Size length, const sockaddr_in6 &dst) + { + UInt_32 checksum = 0; + + if (!src.sin6_addr.u.Word[0]) + { + src = RetrieveSrcAddress(); + if (!src.sin6_addr.u.Word[0]) + { + EHS_LOG_INT(LogType::ERR, 0, "Could not retrieve a suitable global address."); + return checksum; + } + } + + checksum += CalculatePseudoHeaderChecksum({src, dst, (UInt_32)length}); + + while (length > 1) + { + checksum += *buffer++; + length -= sizeof(UInt_16); + } + + if (length == 1) + checksum += *(UInt_8 *)buffer; + + // Carry over any overflow from the 16-bit result + checksum = (checksum >> 16) + (checksum & 0xFFFF); + checksum += (checksum >> 16); + + // Return the 16-bit complement + return ~checksum; + } + + UInt_64 ICMP::SendV6(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT(LogType::WARN, 0, "Socket is not initialized."); + + return 0; + } + + header.checksum = 0; + + Serializer payload(Endianness::LE); + payload.Write(header); + payload.Resize(payload.Size() + size); + + Util::Copy(&payload[payload.GetOffset()], data, size); + + payload.SetOffset(payload.GetOffset() + size); + + sockaddr_in6 dst = {}; + dst.sin6_family = AF_INET6; + inet_pton(AF_INET6, address, &(dst.sin6_addr)); + + header.checksum = ComputeChecksumV6((UInt_16 *)&payload[0], payload.Size(), dst); + + payload.SetOffset(0); + payload.Write(header); + payload.SetOffset(payload.Size()); + + SInt_64 sent = sendto(hdl, (const char *)&payload[0], payload.Size(), 0, (sockaddr *)&dst, sizeof(sockaddr_in6)); + if (sent < 0) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to send packet with error #" + Str_8::FromNum(errno) + "."); + + return 0; + } + + EHS_LOG_SUCCESS(); + + return sent; + } + + UInt_64 ICMP::SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT(LogType::WARN, 0, "Socket is not initialized."); + + return 0; + } + + header.checksum = 0; + + Serializer payload(Endianness::LE); + payload.Write(header); + payload.Resize(payload.Size() + size); + + Util::Copy(&payload[payload.GetOffset()], data, size); + + payload.SetOffset(payload.GetOffset() + size); + + header.checksum = ComputeChecksumV4((UInt_16 *)&payload[0], payload.Size()); + + payload.SetOffset(0); + payload.Write(header); + payload.SetOffset(payload.Size()); + + sockaddr_in dst_addr = {}; + dst_addr.sin_family = AF_INET; + inet_pton(AF_INET, address, &(dst_addr.sin_addr)); + + SInt_64 sent = sendto(hdl, (const char *)&payload[0], payload.Size(), 0, (sockaddr *)&dst_addr, sizeof(dst_addr)); + if (sent < 0) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to send packet with error #" + Str_8::FromNum(errno) + "."); + + return 0; + } + + EHS_LOG_SUCCESS(); + + return sent; + } + + UInt_64 ICMP::ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const + { + if (!IsValid()) + { + EHS_LOG_INT(LogType::WARN, 0, "Socket is not initialized."); + + return 0; + } + + Serializer payload(Endianness::LE); + payload.Resize(1500); + + sockaddr_in6 remote = {}; + socklen_t from_len = sizeof(remote); + + SInt_64 recv = recvfrom(hdl, (char *)&payload[0], 1500, 0, (sockaddr *)&remote, &from_len); + if (recv < 0) + { + int code = errno; + if (code == EAGAIN) + EHS_LOG_SUCCESS(); + else + EHS_LOG_INT(LogType::ERR, 0, "Failed to receive packet with error #" + Str_8::FromNum(code) + "."); + + return 0; + } + + payload.Resize(recv); + + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + EHS_LOG_INT(LogType::ERR, 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(errno) + "."); + + return recv; + } + + address = tmpAddr; + header = payload.Read(); + data = Serializer(payload.GetEndianness(), &payload[payload.GetOffset()], payload.Size() - payload.GetOffset()); + + EHS_LOG_SUCCESS(); + + return recv; + } + + UInt_64 ICMP::ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const + { + if (!IsValid()) + { + EHS_LOG_INT(LogType::WARN, 0, "Socket is not initialized."); + + return 0; + } + + Serializer payload(Endianness::LE); + payload.Resize(1500); + + sockaddr_in remote = {}; + socklen_t from_len = sizeof(remote); + + SInt_64 recv = recvfrom(hdl, (char *)&payload[0], 1500, 0, (sockaddr *)&remote, &from_len); + if (recv < 0) + { + int code = errno; + if (code == EAGAIN) + EHS_LOG_SUCCESS(); + else + EHS_LOG_INT(LogType::ERR, 0, "Failed to receive packet with error #" + Str_8::FromNum(code) + "."); + + return 0; + } + + payload.Resize(recv); + + char tmpAddr[INET_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin_family, &remote.sin_addr, tmpAddr, INET_ADDRSTRLEN)) + { + EHS_LOG_INT(LogType::ERR, 1, "Failed to convert IPv4 address with error #" + Str_8::FromNum(errno) + "."); + + return recv; + } + + address = tmpAddr; + iphdr ipHeader = payload.Read(); + header = payload.Read(); + data = Serializer(payload.GetEndianness(), &payload[payload.GetOffset()], payload.Size() - payload.GetOffset()); + + EHS_LOG_SUCCESS(); + + return recv; + } +} diff --git a/src/system/CPU_ARM64.cpp b/src/system/CPU_ARM64.cpp index cf09004..ba140bf 100644 --- a/src/system/CPU_ARM64.cpp +++ b/src/system/CPU_ARM64.cpp @@ -43,4 +43,9 @@ namespace ehs void CPU::GetBrand(Char_8* input) { } + + UInt_8 CPU::GetCacheLineSize() + { + return 0; + } } \ No newline at end of file