4c5b8faff7
- UserScript Protocol reworked. Needs more testing. May need a significant rewrite in the future, but for now this will have to do - therefore it has been marked "EXPERIMENTAL" for now
542 lines
13 KiB
C++
542 lines
13 KiB
C++
#include <qendian.h>
|
|
|
|
#include "abstractprotocol.h"
|
|
#include "streambase.h"
|
|
#include "protocollistiterator.h"
|
|
|
|
/*!
|
|
\class AbstractProtocol
|
|
|
|
\todo (MED) update AbstractProtocol documentation
|
|
Bare Minimum set of methods that a subclass needs to reimplement
|
|
- protoDataCopyInto() [pure virtual]
|
|
- protoDataCopyFrom() [pure virtual]
|
|
- fieldCount()
|
|
|
|
Any useful protocol should also provide implementations for
|
|
- name()
|
|
- shortName()
|
|
- fieldName()
|
|
|
|
Protocols with meta fields should additionally implement
|
|
- metaFieldCount()
|
|
- isMetaField()
|
|
*/
|
|
AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent)
|
|
{
|
|
//qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next);
|
|
mpStream = stream;
|
|
this->parent = parent;
|
|
prev = next = NULL;
|
|
metaCount = -1;
|
|
protoSize = -1;
|
|
}
|
|
|
|
AbstractProtocol::~AbstractProtocol()
|
|
{
|
|
}
|
|
|
|
AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream,
|
|
AbstractProtocol *parent)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
quint32 AbstractProtocol::protocolNumber() const
|
|
{
|
|
qFatal("Something wrong!!!");
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
/*!
|
|
\fn virtual void protoDataCopyInto(OstProto::OstProto::StreamCore &stream) = 0;
|
|
|
|
Copy the protocol's protobuf into the passed in stream \n
|
|
In the base class this is a pure virtual function. Subclasses should
|
|
implement this function by using - \n
|
|
stream.AddExtension(<ExtId>)->CopyFrom(<protobuf_data>) */
|
|
|
|
/*
|
|
\fn virtual void protoDataCopyFrom(const OstProto::OstProto::StreamCore &stream) = 0;
|
|
*/
|
|
|
|
/*! Returns the full name of the protocol \n
|
|
The default implementation returns a null string */
|
|
QString AbstractProtocol::name() const
|
|
{
|
|
return QString();
|
|
}
|
|
|
|
/*! Returns the short name or abbreviation of the protocol \n
|
|
The default implementation forms and returns a abbreviation composed
|
|
of all the upper case chars in name() \n
|
|
The default implementation caches the abbreviation on its first invocation
|
|
and subsequently returns the cached abbreviation */
|
|
QString AbstractProtocol::shortName() const
|
|
{
|
|
if (protoAbbr.isNull())
|
|
{
|
|
QString abbr;
|
|
|
|
for (int i = 0; i < name().size(); i++)
|
|
if (name().at(i).isUpper()) abbr.append(name().at(i));
|
|
|
|
if (abbr.size())
|
|
protoAbbr = abbr;
|
|
else
|
|
protoAbbr = QString("");
|
|
}
|
|
|
|
return protoAbbr;
|
|
}
|
|
|
|
/*! Returns the number of fields (both Frame and Meta fields) \n
|
|
The default implementation returns zero */
|
|
int AbstractProtocol::fieldCount() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*! Returns the number of meta fields \n
|
|
The default implementation counts and returns the number of fields for which
|
|
the FieldIsMeta flag is set\n
|
|
The default implementation caches the count on its first invocation
|
|
and subsequently returns the cached count */
|
|
int AbstractProtocol::metaFieldCount() const
|
|
{
|
|
if (metaCount < 0)
|
|
{
|
|
int c = 0;
|
|
for (int i = 0; i < fieldCount() ; i++)
|
|
if (fieldFlags(i).testFlag(FieldIsMeta))
|
|
c++;
|
|
metaCount = c;
|
|
}
|
|
|
|
return metaCount;
|
|
}
|
|
|
|
/*! Returns the number of frame fields \n
|
|
Convenience method - same as fieldCount() minus metaFieldCount() */
|
|
int AbstractProtocol::frameFieldCount() const
|
|
{
|
|
//qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount());
|
|
return (fieldCount() - metaFieldCount());
|
|
}
|
|
|
|
AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const
|
|
{
|
|
return FieldIsNormal;
|
|
}
|
|
|
|
/*! Returns the requested field attribute data \n
|
|
Protocols which have meta fields that vary a frame field across
|
|
streams may use the streamIndex to return the appropriate field value \n
|
|
Some field attriubutes e.g. FieldName may be invariant across streams\n
|
|
The default implementation returns the fieldValue() converted to string
|
|
The FieldTextValue attribute may include additional information about
|
|
the field's value e.g. a checksum field may include "(correct)" or
|
|
"(incorrect)" alongwith the actual checksum value. \n
|
|
|
|
The default implementation returns a empty string for FieldName and
|
|
FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for
|
|
FieldValue; subclasses are expected to return meaning values for all
|
|
these attributes. The only exception is the 'FieldBitSize' attribute -
|
|
the default implementation takes the (byte) size of FieldFrameValue,
|
|
multiplies it with 8 and returns the result - this can be used by
|
|
subclasses for fields which are an integral multiple of bytes; for
|
|
fields whose size are a non-integral multiple of bytes or smaller than
|
|
a byte, subclasses should return the correct value. Also for fields
|
|
which represent checksums, subclasses should return a value for
|
|
FieldBitSize - even if it is an integral multiple of bytes
|
|
|
|
\note If a subclass uses any of the below functions to derive
|
|
FieldFrameValue, the subclass should handle and return a value for
|
|
FieldBitSize to prevent endless recrusion -
|
|
- protocolFrameCksum()
|
|
- protocolFramePayloadSize()
|
|
*/
|
|
QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib,
|
|
int streamIndex) const
|
|
{
|
|
switch (attrib)
|
|
{
|
|
case FieldName:
|
|
return QString();
|
|
case FieldBitSize:
|
|
Q_ASSERT_X(!fieldFlags(index).testFlag(FieldIsCksum),
|
|
"AbstractProtocol::fieldData()",
|
|
"FieldBitSize for checksum fields need to be handled by the subclass");
|
|
return fieldData(index, FieldFrameValue, streamIndex).
|
|
toByteArray().size() * 8;
|
|
case FieldValue:
|
|
return 0;
|
|
case FieldFrameValue:
|
|
return QByteArray();
|
|
case FieldTextValue:
|
|
return QString();
|
|
|
|
default:
|
|
qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__,
|
|
attrib);
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
/*! Sets the value of a field corresponding to index \n
|
|
Returns true if field is successfully set, false otherwise \n
|
|
The default implementation always returns false */
|
|
bool AbstractProtocol::setFieldData(int index, const QVariant &value,
|
|
FieldAttrib attrib)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const
|
|
{
|
|
return ProtocolIdNone;
|
|
}
|
|
|
|
quint32 AbstractProtocol::protocolId(ProtocolIdType type) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const
|
|
{
|
|
quint32 id;
|
|
|
|
if (next)
|
|
id = next->protocolId(type);
|
|
else if (parent)
|
|
id = parent->payloadProtocolId(type);
|
|
else
|
|
id = 0xFFFFFFFF;
|
|
|
|
qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id);
|
|
return id;
|
|
}
|
|
|
|
int AbstractProtocol::protocolFrameSize(int streamIndex) const
|
|
{
|
|
if (protoSize < 0)
|
|
{
|
|
int bitsize = 0;
|
|
|
|
for (int i = 0; i < fieldCount(); i++)
|
|
{
|
|
if (!fieldFlags(i).testFlag(FieldIsMeta))
|
|
bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt();
|
|
}
|
|
protoSize = (bitsize+7)/8;
|
|
}
|
|
|
|
qDebug("%s: protoSize = %d", __FUNCTION__, protoSize);
|
|
return protoSize;
|
|
}
|
|
|
|
int AbstractProtocol::protocolFrameOffset(int streamIndex) const
|
|
{
|
|
int size = 0;
|
|
AbstractProtocol *p = prev;
|
|
while (p)
|
|
{
|
|
size += p->protocolFrameSize(streamIndex);
|
|
p = p->prev;
|
|
}
|
|
|
|
if (parent)
|
|
size += parent->protocolFrameOffset(streamIndex);
|
|
|
|
qDebug("%s: ofs = %d", __FUNCTION__, size);
|
|
return size;
|
|
}
|
|
|
|
int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const
|
|
{
|
|
int size = 0;
|
|
AbstractProtocol *p = next;
|
|
while (p)
|
|
{
|
|
size += p->protocolFrameSize(streamIndex);
|
|
p = p->next;
|
|
}
|
|
if (parent)
|
|
size += parent->protocolFramePayloadSize(streamIndex);
|
|
|
|
qDebug("%s: payloadSize = %d", __FUNCTION__, size);
|
|
return size;
|
|
}
|
|
|
|
|
|
/*! Returns a byte array encoding the protocol (and its fields) which can be
|
|
inserted into the stream's frame
|
|
The default implementation forms and returns an ordered concatenation of
|
|
the FrameValue of all the 'frame' fields of the protocol taking care of fields
|
|
which are not an integral number of bytes\n */
|
|
QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const
|
|
{
|
|
QByteArray proto, field;
|
|
uint bits, lastbitpos = 0;
|
|
FieldFlags flags;
|
|
|
|
for (int i=0; i < fieldCount() ; i++)
|
|
{
|
|
flags = fieldFlags(i);
|
|
if (!flags.testFlag(FieldIsMeta))
|
|
{
|
|
bits = fieldData(i, FieldBitSize, streamIndex).toUInt();
|
|
if (bits == 0)
|
|
continue;
|
|
Q_ASSERT(bits > 0);
|
|
|
|
if (forCksum && flags.testFlag(FieldIsCksum))
|
|
{
|
|
field.resize((bits+7)/8);
|
|
field.fill('\0');
|
|
}
|
|
else
|
|
field = fieldData(i, FieldFrameValue, streamIndex).toByteArray();
|
|
qDebug("<<< %d, %d/%d >>>>", proto.size(), bits, field.size());
|
|
|
|
if (bits == (uint) field.size() * 8)
|
|
{
|
|
if (lastbitpos == 0)
|
|
proto.append(field);
|
|
else
|
|
{
|
|
Q_ASSERT(field.size() > 0);
|
|
|
|
char c = proto[proto.size() - 1];
|
|
proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos);
|
|
for (int j = 0; j < field.size() - 1; j++)
|
|
proto.append(field.at(j) << lastbitpos |
|
|
field.at(j+1) >> lastbitpos);
|
|
}
|
|
}
|
|
else if (bits < (uint) field.size() * 8)
|
|
{
|
|
uchar c;
|
|
uint v;
|
|
|
|
v = (field.size()*8) - bits;
|
|
|
|
Q_ASSERT(v < 8);
|
|
|
|
if (lastbitpos == 0)
|
|
{
|
|
for (int j = 0; j < field.size(); j++)
|
|
{
|
|
c = field.at(j) << v;
|
|
if ((j+1) < field.size())
|
|
c |= ((uchar)field.at(j+1) >> (8-v));
|
|
proto.append(c);
|
|
}
|
|
|
|
lastbitpos = (lastbitpos + bits) % 8;
|
|
}
|
|
else
|
|
{
|
|
Q_ASSERT(proto.size() > 0);
|
|
|
|
for (int j = 0; j < field.size(); j++)
|
|
{
|
|
uchar d;
|
|
|
|
c = field.at(j) << v;
|
|
if ((j+1) < field.size())
|
|
c |= ((uchar) field.at(j+1) >> (8-v));
|
|
d = proto[proto.size() - 1];
|
|
proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos);
|
|
if (bits > (8*j + (8 - v)))
|
|
proto.append(c << (8-lastbitpos));
|
|
}
|
|
|
|
lastbitpos = (lastbitpos + bits) % 8;
|
|
}
|
|
}
|
|
else // if (bits > field.size() * 8)
|
|
{
|
|
qFatal("bitsize more than FrameValue size. skipping...");
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return proto;
|
|
}
|
|
|
|
bool AbstractProtocol::isProtocolFrameValueVariable() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool AbstractProtocol::isProtocolFrameSizeVariable() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool AbstractProtocol::isProtocolFramePayloadValueVariable() const
|
|
{
|
|
AbstractProtocol *p = next;
|
|
|
|
while (p)
|
|
{
|
|
if (p->isProtocolFrameValueVariable())
|
|
return true;
|
|
p = p->next;
|
|
}
|
|
if (parent && parent->isProtocolFramePayloadValueVariable())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const
|
|
{
|
|
AbstractProtocol *p = next;
|
|
|
|
while (p)
|
|
{
|
|
if (p->isProtocolFrameSizeVariable())
|
|
return true;
|
|
p = p->next;
|
|
}
|
|
if (parent && parent->isProtocolFramePayloadSizeVariable())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*!
|
|
\note If a subclass uses protocolFrameCksum() from within fieldData() to
|
|
derive a cksum field, it MUST handle and return the 'FieldBitSize'
|
|
attribute also for that particular field instead of using the default
|
|
AbstractProtocol implementation for 'FieldBitSize' - this is required
|
|
to prevent infinite recursion
|
|
*/
|
|
quint32 AbstractProtocol::protocolFrameCksum(int streamIndex,
|
|
CksumType cksumType) const
|
|
{
|
|
static int recursionCount = 0;
|
|
quint32 cksum = 0xFFFFFFFF;
|
|
|
|
recursionCount++;
|
|
Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?");
|
|
|
|
switch(cksumType)
|
|
{
|
|
case CksumIp:
|
|
{
|
|
QByteArray fv;
|
|
quint16 *ip;
|
|
quint32 len, sum = 0;
|
|
|
|
fv = protocolFrameValue(streamIndex, true);
|
|
ip = (quint16*) fv.constData();
|
|
len = fv.size();
|
|
|
|
while(len > 1)
|
|
{
|
|
sum += *ip;
|
|
if(sum & 0x80000000)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
ip++;
|
|
len -= 2;
|
|
}
|
|
|
|
if (len)
|
|
sum += (unsigned short) *(unsigned char *)ip;
|
|
|
|
while(sum>>16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
cksum = qFromBigEndian((quint16) ~sum);
|
|
break;
|
|
}
|
|
|
|
case CksumTcpUdp:
|
|
{
|
|
quint16 cks;
|
|
quint32 sum = 0;
|
|
|
|
cks = protocolFrameCksum(streamIndex, CksumIp);
|
|
sum += (quint16) ~cks;
|
|
cks = protocolFramePayloadCksum(streamIndex, CksumIp);
|
|
sum += (quint16) ~cks;
|
|
cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo);
|
|
sum += (quint16) ~cks;
|
|
|
|
while(sum>>16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
cksum = (~sum) & 0xFFFF;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
recursionCount--;
|
|
return cksum;
|
|
}
|
|
|
|
quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex,
|
|
CksumType cksumType) const
|
|
{
|
|
quint32 sum = 0;
|
|
quint16 cksum;
|
|
AbstractProtocol *p = prev;
|
|
|
|
Q_ASSERT(cksumType == CksumIpPseudo);
|
|
|
|
while (p)
|
|
{
|
|
cksum = p->protocolFrameCksum(streamIndex, cksumType);
|
|
sum += (quint16) ~cksum;
|
|
p = p->prev;
|
|
qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum);
|
|
}
|
|
if (parent)
|
|
{
|
|
cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType);
|
|
sum += (quint16) ~cksum;
|
|
}
|
|
|
|
while(sum>>16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
return (quint16) ~sum;
|
|
}
|
|
|
|
quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex,
|
|
CksumType cksumType) const
|
|
{
|
|
quint32 sum = 0;
|
|
quint16 cksum;
|
|
AbstractProtocol *p = next;
|
|
|
|
Q_ASSERT(cksumType == CksumIp);
|
|
|
|
while (p)
|
|
{
|
|
cksum = p->protocolFrameCksum(streamIndex, cksumType);
|
|
sum += (quint16) ~cksum;
|
|
p = p->next;
|
|
}
|
|
|
|
if (parent)
|
|
{
|
|
cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType);
|
|
sum += (quint16) ~cksum;
|
|
}
|
|
|
|
while(sum>>16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
return (quint16) ~sum;
|
|
}
|
|
|