From af90f90156048a42f6cda965ed700ad4c4b1162f Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 18:49:14 -0700 Subject: [PATCH 01/16] Fixed gitea workflow. --- .gitea/workflows/BuildRelease.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index 74537a3..79f8e0a 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -22,7 +22,7 @@ jobs: - name: Building/Compiling/Installing Project run: | cd ${{ gitea.workspace }} - vcpkg install + echo $env:VCPKG_ROOT mkdir build cd build cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake .. From e0718770457634480a9723a0f5b7c71a46b39b95 Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:15:32 -0700 Subject: [PATCH 02/16] Fixed gitea workflow. --- .gitea/workflows/BuildRelease.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index 79f8e0a..61aac65 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -22,10 +22,10 @@ jobs: - name: Building/Compiling/Installing Project run: | cd ${{ gitea.workspace }} - echo $env:VCPKG_ROOT + echo $VCPKG_ROOT 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=$VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake .. cmake --build . --config Release cmake --install . From bf3b063e96a0616ecd4525949db1b534f4ead1ff Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:17:53 -0700 Subject: [PATCH 03/16] Fixed gitea workflow. --- .gitea/workflows/BuildRelease.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index 61aac65..da24ded 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 }} - echo $VCPKG_ROOT mkdir build cd build - cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake .. + cmake -A x64 -DCMAKE_BUILD_TYPE=Release .. cmake --build . --config Release cmake --install . From ae226c9a8411a0319e03895f0b9b8524cbce0afc Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:20:24 -0700 Subject: [PATCH 04/16] Fixed gitea workflow. --- .gitea/workflows/BuildRelease.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index da24ded..ea77c6d 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -24,7 +24,7 @@ jobs: cd ${{ gitea.workspace }} mkdir build cd build - cmake -A x64 -DCMAKE_BUILD_TYPE=Release .. + cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="C:\Program Files\scripts\buildsystems\vcpkg.cmake" .. cmake --build . --config Release cmake --install . From 6cf4977a6c8605c85c87858ebb77ff29470480dc Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:22:08 -0700 Subject: [PATCH 05/16] Fixed gitea workflow. --- .gitea/workflows/BuildRelease.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index ea77c6d..fb50b72 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -24,7 +24,7 @@ jobs: cd ${{ gitea.workspace }} mkdir build cd build - cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="C:\Program Files\scripts\buildsystems\vcpkg.cmake" .. + cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="C:\Program Files\vcpkg\scripts\buildsystems\vcpkg.cmake" .. cmake --build . --config Release cmake --install . From eb3d345c5ac1d766bda42537d8914dcb7fcf35f1 Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:26:10 -0700 Subject: [PATCH 06/16] Fixed gitea workflow. --- .gitea/workflows/BuildRelease.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index fb50b72..f009b3a 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -24,7 +24,7 @@ jobs: cd ${{ gitea.workspace }} mkdir build cd build - cmake -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="C:\Program Files\vcpkg\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 . From ce225b288823ab050b751246be8b88d7e6676837 Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:42:40 -0700 Subject: [PATCH 07/16] Fixed CMakeLists and BaseICMP.cpp. --- CMakeLists.txt | 13 +++++-------- src/io/socket/BaseICMP.cpp | 2 -- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 930f78b..8a5a021 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,12 +193,10 @@ 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_LNX.h src/io/socket/ICMP_LNX.cpp + include/ehs/io/socket/ICMP.h ) if (IS_OS_WINDOWS) @@ -262,8 +260,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) diff --git a/src/io/socket/BaseICMP.cpp b/src/io/socket/BaseICMP.cpp index 4559e99..48af730 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 { From 1e55b11eed222481de541c5e79b6d2766c197728 Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 25 Mar 2025 19:43:42 -0700 Subject: [PATCH 08/16] Fixed CMakeLists. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a5a021..6b5cf80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,7 +195,6 @@ set(EHS_SOURCES src/io/socket/ehc/NetServerCh.cpp src/io/socket/ehc/NetUtils.cpp include/ehs/io/socket/BaseICMP.h src/io/socket/BaseICMP.cpp - include/ehs/io/socket/ICMP_LNX.h src/io/socket/ICMP_LNX.cpp include/ehs/io/socket/ICMP.h ) @@ -232,6 +231,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") From 86149d246a95b8566854b27f21edf6af8a6843f6 Mon Sep 17 00:00:00 2001 From: Arron Nelson Date: Wed, 26 Mar 2025 10:29:20 -0700 Subject: [PATCH 09/16] Renamed member variables and parameters. --- CMakeLists.txt | 17 +++++++++++------ include/ehs/io/socket/ICMP_W32.h | 10 ++++++++++ src/io/socket/ICMP_W32.cpp | 3 +++ src/system/CPU_ARM64.cpp | 5 +++++ 4 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 include/ehs/io/socket/ICMP_W32.h create mode 100644 src/io/socket/ICMP_W32.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b5cf80..c4fc4b9 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 @@ -214,7 +214,10 @@ 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 ) + + add_link_options(-fno-exceptions -fno-rtti) elseif (IS_OS_LINUX) list(APPEND EHS_SOURCES src/io/socket/UDP_BSD.cpp include/ehs/io/socket/UDP_BSD.h @@ -234,6 +237,8 @@ elseif (IS_OS_LINUX) include/ehs/io/socket/ICMP_LNX.h src/io/socket/ICMP_LNX.cpp ) + add_compile_options(/EHs- /GR-) + #set(LINUX_WINDOW_SYSTEM "Wayland" CACHE STRING "Linux Window System") if (LINUX_WINDOW_SYSTEM STREQUAL "Wayland") diff --git a/include/ehs/io/socket/ICMP_W32.h b/include/ehs/io/socket/ICMP_W32.h new file mode 100644 index 0000000..74cdff8 --- /dev/null +++ b/include/ehs/io/socket/ICMP_W32.h @@ -0,0 +1,10 @@ +#pragma once + +#include "BaseICMP.h" + +namespace ehs +{ + class ICMP : public virtual BaseICMP + { + }; +} \ No newline at end of file diff --git a/src/io/socket/ICMP_W32.cpp b/src/io/socket/ICMP_W32.cpp new file mode 100644 index 0000000..b88c2bc --- /dev/null +++ b/src/io/socket/ICMP_W32.cpp @@ -0,0 +1,3 @@ +// +// Created by Arron Nelson on 3/26/2025. +// 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 From c19c0bc80b267daf0df1e02b028752840d2f79c8 Mon Sep 17 00:00:00 2001 From: Arron Nelson Date: Wed, 26 Mar 2025 10:42:29 -0700 Subject: [PATCH 10/16] Renamed member variables and parameters. --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4fc4b9..2f93464 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,7 +217,6 @@ if (IS_OS_WINDOWS) include/ehs/io/socket/ICMP_W32.h src/io/socket/ICMP_W32.cpp ) - add_link_options(-fno-exceptions -fno-rtti) elseif (IS_OS_LINUX) list(APPEND EHS_SOURCES src/io/socket/UDP_BSD.cpp include/ehs/io/socket/UDP_BSD.h @@ -237,8 +236,6 @@ elseif (IS_OS_LINUX) include/ehs/io/socket/ICMP_LNX.h src/io/socket/ICMP_LNX.cpp ) - add_compile_options(/EHs- /GR-) - #set(LINUX_WINDOW_SYSTEM "Wayland" CACHE STRING "Linux Window System") if (LINUX_WINDOW_SYSTEM STREQUAL "Wayland") From d41f71f17dd520cc798e7246baec38357639ed0a Mon Sep 17 00:00:00 2001 From: Arron Nelson Date: Wed, 26 Mar 2025 11:45:54 -0700 Subject: [PATCH 11/16] Added IPv4 ICMP support to Windows. --- include/ehs/io/socket/ICMP.h | 6 +- include/ehs/io/socket/ICMP_W32.h | 25 +++- src/io/socket/ICMP_W32.cpp | 190 ++++++++++++++++++++++++++++++- 3 files changed, 215 insertions(+), 6 deletions(-) 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_W32.h b/include/ehs/io/socket/ICMP_W32.h index 74cdff8..5d7b7e6 100644 --- a/include/ehs/io/socket/ICMP_W32.h +++ b/include/ehs/io/socket/ICMP_W32.h @@ -4,7 +4,30 @@ namespace ehs { - class ICMP : public virtual BaseICMP + class ICMP : public BaseICMP { + private: + Int_32 hdl; + + public: + ICMP(); + + ICMP(IP version); + + ICMP(ICMP &&icmp) noexcept; + + ICMP(const ICMP &icmp); + + ICMP &operator=(ICMP &&icmp) noexcept; + + 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 SetReceiveTimeout(UInt_64 timeout) override; + + bool IsValid() const override; }; } \ No newline at end of file diff --git a/src/io/socket/ICMP_W32.cpp b/src/io/socket/ICMP_W32.cpp index b88c2bc..ab5d16e 100644 --- a/src/io/socket/ICMP_W32.cpp +++ b/src/io/socket/ICMP_W32.cpp @@ -1,3 +1,187 @@ -// -// Created by Arron Nelson on 3/26/2025. -// +#include "ehs/io/socket/ICMP_W32.h" + +#include +#include +#include + +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() + : 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; + } + + UInt_64 ICMP::Send(const Str_8 &address, ICMP_Header header, const Byte *data, const 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 = ComputeChecksum((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::Receive(Str_8 &address, ICMP_Header header, Serializer &data) + { + 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; + } + + 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; + } +} From 42b26045a65775c37c0e60e64b8fa39dda43c72e Mon Sep 17 00:00:00 2001 From: Karutoh Date: Thu, 27 Mar 2025 00:15:02 -0700 Subject: [PATCH 12/16] Added ICMPv6 support to Linux. --- .gitea/workflows/BuildRelease.yaml | 5 +- README.md | 1 + include/ehs/io/socket/BaseICMP.h | 8 +- include/ehs/io/socket/ICMP_LNX.h | 33 +++- src/io/socket/BaseICMP.cpp | 11 +- src/io/socket/ICMP_LNX.cpp | 276 +++++++++++++++++++++++++---- 6 files changed, 293 insertions(+), 41 deletions(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index f009b3a..6062a6d 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -55,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 @@ -100,6 +101,7 @@ jobs: files: |- ehs-linux-amd64.zip api_key: '${{secrets.RELEASE_TOKEN}}' + pre_release: false Linux-AARCH64-Build: runs-on: linux-aarch64 @@ -143,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/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..b81e12e 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); @@ -39,15 +41,17 @@ namespace ehs virtual UInt_64 Send(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size); - virtual UInt_64 Receive(Str_8 &address, ICMP_Header header, Serializer &data); + virtual 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); }; } diff --git a/include/ehs/io/socket/ICMP_LNX.h b/include/ehs/io/socket/ICMP_LNX.h index eb255fc..68efaaa 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(); + ICMP(); ICMP(IP version); @@ -24,10 +36,27 @@ namespace ehs 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; + UInt_64 Receive(Str_8 &address, ICMP_Header &header, Serializer &data) 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); + + UInt_64 SendV4(const Str_8 &address, ICMP_Header header, const Byte *data, UInt_64 size); + + UInt_64 ReceiveV6(Str_8 &address, ICMP_Header &header, Serializer &data) const; + + UInt_64 ReceiveV4(Str_8 &address, ICMP_Header &header, Serializer &data) const; }; } \ No newline at end of file diff --git a/src/io/socket/BaseICMP.cpp b/src/io/socket/BaseICMP.cpp index 48af730..63ef63f 100644 --- a/src/io/socket/BaseICMP.cpp +++ b/src/io/socket/BaseICMP.cpp @@ -50,7 +50,7 @@ namespace ehs return 0; } - UInt_64 BaseICMP::Receive(Str_8 &address, ICMP_Header header, Serializer &data) + UInt_64 BaseICMP::Receive(Str_8 &address, ICMP_Header &header, Serializer &data) { return 0; } @@ -65,7 +65,7 @@ namespace ehs } ICMP_Header header = { - 8, + version == IP::V6 ? (UInt_8)128 : (UInt_8)8, 0, 0 }; @@ -85,12 +85,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) diff --git a/src/io/socket/ICMP_LNX.cpp b/src/io/socket/ICMP_LNX.cpp index 5e426fd..1274b2f 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,138 @@ 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) + { + if (GetVersion() == IP::V6) + return SendV6(address, header, data, size); + + return SendV4(address, header, data, size); + } + + UInt_64 ICMP::Receive(Str_8 &address, ICMP_Header &header, Serializer &data) + { + if (GetVersion() == IP::V6) + return ReceiveV6(address, header, data); + + return ReceiveV4(address, header, data); + } + + 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 +224,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 +247,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 +383,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; - } } From d8c532718005c6efecc280167acda92e0d31dc09 Mon Sep 17 00:00:00 2001 From: Karutoh Date: Fri, 28 Mar 2025 20:47:27 -0700 Subject: [PATCH 13/16] Added ICMPv6 support to Windows. --- include/ehs/io/socket/BaseICMP.h | 15 +- include/ehs/io/socket/ICMP_LNX.h | 14 +- include/ehs/io/socket/ICMP_W32.h | 29 +++- src/io/socket/BaseICMP.cpp | 34 ++++- src/io/socket/ICMP_LNX.cpp | 18 +-- src/io/socket/ICMP_W32.cpp | 248 +++++++++++++++++++++++++++---- 6 files changed, 306 insertions(+), 52 deletions(-) diff --git a/include/ehs/io/socket/BaseICMP.h b/include/ehs/io/socket/BaseICMP.h index b81e12e..42d3720 100644 --- a/include/ehs/io/socket/BaseICMP.h +++ b/include/ehs/io/socket/BaseICMP.h @@ -39,9 +39,11 @@ 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); @@ -53,5 +55,14 @@ namespace ehs protected: 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_LNX.h b/include/ehs/io/socket/ICMP_LNX.h index 68efaaa..83525f1 100644 --- a/include/ehs/io/socket/ICMP_LNX.h +++ b/include/ehs/io/socket/ICMP_LNX.h @@ -20,7 +20,7 @@ namespace ehs sockaddr_in6 src; public: - ~ICMP(); + ~ICMP() override; ICMP(); @@ -34,9 +34,7 @@ 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; @@ -51,12 +49,12 @@ namespace ehs 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); + 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); + 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; + 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; + 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 index 5d7b7e6..e960be7 100644 --- a/include/ehs/io/socket/ICMP_W32.h +++ b/include/ehs/io/socket/ICMP_W32.h @@ -1,6 +1,11 @@ #pragma once #include "BaseICMP.h" +#include "ehs/System/OS.h" + +#include +#include +#include namespace ehs { @@ -8,8 +13,11 @@ namespace ehs { private: Int_32 hdl; + sockaddr_in6 src; public: + ~ICMP() override; + ICMP(); ICMP(IP version); @@ -22,12 +30,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/src/io/socket/BaseICMP.cpp b/src/io/socket/BaseICMP.cpp index 63ef63f..55273ca 100644 --- a/src/io/socket/BaseICMP.cpp +++ b/src/io/socket/BaseICMP.cpp @@ -45,14 +45,24 @@ namespace ehs return *this; } + void BaseICMP::Release() + { + } + 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) { - return 0; + 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) @@ -112,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 1274b2f..c2cdf6f 100644 --- a/src/io/socket/ICMP_LNX.cpp +++ b/src/io/socket/ICMP_LNX.cpp @@ -79,20 +79,18 @@ namespace ehs return *this; } - UInt_64 ICMP::Send(const Str_8 &address, ICMP_Header header, const Byte *data, const UInt_64 size) + void ICMP::Release() { - if (GetVersion() == IP::V6) - return SendV6(address, header, data, size); + if (close(hdl) == -1) + { + EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); - return SendV4(address, header, data, size); - } + return; + } - UInt_64 ICMP::Receive(Str_8 &address, ICMP_Header &header, Serializer &data) - { - if (GetVersion() == IP::V6) - return ReceiveV6(address, header, data); + hdl = EHS_INVALID_SOCKET; - return ReceiveV4(address, header, data); + EHS_LOG_SUCCESS(); } void ICMP::SetReceiveTimeout(const UInt_64 timeout) diff --git a/src/io/socket/ICMP_W32.cpp b/src/io/socket/ICMP_W32.cpp index ab5d16e..29b98ec 100644 --- a/src/io/socket/ICMP_W32.cpp +++ b/src/io/socket/ICMP_W32.cpp @@ -1,9 +1,5 @@ #include "ehs/io/socket/ICMP_W32.h" -#include -#include -#include - struct iphdr { u_char ip_hl:4, ip_v:4; @@ -19,6 +15,12 @@ struct iphdr namespace ehs { + ICMP::~ICMP() + { + if (close(hdl) == -1) + EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); + } + ICMP::ICMP() : hdl(EHS_INVALID_SOCKET) { @@ -74,7 +76,173 @@ namespace ehs 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(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() + { + 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()) + { + 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()) { @@ -116,7 +284,54 @@ namespace ehs return sent; } - UInt_64 ICMP::Receive(Str_8 &address, ICMP_Header header, Serializer &data) + 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()) { @@ -163,25 +378,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, (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; - } } From ff42dfd3a47b17fb45fd1974f216b69f2974c23f Mon Sep 17 00:00:00 2001 From: Karutoh Date: Fri, 28 Mar 2025 20:53:48 -0700 Subject: [PATCH 14/16] Added ICMPv6 support to Linux. --- include/ehs/io/socket/ICMP_W32.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/ehs/io/socket/ICMP_W32.h b/include/ehs/io/socket/ICMP_W32.h index e960be7..377059d 100644 --- a/include/ehs/io/socket/ICMP_W32.h +++ b/include/ehs/io/socket/ICMP_W32.h @@ -9,6 +9,13 @@ namespace ehs { + struct PseudoICMPv6_Header + { + sockaddr_in6 src; + sockaddr_in6 dst; + UInt_32 length; + }; + class ICMP : public BaseICMP { private: From 8b53b6e4d108c6c9fa4d43a1468c2d766c94f15d Mon Sep 17 00:00:00 2001 From: Karutoh Date: Fri, 28 Mar 2025 21:24:13 -0700 Subject: [PATCH 15/16] Implemented ICMPv6 on Windows. --- include/ehs/io/socket/ICMP_W32.h | 2 +- src/io/socket/ICMP_W32.cpp | 50 +++++++++++++++++++------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/include/ehs/io/socket/ICMP_W32.h b/include/ehs/io/socket/ICMP_W32.h index 377059d..15cc295 100644 --- a/include/ehs/io/socket/ICMP_W32.h +++ b/include/ehs/io/socket/ICMP_W32.h @@ -19,7 +19,7 @@ namespace ehs class ICMP : public BaseICMP { private: - Int_32 hdl; + Socket hdl; sockaddr_in6 src; public: diff --git a/src/io/socket/ICMP_W32.cpp b/src/io/socket/ICMP_W32.cpp index 29b98ec..16ac1a5 100644 --- a/src/io/socket/ICMP_W32.cpp +++ b/src/io/socket/ICMP_W32.cpp @@ -17,7 +17,7 @@ namespace ehs { ICMP::~ICMP() { - if (close(hdl) == -1) + if (closesocket(hdl) == -1) EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); } @@ -78,7 +78,7 @@ namespace ehs void ICMP::Release() { - if (close(hdl) == -1) + if (closesocket(hdl) == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket."); @@ -118,33 +118,43 @@ namespace ehs sockaddr_in6 ICMP::RetrieveSrcAddress() { - ifaddrs *ifaddr; sockaddr_in6 addr = {}; + UInt_32 outBufLen = 15000; + Array buffer(outBufLen); + PIP_ADAPTER_ADDRESSES pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)&buffer[0]; - if (getifaddrs(&ifaddr) == -1) + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAdapterAddresses, (PULONG)&outBufLen) == ERROR_BUFFER_OVERFLOW) { - EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve public address with error #" + Str_8::FromNum(errno) + "."); - - return addr; + buffer.Resize(outBufLen); + pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)&buffer[0]; } - for (ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAdapterAddresses, (PULONG)&outBufLen) == NO_ERROR) { - if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_INET6) - continue; + 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; - addr = *(sockaddr_in6 *)ifa->ifa_addr; - if (!addr.sin6_addr.s6_addr32[0] || IsLinkLocal(addr.sin6_addr)) - continue; + sockaddr_in6 *ipv6Addr = (sockaddr_in6 *)sa; - break; + // Skip link-local addresses + if (IN6_IS_ADDR_LINKLOCAL(&ipv6Addr->sin6_addr)) + continue; + + addr = *ipv6Addr; + + return addr; // Return the first suitable address + } + } } - freeifaddrs(ifaddr); - EHS_LOG_SUCCESS(); - return addr; + return addr; // Return an empty sockaddr_in6 if no valid address was found } UInt_32 ICMP::CalculatePseudoHeaderChecksum(const PseudoICMPv6_Header &header) @@ -171,10 +181,10 @@ namespace ehs { UInt_32 checksum = 0; - if (!src.sin6_addr.s6_addr32[0]) + if (!src.sin6_addr.u.Word[0]) { src = RetrieveSrcAddress(); - if (!src.sin6_addr.s6_addr32[0]) + if (!src.sin6_addr.u.Word[0]) { EHS_LOG_INT(LogType::ERR, 0, "Could not retrieve a suitable global address."); return checksum; @@ -261,7 +271,7 @@ namespace ehs payload.SetOffset(payload.GetOffset() + size); - header.checksum = ComputeChecksum((UInt_16 *)&payload[0], payload.Size()); + header.checksum = ComputeChecksumV4((UInt_16 *)&payload[0], payload.Size()); payload.SetOffset(0); payload.Write(header); From c437a39e1d2138fe6f952f1ebdc9cb09cf00940e Mon Sep 17 00:00:00 2001 From: Karutoh Date: Fri, 28 Mar 2025 21:47:47 -0700 Subject: [PATCH 16/16] Added missing dependency on Windows. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f93464..e3eda96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,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")