ostinato/common/pcapfileformat.cpp

366 lines
9.3 KiB
C++
Raw Normal View History

2011-02-18 12:02:57 -06:00
/*
Copyright (C) 2011 Srivats P.
2011-02-18 12:02:57 -06:00
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 "pdml_p.h"
2011-02-18 12:02:57 -06:00
#include "streambase.h"
#include "hexdump.pb.h"
#include <QDataStream>
#include <QFile>
#include <QProcess>
#include <QTemporaryFile>
2011-02-18 12:02:57 -06:00
#include <QtGlobal>
static inline quint32 swap32(quint32 val)
{
return (((val >> 24) && 0x000000FF) |
((val >> 16) && 0x0000FF00) |
((val << 16) && 0x00FF0000) |
((val << 24) && 0xFF000000));
}
static inline quint16 swap16(quint16 val)
{
return (((val >> 8) && 0x00FF) |
((val << 8) && 0xFF00));
}
const quint32 kPcapFileMagic = 0xa1b2c3d4;
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
const quint16 kPcapFileVersionMajor = 2;
const quint16 kPcapFileVersionMinor = 4;
const quint32 kMaxSnapLen = 65535;
const quint32 kDltEthernet = 1;
2011-02-18 12:02:57 -06:00
PcapFileFormat pcapFileFormat;
PcapFileFormat::PcapFileFormat()
{
}
PcapFileFormat::~PcapFileFormat()
{
}
bool PcapFileFormat::openStreams(const QString fileName,
OstProto::StreamConfigList &streams, QString &error)
{
bool viaPdml = true; // TODO: shd be a param to function
2011-02-18 12:02:57 -06:00
bool isOk = false;
int pktCount;
2011-02-18 12:02:57 -06:00
QFile file(fileName);
QTemporaryFile file2;
2011-02-18 12:02:57 -06:00
quint32 magic;
uchar gzipMagic[2];
int len;
2011-02-18 12:02:57 -06:00
PcapFileHeader fileHdr;
PcapPacketHeader pktHdr;
QByteArray pktBuf;
if (!file.open(QIODevice::ReadOnly))
goto _err_open;
len = file.peek((char*)gzipMagic, sizeof(gzipMagic));
if (len < int(sizeof(gzipMagic)))
goto _err_reading_magic;
2011-02-18 12:02:57 -06:00
if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b))
{
QProcess gzip;
if (!file2.open())
{
error.append("Unable to open temporary file to uncompress .gz\n");
goto _err_unzip_fail;
}
qDebug("decompressing to %s", file2.fileName().toAscii().constData());
gzip.setStandardOutputFile(file2.fileName());
// FIXME: hardcoded prog name
gzip.start("C:/Program Files/CmdLineTools/gzip.exe",
QStringList()
<< "-d"
<< "-c"
<< fileName);
if (!gzip.waitForStarted(-1))
{
error.append(QString("Unable to start gzip\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);
}
2011-02-18 12:02:57 -06:00
fd_ >> magic;
2011-02-18 12:02:57 -06:00
qDebug("magic = %08x", magic);
2011-02-18 12:02:57 -06:00
if (magic == kPcapFileMagicSwapped)
{
// Toggle Byte order
if (fd_.byteOrder() == QDataStream::BigEndian)
fd_.setByteOrder(QDataStream::LittleEndian);
2011-02-18 12:02:57 -06:00
else
fd_.setByteOrder(QDataStream::BigEndian);
2011-02-18 12:02:57 -06:00
}
else if (magic != kPcapFileMagic)
goto _err_bad_magic;
fd_ >> fileHdr.versionMajor;
fd_ >> fileHdr.versionMinor;
fd_ >> fileHdr.thisZone;
fd_ >> fileHdr.sigfigs;
fd_ >> fileHdr.snapLen;
fd_ >> fileHdr.network;
2011-02-18 12:02:57 -06:00
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
2011-02-18 12:02:57 -06:00
pktBuf.resize(fileHdr.snapLen);
if (viaPdml)
{
QTemporaryFile pdmlFile;
PdmlReader reader(&streams);
QProcess tshark;
if (!pdmlFile.open())
{
error.append("Unable to open temporary file to create PDML\n");
goto _non_pdml;
}
qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData());
tshark.setStandardOutputFile(pdmlFile.fileName());
// FIXME: hardcoded prog name
tshark.start("C:/Program Files/Wireshark/Tshark.exe",
QStringList()
<< QString("-r%1").arg(fileName)
<< "-Tpdml");
if (!tshark.waitForStarted(-1))
{
error.append(QString("Unable to start tshark\n"));
goto _non_pdml;
}
if (!tshark.waitForFinished(-1))
{
error.append(QString("Error running tshark\n"));
goto _non_pdml;
}
isOk = reader.read(&pdmlFile, this); // TODO: pass error string?
goto _exit;
}
_non_pdml:
pktCount = 1;
while (!fd_.atEnd())
2011-02-18 12:02:57 -06:00
{
OstProto::Stream *stream = streams.add_stream();
OstProto::Protocol *proto = stream->add_protocol();
OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump);
stream->mutable_stream_id()->set_id(pktCount);
stream->mutable_core()->set_is_enabled(true);
proto->mutable_protocol_id()->set_id(
OstProto::Protocol::kHexDumpFieldNumber);
readPacket(pktHdr, pktBuf);
2011-02-18 12:02:57 -06:00
// 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_core()->set_frame_len(pktHdr.inclLen+4); // FCS
pktCount++;
}
isOk = true;
goto _exit;
#if 1
_err_unsupported_encap:
error = QString(tr("%1 has non-ethernet encapsulation (%2) which is "
"not supported - Sorry!"))
.arg(fileName).arg(fileHdr.network);
goto _exit;
#endif
2011-02-18 12:02:57 -06:00
_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
2011-02-18 12:02:57 -06:00
_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;
2011-02-18 12:02:57 -06:00
_err_open:
error = QString(tr("Unable to open file: %1")).arg(fileName);
goto _exit;
_exit:
file.close();
2011-02-18 12:02:57 -06:00
return isOk;
}
/*!
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;
}
2011-02-18 12:02:57 -06:00
bool PcapFileFormat::saveStreams(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);
2011-02-18 12:02:57 -06:00
fileHdr.magicNumber = kPcapFileMagic;
fileHdr.versionMajor = kPcapFileVersionMajor;
fileHdr.versionMinor = kPcapFileVersionMinor;
fileHdr.thisZone = 0;
fileHdr.sigfigs = 0;
fileHdr.snapLen = kMaxSnapLen;
fileHdr.network = kDltEthernet;
2011-02-18 12:02:57 -06:00
fd_ << fileHdr.magicNumber;
fd_ << fileHdr.versionMajor;
fd_ << fileHdr.versionMinor;
fd_ << fileHdr.thisZone;
fd_ << fileHdr.sigfigs;
fd_ << fileHdr.snapLen;
fd_ << fileHdr.network;
2011-02-18 12:02:57 -06:00
pktBuf.resize(kMaxSnapLen);
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);
// TODO: write actual timing!?!?
pktHdr.tsSec = 0;
pktHdr.tsUsec = 0;
pktHdr.inclLen = pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding
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);
2011-02-18 12:02:57 -06:00
}
file.close();
isOk = true;
goto _exit;
_err_open:
error = QString(tr("Unable to open file: %1")).arg(fileName);
goto _exit;
_exit:
return isOk;
}