/* Copyright (C) 2021 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 */ #include "findreplace.h" #include "abstractprotocol.h" #include "iputils.h" #include "mandatoryfieldsgroup.h" #include "protocolmanager.h" #include "stream.h" #include "uint128.h" #include extern ProtocolManager *OstProtocolManager; // TODO: It might be useful for this dialog to support a "preview" // of the replacements FindReplaceDialog::FindReplaceDialog(Action *action, QWidget *parent) : QDialog(parent), action_(action) { setupUi(this); findMask->setMaskMode(true); replaceMask->setMaskMode(true); // Keep things simple and don't use mask(s) (default) useFindMask->setChecked(false); useReplaceMask->setChecked(false); // TODO: remove combo protocols - see note in StreamBase::findReplace QStringList protocolList = OstProtocolManager->protocolDatabase(); protocolList.sort(); protocol->addItems(protocolList); // Enable this setting if we have streams selected on input selectedStreamsOnly->setEnabled(action->selectedStreamsOnly); // Reset for user input action->selectedStreamsOnly = false; QPushButton *ok = buttonBox->button(QDialogButtonBox::Ok); ok->setText(tr("Replace All")); ok->setDisabled(true); mandatoryFields_ = new MandatoryFieldsGroup(this); mandatoryFields_->add(protocol); mandatoryFields_->add(field); mandatoryFields_->add(findValue); mandatoryFields_->add(findMask); mandatoryFields_->add(replaceValue); mandatoryFields_->add(replaceMask); mandatoryFields_->setSubmitButton(ok); } FindReplaceDialog::~FindReplaceDialog() { delete mandatoryFields_; } void FindReplaceDialog::on_protocol_currentIndexChanged(const QString &name) { field->clear(); fieldAttrib_.clear(); Stream stream; AbstractProtocol *protocol = OstProtocolManager->createProtocol( name, &stream); int count = protocol->fieldCount(); for (int i = 0; i < count; i++) { // XXX: It might be useful to support meta fields too, later! if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField)) continue; int bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize) .toInt(); if (bitSize <= 0) // skip optional fields continue; FieldAttrib fieldAttrib; fieldAttrib.index = i; // fieldIndex fieldAttrib.bitSize = bitSize; // field and fieldAttrib_ have same count and order of fields fieldAttrib_.append(fieldAttrib); field->addItem(protocol->fieldData(i, AbstractProtocol::FieldName) .toString()); } protocolId_ = protocol->protocolNumber(); delete protocol; } void FindReplaceDialog::on_field_currentIndexChanged(int index) { if (index < 0) return; QString fieldName = field->currentText(); FieldAttrib fieldAttrib = fieldAttrib_.at(index); // Use heuristics to determine field type if (fieldAttrib.bitSize == 48) { findMask->setType(FieldEdit::kMacAddress); findValue->setType(FieldEdit::kMacAddress); replaceMask->setType(FieldEdit::kMacAddress); replaceValue->setType(FieldEdit::kMacAddress); } else if ((fieldAttrib.bitSize == 32) && (fieldName.contains(QRegularExpression( "address|source|destination", QRegularExpression::CaseInsensitiveOption)))) { findMask->setType(FieldEdit::kIp4Address); findValue->setType(FieldEdit::kIp4Address); replaceMask->setType(FieldEdit::kIp4Address); replaceValue->setType(FieldEdit::kIp4Address); } else if ((fieldAttrib.bitSize == 128) && (fieldName.contains(QRegularExpression( "address|source|destination", QRegularExpression::CaseInsensitiveOption)))) { findMask->setType(FieldEdit::kIp6Address); findValue->setType(FieldEdit::kIp6Address); replaceMask->setType(FieldEdit::kIp6Address); replaceValue->setType(FieldEdit::kIp6Address); } else { quint64 max = quint64(~0) >> (64-fieldAttrib.bitSize); qDebug("XXXXXX %s bitSize %d max %llx", qPrintable(field->currentText()), fieldAttrib.bitSize, max); findMask->setType(FieldEdit::kUInt64); findMask->setRange(0, max); findValue->setType(FieldEdit::kUInt64); findValue->setRange(0, max); replaceMask->setType(FieldEdit::kUInt64); replaceMask->setRange(0, max); replaceValue->setType(FieldEdit::kUInt64); replaceValue->setRange(0, max); } } void FindReplaceDialog::on_matchAny_toggled(bool checked) { if (checked) { findValueLabel->setHidden(true); findValue->setHidden(true); useFindMask->setHidden(true); findMask->setHidden(true); findMaskHint->setHidden(true); } else { findValueLabel->setVisible(true); findValue->setVisible(true); useFindMask->setVisible(true); if (useFindMask->isChecked()) { findMask->setVisible(true); findMaskHint->setVisible(true); } } } void FindReplaceDialog::on_buttonBox_accepted() { FieldAttrib fieldAttrib = fieldAttrib_.at(field->currentIndex()); action_->protocolField = QString("%1 %2") .arg(protocol->currentText()) .arg(field->currentText()); action_->protocolNumber = protocolId_; action_->fieldIndex = fieldAttrib.index; action_->fieldBitSize = fieldAttrib.bitSize; if (fieldAttrib.bitSize == 128) { // IPv6 address if (matchAny->isChecked()) { action_->findMask.setValue(UInt128(0)); action_->findValue.setValue(UInt128(0)); } else { action_->findMask.setValue( useFindMask->isChecked() ? ipUtils::ip6StringToUInt128(findMask->text()) : ~UInt128(0)); action_->findValue.setValue( ipUtils::ip6StringToUInt128(findValue->text())); } action_->replaceMask.setValue( useReplaceMask->isChecked() ? ipUtils::ip6StringToUInt128(replaceMask->text()) : ~UInt128(0)); action_->replaceValue.setValue( ipUtils::ip6StringToUInt128(replaceValue->text())); } else { // everything else if (matchAny->isChecked()) { action_->findMask.setValue(0); action_->findValue.setValue(0); } else { action_->findMask.setValue( useFindMask->isChecked() ? findMask->text().toULongLong(nullptr, 0) : ~quint64(0)); action_->findValue.setValue( findValue->text().toULongLong(nullptr, 0)); } action_->replaceMask.setValue( useReplaceMask->isChecked() ? replaceMask->text().toULongLong(nullptr, 0) : ~quint64(0)); action_->replaceValue.setValue(QString::number( replaceValue->text().toULongLong(nullptr, 0))); } action_->selectedStreamsOnly = selectedStreamsOnly->isChecked(); }