Files
spice2x-r3d/src/spice2x/hooks/icmphook_net.cpp
T
drmextandGitHub 5e4afecd90 network: NIC spoof matching tunnel (#912)
## Link to GitHub Issue or related Pull Request, if one exists
#911

## Description of change
Add tunnelhost/tunnelclient modes to -nicspoof so two cabs can do
LAN-style BPL matching over a real UDP path (including Wine), while
keeping the fake overlay NIC. Offline spoof remains available as before.

## Testing
DDR
2026-09-11 00:37:45 -07:00

776 lines
24 KiB
C++

#include "icmphook_net.h"
#include "hooks/nicspoof.h"
#include "util/detour.h"
#include "util/logging.h"
#include <iphlpapi.h>
#include <mutex>
#include <deque>
#include <vector>
#include <chrono>
#include <cstring>
#include <atomic>
#include <condition_variable>
#include <unordered_map>
void icmphook_iphlpapi_install();
#ifndef SPICE64
using socket_uint = unsigned int;
#else
using socket_uint = unsigned long long;
#endif
namespace icmphook_internal {
constexpr uint32_t k_loopback_host = 0x7f000001u;
#pragma pack(push, 1)
struct IcmpHeaderView {
uint8_t type;
uint8_t code;
uint16_t checksum_be;
};
#pragma pack(pop)
bool icmp_packet_valid(const uint8_t *icmp, size_t len) {
if (len < sizeof(IcmpHeaderView)) {
return false;
}
unsigned sum = 0;
for (size_t i = 0; i + 1 < len; i += 2) {
sum += (static_cast<unsigned>(icmp[i]) << 8) | icmp[i + 1];
sum = (sum >> 16) + (sum & 0xFFFF);
}
if (len & 1) {
sum += static_cast<unsigned>(icmp[len - 1]) << 8;
sum = (sum >> 16) + (sum & 0xFFFF);
}
sum = (sum >> 16) + (sum & 0xFFFF);
return sum == 0xFFFFu || sum == 0;
}
bool icmp_build_echo_reply(std::vector<uint8_t> &out, const uint8_t *req, size_t req_len) {
if (req_len < sizeof(IcmpHeaderView)) {
return false;
}
out.resize(req_len);
memcpy(out.data(), req, req_len);
auto *hdr = reinterpret_cast<IcmpHeaderView *>(out.data());
hdr->type = 0;
hdr->code = 0;
hdr->checksum_be = 0;
unsigned sum = 0;
for (size_t i = 0; i + 1 < out.size(); i += 2) {
sum += (static_cast<unsigned>(out[i]) << 8) | out[i + 1];
sum = (sum >> 16) + (sum & 0xFFFF);
}
if (out.size() & 1) {
sum += static_cast<unsigned>(out[out.size() - 1]) << 8;
sum = (sum >> 16) + (sum & 0xFFFF);
}
sum = (sum >> 16) + (sum & 0xFFFF);
uint16_t csum = static_cast<uint16_t>(~sum);
hdr->checksum_be = htons(csum);
return true;
}
bool icmp_fix_pointer(
const uint8_t *base,
size_t total_len,
const uint8_t **icmp_start,
size_t *icmp_len) {
if (total_len >= 20 && (base[0] >> 4) == 4) {
size_t ihl = (base[0] & 0x0Fu) * 4;
if (ihl < 20 || ihl > total_len || base[9] != IPPROTO_ICMP) {
return false;
}
*icmp_start = base + ihl;
*icmp_len = total_len - ihl;
return true;
}
*icmp_start = base;
*icmp_len = total_len;
return true;
}
bool ipv4_encode_datagram(
std::vector<uint8_t> &out,
uint32_t src_host_order,
uint32_t dst_host_order,
const uint8_t *payload,
size_t payload_len) {
const size_t hdr_len = 20;
if (payload_len > 0xFFFFu - hdr_len) {
return false;
}
const auto total_len = static_cast<uint16_t>(hdr_len + payload_len);
out.resize(hdr_len + payload_len);
uint8_t *p = out.data();
memset(p, 0, hdr_len);
p[0] = 0x45;
p[1] = 0;
p[2] = static_cast<uint8_t>(total_len >> 8);
p[3] = static_cast<uint8_t>(total_len & 0xFF);
p[6] = 0x40;
p[8] = 1;
p[9] = IPPROTO_ICMP;
uint32_t src_wire = htonl(src_host_order);
uint32_t dst_wire = htonl(dst_host_order);
memcpy(p + 12, &src_wire, 4);
memcpy(p + 16, &dst_wire, 4);
unsigned sum = 0;
for (size_t i = 0; i < hdr_len; i += 2) {
sum += (static_cast<unsigned>(p[i]) << 8) | p[i + 1];
while (sum >> 16) {
sum = (sum & 0xFFFF) + (sum >> 16);
}
}
uint16_t hdr_csum = static_cast<uint16_t>(~sum);
p[10] = static_cast<uint8_t>(hdr_csum >> 8);
p[11] = static_cast<uint8_t>(hdr_csum & 0xFF);
memcpy(p + hdr_len, payload, payload_len);
return true;
}
// Cached adapter list. Populated once per process: adapter changes after the
// first ICMP send are intentionally not picked up, in exchange for avoiding
// repeated IP Helper calls on every emulated send.
std::once_flag g_adapters_cache_init;
std::vector<uint8_t> g_adapters_cache;
void fill_adapters_cache() {
std::call_once(g_adapters_cache_init, []() {
ULONG buf_len = 0;
DWORD r = GetAdaptersInfo(nullptr, &buf_len);
if (r != ERROR_BUFFER_OVERFLOW && r != ERROR_SUCCESS) {
return;
}
if (buf_len == 0) {
return;
}
g_adapters_cache.resize(buf_len);
auto *info = reinterpret_cast<PIP_ADAPTER_INFO>(g_adapters_cache.data());
if (GetAdaptersInfo(info, &buf_len) != ERROR_SUCCESS) {
g_adapters_cache.clear();
}
});
}
uint32_t local_ipv4_for_peer(
uint32_t peer_host_order,
uint32_t bind_pref_host_order,
bool has_bind) {
if (peer_host_order == k_loopback_host) {
return k_loopback_host;
}
if (has_bind && bind_pref_host_order != 0) {
return bind_pref_host_order;
}
fill_adapters_cache();
if (g_adapters_cache.empty()) {
return peer_host_order;
}
auto *info = reinterpret_cast<PIP_ADAPTER_INFO>(g_adapters_cache.data());
for (PIP_ADAPTER_INFO a = info; a != nullptr; a = a->Next) {
uint32_t ip = ntohl(inet_addr(a->IpAddressList.IpAddress.String));
uint32_t mask = ntohl(inet_addr(a->IpAddressList.IpMask.String));
if (mask == 0) {
continue;
}
if ((peer_host_order & mask) == (ip & mask)) {
return ip;
}
}
return peer_host_order;
}
struct EmuSock {
std::deque<std::vector<uint8_t>> queue;
std::condition_variable_any cv;
DWORD rx_timeout_ms = INFINITE;
bool nonblocking = false;
bool has_bind = false;
uint32_t bind_ip_host = 0;
};
std::recursive_mutex g_mu;
std::unordered_map<SOCKET, std::unique_ptr<EmuSock>> g_socks;
std::atomic<uint32_t> g_seq{1};
// Fast-path flag so the bind() hook in networkhook.cpp can short-circuit
// without taking g_mu when the ICMP feature was never enabled.
std::atomic<bool> g_installed{false};
decltype(socket) *socket_orig = nullptr;
decltype(WSASocketW) *WSASocketW_orig = nullptr;
decltype(WSASocketA) *WSASocketA_orig = nullptr;
decltype(closesocket) *closesocket_orig = nullptr;
decltype(bind) *bind_trampoline_orig = nullptr;
decltype(sendto) *sendto_orig = nullptr;
decltype(recvfrom) *recvfrom_orig = nullptr;
decltype(WSASendTo) *WSASendTo_orig = nullptr;
decltype(WSARecvFrom) *WSARecvFrom_orig = nullptr;
decltype(ioctlsocket) *ioctlsocket_orig = nullptr;
decltype(setsockopt) *setsockopt_orig = nullptr;
EmuSock *lookup(SOCKET s) {
std::lock_guard<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it == g_socks.end()) {
return nullptr;
}
return it->second.get();
}
SOCKET alloc_icmp_socket() {
std::lock_guard<std::recursive_mutex> lock(g_mu);
for (;;) {
uint32_t x = g_seq.fetch_add(1, std::memory_order_relaxed);
SOCKET s = (SOCKET)(socket_uint)(0xE0000000u | (x & 0x0FFFFFFFu));
if (s == INVALID_SOCKET) {
continue;
}
if (g_socks.find(s) == g_socks.end()) {
g_socks[s] = std::make_unique<EmuSock>();
return s;
}
}
}
void release_icmp_socket(SOCKET s) {
std::lock_guard<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it != g_socks.end()) {
it->second->cv.notify_all();
g_socks.erase(it);
}
}
bool process_icmp_send(SOCKET s, const uint8_t *buf, int len, const sockaddr *to, int tolen) {
if (len <= 0 || tolen < (int) sizeof(sockaddr_in) || !buf || !to) {
return true;
}
auto *sin = reinterpret_cast<const sockaddr_in *>(to);
if (sin->sin_family != AF_INET) {
return true;
}
uint32_t peer_host = ntohl(sin->sin_addr.s_addr);
const uint8_t *icmp_ptr = nullptr;
size_t icmp_len = 0;
const auto *raw = reinterpret_cast<const uint8_t *>(buf);
if (!icmp_fix_pointer(raw, static_cast<size_t>(len), &icmp_ptr, &icmp_len)) {
return true;
}
if (!icmp_packet_valid(icmp_ptr, icmp_len)) {
return true;
}
if (icmp_len < sizeof(IcmpHeaderView)) {
return true;
}
const auto *ih = reinterpret_cast<const IcmpHeaderView *>(icmp_ptr);
if (ih->type != 8 || ih->code != 0) {
return true;
}
std::vector<uint8_t> icmp_reply;
if (!icmp_build_echo_reply(icmp_reply, icmp_ptr, icmp_len)) {
return true;
}
std::vector<uint8_t> packet;
{
std::lock_guard<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it == g_socks.end()) {
return true;
}
EmuSock *es = it->second.get();
uint32_t local_host = local_ipv4_for_peer(peer_host, es->bind_ip_host, es->has_bind);
if (!ipv4_encode_datagram(
packet, peer_host, local_host, icmp_reply.data(), icmp_reply.size())) {
return true;
}
es->queue.push_back(std::move(packet));
es->cv.notify_all();
}
return true;
}
SOCKET WINAPI socket_hook(int af, int type, int protocol) {
if (af == AF_INET && type == SOCK_RAW && protocol == IPPROTO_ICMP) {
SOCKET s = alloc_icmp_socket();
log_info("network", "ICMP emulation: allocated raw socket {}", (socket_uint) s);
return s;
}
return socket_orig(af, type, protocol);
}
SOCKET WINAPI WSASocketW_hook(
int af,
int type,
int protocol,
LPWSAPROTOCOL_INFOW lpProtocolInfo,
GROUP g,
DWORD dwFlags) {
const bool is_raw_icmp =
af == AF_INET && type == SOCK_RAW && protocol == IPPROTO_ICMP;
if (lpProtocolInfo == nullptr && is_raw_icmp) {
SOCKET s = alloc_icmp_socket();
log_info("network", "ICMP emulation: allocated raw WSASocketW {}", (socket_uint) s);
return s;
}
return WSASocketW_orig(af, type, protocol, lpProtocolInfo, g, dwFlags);
}
SOCKET WINAPI WSASocketA_hook(
int af,
int type,
int protocol,
LPWSAPROTOCOL_INFOA lpProtocolInfo,
GROUP g,
DWORD dwFlags) {
const bool is_raw_icmp =
af == AF_INET && type == SOCK_RAW && protocol == IPPROTO_ICMP;
if (lpProtocolInfo == nullptr && is_raw_icmp) {
SOCKET s = alloc_icmp_socket();
log_info("network", "ICMP emulation: allocated raw WSASocketA {}", (socket_uint) s);
return s;
}
return WSASocketA_orig(af, type, protocol, lpProtocolInfo, g, dwFlags);
}
int WINAPI closesocket_hook(SOCKET s) {
if (lookup(s)) {
release_icmp_socket(s);
return 0;
}
return closesocket_orig(s);
}
int WINAPI bind_hook_ws2(SOCKET s, const sockaddr *name, int namelen) {
{
std::lock_guard<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it != g_socks.end()) {
if (!name || namelen < (int) sizeof(sockaddr_in)) {
WSASetLastError(WSAEFAULT);
return SOCKET_ERROR;
}
auto *in = reinterpret_cast<const sockaddr_in *>(name);
if (in->sin_family != AF_INET) {
WSASetLastError(WSAEAFNOSUPPORT);
return SOCKET_ERROR;
}
it->second->has_bind = true;
it->second->bind_ip_host = ntohl(in->sin_addr.s_addr);
return 0;
}
}
return bind_trampoline_orig(s, name, namelen);
}
int WINAPI sendto_hook(
SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) {
EmuSock *es = lookup(s);
if (!es) {
return sendto_orig(s, buf, len, flags, to, tolen);
}
if (!buf || !to) {
WSASetLastError(WSAEFAULT);
return SOCKET_ERROR;
}
process_icmp_send(s, reinterpret_cast<const uint8_t *>(buf), len, to, tolen);
return len;
}
int WINAPI WSASendTo_hook(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
const sockaddr *lpTo,
int iTolen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
EmuSock *es = lookup(s);
if (!es) {
return WSASendTo_orig(
s,
lpBuffers,
dwBufferCount,
lpNumberOfBytesSent,
dwFlags,
lpTo,
iTolen,
lpOverlapped,
lpCompletionRoutine);
}
if (lpOverlapped != nullptr) {
WSASetLastError(WSAEOPNOTSUPP);
return SOCKET_ERROR;
}
if (dwBufferCount != 1 || !lpBuffers || !lpNumberOfBytesSent) {
WSASetLastError(WSAEINVAL);
return SOCKET_ERROR;
}
int len = (int) lpBuffers[0].len;
process_icmp_send(
s,
reinterpret_cast<const uint8_t *>(lpBuffers[0].buf),
len,
lpTo,
iTolen);
*lpNumberOfBytesSent = static_cast<DWORD>(len);
return 0;
}
// Dequeue one datagram for an emulated ICMP socket into the caller-provided
// buffer. If out_full_dgram_bytes is non-null, it receives the full packet
// size (before truncation to len) so callers can signal WSAEMSGSIZE.
int emu_recv_dequeue(
SOCKET s, char *buf, int len, sockaddr *from, int *fromlen,
size_t *out_full_dgram_bytes) {
if (!buf || len <= 0) {
WSASetLastError(WSAEINVAL);
return SOCKET_ERROR;
}
std::vector<uint8_t> pkt;
{
std::unique_lock<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it == g_socks.end()) {
WSASetLastError(WSAENOTSOCK);
return SOCKET_ERROR;
}
auto wait_pred = [&] {
auto j = g_socks.find(s);
return j == g_socks.end() || !j->second->queue.empty();
};
EmuSock *es = it->second.get();
if (es->nonblocking) {
if (es->queue.empty()) {
WSASetLastError(WSAEWOULDBLOCK);
return SOCKET_ERROR;
}
} else if (es->rx_timeout_ms == INFINITE || es->rx_timeout_ms == 0) {
it->second->cv.wait(lock, wait_pred);
} else {
const auto timeout = std::chrono::milliseconds(es->rx_timeout_ms);
if (!it->second->cv.wait_for(lock, timeout, wait_pred)) {
WSASetLastError(WSAETIMEDOUT);
return SOCKET_ERROR;
}
}
it = g_socks.find(s);
if (it == g_socks.end()) {
WSASetLastError(WSAENOTSOCK);
return SOCKET_ERROR;
}
es = it->second.get();
if (es->queue.empty()) {
WSASetLastError(WSAENOTSOCK);
return SOCKET_ERROR;
}
pkt = std::move(es->queue.front());
es->queue.pop_front();
}
if (out_full_dgram_bytes) {
*out_full_dgram_bytes = pkt.size();
}
const int take = (len < (int) pkt.size()) ? len : (int) pkt.size();
memcpy(buf, pkt.data(), static_cast<size_t>(take));
if (from && fromlen && *fromlen >= (int) sizeof(sockaddr_in)) {
auto *out = reinterpret_cast<sockaddr_in *>(from);
memset(out, 0, sizeof(*out));
out->sin_family = AF_INET;
if (pkt.size() >= 20) {
memcpy(&out->sin_addr, pkt.data() + 12, 4);
} else {
out->sin_addr.s_addr = htonl(k_loopback_host);
}
*fromlen = sizeof(sockaddr_in);
}
return take;
}
int WINAPI recvfrom_hook(
SOCKET s, char *buf, int len, int flags, sockaddr *from, int *fromlen) {
if (!lookup(s)) {
return recvfrom_orig(s, buf, len, flags, from, fromlen);
}
return emu_recv_dequeue(s, buf, len, from, fromlen, nullptr);
}
int WINAPI WSARecvFrom_hook(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
sockaddr *lpFrom,
LPINT lpFromlen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
if (!lookup(s)) {
return WSARecvFrom_orig(
s,
lpBuffers,
dwBufferCount,
lpNumberOfBytesRecvd,
lpFlags,
lpFrom,
lpFromlen,
lpOverlapped,
lpCompletionRoutine);
}
if (lpOverlapped != nullptr) {
WSASetLastError(WSAEOPNOTSUPP);
return SOCKET_ERROR;
}
if (dwBufferCount != 1 || !lpBuffers || !lpNumberOfBytesRecvd) {
WSASetLastError(WSAEINVAL);
return SOCKET_ERROR;
}
sockaddr_in from_tmp{};
int fromlen_tmp = sizeof(from_tmp);
size_t full_bytes = 0;
int r = emu_recv_dequeue(
s,
lpBuffers[0].buf,
(int) lpBuffers[0].len,
reinterpret_cast<sockaddr *>(&from_tmp),
&fromlen_tmp,
&full_bytes);
if (r == SOCKET_ERROR) {
return SOCKET_ERROR;
}
if (full_bytes > lpBuffers[0].len) {
WSASetLastError(WSAEMSGSIZE);
return SOCKET_ERROR;
}
*lpNumberOfBytesRecvd = static_cast<DWORD>(r);
if (lpFlags) {
*lpFlags = 0;
}
if (lpFrom && lpFromlen && *lpFromlen >= (int) sizeof(sockaddr_in)) {
*reinterpret_cast<sockaddr_in *>(lpFrom) = from_tmp;
*lpFromlen = sizeof(sockaddr_in);
}
return 0;
}
int WINAPI ioctlsocket_hook(SOCKET s, long cmd, u_long *argp) {
{
std::lock_guard<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it != g_socks.end()) {
EmuSock *es = it->second.get();
if (argp && static_cast<u_long>(cmd) == FIONBIO) {
es->nonblocking = (*argp != 0);
return 0;
}
WSASetLastError(WSAEOPNOTSUPP);
return SOCKET_ERROR;
}
}
return ioctlsocket_orig(s, cmd, argp);
}
int WINAPI setsockopt_hook(
SOCKET s, int level, int optname, const char *optval, int optlen) {
{
std::lock_guard<std::recursive_mutex> lock(g_mu);
auto it = g_socks.find(s);
if (it != g_socks.end()) {
const bool deref_optval = level == SOL_SOCKET && optlen >= (int) sizeof(DWORD);
if (deref_optval && optval) {
EmuSock *es = it->second.get();
auto v = reinterpret_cast<const DWORD *>(optval);
if (optname == SO_RCVTIMEO) {
DWORD ms = *v;
es->rx_timeout_ms = (ms == 0) ? INFINITE : ms;
return 0;
}
if (optname == SO_SNDTIMEO) {
return 0;
}
WSASetLastError(WSAEOPNOTSUPP);
return SOCKET_ERROR;
}
if (!deref_optval) {
WSASetLastError(WSAEOPNOTSUPP);
return SOCKET_ERROR;
}
}
}
return setsockopt_orig(s, level, optname, optval, optlen);
}
void install_icmphook_hooks() {
static bool done = false;
if (done) {
return;
}
done = true;
const bool defer_divert = nicspoof_tunnel_enabled();
bool ok = true;
// Always own socket creation so raw ICMP sockets become emulated.
ok &= detour::trampoline_try(
"ws2_32.dll", "socket",
(void *) socket_hook, (void **) &socket_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "WSASocketW",
(void *) WSASocketW_hook, (void **) &WSASocketW_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "WSASocketA",
(void *) WSASocketA_hook, (void **) &WSASocketA_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "setsockopt",
(void *) setsockopt_hook, (void **) &setsockopt_orig);
if (defer_divert) {
// NIC tunnel already hooked bind/sendto/recvfrom/closesocket/ioctlsocket.
// Tunnel hooks call icmphook_try_* for emulated ICMP sockets.
log_info("network",
"ICMP emulation: socket hooks installed; divert deferred to "
"NIC tunnel (icmphook_try_*)");
} else {
ok &= detour::trampoline_try(
"ws2_32.dll", "closesocket",
(void *) closesocket_hook, (void **) &closesocket_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "bind",
(void *) bind_hook_ws2, (void **) &bind_trampoline_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "sendto",
(void *) sendto_hook, (void **) &sendto_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "recvfrom",
(void *) recvfrom_hook, (void **) &recvfrom_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "WSASendTo",
(void *) WSASendTo_hook, (void **) &WSASendTo_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "WSARecvFrom",
(void *) WSARecvFrom_hook, (void **) &WSARecvFrom_orig);
ok &= detour::trampoline_try(
"ws2_32.dll", "ioctlsocket",
(void *) ioctlsocket_hook, (void **) &ioctlsocket_orig);
if (!ok) {
log_warning(
"network",
"ICMP emulation: one or more ws2_32 hooks failed to install");
} else {
log_info("network", "ICMP emulation hooks installed (raw ICMP sockets)");
}
}
if (defer_divert && !ok) {
log_warning(
"network",
"ICMP emulation: one or more socket-creation hooks failed");
}
g_installed.store(true, std::memory_order_release);
}
} // namespace icmphook_internal
bool icmphook_is_emulated_socket(SOCKET s) {
if (!icmphook_internal::g_installed.load(std::memory_order_acquire)) {
return false;
}
std::lock_guard<std::recursive_mutex> lock(icmphook_internal::g_mu);
return icmphook_internal::g_socks.find(s) != icmphook_internal::g_socks.end();
}
bool icmphook_try_bind(SOCKET s, const struct sockaddr *name, int namelen, int *out_result) {
if (!icmphook_internal::g_installed.load(std::memory_order_acquire)) {
return false;
}
std::lock_guard<std::recursive_mutex> lock(icmphook_internal::g_mu);
if (icmphook_internal::g_socks.find(s) == icmphook_internal::g_socks.end()) {
return false;
}
*out_result = icmphook_internal::bind_hook_ws2(s, name, namelen);
return true;
}
bool icmphook_try_sendto(SOCKET s, const char *buf, int len, int flags,
const sockaddr *to, int tolen, int *out_result) {
if (!icmphook_is_emulated_socket(s)) {
return false;
}
*out_result = icmphook_internal::sendto_hook(s, buf, len, flags, to, tolen);
return true;
}
bool icmphook_try_recvfrom(SOCKET s, char *buf, int len, int flags,
sockaddr *from, int *fromlen, int *out_result) {
if (!icmphook_is_emulated_socket(s)) {
return false;
}
*out_result = icmphook_internal::recvfrom_hook(s, buf, len, flags, from, fromlen);
return true;
}
bool icmphook_try_WSASendTo(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const sockaddr *lpTo,
int iTolen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, int *out_result) {
if (!icmphook_is_emulated_socket(s)) {
return false;
}
*out_result = icmphook_internal::WSASendTo_hook(
s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo,
iTolen, lpOverlapped, lpCompletionRoutine);
return true;
}
bool icmphook_try_WSARecvFrom(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, sockaddr *lpFrom,
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, int *out_result) {
if (!icmphook_is_emulated_socket(s)) {
return false;
}
*out_result = icmphook_internal::WSARecvFrom_hook(
s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom,
lpFromlen, lpOverlapped, lpCompletionRoutine);
return true;
}
bool icmphook_try_ioctlsocket(SOCKET s, long cmd, u_long *argp, int *out_result) {
if (!icmphook_is_emulated_socket(s)) {
return false;
}
*out_result = icmphook_internal::ioctlsocket_hook(s, cmd, argp);
return true;
}
bool icmphook_try_closesocket(SOCKET s, int *out_result) {
if (!icmphook_is_emulated_socket(s)) {
return false;
}
*out_result = icmphook_internal::closesocket_hook(s);
return true;
}
void icmphook_net_init() {
icmphook_internal::install_icmphook_hooks();
icmphook_iphlpapi_install();
}