From b7946c1384ce7c44172036f0a04410a9c04ed78c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 22:21:36 +0200 Subject: * flesh out design for wifi settings dialog * move the pcap shit to a betterer place --- src/frontend/qt_sdl/LAN_PCap.cpp | 387 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 src/frontend/qt_sdl/LAN_PCap.cpp (limited to 'src/frontend/qt_sdl/LAN_PCap.cpp') diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp new file mode 100644 index 0000000..ce278bc --- /dev/null +++ b/src/frontend/qt_sdl/LAN_PCap.cpp @@ -0,0 +1,387 @@ +/* + Copyright 2016-2020 Arisotura + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +// direct LAN interface. Currently powered by libpcap, may change. + +#include +#include +#include +#include +#include +#include "../Wifi.h" +#include "LAN_PCap.h" +#include "PlatformConfig.h" + +#ifdef __WIN32__ + #include +#else + #include + #include + #include + #include +#endif + + +// welp +#ifndef PCAP_OPENFLAG_PROMISCUOUS +#define PCAP_OPENFLAG_PROMISCUOUS 1 +#endif + + +#define DECL_PCAP_FUNC(ret, name, args, args2) \ + typedef ret (*type_##name) args; \ + type_##name ptr_##name = NULL; \ + ret name args { return ptr_##name args2; } + +DECL_PCAP_FUNC(int, pcap_findalldevs, (pcap_if_t** alldevs, char* errbuf), (alldevs,errbuf)) +DECL_PCAP_FUNC(void, pcap_freealldevs, (pcap_if_t* alldevs), (alldevs)) +DECL_PCAP_FUNC(pcap_t*, pcap_open_live, (const char* src, int snaplen, int flags, int readtimeout, char* errbuf), (src,snaplen,flags,readtimeout,errbuf)) +DECL_PCAP_FUNC(void, pcap_close, (pcap_t* dev), (dev)) +DECL_PCAP_FUNC(int, pcap_setnonblock, (pcap_t* dev, int nonblock, char* errbuf), (dev,nonblock,errbuf)) +DECL_PCAP_FUNC(int, pcap_sendpacket, (pcap_t* dev, const u_char* data, int len), (dev,data,len)) +DECL_PCAP_FUNC(int, pcap_dispatch, (pcap_t* dev, int num, pcap_handler callback, u_char* data), (dev,num,callback,data)) +DECL_PCAP_FUNC(const u_char*, pcap_next, (pcap_t* dev, struct pcap_pkthdr* hdr), (dev,hdr)) + + +namespace LAN_PCap +{ + +const char* PCapLibNames[] = +{ +#ifdef __WIN32__ + // TODO: name for npcap in non-WinPCap mode + "wpcap.dll", +#else + // Linux lib names + "libpcap.so.1", + "libpcap.so", +#endif + NULL +}; + +AdapterData* Adapters = NULL; +int NumAdapters = 0; + +void* PCapLib = NULL; +pcap_t* PCapAdapter = NULL; +AdapterData* PCapAdapterData; + +u8 PacketBuffer[2048]; +int PacketLen; +volatile int RXNum; + + +#define LOAD_PCAP_FUNC(sym) \ + ptr_##sym = (type_##sym)SDL_LoadFunction(lib, #sym); \ + if (!ptr_##sym) return false; + +bool TryLoadPCap(void* lib) +{ + LOAD_PCAP_FUNC(pcap_findalldevs) + LOAD_PCAP_FUNC(pcap_freealldevs) + LOAD_PCAP_FUNC(pcap_open_live) + LOAD_PCAP_FUNC(pcap_close) + LOAD_PCAP_FUNC(pcap_setnonblock) + LOAD_PCAP_FUNC(pcap_sendpacket) + LOAD_PCAP_FUNC(pcap_dispatch) + LOAD_PCAP_FUNC(pcap_next) + + return true; +} + +bool Init(bool open_adapter) +{ + // TODO: how to deal with cases where an adapter is unplugged or changes config?? + if (!PCapLib) + { + PCapLib = NULL; + + for (int i = 0; PCapLibNames[i]; i++) + { + void* lib = SDL_LoadObject(PCapLibNames[i]); + if (!lib) continue; + + if (!TryLoadPCap(lib)) + { + SDL_UnloadObject(lib); + continue; + } + + printf("PCap: lib %s, init successful\n", PCapLibNames[i]); + PCapLib = lib; + break; + } + + if (PCapLib == NULL) + { + printf("PCap: init failed\n"); + return false; + } + } + + PCapAdapter = NULL; + PacketLen = 0; + RXNum = 0; + + NumAdapters = 0; + + char errbuf[PCAP_ERRBUF_SIZE]; + int ret; + + pcap_if_t* alldevs; + ret = pcap_findalldevs(&alldevs, errbuf); + if (ret < 0 || alldevs == NULL) + { + printf("PCap: no devices available\n"); + return false; + } + + pcap_if_t* dev = alldevs; + while (dev) { NumAdapters++; dev = dev->next; } + + Adapters = new AdapterData[NumAdapters]; + memset(Adapters, 0, sizeof(AdapterData)*NumAdapters); + + AdapterData* adata = &Adapters[0]; + dev = alldevs; + while (dev) + { + adata->Internal = dev; + +#ifdef __WIN32__ + // hax + int len = strlen(dev->name); + len -= 12; if (len > 127) len = 127; + strncpy(adata->DeviceName, &dev->name[12], len); + adata->DeviceName[len] = '\0'; +#else + strncpy(adata->DeviceName, dev->name, 127); + adata->DeviceName[127] = '\0'; + + strncpy(adata->FriendlyName, adata->DeviceName, 127); + adata->FriendlyName[127] = '\0'; +#endif // __WIN32__ + + dev = dev->next; + adata++; + } + +#ifdef __WIN32__ + + ULONG bufsize = 16384; + IP_ADAPTER_ADDRESSES* buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize); + ULONG uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize); + if (uret == ERROR_BUFFER_OVERFLOW) + { + HeapFree(GetProcessHeap(), 0, buf); + buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize); + uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize); + } + if (uret != ERROR_SUCCESS) + { + printf("GetAdaptersAddresses() shat itself: %08X\n", uret); + return false; + } + + for (int i = 0; i < NumAdapters; i++) + { + adata = &Adapters[i]; + IP_ADAPTER_ADDRESSES* addr = buf; + while (addr) + { + if (strcmp(addr->AdapterName, adata->DeviceName)) + { + addr = addr->Next; + continue; + } + + WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, NULL, NULL); + adata->FriendlyName[127] = '\0'; + + WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, NULL, NULL); + adata->Description[127] = '\0'; + + if (addr->PhysicalAddressLength != 6) + { + printf("weird MAC addr length %d for %s\n", addr->PhysicalAddressLength, addr->AdapterName); + } + else + memcpy(adata->MAC, addr->PhysicalAddress, 6); + + IP_ADAPTER_UNICAST_ADDRESS* ipaddr = addr->FirstUnicastAddress; + while (ipaddr) + { + SOCKADDR* sa = ipaddr->Address.lpSockaddr; + if (sa->sa_family == AF_INET) + { + struct in_addr sa4 = ((sockaddr_in*)sa)->sin_addr; + memcpy(adata->IP_v4, &sa4, 4); + } + + ipaddr = ipaddr->Next; + } + + break; + } + } + + HeapFree(GetProcessHeap(), 0, buf); + +#else + + struct ifaddrs* addrs; + if (getifaddrs(&addrs) != 0) + { + printf("getifaddrs() shat itself :(\n"); + return false; + } + + for (int i = 0; i < NumAdapters; i++) + { + adata = &Adapters[i]; + struct ifaddrs* curaddr = addrs; + while (curaddr) + { + if (strcmp(curaddr->ifa_name, adata->DeviceName)) + { + curaddr = curaddr->ifa_next; + continue; + } + + if (!curaddr->ifa_addr) + { + printf("Device (%s) does not have an address :/\n", curaddr->ifa_name); + curaddr = curaddr->ifa_next; + continue; + } + + u16 af = curaddr->ifa_addr->sa_family; + if (af == AF_INET) + { + struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr; + memcpy(adata->IP_v4, &sa->sin_addr, 4); + } + else if (af == AF_PACKET) + { + struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr; + if (sa->sll_halen != 6) + printf("weird MAC length %d for %s\n", sa->sll_halen, curaddr->ifa_name); + else + memcpy(adata->MAC, sa->sll_addr, 6); + } + + curaddr = curaddr->ifa_next; + } + } + + freeifaddrs(addrs); + +#endif // __WIN32__ + + if (!open_adapter) return true; + if (PCapAdapter) pcap_close(PCapAdapter); + + // open pcap device + PCapAdapterData = &Adapters[0]; + for (int i = 0; i < NumAdapters; i++) + { + if (!strncmp(Adapters[i].DeviceName, Config::LANDevice, 128)) + PCapAdapterData = &Adapters[i]; + } + + dev = (pcap_if_t*)PCapAdapterData->Internal; + PCapAdapter = pcap_open_live(dev->name, 2048, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf); + if (!PCapAdapter) + { + printf("PCap: failed to open adapter %s\n", errbuf); + return false; + } + + pcap_freealldevs(alldevs); + + if (pcap_setnonblock(PCapAdapter, 1, errbuf) < 0) + { + printf("PCap: failed to set nonblocking mode\n"); + pcap_close(PCapAdapter); PCapAdapter = NULL; + return false; + } + + return true; +} + +void DeInit() +{ + if (PCapLib) + { + if (PCapAdapter) + { + pcap_close(PCapAdapter); + PCapAdapter = NULL; + } + + SDL_UnloadObject(PCapLib); + PCapLib = NULL; + } +} + + +void RXCallback(u_char* blarg, const struct pcap_pkthdr* header, const u_char* data) +{ + while (RXNum > 0); + + if (header->len > 2048-64) return; + + PacketLen = header->len; + memcpy(PacketBuffer, data, PacketLen); + RXNum = 1; +} + +int SendPacket(u8* data, int len) +{ + if (PCapAdapter == NULL) + return 0; + + if (len > 2048) + { + printf("LAN_SendPacket: error: packet too long (%d)\n", len); + return 0; + } + + pcap_sendpacket(PCapAdapter, data, len); + // TODO: check success + return len; +} + +int RecvPacket(u8* data) +{ + if (PCapAdapter == NULL) + return 0; + + int ret = 0; + if (RXNum > 0) + { + memcpy(data, PacketBuffer, PacketLen); + ret = PacketLen; + RXNum = 0; + } + + pcap_dispatch(PCapAdapter, 1, RXCallback, NULL); + return ret; +} + +} -- cgit v1.2.3