mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-09-27 17:28:16 +03:00
## 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
646 lines
20 KiB
C++
646 lines
20 KiB
C++
#define _WIN32_WINNT 0x0601
|
|
|
|
#include "nicspoof.h"
|
|
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <windows.h>
|
|
#include <iphlpapi.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "util/detour.h"
|
|
#include "util/logging.h"
|
|
#include "hooks/nicspoof_tunnel.h"
|
|
|
|
namespace nicspoof_detail {
|
|
NicSpoofConfig g_cfg;
|
|
|
|
constexpr uint32_t k_default_local_ip = 0x0A64640Au; /* 10.100.100.10 */
|
|
constexpr uint32_t k_gateway = 0x0A646401u; /* 10.100.100.1 */
|
|
constexpr uint32_t k_dns1 = 0x0A02010Au; /* 10.2.1.10 */
|
|
constexpr uint32_t k_dns2 = 0x0A02011Eu; /* 10.2.1.30 */
|
|
constexpr uint32_t k_dhcp = 0xC0A80001u; /* 192.168.0.1 */
|
|
constexpr uint32_t k_mask = 0xFF000000u; /* 255.0.0.0 */
|
|
constexpr int k_prefix_len = 8;
|
|
constexpr DWORD k_ifindex = 77;
|
|
constexpr uint8_t k_fake_mac[6] = {0x12, 0x37, 0x13, 0x37, 0x13, 0x37};
|
|
constexpr char k_hostname[] = "N1C5P00F";
|
|
constexpr char k_domain[] = "sp2x";
|
|
constexpr char k_hostname_fqdn[] = "N1C5P00F.sp2x";
|
|
constexpr wchar_t k_domain_w[] = L"sp2x";
|
|
constexpr char k_adapter_name[] = "{NICSPOOF-0880-0001-4250-4E6963537066}";
|
|
constexpr char k_adapter_desc[] = "NicSpoof Virtual Ethernet";
|
|
constexpr wchar_t k_friendly_name[] = L"NicSpoof";
|
|
constexpr wchar_t k_adapter_desc_w[] = L"NicSpoof Virtual Ethernet";
|
|
|
|
uint32_t g_local_ip = k_default_local_ip;
|
|
uint32_t g_subnet = k_default_local_ip & k_mask;
|
|
|
|
#define ALIGN_UP_PTR(p, a) \
|
|
((BYTE *)(((ULONG_PTR)(p) + ((ULONG_PTR)(a) - 1)) & ~((ULONG_PTR)(a) - 1)))
|
|
|
|
bool g_log_adapters = true;
|
|
bool g_log_params = true;
|
|
|
|
[[maybe_unused]] decltype(GetAdaptersAddresses) *GetAdaptersAddresses_orig = nullptr;
|
|
[[maybe_unused]] decltype(GetAdaptersInfo) *GetAdaptersInfo_orig = nullptr;
|
|
[[maybe_unused]] decltype(GetNetworkParams) *GetNetworkParams_orig = nullptr;
|
|
[[maybe_unused]] decltype(getaddrinfo) *getaddrinfo_orig = nullptr;
|
|
[[maybe_unused]] decltype(freeaddrinfo) *freeaddrinfo_orig = nullptr;
|
|
|
|
void ip_to_str(uint32_t ip, char *out, size_t n) {
|
|
if (!out || n == 0) {
|
|
return;
|
|
}
|
|
snprintf(out, n, "%u.%u.%u.%u",
|
|
(ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
|
|
out[n - 1] = 0;
|
|
}
|
|
|
|
uint32_t parse_ipv4(const char *s) {
|
|
unsigned a = 0;
|
|
unsigned b = 0;
|
|
unsigned c = 0;
|
|
unsigned d = 0;
|
|
char trail = 0;
|
|
if (!s) {
|
|
return 0;
|
|
}
|
|
if (sscanf(s, "%u.%u.%u.%u%c", &a, &b, &c, &d, &trail) != 4) {
|
|
return 0;
|
|
}
|
|
if (a > 255 || b > 255 || c > 255 || d > 255) {
|
|
return 0;
|
|
}
|
|
return (a << 24) | (b << 16) | (c << 8) | d;
|
|
}
|
|
|
|
bool name_is_local(const char *name) {
|
|
if (!name || !name[0]) {
|
|
return true;
|
|
}
|
|
if (_stricmp(name, "localhost") == 0) {
|
|
return true;
|
|
}
|
|
if (_stricmp(name, k_hostname) == 0) {
|
|
return true;
|
|
}
|
|
if (_stricmp(name, k_hostname_fqdn) == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool name_is_numeric_ipv4(const char *name) {
|
|
unsigned a = 0;
|
|
unsigned b = 0;
|
|
unsigned c = 0;
|
|
unsigned d = 0;
|
|
char trail = 0;
|
|
if (!name) {
|
|
return false;
|
|
}
|
|
if (sscanf(name, "%u.%u.%u.%u%c", &a, &b, &c, &d, &trail) != 4) {
|
|
return false;
|
|
}
|
|
return a <= 255 && b <= 255 && c <= 255 && d <= 255;
|
|
}
|
|
|
|
DWORD WINAPI GetAdaptersInfo_hook(PIP_ADAPTER_INFO p, PULONG s) {
|
|
ULONG need = sizeof(IP_ADAPTER_INFO) + 32;
|
|
char ip[16];
|
|
char gw[16];
|
|
char dhcp[16];
|
|
|
|
if (!s) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (!p || *s < need) {
|
|
*s = need;
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
memset(p, 0, need);
|
|
p->Index = k_ifindex;
|
|
strcpy(p->AdapterName, k_adapter_name);
|
|
strcpy(p->Description, k_adapter_desc);
|
|
p->AddressLength = 6;
|
|
memcpy(p->Address, k_fake_mac, 6);
|
|
p->Type = MIB_IF_TYPE_ETHERNET;
|
|
p->DhcpEnabled = 1;
|
|
ip_to_str(g_local_ip, ip, sizeof(ip));
|
|
ip_to_str(k_gateway, gw, sizeof(gw));
|
|
ip_to_str(k_dhcp, dhcp, sizeof(dhcp));
|
|
strcpy(p->IpAddressList.IpAddress.String, ip);
|
|
ip_to_str(k_mask, p->IpAddressList.IpMask.String,
|
|
sizeof(p->IpAddressList.IpMask.String));
|
|
p->IpAddressList.Context = 0;
|
|
p->Next = nullptr;
|
|
strcpy(p->GatewayList.IpAddress.String, gw);
|
|
p->GatewayList.IpMask.String[0] = 0;
|
|
p->GatewayList.Context = 0;
|
|
p->GatewayList.Next = nullptr;
|
|
strcpy(p->DhcpServer.IpAddress.String, dhcp);
|
|
p->DhcpServer.IpMask.String[0] = 0;
|
|
p->DhcpServer.Context = 0;
|
|
p->DhcpServer.Next = nullptr;
|
|
*s = need;
|
|
if (g_log_adapters) {
|
|
log_misc("network", "NIC spoof GetAdaptersInfo ip={} gw={} ifindex={}",
|
|
ip, gw, static_cast<unsigned>(k_ifindex));
|
|
g_log_adapters = false;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD WINAPI GetNetworkParams_hook(PFIXED_INFO p, PULONG s) {
|
|
/* FIXED_INFO embeds the first IP_ADDR_STRING; second follows in the buffer. */
|
|
ULONG need = static_cast<ULONG>(sizeof(FIXED_INFO) + sizeof(IP_ADDR_STRING));
|
|
char dns1[16];
|
|
char dns2[16];
|
|
PIP_ADDR_STRING dns_second = nullptr;
|
|
|
|
if (!s) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (!p || *s < need) {
|
|
*s = need;
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
memset(p, 0, need);
|
|
strncpy(p->HostName, k_hostname, sizeof(p->HostName) - 1);
|
|
strncpy(p->DomainName, k_domain, sizeof(p->DomainName) - 1);
|
|
ip_to_str(k_dns1, dns1, sizeof(dns1));
|
|
ip_to_str(k_dns2, dns2, sizeof(dns2));
|
|
strcpy(p->DnsServerList.IpAddress.String, dns1);
|
|
p->DnsServerList.IpMask.String[0] = 0;
|
|
p->DnsServerList.Context = 0;
|
|
dns_second = reinterpret_cast<PIP_ADDR_STRING>(
|
|
reinterpret_cast<BYTE *>(p) + sizeof(FIXED_INFO));
|
|
memset(dns_second, 0, sizeof(*dns_second));
|
|
strcpy(dns_second->IpAddress.String, dns2);
|
|
dns_second->IpMask.String[0] = 0;
|
|
dns_second->Context = 0;
|
|
dns_second->Next = nullptr;
|
|
p->DnsServerList.Next = dns_second;
|
|
p->CurrentDnsServer = &p->DnsServerList;
|
|
p->NodeType = 1;
|
|
p->EnableDns = 1;
|
|
*s = need;
|
|
if (g_log_params) {
|
|
log_misc("network",
|
|
"NIC spoof GetNetworkParams host={} domain={} dns={} dns2={}",
|
|
k_hostname, k_domain, dns1, dns2);
|
|
g_log_params = false;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
ULONG WINAPI GetAdaptersAddresses_hook(
|
|
ULONG Family,
|
|
ULONG Flags,
|
|
PVOID Reserved,
|
|
PIP_ADAPTER_ADDRESSES AdapterAddresses,
|
|
PULONG SizePointer) {
|
|
enum { STR_SLOT = 64 };
|
|
/*
|
|
* Layout is fixed: adapter header, 4 string slots, then unicast/prefix/
|
|
* gateway/dns + sockaddrs with alignment slack. need is computed to always
|
|
* fit; never return BUFFER_OVERFLOW after a successful size probe.
|
|
*/
|
|
ULONG need = static_cast<ULONG>(sizeof(IP_ADAPTER_ADDRESSES) + STR_SLOT * 4 +
|
|
sizeof(void *) * 8 +
|
|
sizeof(IP_ADAPTER_UNICAST_ADDRESS) +
|
|
sizeof(IP_ADAPTER_PREFIX) +
|
|
sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) * 2 +
|
|
sizeof(IP_ADAPTER_GATEWAY_ADDRESS) +
|
|
sizeof(sockaddr_in) * 5 + 64);
|
|
BYTE *blob = nullptr;
|
|
PIP_ADAPTER_ADDRESSES a = nullptr;
|
|
PIP_ADAPTER_UNICAST_ADDRESS u = nullptr;
|
|
PIP_ADAPTER_PREFIX pref = nullptr;
|
|
PIP_ADAPTER_GATEWAY_ADDRESS gw = nullptr;
|
|
PIP_ADAPTER_DNS_SERVER_ADDRESS dns = nullptr;
|
|
PIP_ADAPTER_DNS_SERVER_ADDRESS dns2 = nullptr;
|
|
sockaddr_in *sa = nullptr;
|
|
sockaddr_in *sm = nullptr;
|
|
sockaddr_in *sg = nullptr;
|
|
sockaddr_in *ds = nullptr;
|
|
sockaddr_in *ds2 = nullptr;
|
|
|
|
(void)Reserved;
|
|
(void)Flags;
|
|
|
|
if (Family != AF_INET && Family != AF_UNSPEC) {
|
|
if (SizePointer) {
|
|
*SizePointer = 0;
|
|
}
|
|
return ERROR_NO_DATA;
|
|
}
|
|
if (!SizePointer) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (!AdapterAddresses || *SizePointer < need) {
|
|
*SizePointer = need;
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
memset(AdapterAddresses, 0, need);
|
|
a = AdapterAddresses;
|
|
blob = reinterpret_cast<BYTE *>(a + 1);
|
|
|
|
a->Length = sizeof(IP_ADAPTER_ADDRESSES);
|
|
a->IfIndex = k_ifindex;
|
|
a->AdapterName = reinterpret_cast<PCHAR>(blob);
|
|
strcpy(reinterpret_cast<char *>(blob), k_adapter_name);
|
|
blob += STR_SLOT;
|
|
a->FriendlyName = reinterpret_cast<PWCHAR>(blob);
|
|
wcscpy(reinterpret_cast<wchar_t *>(blob), k_friendly_name);
|
|
blob += STR_SLOT;
|
|
a->Description = reinterpret_cast<PWCHAR>(blob);
|
|
wcscpy(reinterpret_cast<wchar_t *>(blob), k_adapter_desc_w);
|
|
blob += STR_SLOT;
|
|
a->PhysicalAddressLength = 6;
|
|
memcpy(a->PhysicalAddress, k_fake_mac, 6);
|
|
a->Flags = IP_ADAPTER_DHCP_ENABLED;
|
|
a->Mtu = 1500;
|
|
a->IfType = IF_TYPE_ETHERNET_CSMACD;
|
|
a->OperStatus = IfOperStatusUp;
|
|
a->Ipv4Enabled = 1;
|
|
a->DnsSuffix = reinterpret_cast<PWCHAR>(blob);
|
|
wcsncpy(reinterpret_cast<wchar_t *>(blob), k_domain_w,
|
|
(STR_SLOT / sizeof(wchar_t)) - 1);
|
|
reinterpret_cast<wchar_t *>(blob)[(STR_SLOT / sizeof(wchar_t)) - 1] = 0;
|
|
blob += STR_SLOT;
|
|
|
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
|
u = reinterpret_cast<PIP_ADAPTER_UNICAST_ADDRESS>(blob);
|
|
blob += sizeof(*u);
|
|
memset(u, 0, sizeof(*u));
|
|
u->Length = sizeof(*u);
|
|
u->DadState = IpDadStatePreferred;
|
|
u->OnLinkPrefixLength = static_cast<UCHAR>(k_prefix_len);
|
|
sa = reinterpret_cast<sockaddr_in *>(blob);
|
|
blob += sizeof(*sa);
|
|
memset(sa, 0, sizeof(*sa));
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_addr.s_addr = htonl(g_local_ip);
|
|
u->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sa);
|
|
u->Address.iSockaddrLength = sizeof(*sa);
|
|
a->FirstUnicastAddress = u;
|
|
|
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
|
pref = reinterpret_cast<PIP_ADAPTER_PREFIX>(blob);
|
|
blob += sizeof(*pref);
|
|
memset(pref, 0, sizeof(*pref));
|
|
pref->Length = sizeof(*pref);
|
|
pref->PrefixLength = static_cast<ULONG>(k_prefix_len);
|
|
sm = reinterpret_cast<sockaddr_in *>(blob);
|
|
blob += sizeof(*sm);
|
|
memset(sm, 0, sizeof(*sm));
|
|
sm->sin_family = AF_INET;
|
|
sm->sin_addr.s_addr = htonl(g_subnet);
|
|
pref->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sm);
|
|
pref->Address.iSockaddrLength = sizeof(*sm);
|
|
a->FirstPrefix = pref;
|
|
|
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
|
gw = reinterpret_cast<PIP_ADAPTER_GATEWAY_ADDRESS>(blob);
|
|
blob += sizeof(*gw);
|
|
memset(gw, 0, sizeof(*gw));
|
|
gw->Length = sizeof(*gw);
|
|
sg = reinterpret_cast<sockaddr_in *>(blob);
|
|
blob += sizeof(*sg);
|
|
memset(sg, 0, sizeof(*sg));
|
|
sg->sin_family = AF_INET;
|
|
sg->sin_addr.s_addr = htonl(k_gateway);
|
|
gw->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sg);
|
|
gw->Address.iSockaddrLength = sizeof(*sg);
|
|
a->FirstGatewayAddress = gw;
|
|
|
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
|
dns = reinterpret_cast<PIP_ADAPTER_DNS_SERVER_ADDRESS>(blob);
|
|
blob += sizeof(*dns);
|
|
memset(dns, 0, sizeof(*dns));
|
|
ds = reinterpret_cast<sockaddr_in *>(blob);
|
|
blob += sizeof(*ds);
|
|
memset(ds, 0, sizeof(*ds));
|
|
ds->sin_family = AF_INET;
|
|
ds->sin_addr.s_addr = htonl(k_dns1);
|
|
dns->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(ds);
|
|
dns->Address.iSockaddrLength = sizeof(*ds);
|
|
|
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
|
dns2 = reinterpret_cast<PIP_ADAPTER_DNS_SERVER_ADDRESS>(blob);
|
|
blob += sizeof(*dns2);
|
|
memset(dns2, 0, sizeof(*dns2));
|
|
ds2 = reinterpret_cast<sockaddr_in *>(blob);
|
|
blob += sizeof(*ds2);
|
|
memset(ds2, 0, sizeof(*ds2));
|
|
ds2->sin_family = AF_INET;
|
|
ds2->sin_addr.s_addr = htonl(k_dns2);
|
|
dns2->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(ds2);
|
|
dns2->Address.iSockaddrLength = sizeof(*ds2);
|
|
dns2->Next = nullptr;
|
|
dns->Next = dns2;
|
|
a->FirstDnsServerAddress = dns;
|
|
|
|
a->Next = nullptr;
|
|
*SizePointer = need;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
constexpr uint32_t AI_MAGIC = 0x4E535041u; /* 'NSPA' */
|
|
|
|
struct SpoofAiHdr {
|
|
uint32_t magic;
|
|
ADDRINFOA ai;
|
|
};
|
|
|
|
SpoofAiHdr *ai_hdr_from_ai(ADDRINFOA *ai) {
|
|
SpoofAiHdr *h = nullptr;
|
|
if (!ai) {
|
|
return nullptr;
|
|
}
|
|
h = reinterpret_cast<SpoofAiHdr *>(
|
|
reinterpret_cast<BYTE *>(ai) - offsetof(SpoofAiHdr, ai));
|
|
if (h->magic != AI_MAGIC) {
|
|
return nullptr;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
/* Wine packed getaddrinfo: sockaddr lives immediately after addrinfo. */
|
|
bool ai_is_wine_packed(ADDRINFOA *ai) {
|
|
BYTE *p = reinterpret_cast<BYTE *>(ai);
|
|
BYTE *addr = nullptr;
|
|
if (!ai || !ai->ai_addr) {
|
|
return false;
|
|
}
|
|
addr = reinterpret_cast<BYTE *>(ai->ai_addr);
|
|
return addr >= p + sizeof(*ai) &&
|
|
addr < p + sizeof(*ai) + sizeof(SOCKADDR_STORAGE) + 64;
|
|
}
|
|
|
|
/*
|
|
* Dual-safe free for Wine + native Windows:
|
|
* - Our results: magic header + separate mallocs for addr/canon.
|
|
* - Wine packed foreign: single free(ai).
|
|
* - Else (native Windows foreign): free canon, addr, node per link.
|
|
*/
|
|
void WSAAPI freeaddrinfo_hook(PADDRINFOA ai) {
|
|
while (ai) {
|
|
ADDRINFOA *next = ai->ai_next;
|
|
SpoofAiHdr *hdr = ai_hdr_from_ai(ai);
|
|
if (hdr) {
|
|
free(ai->ai_canonname);
|
|
free(ai->ai_addr);
|
|
free(hdr);
|
|
} else if (ai_is_wine_packed(ai)) {
|
|
free(ai);
|
|
break;
|
|
} else {
|
|
free(ai->ai_canonname);
|
|
free(ai->ai_addr);
|
|
free(ai);
|
|
}
|
|
ai = next;
|
|
}
|
|
}
|
|
|
|
INT WSAAPI getaddrinfo_hook(
|
|
PCSTR pNodeName,
|
|
PCSTR pServiceName,
|
|
const ADDRINFOA *pHints,
|
|
PADDRINFOA *ppResult) {
|
|
SpoofAiHdr *hdr = nullptr;
|
|
ADDRINFOA *ai = nullptr;
|
|
sockaddr_in *sa = nullptr;
|
|
char *canon = nullptr;
|
|
const char *name_src = nullptr;
|
|
size_t name_len = 0;
|
|
uint32_t ip = 0;
|
|
int port = 0;
|
|
|
|
if (!ppResult) {
|
|
return EAI_FAIL;
|
|
}
|
|
*ppResult = nullptr;
|
|
|
|
if (pHints && pHints->ai_family == AF_INET6) {
|
|
return EAI_FAMILY;
|
|
}
|
|
|
|
if (!pNodeName || !pNodeName[0] || name_is_local(pNodeName)) {
|
|
if (pNodeName && (_stricmp(pNodeName, k_hostname) == 0 ||
|
|
_stricmp(pNodeName, k_hostname_fqdn) == 0)) {
|
|
ip = g_local_ip;
|
|
} else {
|
|
ip = 0x7F000001u;
|
|
}
|
|
} else if (name_is_numeric_ipv4(pNodeName)) {
|
|
ip = parse_ipv4(pNodeName);
|
|
} else {
|
|
/* offline=1: map external names to local_ip */
|
|
ip = g_local_ip;
|
|
}
|
|
|
|
if (pServiceName && pServiceName[0]) {
|
|
port = atoi(pServiceName);
|
|
if (port < 0 || port > 65535) {
|
|
port = 0;
|
|
}
|
|
}
|
|
|
|
name_src = (pNodeName && pNodeName[0]) ? pNodeName : "localhost";
|
|
name_len = strlen(name_src) + 1;
|
|
if (name_len > 256) {
|
|
name_len = 256;
|
|
}
|
|
|
|
hdr = static_cast<SpoofAiHdr *>(calloc(1, sizeof(*hdr)));
|
|
sa = static_cast<sockaddr_in *>(calloc(1, sizeof(*sa)));
|
|
canon = static_cast<char *>(malloc(name_len));
|
|
if (!hdr || !sa || !canon) {
|
|
free(hdr);
|
|
free(sa);
|
|
free(canon);
|
|
return EAI_MEMORY;
|
|
}
|
|
hdr->magic = AI_MAGIC;
|
|
ai = &hdr->ai;
|
|
memcpy(canon, name_src, name_len - 1);
|
|
canon[name_len - 1] = 0;
|
|
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_addr.s_addr = htonl(ip);
|
|
if (port > 0) {
|
|
sa->sin_port = htons(static_cast<u_short>(port));
|
|
}
|
|
|
|
ai->ai_family = AF_INET;
|
|
ai->ai_socktype = pHints && pHints->ai_socktype ?
|
|
pHints->ai_socktype : SOCK_STREAM;
|
|
ai->ai_protocol = pHints && pHints->ai_protocol ?
|
|
pHints->ai_protocol : IPPROTO_TCP;
|
|
ai->ai_addrlen = sizeof(*sa);
|
|
ai->ai_addr = reinterpret_cast<struct sockaddr *>(sa);
|
|
ai->ai_canonname = canon;
|
|
ai->ai_next = nullptr;
|
|
*ppResult = ai;
|
|
return 0;
|
|
}
|
|
|
|
void install_nicspoof_hooks() {
|
|
static bool done = false;
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
|
|
char ipstr[16];
|
|
char maskstr[16];
|
|
char gwstr[16];
|
|
ip_to_str(g_local_ip, ipstr, sizeof(ipstr));
|
|
ip_to_str(k_mask, maskstr, sizeof(maskstr));
|
|
ip_to_str(k_gateway, gwstr, sizeof(gwstr));
|
|
log_info("network",
|
|
"NIC spoof enabled ip={} mask={}/{} gw={} "
|
|
"mac={:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X} host={} ifindex={}",
|
|
ipstr, maskstr, k_prefix_len, gwstr,
|
|
static_cast<unsigned>(k_fake_mac[0]),
|
|
static_cast<unsigned>(k_fake_mac[1]),
|
|
static_cast<unsigned>(k_fake_mac[2]),
|
|
static_cast<unsigned>(k_fake_mac[3]),
|
|
static_cast<unsigned>(k_fake_mac[4]),
|
|
static_cast<unsigned>(k_fake_mac[5]),
|
|
k_hostname, static_cast<unsigned>(k_ifindex));
|
|
|
|
bool ok = true;
|
|
ok &= detour::trampoline_try(
|
|
"iphlpapi.dll", "GetAdaptersAddresses",
|
|
(void *) GetAdaptersAddresses_hook,
|
|
(void **) &GetAdaptersAddresses_orig);
|
|
ok &= detour::trampoline_try(
|
|
"iphlpapi.dll", "GetAdaptersInfo",
|
|
(void *) GetAdaptersInfo_hook,
|
|
(void **) &GetAdaptersInfo_orig);
|
|
ok &= detour::trampoline_try(
|
|
"iphlpapi.dll", "GetNetworkParams",
|
|
(void *) GetNetworkParams_hook,
|
|
(void **) &GetNetworkParams_orig);
|
|
ok &= detour::trampoline_try(
|
|
"ws2_32.dll", "getaddrinfo",
|
|
(void *) getaddrinfo_hook,
|
|
(void **) &getaddrinfo_orig);
|
|
ok &= detour::trampoline_try(
|
|
"ws2_32.dll", "freeaddrinfo",
|
|
(void *) freeaddrinfo_hook,
|
|
(void **) &freeaddrinfo_orig);
|
|
|
|
{
|
|
HMODULE ws = GetModuleHandleA("ws2_32.dll");
|
|
void *faa = nullptr;
|
|
void *fai = nullptr;
|
|
if (ws) {
|
|
faa = reinterpret_cast<void *>(GetProcAddress(ws, "FreeAddrInfoA"));
|
|
fai = reinterpret_cast<void *>(GetProcAddress(ws, "freeaddrinfo"));
|
|
}
|
|
if (faa && faa != fai) {
|
|
decltype(freeaddrinfo) *FreeAddrInfoA_orig = nullptr;
|
|
if (!detour::trampoline_try(
|
|
"ws2_32.dll", "FreeAddrInfoA",
|
|
(void *) freeaddrinfo_hook,
|
|
(void **) &FreeAddrInfoA_orig)) {
|
|
log_warning("network",
|
|
"NIC spoof: FreeAddrInfoA hook was not installed");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
log_warning("network",
|
|
"NIC spoof: one or more hooks failed to install");
|
|
} else {
|
|
log_info("network",
|
|
"NIC spoof hooks installed "
|
|
"(GAA/GAI/GNP/getaddrinfo/freeaddrinfo)");
|
|
}
|
|
}
|
|
|
|
bool ip_in_overlay_range(uint32_t ip) {
|
|
const uint32_t net = k_gateway & k_mask;
|
|
const uint32_t bcast = net | ~k_mask;
|
|
if ((ip & k_mask) != net) {
|
|
return false;
|
|
}
|
|
if (ip == net || ip == bcast || ip == k_gateway ||
|
|
ip == k_dns1 || ip == k_dns2) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void init_impl() {
|
|
if (g_cfg.mode == NicSpoofMode::Off) {
|
|
return;
|
|
}
|
|
|
|
g_local_ip = g_cfg.local_ip ? g_cfg.local_ip : k_default_local_ip;
|
|
if (!ip_in_overlay_range(g_local_ip)) {
|
|
char bad[16];
|
|
ip_to_str(g_local_ip, bad, sizeof(bad));
|
|
log_warning("network",
|
|
"NIC spoof: IP {} is outside 10.0.0.0/8 usable range; "
|
|
"using 10.100.100.10",
|
|
bad);
|
|
g_local_ip = k_default_local_ip;
|
|
}
|
|
g_subnet = g_local_ip & k_mask;
|
|
|
|
const char *mode_str = "offline";
|
|
if (g_cfg.mode == NicSpoofMode::TunnelHost) {
|
|
mode_str = "tunnelhost";
|
|
} else if (g_cfg.mode == NicSpoofMode::TunnelClient) {
|
|
mode_str = "tunnelclient";
|
|
}
|
|
log_info("network", "NIC spoof mode={}", mode_str);
|
|
|
|
install_nicspoof_hooks();
|
|
|
|
if (g_cfg.mode == NicSpoofMode::TunnelHost ||
|
|
g_cfg.mode == NicSpoofMode::TunnelClient) {
|
|
nicspoof_tunnel_init(g_cfg);
|
|
}
|
|
}
|
|
|
|
} // namespace nicspoof_detail
|
|
|
|
void nicspoof_configure(const NicSpoofConfig &cfg) {
|
|
nicspoof_detail::g_cfg = cfg;
|
|
}
|
|
|
|
bool nicspoof_tunnel_enabled() {
|
|
return nicspoof_detail::g_cfg.mode == NicSpoofMode::TunnelHost ||
|
|
nicspoof_detail::g_cfg.mode == NicSpoofMode::TunnelClient;
|
|
}
|
|
|
|
uint32_t nicspoof_local_ip() {
|
|
return nicspoof_detail::g_local_ip;
|
|
}
|
|
|
|
uint32_t nicspoof_mask() {
|
|
return nicspoof_detail::k_mask;
|
|
}
|
|
|
|
uint32_t nicspoof_subnet() {
|
|
return nicspoof_detail::g_subnet;
|
|
}
|
|
|
|
void nicspoof_init() {
|
|
nicspoof_detail::init_impl();
|
|
}
|