aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/qt_sdl/LAN_PCap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/qt_sdl/LAN_PCap.cpp')
-rw-r--r--src/frontend/qt_sdl/LAN_PCap.cpp387
1 files changed, 387 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <SDL2/SDL.h>
+#include <pcap/pcap.h>
+#include "../Wifi.h"
+#include "LAN_PCap.h"
+#include "PlatformConfig.h"
+
+#ifdef __WIN32__
+ #include <iphlpapi.h>
+#else
+ #include <sys/types.h>
+ #include <ifaddrs.h>
+ #include <netinet/in.h>
+ #include <linux/if_packet.h>
+#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;
+}
+
+}