/* * Copyright (C) 2021 The DOSBox Team * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #if C_PCAP #include "ethernet_pcap.h" #include "dosbox.h" #include "logging.h" #include "support.h" /* strcasecmp */ #include extern std::string niclist; #ifdef WIN32 #include // DLL loading #define pcap_sendpacket(A,B,C) PacketSendPacket(A,B,C) #define pcap_close(A) PacketClose(A) #define pcap_freealldevs(A) PacketFreealldevs(A) #define pcap_open(A,B,C,D,E,F) PacketOpen(A,B,C,D,E,F) #define pcap_next_ex(A,B,C) PacketNextEx(A,B,C) #define pcap_findalldevs_ex(A,B,C,D) PacketFindALlDevsEx(A,B,C,D) #define pcap_geterr(A) PacketGetError(A) int (*PacketSendPacket)(pcap_t *, const u_char *, int) = 0; void (*PacketClose)(pcap_t *) = 0; void (*PacketFreealldevs)(pcap_if_t *) = 0; pcap_t* (*PacketOpen)(char const *,int,int,int,struct pcap_rmtauth *,char *) = 0; int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **) = 0; int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *) = 0; char* (*PacketGetError)(pcap_t *) = nullptr; char pcap_src_if_string[] = PCAP_SRC_IF_STRING; bool LoadPcapLibrary() { // remember if we've already initialized the library static HINSTANCE pcapinst = (HINSTANCE)-1; if(pcapinst!=(HINSTANCE)-1) { return (pcapinst!=NULL); } // init the library pcapinst = LoadLibrary("WPCAP.DLL"); if(pcapinst==NULL) { niclist = "WinPcap has to be installed for the NE2000 to work."; LOG_MSG(niclist.c_str()); return false; } FARPROC psp; #ifdef __MINGW32__ // C++ defines function and data pointers as separate types to reflect // Harvard architecture machines (like the Arduino). As such, casting // between them isn't portable and GCC will helpfully warn us about it. // We're only running this code on Windows which explicitly allows this // behaviour, so silence the warning to avoid confusion. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif psp = GetProcAddress(pcapinst,"pcap_sendpacket"); if(!PacketSendPacket) PacketSendPacket = (int (__cdecl *)(pcap_t *,const u_char *,int))psp; psp = GetProcAddress(pcapinst,"pcap_close"); if(!PacketClose) PacketClose = (void (__cdecl *)(pcap_t *)) psp; psp = GetProcAddress(pcapinst,"pcap_freealldevs"); if(!PacketFreealldevs) PacketFreealldevs = (void (__cdecl *)(pcap_if_t *)) psp; psp = GetProcAddress(pcapinst,"pcap_open"); if(!PacketOpen) PacketOpen = (pcap_t* (__cdecl *)(char const *,int,int,int,struct pcap_rmtauth *,char *)) psp; psp = GetProcAddress(pcapinst,"pcap_next_ex"); if(!PacketNextEx) PacketNextEx = (int (__cdecl *)(pcap_t *, struct pcap_pkthdr **, const u_char **)) psp; psp = GetProcAddress(pcapinst,"pcap_findalldevs_ex"); if(!PacketFindALlDevsEx) PacketFindALlDevsEx = (int (__cdecl *)(char *, struct pcap_rmtauth *, pcap_if_t **, char *)) psp; psp = GetProcAddress(pcapinst,"pcap_geterr"); if(!PacketGetError) PacketGetError = (char* (__cdecl *)(pcap_t *)) psp; #ifdef __MINGW32__ #pragma GCC diagnostic pop #endif if(PacketFindALlDevsEx==0 || PacketNextEx==0 || PacketOpen==0 || PacketFreealldevs==0 || PacketClose==0 || PacketSendPacket==0 || PacketGetError==0) { niclist = "Incorrect or non-functional WinPcap version."; LOG_MSG(niclist.c_str()); pcapinst = NULL; return false; } return true; } #endif PcapEthernetConnection::PcapEthernetConnection() : EthernetConnection() { } PcapEthernetConnection::~PcapEthernetConnection() { if(adhandle) pcap_close(adhandle); } bool PcapEthernetConnection::Initialize(Section* config) { Section_prop *section = static_cast(config); const char* realnicstring = section->Get_string("realnic"); #ifdef WIN32 if(!LoadPcapLibrary()) { return false; } #endif // find out which pcap device to use pcap_if_t *alldevs; pcap_if_t *currentdev = NULL; char errbuf[PCAP_ERRBUF_SIZE]; unsigned int userdev; #ifdef WIN32 if (pcap_findalldevs_ex(pcap_src_if_string, NULL, &alldevs, errbuf) == -1) #else if (pcap_findalldevs(&alldevs, errbuf) == -1) #endif { niclist = "Cannot enumerate network interfaces: "+std::string(errbuf); LOG_MSG("%s", niclist.c_str()); return false; } { Bitu i = 0; niclist = "Network Interface List\n-------------------------------------------------------------\n"; for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) { const char* desc = "no description"; if(currentdev->description) desc=currentdev->description; i++; niclist+=(i<10?" ":"")+std::to_string(i)+" "+currentdev->name+"\n ("+desc+")\n"; } } if (!strcasecmp(realnicstring,"list")) { // print list and quit std::istringstream in(("\n"+niclist+"\n").c_str()); if (in) for (std::string line; std::getline(in, line); ) LOG_MSG("%s", line.c_str()); LOG_MSG("NOTE: No interface was chosen because the realnic setting is set to 'list', set realnic to the number of the interface you want to use."); pcap_freealldevs(alldevs); return false; } else if(1==sscanf(realnicstring,"%u",&userdev)) { // user passed us a number Bitu i = 0; currentdev=alldevs; while(currentdev!=NULL) { i++; if(i==userdev) break; else currentdev=currentdev->next; } } else { // user might have passed a piece of name for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) { if(strstr(currentdev->name,realnicstring)) { break; }else if(currentdev->description!=NULL && strstr(currentdev->description,realnicstring)) { break; } } } if(currentdev==NULL) { LOG_MSG("Unable to find network interface - check realnic parameter\n"); pcap_freealldevs(alldevs); return false; } // print out which interface we are going to use const char* desc = "no description"; if(currentdev->description) desc=currentdev->description; LOG_MSG("Using Network interface:\n%s\n(%s)\n",currentdev->name,desc); const char *timeoutstr = section->Get_string("timeout"); char *end; int timeout = -1; if (!strlen(timeoutstr)||(timeoutstr[0]!='-'&&!isdigit(timeoutstr[0]))) { // Default timeout values #ifdef MACOSX timeout = 3000; // For macOS, use 3000ms as it does not appear to support -1 #else timeout = -1; // For other platforms, use -1 which should mean "non-blocking mode" #endif } else timeout = strtoul(timeoutstr,&end,10); // attempt to open it #ifdef WIN32 if ( (adhandle= pcap_open( currentdev->name, // name of the device 65536, // portion of the packet to capture // 65536 = whole packet PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode timeout, // read timeout NULL, // authentication on the remote machine errbuf // error buffer ) ) == NULL) #else /*pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf)*/ if ( (adhandle= pcap_open_live( currentdev->name, // name of the device 65536, // portion of the packet to capture // 65536 = whole packet true, // promiscuous mode timeout, // read timeout errbuf // error buffer ) ) == NULL) #endif { LOG_MSG("\nUnable to open the interface: %s.", errbuf); pcap_freealldevs(alldevs); return false; } pcap_freealldevs(alldevs); #ifndef WIN32 pcap_setnonblock(adhandle,1,errbuf); #endif return true; } void PcapEthernetConnection::SendPacket(const uint8_t* packet, int len) { static unsigned long txn = 0; if (((++txn) & (txn - 1)) == 0) /* powers of 2, avoid spam */ LOG_MSG("PCAP TX #%lu len=%d dst=%02x:%02x:%02x src=%02x:%02x:%02x", txn, len, packet[0],packet[1],packet[2], packet[6],packet[7],packet[8]); int ret = pcap_sendpacket(adhandle, packet, len); if(ret) LOG_MSG("PCAP error: %s", pcap_geterr(adhandle)); } void PcapEthernetConnection::GetPackets(std::function callback) { int res; struct pcap_pkthdr *header; u_char *pkt_data; static unsigned long rxn = 0; //#if 0 while((res = pcap_next_ex( adhandle, &header, (const u_char **)&pkt_data)) > 0) { if (((++rxn) & (rxn - 1)) == 0) LOG_MSG("PCAP RX #%lu len=%d dst=%02x:%02x:%02x src=%02x:%02x:%02x", rxn, header->len, pkt_data[0],pkt_data[1],pkt_data[2], pkt_data[6],pkt_data[7],pkt_data[8]); callback(pkt_data, header->len); } //#endif } #endif