diff --git a/client/ostinato.pro b/client/ostinato.pro index 707969e..ff54e47 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,6 +1,6 @@ TEMPLATE = app CONFIG += qt debug -QT += network +QT += network script INCLUDEPATH += "../rpc/" "../common/" LIBS += -lprotobuf win32:LIBS += -L"../common/debug" -lostproto diff --git a/common/ostproto.pro b/common/ostproto.pro index 38064b3..19121ca 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -1,6 +1,6 @@ TEMPLATE = lib CONFIG += qt staticlib -QT += network +QT += network script LIBS += \ -lprotobuf FORMS += \ @@ -14,6 +14,7 @@ FORMS += \ ip4.ui \ tcp.ui \ udp.ui \ + userscript.ui \ sample.ui PROTOS += \ protocol.proto \ @@ -31,6 +32,7 @@ PROTOS += \ ip4.proto \ tcp.proto \ udp.proto \ + userscript.proto \ sample.proto HEADERS += \ abstractprotocol.h \ @@ -53,6 +55,7 @@ HEADERS += \ ip4.h \ tcp.h \ udp.h \ + userscript.h \ sample.h SOURCES += \ abstractprotocol.cpp \ @@ -71,6 +74,7 @@ SOURCES += \ ip4.cpp \ tcp.cpp \ udp.cpp \ + userscript.cpp \ sample.cpp protobuf_decl.name = protobuf header diff --git a/common/protocol.proto b/common/protocol.proto index 8b8a8f7..30bec18 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -71,6 +71,7 @@ message Protocol { kMacFieldNumber = 51; kPayloadFieldNumber = 52; kSampleFieldNumber = 53; + kUserScriptFieldNumber = 54; kEth2FieldNumber = 121; kDot3FieldNumber = 122; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 3a9e254..7c8763d 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -2,7 +2,6 @@ #include "abstractprotocol.h" #include "protocol.pb.h" -#include "sample.h" #include "mac.h" #include "payload.h" #include "eth2.h" @@ -16,6 +15,8 @@ #include "ip4.h" #include "tcp.h" #include "udp.h" +#include "userscript.h" +#include "sample.h" ProtocolManager OstProtocolManager; @@ -53,6 +54,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, (void*) SampleProtocol::createInstance); @@ -64,7 +67,7 @@ void ProtocolManager::registerProtocol(int protoNumber, { AbstractProtocol *p; - //! \todo (MED) validate incoming params for duplicates with existing + Q_ASSERT(!factory.contains(protoNumber)); factory.insert(protoNumber, protoInstanceCreator); diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..5cb792c --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,492 @@ +#include "userscript.h" + +#include +#include +#include +#include + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), _protocol(protocol) +{ + setupUi(this); +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ +} + +void UserScriptConfigForm::on_compileButton_clicked(bool checked) +{ + _protocol->storeConfigWidget(); + if (_protocol->userScriptErrorLineNumber() >= 0) + { + statusLabel->setText("Fail"); + QMessageBox::critical(this, "Error", + QString("Line %1: %2").arg( + _protocol->userScriptErrorLineNumber()).arg( + _protocol->userScriptErrorText())); + } + else + { + statusLabel->setText("Success"); + } +} + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + _userProtocol(this) +{ + configForm = NULL; + _isUpdated = false; + _errorLineNumber = -1; + + _userProtocolScriptValue = _scriptEngine.newQObject(&_userProtocol); + _userProtocolScriptValue.setProperty("protocolId", _scriptEngine.newObject()); + _scriptEngine.globalObject().setProperty("protocol", _userProtocolScriptValue); + + + QScriptValue meta = _scriptEngine.newQMetaObject(_userProtocol.metaObject()); + _scriptEngine.globalObject().setProperty("Protocol", meta); + +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript}").arg(_userProtocol.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script}").arg(_userProtocol.shortName()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + if (_userProtocol._protocolId.contains(static_cast(type))) + return _userProtocol._protocolId.value(static_cast(type)); + else + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + { + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + QScriptValue userFunction; + QByteArray fv; + + evaluateUserScript(); // FIXME: don't call everytime! + + if (_userProtocol.isProtocolFrameFixed()) + { + fv = _userProtocol.protocolFrameFixedValue(); + if (fv.size()) + return fv; + else + return QByteArray(4, 0); // FIXME + } + + userFunction = _userProtocolScriptValue.property( + "protocolFrameValue"); + + qDebug("userscript protoFrameVal(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (userFunction.isValid() && userFunction.isFunction()) + { + QScriptValue userValue; + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() + << QScriptValue(&_scriptEngine, streamIndex)); + + qDebug("userscript value: isValid:%d/isArray:%d", + userValue.isValid(), userValue.isArray()); + + if (userValue.isArray()) + { + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + } + } + + if (fv.size()) + return fv; + else + return QByteArray(4, 0); // FIXME + } + //case FieldBitSize: + //return 3; + default: + break; + } + break; + + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return _userProtocol.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return _userProtocol.isProtocolFrameSizeVariable(); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +bool UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + + _errorLineNumber = -1; + _userProtocol.reset(); + _userProtocolScriptValue.setProperty("protocolFrameValue", QScriptValue()); + + _scriptEngine.evaluate( + fieldData(userScript_program, FieldValue).toString()); + if (_scriptEngine.hasUncaughtException()) + goto _error_exception; + + userFunction = _userProtocolScriptValue.property( + "protocolFrameValue"); + qDebug("userscript eval protoFrameVal(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + if (!userFunction.isValid()) + goto _error_frame_value_not_set; + + if (userFunction.isArray()) + { + QList pktBuf; + + userValue = userFunction; + _userProtocol.setProtocolFrameValueVariable(false); + + qScriptValueToSequence(userValue, pktBuf); + _userProtocol.setProtocolFrameFixedValue(pktBuf); + } + else if (userFunction.isFunction()) + { + userValue = userFunction.call(); + if (_scriptEngine.hasUncaughtException()) + goto _error_exception; + qDebug("userscript eval value: isValid:%d/isArray:%d", + userValue.isValid(), userValue.isArray()); + if (!userValue.isArray()) + goto _error_not_an_array; + + if (_userProtocol.isProtocolFrameFixed()) + { + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + _userProtocol.setProtocolFrameFixedValue(pktBuf); + } + } + else + goto _error_frame_value_type; + + + userValue = _userProtocolScriptValue.property("protocolId"); + + if (userValue.isValid() && userValue.isObject()) + { + QMetaEnum meta; + QScriptValue userProtocolId; + + meta = _userProtocol.metaObject()->enumerator( + _userProtocol.metaObject()->indexOfEnumerator("ProtocolIdType")); + for (int i = 0; i < meta.keyCount(); i++) + { + userProtocolId = userValue.property(meta.key(i)); + + qDebug("userscript protoId: isValid:%d/isNumber:%d", + userProtocolId.isValid(), + userProtocolId.isNumber()); + + if (userProtocolId.isValid() && userProtocolId.isNumber()) + _userProtocol._protocolId.insert( + meta.value(i), userProtocolId.toUInt32()); + } + } + + _isUpdated = true; + return true; + +_error_not_an_array: + _errorLineNumber = userScriptLineCount(); + _errorText = QString("property 'protocolFrameValue' returns invalid array"); + goto _error; + +_error_frame_value_type: + _errorLineNumber = userScriptLineCount(); + _errorText = QString("property 'protocolFrameValue' is neither an array nor a function"); + goto _error; + +_error_frame_value_not_set: + _errorLineNumber = userScriptLineCount(); + _errorText = QString("mandatory property 'protocolFrameValue' is not set"); + goto _error; + +_error_exception: + _errorLineNumber = _scriptEngine.uncaughtExceptionLineNumber(); + _errorText = _scriptEngine.uncaughtException().toString(); + goto _error; + +_error: + _userProtocol.reset(); + return false; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return _errorLineNumber; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return _errorText; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// --------------------------------------------------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : _parent (parent) +{ + reset(); +} + +bool UserProtocol::isProtocolFrameFixed() const +{ + return !isProtocolFrameValueVariable() && !isProtocolFrameSizeVariable(); +} + +const QByteArray& UserProtocol::protocolFrameFixedValue() const +{ + return _protocolFrameFixedValue; +} + +void UserProtocol::setProtocolFrameFixedValue(const QByteArray& value) +{ + _protocolFrameFixedValue = value; +} + +void UserProtocol::setProtocolFrameFixedValue(const QList& value) +{ + _protocolFrameFixedValue.resize(value.size()); + for (int i = 0; i < value.size(); i++) + _protocolFrameFixedValue[i] = value.at(i) & 0xFF; +} + +void UserProtocol::reset() +{ + _name = QString(); + _shortName = QString(); + _protocolId.clear(); + _protocolFrameValueVariable = false; + _protocolFrameSizeVariable = false; + _protocolFrameFixedValue.resize(0); +} + +QString UserProtocol::name() const +{ + return _name; +} + +void UserProtocol::setName(QString &name) +{ + _name = name; +} + +QString UserProtocol::shortName() const +{ + return _shortName; +} + +void UserProtocol::setShortName(QString &shortName) +{ + _shortName = shortName; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return _protocolFrameValueVariable; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + qDebug("%s: %d", __FUNCTION__, variable); + _protocolFrameValueVariable = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return _protocolFrameSizeVariable; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + _protocolFrameSizeVariable = variable; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return _parent->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return _parent->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return _parent->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return _parent->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return _parent->isProtocolFramePayloadSizeVariable(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return _parent->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return _parent->protocolFramePayloadCksum(streamIndex, cksumType); +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..504e285 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,160 @@ +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + friend class UserScriptProtocol; + + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(QString shortName READ shortName WRITE setShortName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp + }; + + UserProtocol(AbstractProtocol *parent); + + bool isProtocolFrameFixed() const; + const QByteArray& protocolFrameFixedValue() const; + void setProtocolFrameFixedValue(const QByteArray& value); + void setProtocolFrameFixedValue(const QList& value); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + QString shortName() const; + void setShortName(QString &shortName); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *_parent; + + QString _name; + QString _shortName; + QMap _protocolId; + bool _protocolFrameValueVariable; + bool _protocolFrameSizeVariable; + QByteArray _protocolFrameFixedValue; + +}; + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + UserScriptProtocol *_protocol; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + +class UserScriptProtocol : public AbstractProtocol +{ + friend class UserScriptConfigForm; + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + bool evaluateUserScript() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + + int userScriptLineCount() const; + + + OstProto::UserScript data; + UserScriptConfigForm *configForm; + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + + mutable QScriptEngine _scriptEngine; + mutable UserProtocol _userProtocol; + mutable QScriptValue _userProtocolScriptValue; + + mutable bool _isUpdated; + mutable int _errorLineNumber; + mutable QString _errorText; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..3f26cd2 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,14 @@ +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 54; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..a5dde73 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,61 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + + Compilation Status: + + + + + + + Unknown + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Compile + + + + + + + + + + diff --git a/server/drone.pro b/server/drone.pro index c42d9a4..f569b4d 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,6 +1,6 @@ TEMPLATE = app CONFIG += qt debug -QT += network +QT += network script DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "../rpc" LIBS += -lprotobuf