|
From: <cn...@us...> - 2024-08-21 19:57:07
|
Revision: 1516
http://sourceforge.net/p/seq/svn/1516
Author: cn187
Date: 2024-08-21 19:57:04 +0000 (Wed, 21 Aug 2024)
Log Message:
-----------
Rework packet capture logic to improve reliability of session detection and fix misc issues
Modified Paths:
--------------
showeq/trunk/src/Makefile.am
showeq/trunk/src/interface.cpp
showeq/trunk/src/packet.cpp
showeq/trunk/src/packet.h
showeq/trunk/src/packetcapture.cpp
showeq/trunk/src/packetcapture.h
showeq/trunk/src/packetcommon.h
showeq/trunk/src/packetstream.cpp
showeq/trunk/src/packetstream.h
showeq/trunk/src/util.cpp
showeq/trunk/src/util.h
Added Paths:
-----------
showeq/trunk/src/packetcaptureprovider.cpp
showeq/trunk/src/packetcaptureprovider.h
Modified: showeq/trunk/src/Makefile.am
===================================================================
--- showeq/trunk/src/Makefile.am 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/Makefile.am 2024-08-21 19:57:04 UTC (rev 1516)
@@ -15,7 +15,7 @@
datalocationmgr.cpp eqstr.cpp messages.cpp message.cpp messagefilter.cpp messagewindow.cpp \
messageshell.cpp terminal.cpp filteredspawnlog.cpp messagefilterdialog.cpp \
diagnosticmessages.cpp mapicon.cpp filternotifications.cpp netstream.cpp guildshell.cpp \
- guildlist.cpp bazaarlog.cpp mapicondialog.cpp
+ guildlist.cpp bazaarlog.cpp mapicondialog.cpp packetcaptureprovider.cpp
showeq_moc_SRCS = bazaarlog.moc category.moc combatlog.moc compass.moc \
compassframe.moc datetimemgr.moc editor.moc experiencelog.moc \
@@ -113,7 +113,7 @@
EXTRA_DIST = h2info.pl
-noinst_HEADERS = classes.h compass.h everquest.h interface.h main.h map.h filter.h vpacket.h editor.h packet.h packetcapture.h packetcommon.h packetformat.h packetstream.h packetfragment.h packetinfo.h races.h skills.h spells.h util.h experiencelog.h combatlog.h spawn.h spawnshell.h spawnlist.h spellshell.h spelllist.h languages.h weapons.h weapons1.h weapons27.h weapons28.h weapons29.h weapons2a.h weapons2b.h weapons2c.h weapons2d.h weapons2e.h weapons2f.h weapons30.h weapons4e.h decode.h cgiconv.h skilllist.h statlist.h deity.h player.h crctab.h filtermgr.h point.h pointarray.h mapcore.h category.h compassframe.h group.h guild.h fixpt.h netdiag.h zones.h logger.h xmlconv.h xmlpreferences.h seqwindow.h seqlistview.h zonemgr.h spawnmonitor.h spawnpointlist.h typenames.h spawnlistcommon.h spawnlist2.h datetimemgr.h spawnlog.h packetlog.h datalocationmgr.h eqstr.h messages.h messagefilter.h messagewindow.h messageshell.h terminal.h filteredspawnlog.h messagefilterdialog.h diagnosticmessages.h mapicon.h mapicondialog.h mapicondialog.ui filternotifications.h netstream.h guildshell.h guildlist.h bazaarlog.h message.h s_everquest.h staticspells.h
+noinst_HEADERS = classes.h compass.h everquest.h interface.h main.h map.h filter.h vpacket.h editor.h packet.h packetcapture.h packetcommon.h packetformat.h packetstream.h packetfragment.h packetinfo.h races.h skills.h spells.h util.h experiencelog.h combatlog.h spawn.h spawnshell.h spawnlist.h spellshell.h spelllist.h languages.h weapons.h weapons1.h weapons27.h weapons28.h weapons29.h weapons2a.h weapons2b.h weapons2c.h weapons2d.h weapons2e.h weapons2f.h weapons30.h weapons4e.h decode.h cgiconv.h skilllist.h statlist.h deity.h player.h crctab.h filtermgr.h point.h pointarray.h mapcore.h category.h compassframe.h group.h guild.h fixpt.h netdiag.h zones.h logger.h xmlconv.h xmlpreferences.h seqwindow.h seqlistview.h zonemgr.h spawnmonitor.h spawnpointlist.h typenames.h spawnlistcommon.h spawnlist2.h datetimemgr.h spawnlog.h packetlog.h datalocationmgr.h eqstr.h messages.h messagefilter.h messagewindow.h messageshell.h terminal.h filteredspawnlog.h messagefilterdialog.h diagnosticmessages.h mapicon.h mapicondialog.h mapicondialog.ui filternotifications.h netstream.h guildshell.h guildlist.h bazaarlog.h message.h s_everquest.h staticspells.h packetcaptureprovider.h
CLEANFILES = $(nodist_showeq_SOURCES)
Modified: showeq/trunk/src/interface.cpp
===================================================================
--- showeq/trunk/src/interface.cpp 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/interface.cpp 2024-08-21 19:57:04 UTC (rev 1516)
@@ -71,7 +71,6 @@
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
-#include <ifaddrs.h>
#include <QFont>
#include <QApplication>
@@ -150,7 +149,7 @@
m_netDiag(0),
m_messageFilterDialog(0),
m_guildListWindow(0),
- m_deviceList(enumerateDevices())
+ m_deviceList(enumerateNetworkDevices())
{
setObjectName(name);
setWindowFlags(Qt::Window);
@@ -5230,7 +5229,7 @@
maclst, 0, true, &ok);
if (ok)
{
- if (address.length() != 17)
+ if (address.length() != 17 && !address.isEmpty())
{
seqWarn("Invalid MAC Address (%s)! Ignoring!", address.toLatin1().data());
return;
@@ -5246,35 +5245,10 @@
}
}
-QStringList EQInterface::enumerateDevices()
-{
- struct ifaddrs *ifaddr, *ifa;
- int n;
- QStringList devices;
- if (getifaddrs(&ifaddr) == -1)
- {
- seqWarn("Could not enumerate network devices");
- return QStringList();
- }
-
- for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++)
- {
- if (ifa->ifa_addr == NULL)
- continue;
-
- if (ifa->ifa_addr->sa_family == AF_INET)
- devices.append(ifa->ifa_name);
- }
-
- freeifaddrs(ifaddr);
-
- return devices;
-}
-
QString EQInterface::promptForNetDevice()
{
- m_deviceList = enumerateDevices();
+ m_deviceList = enumerateNetworkDevices();
int current = 0;
if (m_packet)
Modified: showeq/trunk/src/packet.cpp
===================================================================
--- showeq/trunk/src/packet.cpp 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packet.cpp 2024-08-21 19:57:04 UTC (rev 1516)
@@ -1,6 +1,6 @@
/*
* packet.cpp
- * Copyright 2000-2008, 2016, 2019 by the respective ShowEQ Developers
+ * Copyright 2000-2024 by the respective ShowEQ Developers
* Portions Copyright 2001-2004,2007 Zaphod (do...@us...).
*
* This file is part of ShowEQ.
@@ -36,6 +36,7 @@
#include "everquest.h"
#include "packet.h"
+#include "packetcommon.h"
#include "packetcapture.h"
#include "packetformat.h"
#include "packetstream.h"
@@ -73,14 +74,6 @@
//----------------------------------------------------------------------
// constants
-const in_port_t WorldServerGeneralMinPort = 9000;
-const in_port_t WorldServerGeneralMaxPort = 9013;
-const in_port_t WorldServerChatPort = 9876;
-const in_port_t WorldServerChat2Port = 9875; // xgame tells, mail
-const in_port_t LoginServerMinPort = 15900;
-const in_port_t LoginServerMaxPort = 15910;
-const in_port_t ChatServerPort = 5998;
-
//----------------------------------------------------------------------
// Here begins the code
@@ -188,54 +181,38 @@
// no client/server ports yet
m_clientPort = 0;
m_serverPort = 0;
-
- struct hostent *he;
- struct in_addr ia;
+
if (m_ip.isEmpty() && m_mac.isEmpty())
- seqFatal("No address specified");
-
- if (!m_ip.isEmpty())
{
- /* Substitute "special" IP which is interpreted
- to set up a different filter for picking up new sessions */
-
- if (m_ip == "auto")
- inet_aton (AUTOMATIC_CLIENT_IP, &ia);
- else if (inet_aton (m_ip.toLatin1().data(), &ia) == 0)
- {
- he = gethostbyname(m_ip.toLatin1().data());
- if (!he)
- seqFatal("Invalid address; %s", m_ip.toLatin1().data());
-
- memcpy (&ia, he->h_addr_list[0], he->h_length);
- }
- m_client_addr = ia.s_addr;
- m_ip = inet_ntoa(ia);
-
- if (m_ip == AUTOMATIC_CLIENT_IP)
- {
- m_detectingClient = true;
- seqInfo("Listening for first client seen.");
- }
- else
- {
- m_detectingClient = false;
- seqInfo("Listening for client: %s", m_ip.toLatin1().data());
- }
+ seqInfo("No address specified. Defaulting to client auto-detect");
+ m_ip = AUTOMATIC_CLIENT_IP;
}
+ validateIP();
+
if (m_playbackPackets == PLAYBACK_OFF)
{
// create the pcap object and initialize, either with MAC or IP
m_packetCapture = new PacketCaptureThread();
if (m_mac.length() == 17)
+ {
+ seqInfo("Listening for client MAC: %s", m_mac.toLatin1().data());
+
m_packetCapture->start(m_device.toLatin1().data(),
m_mac.toLatin1().data(),
m_realtime, MAC_ADDRESS_TYPE );
+ }
else
+ {
+ if (m_detectingClient)
+ seqInfo("Listening for next client seen. (you must zone for this to work!)");
+ else
+ seqInfo("Listening for client: %s", m_ip.toLatin1().data());
+
m_packetCapture->start(m_device.toLatin1().data(),
m_ip.toLatin1().data(),
m_realtime, IP_ADDRESS_TYPE );
+ }
emit filterChanged();
}
else if (m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP)
@@ -310,6 +287,43 @@
}
}
+//helper function to verify specified IP is a valid IP or hostname, and/or
+//to set up auto detection.
+void EQPacket::validateIP()
+{
+ struct in_addr ia;
+ struct hostent *he;
+
+ if (m_ip.isEmpty() || m_ip == AUTOMATIC_CLIENT_IP)
+ {
+ /* Substitute "special" IP which is interpreted
+ to set up a different filter for picking up new sessions */
+ inet_aton (AUTOMATIC_CLIENT_IP, &ia);
+ }
+ else if (inet_aton (m_ip.toLatin1().data(), &ia) == 0)
+ {
+ he = gethostbyname(m_ip.toLatin1().data());
+ if (he)
+ {
+ memcpy (&ia, he->h_addr_list[0], he->h_length);
+ }
+ else
+ {
+ // If the IP or host is invalid, default to auto-detect, rather
+ // than immediately exiting (the previous behavior)
+ seqWarn("Invalid address or hostname: %s", m_ip.toLatin1().data());
+ seqWarn("Defaulting to client auto-detect");
+ m_ip = AUTOMATIC_CLIENT_IP;
+ inet_aton (AUTOMATIC_CLIENT_IP, &ia);
+ }
+ }
+ m_client_addr = ia.s_addr;
+ m_ip = inet_ntoa(ia);
+
+ m_detectingClient = (m_ip == AUTOMATIC_CLIENT_IP);
+
+}
+
////////////////////////////////////////////////////
// Destructor
EQPacket::~EQPacket()
@@ -564,9 +578,9 @@
this,
SIGNAL(sessionTrackingChanged(uint8_t)));
connect(stream,
- SIGNAL(lockOnClient(in_port_t, in_port_t)),
+ SIGNAL(lockOnClient(in_port_t, in_port_t, in_addr_t)),
this,
- SLOT(lockOnClient(in_port_t, in_port_t)));
+ SLOT(lockOnClient(in_port_t, in_port_t, in_addr_t)));
connect(stream,
SIGNAL(closing(uint32_t, EQStreamID)),
this,
@@ -704,23 +718,32 @@
// If we just closed the zone server session, unlatch the client port
if (streamId == zone2client || streamId == client2zone)
{
- m_clientPort = 0;
- m_serverPort = 0;
+ unlatchClientPort();
- emit clientPortLatched(m_clientPort);
-
seqInfo("EQPacket: SessionDisconnect detected, awaiting next zone session, pcap filter: EQ Client %s",
- m_ip.toLatin1().data());
+ (m_ip == AUTOMATIC_CLIENT_IP) ? "auto-detect" : m_ip.toLatin1().data());
}
}
+// Unlatch a locked-on client port, so the next client is detected correctly
+void EQPacket::unlatchClientPort()
+{
+ m_clientPort = 0;
+ m_serverPort = 0;
+ emit clientPortLatched(m_clientPort);
+}
+
+
////////////////////////////////////////////////////
// Locks onto a specific client port (for session tracking)
-void EQPacket::lockOnClient(in_port_t serverPort, in_port_t clientPort)
+void EQPacket::lockOnClient(in_port_t serverPort, in_port_t clientPort, in_addr_t clientAddr)
{
m_serverPort = serverPort;
m_clientPort = clientPort;
+ m_client_addr = clientAddr;
+ in_addr ia = inet_makeaddr(ntohl(m_client_addr), ntohl(m_client_addr));
+
if (m_playbackPackets == PLAYBACK_OFF ||
m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP)
{
@@ -753,7 +776,7 @@
else
{
seqInfo("EQPacket: SessionRequest detected, pcap filter: EQ Client %s, Client port %d. Server port %d",
- m_ip.toLatin1().data(), m_clientPort, m_serverPort);
+ inet_ntoa(ia), m_clientPort, m_serverPort);
}
emit clientPortLatched(m_clientPort);
@@ -928,14 +951,14 @@
void EQPacket::monitorIPClient(const QString& ip)
{
m_ip = ip;
- struct in_addr ia;
- inet_aton (m_ip.toLatin1().data(), &ia);
- m_client_addr = ia.s_addr;
+
+ validateIP();
+
emit clientChanged(m_client_addr);
resetEQPacket();
- seqInfo("Listening for IP client: %s", m_ip.toLatin1().data());
+ seqInfo("Listening for IP client: %s", (m_ip == AUTOMATIC_CLIENT_IP) ? "auto-detect" : m_ip.toLatin1().data());
if (m_playbackPackets == PLAYBACK_OFF ||
m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP)
{
@@ -952,24 +975,32 @@
void EQPacket::monitorMACClient(const QString& mac)
{
m_mac = mac;
- m_detectingClient = true;
struct in_addr ia;
inet_aton (AUTOMATIC_CLIENT_IP, &ia);
+ m_detectingClient = true;
m_client_addr = ia.s_addr;
emit clientChanged(m_client_addr);
resetEQPacket();
- seqInfo("Listening for MAC client: %s", m_mac.toLatin1().data());
+ if (!m_mac.isEmpty() && m_mac.length() == 17)
+ {
+ seqInfo("Listening for MAC client: %s", m_mac.toLatin1().data());
- if (m_playbackPackets == PLAYBACK_OFF ||
- m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP)
+ if (m_playbackPackets == PLAYBACK_OFF ||
+ m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP)
+ {
+ m_packetCapture->setFilter(m_device.toLatin1().data(),
+ m_mac.toLatin1().data(),
+ m_realtime,
+ MAC_ADDRESS_TYPE, 0, 0);
+ emit filterChanged();
+ }
+ }
+ else
{
- m_packetCapture->setFilter(m_device.toLatin1().data(),
- m_ip.toLatin1().data(),
- m_realtime,
- IP_ADDRESS_TYPE, 0, 0);
- emit filterChanged();
+ seqWarn("Invalid MAC specified. Defaulting to auto-detect of next client.");
+ monitorNextClient();
}
}
@@ -1012,50 +1043,31 @@
// stop the current packet capture
m_packetCapture->stop();
- // setup for capture on new device
- if (!m_ip.isEmpty())
- {
- struct hostent *he;
- struct in_addr ia;
+ validateIP();
- /* Substitute "special" IP which is interpreted
- to set up a different filter for picking up new sessions */
-
- if (m_ip == "auto")
- inet_aton (AUTOMATIC_CLIENT_IP, &ia);
- else if (inet_aton (m_ip.toLatin1().data(), &ia) == 0)
- {
- he = gethostbyname(m_ip.toLatin1().data());
- if (!he)
- seqFatal("Invalid address; %s", m_ip.toLatin1().data());
-
- memcpy (&ia, he->h_addr_list[0], he->h_length);
- }
- m_client_addr = ia.s_addr;
- m_ip = inet_ntoa(ia);
-
- if (m_ip == AUTOMATIC_CLIENT_IP)
- {
- m_detectingClient = true;
- seqInfo("Listening for first client seen.");
- }
- else
- {
- m_detectingClient = false;
- seqInfo("Listening for client: %s", m_ip.toLatin1().data());
- }
- }
-
resetEQPacket();
// restart packet capture
if (m_mac.length() == 17)
+ {
+ seqInfo("Listening for client MAC: %s", m_mac.toLatin1().data());
+
m_packetCapture->start(m_device.toLatin1().data(),
m_mac.toLatin1().data(),
m_realtime, MAC_ADDRESS_TYPE );
+ }
else
- m_packetCapture->start(m_device.toLatin1().data(), m_ip.toLatin1().data(),
- m_realtime, IP_ADDRESS_TYPE );
+ {
+ if (m_detectingClient)
+ seqInfo("Listening for next client seen. (you must zone for this to work!)");
+ else
+ seqInfo("Listening for client: %s", m_ip.toLatin1().data());
+
+ m_packetCapture->start(m_device.toLatin1().data(),
+ m_ip.toLatin1().data(),
+ m_realtime, IP_ADDRESS_TYPE );
+ }
+
emit filterChanged();
}
@@ -1134,10 +1146,7 @@
m_zone2ClientStream->reset();
m_zone2ClientStream->setSessionTracking(m_session_tracking);
- m_clientPort = 0;
- m_serverPort = 0;
-
- emit clientPortLatched(m_clientPort);
+ unlatchClientPort();
}
///////////////////////////////////////////
Modified: showeq/trunk/src/packet.h
===================================================================
--- showeq/trunk/src/packet.h 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packet.h 2024-08-21 19:57:04 UTC (rev 1516)
@@ -1,6 +1,6 @@
/*
* packet.h
- * Copyright 2000-2008, 2019 by the respective ShowEQ Developers
+ * Copyright 2000-2024 by the respective ShowEQ Developers
* Portions Copyright 2001-2003 Zaphod (do...@us...).
*
* This file is part of ShowEQ.
@@ -51,7 +51,7 @@
//----------------------------------------------------------------------
// forward declarations
class VPacket;
-class PacketCaptureThread;
+class PacketCaptureProviderThread;
class EQPacketStream;
class EQUDPIPPacketFormat;
class EQPacketTypeDB;
@@ -121,7 +121,8 @@
protected slots:
void closeStream(uint32_t sessionId, EQStreamID streamId);
- void lockOnClient(in_port_t serverPort, in_port_t clientPort);
+ void unlatchClientPort();
+ void lockOnClient(in_port_t serverPort, in_port_t clientPort, in_addr_t clientAddr);
signals:
// used for net_stats display
@@ -158,8 +159,9 @@
bool unknown);
private:
+ void validateIP();
- PacketCaptureThread* m_packetCapture;
+ PacketCaptureProviderThread* m_packetCapture;
VPacket* m_vPacket;
QTimer* m_timer;
Modified: showeq/trunk/src/packetcapture.cpp
===================================================================
--- showeq/trunk/src/packetcapture.cpp 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packetcapture.cpp 2024-08-21 19:57:04 UTC (rev 1516)
@@ -1,6 +1,6 @@
/*
* packetcapture.cpp
- * Copyright 2000-2008, 2019 by the respective ShowEQ Developers
+ * Copyright 2000-2024 by the respective ShowEQ Developers
* Portions Copyright 2001-2003 Zaphod (do...@us...).
*
* This file is part of ShowEQ.
@@ -31,6 +31,7 @@
#include <arpa/inet.h>
#include "packetcapture.h"
+#include "packetcommon.h"
#include "diagnosticmessages.h"
//#define PCAP_DEBUG 1
@@ -39,7 +40,8 @@
// PacketCaptureThread
// start and stop the thread
// get packets to the processing engine(dispatchPacket)
-PacketCaptureThread::PacketCaptureThread() :
+PacketCaptureThread::PacketCaptureThread() : PacketCaptureProviderThread(),
+ m_pcache_pcap(NULL),
m_playbackSpeed(0)
{
}
@@ -52,25 +54,6 @@
pcap_close(m_pcache_pcap);
}
- // Drop the packets we have lying around
- pthread_mutex_lock (&m_pcache_mutex);
-
- struct packetCache *pc = m_pcache_first;
- struct packetCache* freeMe = NULL;
-
- while (pc)
- {
- freeMe = pc;
- pc = pc->next;
-
- free(freeMe);
- }
-
- m_pcache_first = NULL;
- m_pcache_last = NULL;
- m_pcache_closed = true;
-
- pthread_mutex_unlock (&m_pcache_mutex);
}
void PacketCaptureThread::setPlaybackSpeed(int playbackSpeed)
@@ -99,69 +82,55 @@
{
char ebuf[PCAP_ERRBUF_SIZE]; // pcap error buffer
char filter_buf[256]; // pcap filter buffer
- struct bpf_program bpp;
- struct sched_param sp;
- bpf_u_int32 mask; // sniff device netmask
- bpf_u_int32 net; // sniff device ip
seqInfo("Initializing Packet Capture Thread: ");
m_pcache_closed = false;
- // Fetch the netmask for the device to use later with the filter.
- if (pcap_lookupnet(device, &net, &mask, ebuf) == -1)
- {
- // Couldn't find net mask. Just leave it open.
- seqWarn("Couldn't determine netmask of device %s. Using 0.0.0.0. Error was %s",
- device, ebuf);
- }
+ /* We've replaced pcap_open_live() with pcap_create/pcap_activate.
+ * This allows us to use immedate mode, rather than approximating it with
+ * a 1ms timeout value.
+ *
+ * NOTE: a race condition exists between this thread and the main thread
+ * - any artificial delay in getting packets can cause filtering problems
+ * and cause us to miss new stream when the player zones.
+ */
- // create pcap style filter expressions
- if (address_type == IP_ADDRESS_TYPE)
+ // initialize the pcap object
+ m_pcache_pcap = pcap_create((const char*)device, ebuf);
+
+ if (!m_pcache_pcap)
{
- if (strcmp(host, AUTOMATIC_CLIENT_IP) == 0)
+ seqFatal("pcap_error:pcap_create(%s): %s", device, ebuf);
+ if ((getuid() != 0) && (geteuid() != 0))
{
- seqInfo("Filtering packets on device %s, searching for EQ client...", device);
- sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800 and not broadcast and not multicast");
+ seqWarn("Make sure you are running ShowEQ as root.");
}
- else
- {
- seqInfo("Filtering packets on device %s, IP host %s", device, host);
- sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and host %s and ether proto 0x0800 and not broadcast and not multicast", host);
- }
+ exit(1);
}
- else if (address_type == MAC_ADDRESS_TYPE)
+
+ pcap_set_promisc(m_pcache_pcap, 1);
+ //PCAP docs say 64K snaplen should be enough for most networks.
+ pcap_set_snaplen(m_pcache_pcap, UINT16_MAX);
+ // default buffer size is 2M: 2*1024*1024
+ // but we can increase it in the future if needed
+ //pcap_set_buffer_size(m_pcache_pcap, 4*1024*1024);
+
+#ifndef __FreeBSD__
+ pcap_set_immediate_mode(m_pcache_pcap, 1);
+#endif
+
+ int act = pcap_activate(m_pcache_pcap);
+ if (act > 0)
{
- seqInfo("Filtering packets on device %s, MAC host %s", device, host);
- sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether host %s and ether proto 0x0800 and not broadcast and not multicast", host);
+ seqWarn("pcap_warning:pcap_activate:%s", pcap_statustostr(act));
}
- else
+ else if (act < 0)
{
- seqFatal("pcap_error:filter_string: unknown address_type (%d)", address_type);
- exit(0);
+ seqFatal("pcap_error:pcap_activate:%s", pcap_statustostr(act));
+ pcap_close(m_pcache_pcap);
+ exit(1);
}
- /* A word about pcap_open_live() from the docs
- ** to_ms specifies the read timeout in milliseconds. The
- ** read timeout is used to arrange that the read not necessarily
- ** return immediately when a packet is seen, but that it wait
- ** for some amount of time to allow more packets to arrive and
- ** to read multiple packets from the OS kernel in one operation.
- ** Not all platforms support a read timeout; on platforms that
- ** don't, the read timeout is ignored.
- **
- ** In Linux 2.4.x with the to_ms set to 0 we got packets immediatly,
- ** and thats what we need in this application. However, as of libpcap
- ** 1.9.1, a timeout of 0 means infinity, so that no longer works. A
- ** negative timeout will use the default kernal timeout, which can
- ** vary. So the most prudent option is to set the timeout as low
- ** as we can, to 1 ms.
- **
- ** a race condition exists between this thread and the main thread
- ** any artificial delay in getting packets can cause filtering problems
- ** and cause us to miss new stream when the player zones.
- */
- // initialize the pcap object
- m_pcache_pcap = pcap_open_live((char *) device, BUFSIZ, true, 1, ebuf);
#ifdef __FreeBSD__
// if we're on FreeBSD, we need to call ioctl on the file descriptor
// with BIOCIMMEDIATE to get the kernel Berkeley Packet Filter device
@@ -171,7 +140,7 @@
// knows a less hacky way of doing this, I'd love to hear about it.
// the problem here is that libpcap doesn't expose an API to do this
// in any way
- int fd = *((int*)m_pcache_pcap);
+ int fd = pcap_fileno(m_pcache_pcap);
int temp = 1;
if ( ioctl( fd, BIOCIMMEDIATE, &temp ) < 0 )
{
@@ -178,44 +147,13 @@
seqWarn("PCAP couldn't set immediate mode on BSD" );
}
#endif
- if (!m_pcache_pcap)
- {
- seqWarn("pcap_error:pcap_open_live(%s): %s", device, ebuf);
- if ((getuid() != 0) && (geteuid() != 0))
- {
- seqWarn("Make sure you are running ShowEQ as root.");
- }
- exit(0);
- }
- if (pcap_compile(m_pcache_pcap, &bpp, filter_buf, 1, net) == -1)
- {
- pcap_perror (m_pcache_pcap, (char*)"pcap_error:pcap_compile");
- exit(0);
- }
-
- if (pcap_setfilter (m_pcache_pcap, &bpp) == -1)
- {
- pcap_perror (m_pcache_pcap, (char*)"pcap_error:pcap_setfilter");
- exit(0);
- }
-
- pcap_freecode(&bpp);
-
m_pcache_first = m_pcache_last = NULL;
- pthread_mutex_init (&m_pcache_mutex, NULL);
pthread_create (&m_tid, NULL, loop, (void*)this);
- if (realtime)
- {
- memset (&sp, 0, sizeof (sp));
- sp.sched_priority = 1;
- if (pthread_setschedparam (m_tid, SCHED_RR, &sp) != 0)
- {
- seqWarn("Failed to set capture thread realtime.");
- }
- }
+ this->setFilter(device, host, realtime, address_type, 0, 0);
+
}
//------------------------------------------------------------------------
@@ -247,7 +185,6 @@
m_pcache_first = m_pcache_last = NULL;
- pthread_mutex_init(&m_pcache_mutex, NULL);
pthread_create(&m_tid, NULL, loop, (void*)this);
}
@@ -254,8 +191,11 @@
void PacketCaptureThread::stop()
{
// close the pcap session
- pcap_close(m_pcache_pcap);
- m_pcache_pcap = NULL;
+ if (m_pcache_pcap)
+ {
+ pcap_close(m_pcache_pcap);
+ m_pcache_pcap = NULL;
+ }
}
void* PacketCaptureThread::loop (void *param)
@@ -399,37 +339,6 @@
pthread_mutex_unlock (&myThis->m_pcache_mutex);
}
-uint16_t PacketCaptureThread::getPacket(unsigned char *buff)
-{
- uint16_t ret;
- struct packetCache *pc = NULL;
-
- pthread_mutex_lock (&m_pcache_mutex);
-
- ret = 0;
-
- pc = m_pcache_first;
-
- if (pc)
- {
- m_pcache_first = pc->next;
-
- if (!m_pcache_first)
- m_pcache_last = NULL;
- }
-
- pthread_mutex_unlock (&m_pcache_mutex);
-
- if (pc)
- {
- ret = pc->len;
- memcpy (buff, pc->data, ret);
- free (pc);
- }
-
- return ret;
-}
-
void PacketCaptureThread::setFilter (const char *device,
const char *hostname,
bool realtime,
@@ -437,12 +346,13 @@
uint16_t zone_port,
uint16_t client_port)
{
- char filter_buf[256]; // pcap filter buffer
+ char filter_buf[256]; // pcap filter buffer
+ char* pfb = filter_buf;
char ebuf[PCAP_ERRBUF_SIZE];
struct bpf_program bpp;
struct sched_param sp;
- bpf_u_int32 mask; // sniff device netmask
- bpf_u_int32 net; // sniff device ip
+ bpf_u_int32 mask = 0; // sniff device netmask
+ bpf_u_int32 net = 0 ; // sniff device ip
// Fetch the netmask for the device to use later with the filter
if (pcap_lookupnet(device, &net, &mask, ebuf) == -1)
@@ -452,51 +362,47 @@
device, ebuf);
}
- /* Listen to World Server or the specified Zone Server */
- if (address_type == IP_ADDRESS_TYPE && client_port)
+ if (!client_port && !zone_port)
{
- // Restrict to client port and ip, plus world streams.
- sprintf(filter_buf,
- "udp and (portrange 9000-9007 or port 9876 or port %d) and host %s and ether proto 0x0800 and not broadcast and not multicast",
- client_port, hostname);
+ //no client/zone port detected, so leave it open
+ pfb += sprintf(pfb, "udp[0:2] > 1024 and udp[2:2] > 1024");
}
- else if (address_type == IP_ADDRESS_TYPE && zone_port)
+ else
{
- // Restrict to zone port and world streams.
- sprintf(filter_buf,
- "udp and (portrange 9000-9007 or port 9876 or port %d) and host %s and ether proto 0x0800 and not broadcast and not multicast",
- zone_port, hostname);
+ // restrict to client/zone port and world server ports
+ pfb += sprintf(pfb, "udp");
+ pfb += sprintf(pfb, " and (portrange %d-%d or port %d or port %d)",
+ WorldServerGeneralMinPort, WorldServerGeneralMaxPort,
+ WorldServerChatPort, (client_port) ? client_port : zone_port);
}
- else if (address_type == MAC_ADDRESS_TYPE && client_port)
+
+ if (address_type == IP_ADDRESS_TYPE)
{
- // Restrict to client port and world streams.
- sprintf(filter_buf,
- "udp and (portrange 9000-9007 or port 9876 or port %d) and ether host %s and ether proto 0x0800 and not broadcast and not multicast",
- client_port, hostname);
+ if (hostname && strlen(hostname) && strcmp(hostname, AUTOMATIC_CLIENT_IP) != 0)
+ // host was specified/detected
+ pfb += sprintf(pfb, " and host %s", hostname);
}
- else if (address_type == MAC_ADDRESS_TYPE && zone_port)
+ else if (address_type == MAC_ADDRESS_TYPE)
{
- // Restrict to zone port and world streams.
- sprintf(filter_buf,
- "udp and (portrange 9000-9007 or port 9876 or port %d) and ether host %s and ether proto 0x0800 and not broadcast and not multicast",
- zone_port, hostname);
+ if (hostname && strlen(hostname) && strcmp(hostname, AUTOMATIC_CLIENT_IP) != 0)
+ // mac was specified
+ pfb += sprintf(pfb, " and ether host %s", hostname);
}
- else if (hostname != NULL && !client_port && !zone_port)
+ else if (address_type == DEFAULT_ADDRESS_TYPE)
{
- // Leave wide open.
- sprintf(filter_buf,
- "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800 and host %s and not broadcast and not multicast",
- hostname);
+ //don't specify IP or MAC address in filter string
}
else
{
- // Not even a hostname. Leave really wide open!
- seqInfo("Filtering packets on device %s, searching for EQ client...",
- device);
- sprintf(filter_buf,
- "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800 and not broadcast and not multicast");
+ seqFatal("pcap_error:filter_string: unknown address_type (%d)", address_type);
+ exit(0);
}
+ //restrict to ipv4, and ignore broad/multi-cast packets
+ pfb += sprintf(pfb, " and ether proto 0x800 and not broadcast and not multicast");
+
+ seqInfo("Filtering packets on device %s", device);
+
if (pcap_compile (m_pcache_pcap, &bpp, filter_buf, 1, net) == -1)
{
seqWarn("%s",filter_buf);
Modified: showeq/trunk/src/packetcapture.h
===================================================================
--- showeq/trunk/src/packetcapture.h 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packetcapture.h 2024-08-21 19:57:04 UTC (rev 1516)
@@ -1,6 +1,6 @@
/*
* packetcapture.h
- * Copyright 2000-2006, 2019 by the respective ShowEQ Developers
+ * Copyright 2000-2024 by the respective ShowEQ Developers
* Portions Copyright 2001-2003 Zaphod (do...@us...).
*
* This file is part of ShowEQ.
@@ -36,6 +36,7 @@
#include <QString>
+#include "packetcaptureprovider.h"
#include "packetcommon.h"
@@ -50,51 +51,43 @@
//----------------------------------------------------------------------
// PacketCaptureThread
-class PacketCaptureThread
+class PacketCaptureThread : public PacketCaptureProviderThread
{
- public:
- PacketCaptureThread();
- ~PacketCaptureThread();
+ public:
+ PacketCaptureThread();
+ ~PacketCaptureThread();
- // Set the playback speed for offline packet capture. Valid values
- // are -1-9, 1 is 1x, 2 is 2x, etc. -1 is paused. 0 is as fast as
- // possible (no throttle)
- void setPlaybackSpeed(int playbackSpeed);
- int getPlaybackSpeed() { return (m_playbackSpeed == 100 ? 0 : m_playbackSpeed); }
+ bool offlinePlaybackSupported() { return true; }
- void start (const char *device, const char *host, bool realtime, uint8_t address_type);
- void startOffline(const char* filename, int playbackSpeed);
- void stop ();
- uint16_t getPacket (unsigned char *buff);
- void setFilter (const char *device, const char *hostname, bool realtime,
- uint8_t address_type, uint16_t zone_server_port, uint16_t client_port);
- const QString getFilter();
-
- private:
- static void* loop(void *param);
- static void packetCallBack(u_char * param, const struct pcap_pkthdr *ph, const u_char *data);
+ // Set the playback speed for offline packet capture. Valid values
+ // are -1-9, 1 is 1x, 2 is 2x, etc. -1 is paused. 0 is as fast as
+ // possible (no throttle)
+ void setPlaybackSpeed(int playbackSpeed);
+ int getPlaybackSpeed() { return (m_playbackSpeed == 100 ? 0 : m_playbackSpeed); }
- struct packetCache
- {
- struct packetCache *next;
- ssize_t len;
- unsigned char data[0];
- };
- struct packetCache *m_pcache_first;
- struct packetCache *m_pcache_last;
- bool m_pcache_closed;
+ void start (const char *device, const char *host, bool realtime, uint8_t address_type);
+ void startOffline(const char* filename, int playbackSpeed);
+ void stop ();
- pthread_t m_tid;
- pthread_mutex_t m_pcache_mutex;
+ //moved to base class
+ //uint16_t getPacket (unsigned char *buff);
- pcap_t *m_pcache_pcap;
+ void setFilter (const char *device, const char *hostname, bool realtime,
+ uint8_t address_type, uint16_t zone_server_port, uint16_t client_port);
+ const QString getFilter();
- QString m_pcapFilter;
+ private:
+ static void* loop(void *param);
+ static void packetCallBack(u_char * param, const struct pcap_pkthdr *ph, const u_char *data);
- // Playback controls for offline file processing
- int m_playbackSpeed; // -1=paused, 0=max, 1=1x speed, 2=2x speed, up to 9
- timeval m_tvLastProcessedActual;
- timeval m_tvLastProcessedOriginal;
+ pcap_t *m_pcache_pcap;
+
+ QString m_pcapFilter;
+
+ // Playback controls for offline file processing
+ int m_playbackSpeed; // -1=paused, 0=max, 1=1x speed, 2=2x speed, up to 9
+ timeval m_tvLastProcessedActual;
+ timeval m_tvLastProcessedOriginal;
};
#endif // _PACKETCAPTURE_H_
Added: showeq/trunk/src/packetcaptureprovider.cpp
===================================================================
--- showeq/trunk/src/packetcaptureprovider.cpp (rev 0)
+++ showeq/trunk/src/packetcaptureprovider.cpp 2024-08-21 19:57:04 UTC (rev 1516)
@@ -0,0 +1,89 @@
+/*
+ * packetcaptureprovider.cpp
+ * Copyright 2000-2024 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "packetcaptureprovider.h"
+
+
+PacketCaptureProviderThread::PacketCaptureProviderThread() :
+ m_pcache_first(NULL),
+ m_pcache_last(NULL),
+ m_pcache_closed(true)
+{
+ pthread_mutex_init(&m_pcache_mutex, NULL);
+}
+
+PacketCaptureProviderThread::~PacketCaptureProviderThread()
+{
+ // Drop the packets we have lying around
+ pthread_mutex_lock (&m_pcache_mutex);
+
+ struct packetCache *pc = m_pcache_first;
+ struct packetCache* freeMe = NULL;
+
+ while (pc)
+ {
+ freeMe = pc;
+ pc = pc->next;
+
+ free(freeMe);
+ }
+
+ m_pcache_first = NULL;
+ m_pcache_last = NULL;
+ m_pcache_closed = true;
+
+ pthread_mutex_unlock (&m_pcache_mutex);
+
+ pthread_mutex_destroy(&m_pcache_mutex);
+
+}
+
+uint16_t PacketCaptureProviderThread::getPacket (unsigned char *buf)
+{
+ uint16_t ret;
+ struct packetCache *pc = NULL;
+
+ pthread_mutex_lock (&m_pcache_mutex);
+
+ ret = 0;
+
+ pc = m_pcache_first;
+
+ if (pc)
+ {
+ m_pcache_first = pc->next;
+
+ if (!m_pcache_first)
+ m_pcache_last = NULL;
+ }
+
+ pthread_mutex_unlock (&m_pcache_mutex);
+
+ if (pc)
+ {
+ ret = pc->len;
+ memcpy (buf, pc->data, ret);
+ free (pc);
+ }
+
+ return ret;
+}
Added: showeq/trunk/src/packetcaptureprovider.h
===================================================================
--- showeq/trunk/src/packetcaptureprovider.h (rev 0)
+++ showeq/trunk/src/packetcaptureprovider.h 2024-08-21 19:57:04 UTC (rev 1516)
@@ -0,0 +1,68 @@
+/*
+ * packetcaptureprovider.h
+ * Copyright 2000-2024 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PACKETCAPTUREPROVIDERTHREAD_H_
+#define _PACKETCAPTUREPROVIDERTHREAD_H_
+
+#include <cstdint>
+#include <QString>
+
+class PacketCaptureProviderThread
+{
+ public:
+ PacketCaptureProviderThread();
+ virtual ~PacketCaptureProviderThread();
+
+ virtual bool offlinePlaybackSupported() = 0;
+ virtual void startOffline(const char* filename, int playbackSpeed) = 0;
+ virtual void setPlaybackSpeed(int playbackSpeed) = 0;
+ virtual int getPlaybackSpeed() = 0;
+
+ virtual void start (const char *device, const char *host, bool realtime, uint8_t address_type) = 0;
+ virtual void stop () = 0;
+
+ virtual uint16_t getPacket (unsigned char *buf);
+
+ virtual void setFilter (const char *device, const char *hostname, bool realtime,
+ uint8_t address_type, uint16_t zone_server_port, uint16_t client_port) = 0;
+ virtual const QString getFilter() = 0;
+
+
+ protected:
+
+ struct packetCache
+ {
+ struct packetCache *next;
+ ssize_t len;
+ unsigned char data[0];
+ };
+ struct packetCache *m_pcache_first;
+ struct packetCache *m_pcache_last;
+ bool m_pcache_closed;
+
+ pthread_t m_tid;
+ pthread_mutex_t m_pcache_mutex;
+
+};
+
+#endif
+
Modified: showeq/trunk/src/packetcommon.h
===================================================================
--- showeq/trunk/src/packetcommon.h 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packetcommon.h 2024-08-21 19:57:04 UTC (rev 1516)
@@ -46,11 +46,20 @@
#include <endian.h>
#endif
+#include <netdb.h>
//----------------------------------------------------------------------
// Constants
const char* const AUTOMATIC_CLIENT_IP = "127.0.0.0";
+const in_port_t WorldServerGeneralMinPort = 9000;
+const in_port_t WorldServerGeneralMaxPort = 9015;
+const in_port_t WorldServerChatPort = 9876;
+const in_port_t WorldServerChat2Port = 9875; // xgame tells, mail
+const in_port_t LoginServerMinPort = 15900;
+const in_port_t LoginServerMaxPort = 15910;
+const in_port_t ChatServerPort = 5998;
+
// Preference constants for VPacket.Playback.
#define PLAYBACK_OFF 0
#define PLAYBACK_FORMAT_SEQ 1
Modified: showeq/trunk/src/packetstream.cpp
===================================================================
--- showeq/trunk/src/packetstream.cpp 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packetstream.cpp 2024-08-21 19:57:04 UTC (rev 1516)
@@ -92,6 +92,7 @@
m_sessionId(0),
m_sessionKey(0),
m_sessionClientPort(0),
+ m_sessionClientIP(0),
m_maxLength(0),
m_decodeKey(0),
m_validKey(true)
@@ -196,6 +197,7 @@
m_arqSeqExp = 0;
m_arqSeqFound = false;
m_sessionClientPort = 0;
+ m_sessionClientIP = 0;
m_sessionId = 0;
m_sessionKey = 0;
}
@@ -543,9 +545,11 @@
// Only accept packets that correspond to our latched client port, if
// it is set. This helps filter out multiple sessions on the same physical
// host when two eq clients zone at the same time. The first one will win.
- if (m_sessionClientPort != 0 &&
+ if (m_sessionClientPort != 0 && m_sessionClientIP != 0 &&
((dir() == DIR_Server && m_sessionClientPort != packet.getDestPort()) ||
- (dir() == DIR_Client && m_sessionClientPort != packet.getSourcePort())))
+ (dir() == DIR_Client && m_sessionClientPort != packet.getSourcePort()) ||
+ (dir() == DIR_Server && m_sessionClientIP != packet.getIPv4DestN()) ||
+ (dir() == DIR_Client && m_sessionClientIP != packet.getIPv4SourceN())))
{
#if (defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1)) || (defined(PACKET_SESSION_DIAG) && PACKET_SESSION_DIAG > 1)
seqDebug("discarding packet %s:%d ==>%s:%d netopcode=%04x size=%d. Multiple sessions on the same box? Ignoring all but one of them. Latched client port %d. Session tracking %s.",
@@ -1038,6 +1042,7 @@
// later. SessionRequest should always be an outer protocol packet
// so we can cast it to EQUDPIPPacketFormat to get the ip headers.
m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getSourcePort();
+ m_sessionClientIP = ((EQUDPIPPacketFormat&) packet).getIPv4SourceN();
}
}
break;
@@ -1132,6 +1137,7 @@
// later. SessionRequest should always be an outer protocol packet
// so we can cast it to EQUDPIPPacketFormat to get the ip headers.
m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getDestPort();
+ m_sessionClientIP = ((EQUDPIPPacketFormat&) packet).getIPv4DestN();
// If this is the world server talking to us, reset session tracking if
// it is on so we unlatch the client in case of getting kicked.
@@ -1149,8 +1155,9 @@
// headers!
m_session_tracking_enabled = 2;
- emit lockOnClient(((EQUDPIPPacketFormat&) packet).getSourcePort(),
- ((EQUDPIPPacketFormat&) packet).getDestPort());
+ emit lockOnClient(((EQUDPIPPacketFormat&) packet).getSourcePort(),
+ ((EQUDPIPPacketFormat&) packet).getDestPort(),
+ ((EQUDPIPPacketFormat&) packet).getIPv4DestN());
emit sessionTrackingChanged(m_session_tracking_enabled);
}
}
@@ -1200,6 +1207,7 @@
emit sessionTrackingChanged(m_session_tracking_enabled);
m_sessionClientPort = 0;
+ m_sessionClientIP = 0;
}
emit closing(m_sessionId, m_streamid);
Modified: showeq/trunk/src/packetstream.h
===================================================================
--- showeq/trunk/src/packetstream.h 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/packetstream.h 2024-08-21 19:57:04 UTC (rev 1516)
@@ -112,7 +112,7 @@
// this signals a change in the session tracking state
void sessionTrackingChanged(uint8_t);
- void lockOnClient(in_port_t serverPort, in_port_t clientPort);
+ void lockOnClient(in_port_t serverPort, in_port_t clientPort, in_addr_t clientAddr);
// Signal a new session key being received
void sessionKey(uint32_t sessionId, EQStreamID streadid, uint32_t sessionKey);
@@ -155,6 +155,7 @@
uint32_t m_sessionId;
uint32_t m_sessionKey;
in_port_t m_sessionClientPort;
+ in_addr_t m_sessionClientIP;
uint32_t m_maxLength;
// encryption
Modified: showeq/trunk/src/util.cpp
===================================================================
--- showeq/trunk/src/util.cpp 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/util.cpp 2024-08-21 19:57:04 UTC (rev 1516)
@@ -29,6 +29,7 @@
#include <cstdio>
#include <cstring>
#include <sys/time.h>
+#include <ifaddrs.h>
#include <QColor>
#include <QFileInfo>
@@ -1044,3 +1045,32 @@
return crc ^ 0xffffffffL;
}
+
+QStringList enumerateNetworkDevices()
+{
+ struct ifaddrs *ifaddr, *ifa;
+ int n;
+ QStringList devices;
+
+ if (getifaddrs(&ifaddr) == -1)
+ {
+ seqWarn("Could not enumerate network devices");
+ return QStringList();
+ }
+
+ for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++)
+ {
+ if (ifa->ifa_addr == NULL)
+ continue;
+
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ devices.append(ifa->ifa_name);
+ }
+
+ freeifaddrs(ifaddr);
+
+ return devices;
+}
+
+
+
Modified: showeq/trunk/src/util.h
===================================================================
--- showeq/trunk/src/util.h 2024-08-21 19:56:56 UTC (rev 1515)
+++ showeq/trunk/src/util.h 2024-08-21 19:57:04 UTC (rev 1516)
@@ -82,6 +82,8 @@
uint32_t calcCRC32(const uint8_t* p, uint32_t length);
uint16_t calcCRC16(uint8_t* p, uint32_t length, uint32_t seed);
+QStringList enumerateNetworkDevices();
+
// Templatized function to generate a string representing the bit pattern
// of the passed in value.
// Note: For use with integral data types.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|