/* decode_as_model.cpp * Data model for Decode As records. * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "decode_as_model.h" #include #include #include #include #include #include #include #include static const char *DEFAULT_TABLE = "tcp.port"; // Arbitrary static const char *DEFAULT_UI_TABLE = "TCP port"; // Arbitrary DecodeAsItem::DecodeAsItem() : tableName_(DEFAULT_TABLE), tableUIName_(DEFAULT_UI_TABLE), selectorUint_(0), selectorString_(""), default_proto_(DECODE_AS_NONE), current_proto_(DECODE_AS_NONE), dissector_handle_(NULL) { } DecodeAsItem::~DecodeAsItem() { } DecodeAsModel::DecodeAsModel(QObject *parent, capture_file *cf) : QAbstractTableModel(parent), cap_file_(cf) { } Qt::ItemFlags DecodeAsModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; DecodeAsItem* item = decode_as_items_[index.row()]; Qt::ItemFlags flags = QAbstractTableModel::flags(index); switch(index.column()) { case DecodeAsModel::colTable: case DecodeAsModel::colProtocol: flags |= Qt::ItemIsEditable; break; case DecodeAsModel::colSelector: { ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); if (selector_type != FT_NONE) flags |= Qt::ItemIsEditable; break; } } return flags; } QVariant DecodeAsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } DecodeAsItem* item; switch (role) { case Qt::ToolTipRole: switch (index.column()) { case colTable: return tr("Match using this field"); case colSelector: return tr("Current\"Decode As\" behavior"); case colType: return QVariant(); case colDefault: return tr("Default \"Decode As\" behavior"); case colProtocol: return tr("Change behavior when the protocol field matches this value"); } return QVariant(); case Qt::DisplayRole: case Qt::EditRole: item = decode_as_items_[index.row()]; if (item == NULL) return QVariant(); switch (index.column()) { case colTable: return item->tableUIName_; case colSelector: { ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); if (IS_FT_UINT(selector_type)) { return entryString(item->tableName_, GUINT_TO_POINTER(item->selectorUint_)); } else if (IS_FT_STRING(selector_type)) { return entryString(item->tableName_, (gpointer)item->selectorString_.toUtf8().constData()); } return DECODE_AS_NONE; } case colType: { ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); if (IS_FT_STRING(selector_type)) { return tr("String"); } else if (IS_FT_UINT(selector_type)) { QString type_desc = tr("Integer, base "); switch (get_dissector_table_param(item->tableName_)) { case BASE_OCT: type_desc.append("8"); break; case BASE_DEC: type_desc.append("10"); break; case BASE_HEX: type_desc.append("16"); break; default: type_desc.append(tr("unknown")); } return type_desc; } else if (selector_type == FT_NONE) { return tr(""); } break; } case colDefault: return item->default_proto_; case colProtocol: return item->current_proto_; } return QVariant(); case Qt::UserRole: item = decode_as_items_[index.row()]; return QVariant::fromValue(static_cast(item)); } return QVariant(); } QVariant DecodeAsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch (section) { case colTable: return tr("Field"); case colSelector: return tr("Value"); case colType: return tr("Type"); case colDefault: return tr("Default"); case colProtocol: return tr("Current"); default: g_assert_not_reached(); } return QVariant(); } int DecodeAsModel::rowCount(const QModelIndex &parent) const { // there are no children if (parent.isValid()) { return 0; } return decode_as_items_.count(); } int DecodeAsModel::columnCount(const QModelIndex &parent) const { // there are no children if (parent.isValid()) { return 0; } return colDecodeAsMax; } bool DecodeAsModel::setData(const QModelIndex &cur_index, const QVariant &value, int role) { if (!cur_index.isValid()) return false; if (role != Qt::EditRole) return false; if (data(cur_index, role) == value) { // Data appears unchanged, do not do additional checks. return true; } DecodeAsItem* item = decode_as_items_[cur_index.row()]; switch(cur_index.column()) { case DecodeAsModel::colTable: { QString valueStr = value.toString(); //grab the table values from the Decode As list because they are persistent for (GList *cur = decode_as_list; cur; cur = cur->next) { decode_as_t *entry = (decode_as_t *) cur->data; if (valueStr.compare(get_dissector_table_ui_name(entry->table_name)) == 0) { item->tableName_ = entry->table_name; item->tableUIName_ = get_dissector_table_ui_name(entry->table_name); //all other columns affected emit dataChanged(index(cur_index.row(), colSelector), index(cur_index.row(), colProtocol)); } } } break; case DecodeAsModel::colProtocol: item->current_proto_ = value.toString(); break; case DecodeAsModel::colSelector: { ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); if (IS_FT_STRING(selector_type)) { item->selectorString_ = value.toString(); } else if (IS_FT_UINT(selector_type)) { item->selectorUint_ = value.toUInt(); } } break; } return true; } bool DecodeAsModel::insertRows(int row, int count, const QModelIndex &/*parent*/) { // support insertion of just one item for now. if (count != 1 || row < 0 || row > rowCount()) return false; beginInsertRows(QModelIndex(), row, row); DecodeAsItem* item = new DecodeAsItem(); if (cap_file_ && cap_file_->edt) { //populate the new Decode As item with the last protocol layer //that can support Decode As wmem_list_frame_t * protos = wmem_list_head(cap_file_->edt->pi.layers); gint8 curr_layer_num_saved = cap_file_->edt->pi.curr_layer_num; guint8 curr_layer_num = 1; while (protos != NULL) { int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); const gchar * proto_name = proto_get_protocol_filter_name(proto_id); for (GList *cur = decode_as_list; cur; cur = cur->next) { decode_as_t *entry = (decode_as_t *) cur->data; if (g_strcmp0(proto_name, entry->name) == 0) { dissector_handle_t dissector = NULL; ftenum_t selector_type = get_dissector_table_selector_type(entry->table_name); //reset the default and current protocols in case previous layer //populated it and this layer doesn't have a handle for it item->default_proto_ = item->current_proto_ = DECODE_AS_NONE; item->tableName_ = entry->table_name; item->tableUIName_ = get_dissector_table_ui_name(entry->table_name); //see if there is a default dissector that matches value if (IS_FT_STRING(selector_type)) { //pick the first value in the packet as the default cap_file_->edt->pi.curr_layer_num = curr_layer_num; gpointer selector = entry->values[0].build_values[0](&cap_file_->edt->pi); if (selector != NULL) { item->selectorString_ = entryString(item->tableName_, selector); dissector = dissector_get_default_string_handle(entry->table_name, (const gchar*)selector); } else { item->selectorString_ = ""; } } else if (IS_FT_UINT(selector_type)) { //pick the first value in the packet as the default cap_file_->edt->pi.curr_layer_num = curr_layer_num; item->selectorUint_ = GPOINTER_TO_UINT(entry->values[0].build_values[0](&cap_file_->edt->pi)); dissector = dissector_get_default_uint_handle(entry->table_name, item->selectorUint_); } else if (selector_type == FT_NONE) { // There is no default for an FT_NONE dissector table dissector = NULL; } if (dissector != NULL) { item->default_proto_ = dissector_handle_get_short_name(dissector); //When adding a new record, "default" should equal "current", so the user can //explicitly change it item->current_proto_ = item->default_proto_; } } } protos = wmem_list_frame_next(protos); curr_layer_num++; } cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved; } decode_as_items_ << item; endInsertRows(); return true; } bool DecodeAsModel::removeRows(int row, int count, const QModelIndex &/*parent*/) { if (count != 1 || row < 0 || row >= rowCount()) return false; beginRemoveRows(QModelIndex(), row, row); decode_as_items_.removeAt(row); endRemoveRows(); return true; } bool DecodeAsModel::copyRow(int dst_row, int src_row) { if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) { return false; } DecodeAsItem* src = decode_as_items_[src_row]; DecodeAsItem* dst = decode_as_items_[dst_row]; dst->tableName_ = src->tableName_; dst->tableUIName_ = src->tableUIName_; dst->selectorUint_ = src->selectorUint_; dst->selectorString_ = src->selectorString_; dst->default_proto_ = src->default_proto_; dst->current_proto_ = src->current_proto_; dst->dissector_handle_ = src->dissector_handle_; QVector roles; roles << Qt::EditRole << Qt::BackgroundRole; emit dataChanged(index(dst_row, 0), index(dst_row, columnCount()) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) , roles #endif ); return true; } QString DecodeAsModel::entryString(const gchar *table_name, gpointer value) { QString entry_str; ftenum_t selector_type = get_dissector_table_selector_type(table_name); switch (selector_type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: { uint num_val = GPOINTER_TO_UINT(value); switch (get_dissector_table_param(table_name)) { case BASE_DEC: entry_str = QString::number(num_val); break; case BASE_HEX: int width; switch (selector_type) { case FT_UINT8: width = 2; break; case FT_UINT16: width = 4; break; case FT_UINT24: width = 6; break; case FT_UINT32: width = 8; break; default: g_assert_not_reached(); break; } entry_str = QString("%1").arg(int_to_qstring(num_val, width, 16)); break; case BASE_OCT: entry_str = "0" + QString::number(num_val, 8); break; } break; } case FT_STRING: case FT_STRINGZ: case FT_UINT_STRING: case FT_STRINGZPAD: entry_str = (char *)value; break; case FT_GUID: //TODO: DCE/RPC dissector table break; case FT_NONE: //doesn't really matter, just avoiding the assert return "0"; default: g_assert_not_reached(); break; } return entry_str; } void DecodeAsModel::fillTable() { decode_as_items_.clear(); emit beginResetModel(); dissector_all_tables_foreach_changed(buildChangedList, this); decode_dcerpc_add_show_list(buildDceRpcChangedList, this); emit endResetModel(); } void DecodeAsModel::setDissectorHandle(const QModelIndex &index, dissector_handle_t dissector_handle) { DecodeAsItem* item = decode_as_items_[index.row()]; if (item != NULL) item->dissector_handle_ = dissector_handle; } void DecodeAsModel::buildChangedList(const gchar *table_name, ftenum_t, gpointer key, gpointer value, gpointer user_data) { DecodeAsModel *model = (DecodeAsModel*)user_data; if (model == NULL) return; dissector_handle_t default_dh, current_dh; QString default_proto_name(DECODE_AS_NONE), current_proto_name(DECODE_AS_NONE); DecodeAsItem* item = new DecodeAsItem(); ftenum_t selector_type = get_dissector_table_selector_type(table_name); item->tableName_ = table_name; item->tableUIName_ = get_dissector_table_ui_name(table_name); if (IS_FT_UINT(selector_type)) { item->selectorUint_ = GPOINTER_TO_UINT(key); } else if (IS_FT_STRING(selector_type)) { item->selectorString_ = entryString(table_name, key); } default_dh = dtbl_entry_get_initial_handle((dtbl_entry_t *)value); if (default_dh) { default_proto_name = dissector_handle_get_short_name(default_dh); } item->default_proto_ = default_proto_name; current_dh = dtbl_entry_get_handle((dtbl_entry_t *)value); if (current_dh) { current_proto_name = QString(dissector_handle_get_short_name(current_dh)); } item->current_proto_ = current_proto_name; item->dissector_handle_ = current_dh; model->decode_as_items_ << item; } void DecodeAsModel::buildDceRpcChangedList(gpointer, gpointer) { // decode_dcerpc_bind_values_t *binding = (decode_dcerpc_bind_values_t *)data; } typedef QPair UintPair; typedef QPair CharPtrPair; void DecodeAsModel::gatherChangedEntries(const gchar *table_name, ftenum_t selector_type, gpointer key, gpointer, gpointer user_data) { DecodeAsModel *model = qobject_cast((DecodeAsModel*)user_data); if (model == NULL) return; switch (selector_type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: model->changed_uint_entries_ << UintPair(table_name, GPOINTER_TO_UINT(key)); break; case FT_NONE: //need to reset dissector table, so this needs to be in a changed list, //might as well be the uint one. model->changed_uint_entries_ << UintPair(table_name, 0); break; case FT_STRING: case FT_STRINGZ: case FT_UINT_STRING: case FT_STRINGZPAD: model->changed_string_entries_ << CharPtrPair(table_name, (const char *) key); break; default: break; } } void DecodeAsModel::applyChanges() { dissector_table_t sub_dissectors; module_t *module; pref_t* pref_value; dissector_handle_t handle; // Reset all dissector tables, then apply all rules from model. // We can't call g_hash_table_removed from g_hash_table_foreach, which // means we can't call dissector_reset_{string,uint} from // dissector_all_tables_foreach_changed. Collect changed entries in // lists and remove them separately. // // If dissector_all_tables_remove_changed existed we could call it // instead. dissector_all_tables_foreach_changed(gatherChangedEntries, this); foreach (UintPair uint_entry, changed_uint_entries_) { /* Set "Decode As preferences" to default values */ sub_dissectors = find_dissector_table(uint_entry.first); handle = dissector_get_uint_handle(sub_dissectors, uint_entry.second); if (handle != NULL) { module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(handle))); pref_value = prefs_find_preference(module, uint_entry.first); if (pref_value != NULL) { module->prefs_changed = TRUE; reset_pref(pref_value); } } dissector_reset_uint(uint_entry.first, uint_entry.second); } changed_uint_entries_.clear(); foreach (CharPtrPair char_ptr_entry, changed_string_entries_) { dissector_reset_string(char_ptr_entry.first, char_ptr_entry.second); } changed_string_entries_.clear(); foreach(DecodeAsItem *item, decode_as_items_) { decode_as_t *decode_as_entry; if (item->current_proto_.isEmpty()) { continue; } for (GList *cur = decode_as_list; cur; cur = cur->next) { decode_as_entry = (decode_as_t *) cur->data; if (!g_strcmp0(decode_as_entry->table_name, item->tableName_)) { ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); gpointer selector_value; QByteArray byteArray; switch (selector_type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: selector_value = GUINT_TO_POINTER(item->selectorUint_); break; case FT_STRING: case FT_STRINGZ: case FT_UINT_STRING: case FT_STRINGZPAD: byteArray = item->selectorString_.toUtf8(); selector_value = (gpointer) byteArray.constData(); break; case FT_NONE: //selector value is ignored, but dissector table needs to happen selector_value = NULL; break; default: continue; } if ((item->current_proto_ == DECODE_AS_NONE) || !item->dissector_handle_) { decode_as_entry->reset_value(decode_as_entry->table_name, selector_value); sub_dissectors = find_dissector_table(decode_as_entry->table_name); /* For now, only numeric dissector tables can use preferences */ if (IS_FT_UINT(dissector_table_get_type(sub_dissectors))) { if (item->dissector_handle_ != NULL) { module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(item->dissector_handle_))); pref_value = prefs_find_preference(module, decode_as_entry->table_name); if (pref_value != NULL) { module->prefs_changed = TRUE; prefs_remove_decode_as_value(pref_value, item->selectorUint_, TRUE); } } } break; } else { decode_as_entry->change_value(decode_as_entry->table_name, selector_value, &item->dissector_handle_, (char *) item->current_proto_.toUtf8().constData()); sub_dissectors = find_dissector_table(decode_as_entry->table_name); /* For now, only numeric dissector tables can use preferences */ if (IS_FT_UINT(dissector_table_get_type(sub_dissectors))) { module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(item->dissector_handle_))); pref_value = prefs_find_preference(module, decode_as_entry->table_name); if (pref_value != NULL) { module->prefs_changed = TRUE; prefs_add_decode_as_value(pref_value, item->selectorUint_, FALSE); } } break; } } } } } /* * Editor modelines * * Local Variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */