b0a81fb231
These are the existing and desired defaults - * GUI: recalcCksum = true * PCAP import test: recalcCksum = false With the existing implementation, doing PCAP import in 'raw' mode twice would lead to recalcCksum being checked in the dialog the second time. This was because we were always forcing it to be true for the GUI case without checking for anything.
752 lines
22 KiB
C++
752 lines
22 KiB
C++
/*
|
|
Copyright (C) 2011 Srivats P.
|
|
|
|
This file is part of "Ostinato"
|
|
|
|
This 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.
|
|
|
|
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, see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
#include "pcapfileformat.h"
|
|
|
|
#include "pdmlreader.h"
|
|
#include "ostprotolib.h"
|
|
#include "streambase.h"
|
|
#include "hexdump.pb.h"
|
|
|
|
#include <QDataStream>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QProcess>
|
|
#include <QTemporaryFile>
|
|
#include <QtGlobal>
|
|
|
|
const quint32 kPcapFileMagic = 0xa1b2c3d4;
|
|
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
|
|
const quint32 kNanoSecondPcapFileMagic = 0xa1b23c4d;
|
|
const quint32 kNanoSecondPcapFileMagicSwapped = 0x4d3cb2a1;
|
|
const quint16 kPcapFileVersionMajor = 2;
|
|
const quint16 kPcapFileVersionMinor = 4;
|
|
const quint32 kMaxSnapLen = 65535;
|
|
const quint32 kDltEthernet = 1;
|
|
|
|
PcapFileFormat pcapFileFormat;
|
|
|
|
PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options)
|
|
: QDialog(NULL)
|
|
{
|
|
setupUi(this);
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
options_ = options;
|
|
|
|
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
|
// XXX: By default this key is absent - so that pcap import tests
|
|
// evaluate to false and hence show minimal diffs.
|
|
// However, for the GUI user, this should be enabled by default.
|
|
recalculateCksums->setChecked(
|
|
options_->value("RecalculateCksums", QVariant(true))
|
|
.toBool());
|
|
doDiff->setChecked(options_->value("DoDiff").toBool());
|
|
|
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
|
}
|
|
|
|
PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
|
{
|
|
}
|
|
|
|
void PcapImportOptionsDialog::accept()
|
|
{
|
|
options_->insert("ViaPdml", viaPdml->isChecked());
|
|
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
|
options_->insert("DoDiff", doDiff->isChecked());
|
|
|
|
QDialog::accept();
|
|
}
|
|
|
|
PcapFileFormat::PcapFileFormat()
|
|
{
|
|
importOptions_.insert("ViaPdml", true);
|
|
importOptions_.insert("DoDiff", true);
|
|
}
|
|
|
|
PcapFileFormat::~PcapFileFormat()
|
|
{
|
|
}
|
|
|
|
bool PcapFileFormat::open(const QString fileName,
|
|
OstProto::StreamConfigList &streams, QString &error)
|
|
{
|
|
bool isOk = false;
|
|
QFile file(fileName);
|
|
QTemporaryFile file2;
|
|
quint32 magic;
|
|
uchar gzipMagic[2];
|
|
bool nsecResolution = false;
|
|
int len;
|
|
PcapFileHeader fileHdr;
|
|
PcapPacketHeader pktHdr;
|
|
OstProto::Stream *prevStream = NULL;
|
|
quint64 lastXsec = 0;
|
|
int pktCount;
|
|
qint64 byteCount = 0;
|
|
qint64 byteTotal;
|
|
QByteArray pktBuf;
|
|
bool tryConvert = true;
|
|
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
goto _err_open;
|
|
|
|
len = file.peek((char*)gzipMagic, sizeof(gzipMagic));
|
|
if (len < int(sizeof(gzipMagic)))
|
|
goto _err_reading_magic;
|
|
|
|
if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b))
|
|
{
|
|
QProcess gzip;
|
|
|
|
emit status("Decompressing...");
|
|
emit target(0);
|
|
|
|
if (!file2.open())
|
|
{
|
|
error.append("Unable to open temporary file to uncompress .gz\n");
|
|
goto _err_unzip_fail;
|
|
}
|
|
|
|
qDebug("decompressing to %s", qPrintable(file2.fileName()));
|
|
|
|
gzip.setStandardOutputFile(file2.fileName());
|
|
gzip.start(OstProtoLib::gzipPath(),
|
|
QStringList()
|
|
<< "-d"
|
|
<< "-c"
|
|
<< fileName);
|
|
if (!gzip.waitForStarted(-1))
|
|
{
|
|
error.append(QString("Unable to start gzip. Check path in Preferences.\n"));
|
|
goto _err_unzip_fail;
|
|
}
|
|
|
|
if (!gzip.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running gzip\n"));
|
|
goto _err_unzip_fail;
|
|
}
|
|
|
|
file2.seek(0);
|
|
|
|
fd_.setDevice(&file2);
|
|
}
|
|
else
|
|
{
|
|
fd_.setDevice(&file);
|
|
}
|
|
|
|
_retry:
|
|
byteTotal = fd_.device()->size() - sizeof(fileHdr);
|
|
|
|
emit status("Reading File Header...");
|
|
emit target(0);
|
|
|
|
fd_ >> magic;
|
|
|
|
qDebug("magic = %08x", magic);
|
|
|
|
if (magic == kPcapFileMagic)
|
|
{
|
|
// Do nothing
|
|
}
|
|
else if (magic == kNanoSecondPcapFileMagic)
|
|
{
|
|
nsecResolution = true;
|
|
}
|
|
else if ((magic == kPcapFileMagicSwapped)
|
|
|| (magic == kNanoSecondPcapFileMagicSwapped))
|
|
{
|
|
// Toggle Byte order
|
|
if (fd_.byteOrder() == QDataStream::BigEndian)
|
|
fd_.setByteOrder(QDataStream::LittleEndian);
|
|
else
|
|
fd_.setByteOrder(QDataStream::BigEndian);
|
|
|
|
nsecResolution = (magic == kNanoSecondPcapFileMagicSwapped);
|
|
}
|
|
else // Not a pcap file (could be pcapng or something else)
|
|
{
|
|
if (tryConvert)
|
|
{
|
|
// Close and reopen the temp file to be safe
|
|
file2.close();
|
|
if (!file2.open())
|
|
{
|
|
error.append("Unable to open temporary file to convert to PCAP\n");
|
|
goto _err_convert2pcap;
|
|
}
|
|
fd_.setDevice(0); // disconnect data stream from file
|
|
|
|
if (convertToStandardPcap(fileName, file2.fileName(), error))
|
|
{
|
|
fd_.setDevice(&file2);
|
|
tryConvert = false;
|
|
goto _retry;
|
|
}
|
|
else
|
|
{
|
|
error = QString(tr("Unable to convert %1 to standard PCAP format"))
|
|
.arg(fileName);
|
|
goto _err_convert2pcap;
|
|
}
|
|
}
|
|
else
|
|
goto _err_bad_magic;
|
|
}
|
|
|
|
qDebug("reading filehdr");
|
|
|
|
fd_ >> fileHdr.versionMajor;
|
|
fd_ >> fileHdr.versionMinor;
|
|
fd_ >> fileHdr.thisZone;
|
|
fd_ >> fileHdr.sigfigs;
|
|
fd_ >> fileHdr.snapLen;
|
|
fd_ >> fileHdr.network;
|
|
|
|
qDebug("version check");
|
|
if ((fileHdr.versionMajor != kPcapFileVersionMajor) ||
|
|
(fileHdr.versionMinor != kPcapFileVersionMinor))
|
|
goto _err_unsupported_version;
|
|
|
|
#if 1
|
|
// XXX: we support only Ethernet, for now
|
|
if (fileHdr.network != kDltEthernet)
|
|
goto _err_unsupported_encap;
|
|
#endif
|
|
|
|
pktBuf.resize(fileHdr.snapLen);
|
|
|
|
// XXX: PDML also needs the PCAP file to cross check packet bytes
|
|
// with the PDML data, so we can't do PDML conversion any earlier
|
|
// than this
|
|
qDebug("pdml check");
|
|
if (importOptions_.value("ViaPdml").toBool())
|
|
{
|
|
QProcess tshark;
|
|
QTemporaryFile pdmlFile;
|
|
PdmlReader reader(&streams, importOptions_);
|
|
|
|
if (!pdmlFile.open())
|
|
{
|
|
error.append("Unable to open temporary file to create PDML\n");
|
|
goto _non_pdml;
|
|
}
|
|
|
|
qDebug("generating PDML %s", qPrintable(pdmlFile.fileName()));
|
|
emit status("Generating PDML...");
|
|
emit target(0);
|
|
|
|
tshark.setStandardOutputFile(pdmlFile.fileName());
|
|
tshark.start(OstProtoLib::tsharkPath(),
|
|
QStringList()
|
|
<< QString("-r%1").arg(fileName)
|
|
<< "-otcp.desegment_tcp_streams:FALSE"
|
|
<< "-Tpdml");
|
|
if (!tshark.waitForStarted(-1))
|
|
{
|
|
error.append(QString("Unable to start tshark. Check path in preferences.\n"));
|
|
goto _non_pdml;
|
|
}
|
|
|
|
if (!tshark.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running tshark\n"));
|
|
goto _non_pdml;
|
|
}
|
|
|
|
connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int)));
|
|
|
|
emit status("Reading PDML packets...");
|
|
emit target(100); // in percentage
|
|
|
|
// pdml reader needs pcap, so pass self
|
|
isOk = reader.read(&pdmlFile, this, &stop_);
|
|
|
|
if (stop_)
|
|
goto _user_cancel;
|
|
|
|
if (!isOk)
|
|
{
|
|
error.append(QString("Error processing PDML (%1, %2): %3\n")
|
|
.arg(reader.lineNumber())
|
|
.arg(reader.columnNumber())
|
|
.arg(reader.errorString()));
|
|
goto _exit;
|
|
}
|
|
|
|
if (!importOptions_.value("DoDiff").toBool())
|
|
goto _exit;
|
|
|
|
|
|
// !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!
|
|
// Let's do the diff ...
|
|
// !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!
|
|
|
|
QProcess awk;
|
|
QProcess diff;
|
|
QTemporaryFile originalTextFile;
|
|
QTemporaryFile importedPcapFile;
|
|
QTemporaryFile importedTextFile;
|
|
QTemporaryFile diffFile;
|
|
const QString kAwkFilter =
|
|
"/^[^0]/ { "
|
|
"printf \" %s \", $1;"
|
|
"for (i=4; i<NF; i++) printf \"%s \", $i;"
|
|
"next;"
|
|
"}"
|
|
"// {print}";
|
|
|
|
// Convert original file to text ...
|
|
if (!originalTextFile.open())
|
|
{
|
|
error.append("Unable to open temporary file to create text file "
|
|
"(original) for diff\n");
|
|
goto _diff_fail;
|
|
}
|
|
qDebug("generating text file (original) %s",
|
|
qPrintable(originalTextFile.fileName()));
|
|
|
|
emit status("Preparing original PCAP for diff...");
|
|
emit target(0);
|
|
|
|
tshark.setStandardOutputProcess(&awk);
|
|
awk.setStandardOutputFile(originalTextFile.fileName());
|
|
tshark.start(OstProtoLib::tsharkPath(),
|
|
QStringList()
|
|
<< QString("-r%1").arg(fileName)
|
|
<< "-otcp.desegment_tcp_streams:FALSE"
|
|
<< "-P"
|
|
<< "-x");
|
|
if (!tshark.waitForStarted(-1))
|
|
{
|
|
error.append(QString("Unable to start tshark. Check path in Preferences.\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
awk.start(OstProtoLib::awkPath(),
|
|
QStringList() << kAwkFilter);
|
|
if (!awk.waitForStarted(-1))
|
|
{
|
|
tshark.kill();
|
|
error.append(QString("Unable to start awk. Check path in Preferences.\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
if (!tshark.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running tshark\n"));
|
|
goto _diff_fail;
|
|
}
|
|
if (!awk.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running awk\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
// Save imported file as PCAP
|
|
if (!importedPcapFile.open())
|
|
{
|
|
error.append("Unable to open temporary file to create pcap file "
|
|
"from imported streams for diff\n");
|
|
goto _diff_fail;
|
|
}
|
|
|
|
if (!save(streams, importedPcapFile.fileName(), error))
|
|
{
|
|
error.append("Error saving imported streams as PCAP for diff");
|
|
goto _diff_fail;
|
|
}
|
|
|
|
// Convert imported file to text ...
|
|
if (!importedTextFile.open())
|
|
{
|
|
error.append("Unable to open temporary file to create text file "
|
|
"(imported) for diff\n");
|
|
goto _diff_fail;
|
|
}
|
|
qDebug("generating text file (imported) %s",
|
|
qPrintable(importedTextFile.fileName()));
|
|
|
|
emit status("Preparing imported PCAP for diff...");
|
|
emit target(0);
|
|
|
|
tshark.setStandardOutputProcess(&awk);
|
|
awk.setStandardOutputFile(importedTextFile.fileName());
|
|
tshark.start(OstProtoLib::tsharkPath(),
|
|
QStringList()
|
|
<< QString("-r%1").arg(importedPcapFile.fileName())
|
|
<< "-otcp.desegment_tcp_streams:FALSE"
|
|
<< "-P"
|
|
<< "-x");
|
|
if (!tshark.waitForStarted(-1))
|
|
{
|
|
error.append(QString("Unable to start tshark. Check path in Preferences.\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
awk.start(OstProtoLib::awkPath(),
|
|
QStringList() << kAwkFilter);
|
|
if (!awk.waitForStarted(-1))
|
|
{
|
|
tshark.kill();
|
|
error.append(QString("Unable to start awk. Check path in Preferences.\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
if (!tshark.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running tshark\n"));
|
|
goto _diff_fail;
|
|
}
|
|
if (!awk.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running awk\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
// Now do the diff of the two text files ...
|
|
if (!diffFile.open())
|
|
{
|
|
error.append("Unable to open temporary file to store diff\n");
|
|
goto _diff_fail;
|
|
}
|
|
qDebug("diffing %s and %s > %s",
|
|
qPrintable(originalTextFile.fileName()),
|
|
qPrintable(importedTextFile.fileName()),
|
|
qPrintable(diffFile.fileName()));
|
|
|
|
emit status("Taking diff...");
|
|
emit target(0);
|
|
|
|
diff.setStandardOutputFile(diffFile.fileName());
|
|
diff.start(OstProtoLib::diffPath(),
|
|
QStringList()
|
|
<< "-u"
|
|
<< "-F^ [1-9]"
|
|
<< QString("--label=%1 (actual)")
|
|
.arg(QFileInfo(fileName).fileName())
|
|
<< QString("--label=%1 (imported)")
|
|
.arg(QFileInfo(fileName).fileName())
|
|
<< originalTextFile.fileName()
|
|
<< importedTextFile.fileName());
|
|
if (!diff.waitForStarted(-1))
|
|
{
|
|
error.append(QString("Unable to start diff. Check path in Preferences.\n")
|
|
.arg(diff.exitCode()));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
if (!diff.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running diff\n"));
|
|
goto _diff_fail;
|
|
}
|
|
|
|
diffFile.close();
|
|
if (diffFile.size())
|
|
{
|
|
error.append(tr("<p>There is a diff between the original and imported streams. See details to review the diff.</p><p>Why a diff? See <a href='%1'>possible reasons</a>.</p>\n\n\n\n").arg("https://jump.ostinato.org/pcapdiff"));
|
|
diffFile.open();
|
|
diffFile.seek(0);
|
|
error.append(QString(diffFile.readAll()));
|
|
}
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
_non_pdml:
|
|
qDebug("pcap resolution: %s", nsecResolution ? "nsec" : "usec");
|
|
emit status("Reading Packets...");
|
|
emit target(100); // in percentage
|
|
pktCount = 1;
|
|
while (!fd_.atEnd())
|
|
{
|
|
OstProto::Stream *stream = streams.add_stream();
|
|
OstProto::Protocol *proto = stream->add_protocol();
|
|
OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump);
|
|
|
|
proto->mutable_protocol_id()->set_id(
|
|
OstProto::Protocol::kHexDumpFieldNumber);
|
|
|
|
readPacket(pktHdr, pktBuf);
|
|
|
|
// validations on inclLen <= origLen && inclLen <= snapLen
|
|
Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if
|
|
|
|
hexDump->set_content(pktBuf.data(), pktHdr.inclLen);
|
|
hexDump->set_pad_until_end(false);
|
|
|
|
stream->mutable_stream_id()->set_id(pktCount);
|
|
stream->mutable_core()->set_is_enabled(true);
|
|
stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS
|
|
|
|
stream->mutable_control()->set_num_packets(1);
|
|
|
|
// setup packet rate to the timing in pcap (as close as possible)
|
|
// use quint64 rather than double to store micro/nano second as
|
|
// it has a larger range (~580 years) and therefore better accuracy
|
|
const quint64 kXsecsInSec = nsecResolution ? 1e9 : 1e6;
|
|
quint64 xsec = (pktHdr.tsSec*kXsecsInSec + pktHdr.tsUsec);
|
|
quint64 delta = xsec - lastXsec;
|
|
qDebug("pktCount = %d, delta = %llu", pktCount, delta);
|
|
|
|
if ((pktCount != 1) && delta)
|
|
stream->mutable_control()->set_packets_per_sec(double(kXsecsInSec)/delta);
|
|
|
|
if (prevStream)
|
|
prevStream->mutable_control()->CopyFrom(stream->control());
|
|
|
|
lastXsec = xsec;
|
|
prevStream = stream;
|
|
pktCount++;
|
|
byteCount += pktHdr.inclLen + sizeof(pktHdr);
|
|
emit progress(int(byteCount*100/byteTotal)); // in percentage
|
|
if (stop_)
|
|
goto _user_cancel;
|
|
}
|
|
|
|
isOk = true;
|
|
goto _exit;
|
|
|
|
_user_cancel:
|
|
isOk = true;
|
|
goto _exit;
|
|
|
|
_diff_fail:
|
|
goto _exit;
|
|
|
|
_err_unsupported_encap:
|
|
error = QString(tr("%1 has non-ethernet encapsulation (%2) which is "
|
|
"not supported - Sorry!"))
|
|
.arg(QFileInfo(fileName).fileName()).arg(fileHdr.network);
|
|
goto _exit;
|
|
|
|
_err_unsupported_version:
|
|
error = QString(tr("%1 is in PCAP version %2.%3 format which is "
|
|
"not supported - Sorry!"))
|
|
.arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor);
|
|
goto _exit;
|
|
|
|
_err_bad_magic:
|
|
error = QString(tr("%1 is not a valid PCAP file")).arg(fileName);
|
|
goto _exit;
|
|
|
|
#if 0
|
|
_err_truncated:
|
|
error = QString(tr("%1 is too short")).arg(fileName);
|
|
goto _exit;
|
|
#endif
|
|
|
|
_err_unzip_fail:
|
|
goto _exit;
|
|
|
|
_err_reading_magic:
|
|
error = QString(tr("Unable to read magic from %1")).arg(fileName);
|
|
goto _exit;
|
|
|
|
_err_convert2pcap:
|
|
goto _exit;
|
|
|
|
_err_open:
|
|
error = QString(tr("Unable to open file: %1")).arg(fileName);
|
|
goto _exit;
|
|
|
|
_exit:
|
|
if (!error.isEmpty())
|
|
qDebug("%s", qPrintable(error));
|
|
file.close();
|
|
return isOk;
|
|
}
|
|
|
|
/*!
|
|
Converts a non-PCAP capture file to standard PCAP file format using tshark
|
|
|
|
Returns true if conversion was successful, false otherwise.
|
|
*/
|
|
bool PcapFileFormat::convertToStandardPcap(
|
|
QString fileName, QString outputFileName, QString &error)
|
|
{
|
|
qDebug("converting to PCAP %s", qPrintable(outputFileName));
|
|
emit status("Unsupported format. Converting to standard PCAP format...");
|
|
emit target(0);
|
|
|
|
QProcess tshark;
|
|
tshark.start(OstProtoLib::tsharkPath(),
|
|
QStringList()
|
|
<< QString("-r%1").arg(fileName)
|
|
<< "-Fnsecpcap"
|
|
<< QString("-w%1").arg(outputFileName));
|
|
if (!tshark.waitForStarted(-1))
|
|
{
|
|
error.append(QString("Unable to start tshark. Check path in preferences.\n"));
|
|
return false;
|
|
}
|
|
|
|
if (!tshark.waitForFinished(-1))
|
|
{
|
|
error.append(QString("Error running tshark\n"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
Reads packet meta data into pktHdr and packet content into buf.
|
|
|
|
Returns true if packet is read successfully, false otherwise.
|
|
*/
|
|
bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf)
|
|
{
|
|
quint32 len;
|
|
|
|
// TODO: chk fd_.status()
|
|
|
|
// read PcapPacketHeader
|
|
fd_ >> pktHdr.tsSec;
|
|
fd_ >> pktHdr.tsUsec;
|
|
fd_ >> pktHdr.inclLen;
|
|
fd_ >> pktHdr.origLen;
|
|
|
|
// TODO: chk fd_.status()
|
|
|
|
// XXX: should never be required, but we play safe
|
|
if (quint32(pktBuf.size()) < pktHdr.inclLen)
|
|
pktBuf.resize(pktHdr.inclLen);
|
|
|
|
// read Pkt contents
|
|
len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while?
|
|
|
|
Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert
|
|
pktBuf.resize(len);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PcapFileFormat::save(const OstProto::StreamConfigList streams,
|
|
const QString fileName, QString &error)
|
|
{
|
|
bool isOk = false;
|
|
QFile file(fileName);
|
|
PcapFileHeader fileHdr;
|
|
PcapPacketHeader pktHdr;
|
|
QByteArray pktBuf;
|
|
|
|
if (!file.open(QIODevice::WriteOnly))
|
|
goto _err_open;
|
|
|
|
fd_.setDevice(&file);
|
|
|
|
fileHdr.magicNumber = kNanoSecondPcapFileMagic;
|
|
fileHdr.versionMajor = kPcapFileVersionMajor;
|
|
fileHdr.versionMinor = kPcapFileVersionMinor;
|
|
fileHdr.thisZone = 0;
|
|
fileHdr.sigfigs = 0;
|
|
fileHdr.snapLen = kMaxSnapLen;
|
|
fileHdr.network = kDltEthernet;
|
|
|
|
fd_ << fileHdr.magicNumber;
|
|
fd_ << fileHdr.versionMajor;
|
|
fd_ << fileHdr.versionMinor;
|
|
fd_ << fileHdr.thisZone;
|
|
fd_ << fileHdr.sigfigs;
|
|
fd_ << fileHdr.snapLen;
|
|
fd_ << fileHdr.network;
|
|
|
|
pktBuf.resize(kMaxSnapLen);
|
|
|
|
emit status("Writing Packets...");
|
|
emit target(streams.stream_size());
|
|
|
|
pktHdr.tsSec = 0;
|
|
pktHdr.tsUsec = 0;
|
|
for (int i = 0; i < streams.stream_size(); i++)
|
|
{
|
|
StreamBase s;
|
|
|
|
s.setId(i);
|
|
s.protoDataCopyFrom(streams.stream(i));
|
|
// TODO: expand frameIndex for each stream
|
|
s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0);
|
|
|
|
pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0
|
|
pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding
|
|
|
|
qDebug("savepcap i=%d, incl/orig len = %d/%d", i,
|
|
pktHdr.inclLen, pktHdr.origLen);
|
|
|
|
if (pktHdr.inclLen > fileHdr.snapLen)
|
|
pktHdr.inclLen = fileHdr.snapLen;
|
|
|
|
fd_ << pktHdr.tsSec;
|
|
fd_ << pktHdr.tsUsec;
|
|
fd_ << pktHdr.inclLen;
|
|
fd_ << pktHdr.origLen;
|
|
fd_.writeRawData(pktBuf.data(), pktHdr.inclLen);
|
|
|
|
if (s.packetRate())
|
|
{
|
|
quint64 delta = quint64(1e9/s.packetRate());
|
|
pktHdr.tsSec += delta/quint32(1e9);
|
|
pktHdr.tsUsec += delta % quint32(1e9);
|
|
}
|
|
|
|
if (pktHdr.tsUsec >= quint32(1e9))
|
|
{
|
|
pktHdr.tsSec++;
|
|
pktHdr.tsUsec -= quint32(1e9);
|
|
}
|
|
|
|
emit progress(i);
|
|
}
|
|
|
|
file.close();
|
|
|
|
isOk = true;
|
|
goto _exit;
|
|
|
|
_err_open:
|
|
error = QString(tr("Unable to open file: %1")).arg(fileName);
|
|
goto _exit;
|
|
|
|
_exit:
|
|
return isOk;
|
|
}
|
|
|
|
QDialog* PcapFileFormat::openOptionsDialog()
|
|
{
|
|
return new PcapImportOptionsDialog(&importOptions_);
|
|
}
|
|
|
|
bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/)
|
|
{
|
|
// TODO
|
|
return true;
|
|
}
|
|
|
|
bool PcapFileFormat::isMyFileType(const QString fileType)
|
|
{
|
|
if (fileType.startsWith("PCAP"))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|