Browse Source

Render banned peers in the peers tab

pull/9/head
Duke Leto 3 years ago
parent
commit
e58484231f
  1. 2
      silentdragon.pro
  2. 128
      src/bannedpeerstablemodel.cpp
  3. 36
      src/bannedpeerstablemodel.h
  4. 93
      src/mainwindow.cpp
  5. 31
      src/mainwindow.ui
  6. 28
      src/peerstablemodel.cpp
  7. 2
      src/peerstablemodel.h
  8. 49
      src/rpc.cpp
  9. 9
      src/rpc.h

2
silentdragon.pro

@ -50,6 +50,7 @@ SOURCES += \
src/senttxstore.cpp \
src/txtablemodel.cpp \
src/peerstablemodel.cpp \
src/bannedpeerstablemodel.cpp \
src/qrcodelabel.cpp \
src/connection.cpp \
src/fillediconlabel.cpp \
@ -75,6 +76,7 @@ HEADERS += \
src/settings.h \
src/txtablemodel.h \
src/peerstablemodel.h \
src/bannedpeerstablemodel.h \
src/senttxstore.h \
src/qrcodelabel.h \
src/connection.h \

128
src/bannedpeerstablemodel.cpp

@ -0,0 +1,128 @@
// Copyright 2019-2021 The Hush developers
// Released under the GPLv3
#include "settings.h"
#include "rpc.h"
BannedPeersTableModel::BannedPeersTableModel(QObject *parent)
: QAbstractTableModel(parent) {
headers << QObject::tr("Address") << QObject::tr("Subnet") << QObject::tr("Banned Until");
}
BannedPeersTableModel::~BannedPeersTableModel() {
delete modeldata;
}
void BannedPeersTableModel::addData(const QList<BannedPeerItem>& data) {
bannedPeers = new QList<BannedPeerItem>();
std::copy(data.begin(), data.end(), std::back_inserter(*bannedPeers));
updateAllData();
}
void BannedPeersTableModel::updateAllData() {
auto newmodeldata = new QList<BannedPeerItem>();
// Copy peer data so GUI can use it
if (bannedPeers != nullptr) std::copy( bannedPeers->begin(), bannedPeers->end(), std::back_inserter(*newmodeldata));
// Sort by banned_until
std::sort(newmodeldata->begin(), newmodeldata->end(), [=] (auto a, auto b) {
return a.banned_until > b.banned_until; // reverse sort
});
// And then swap out the modeldata with the new one.
delete modeldata;
modeldata = newmodeldata;
// do magic
dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1));
layoutChanged();
}
int BannedPeersTableModel::rowCount(const QModelIndex&) const
{
if (modeldata == nullptr) return 0;
return modeldata->size();
}
int BannedPeersTableModel::columnCount(const QModelIndex&) const
{
return headers.size();
}
QVariant BannedPeersTableModel::data(const QModelIndex &index, int role) const
{
auto dat = modeldata->at(index.row());
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: return dat.address;
case 1: return dat.subnet;
case 2: return QDateTime::fromSecsSinceEpoch(dat.banned_until).toLocalTime().toString();
}
}
// we show mask because it's possible to ban ranges of addresses
if (role == Qt::ToolTipRole) {
switch (index.column()) {
case 0: return "Network Address";
case 1: return "Subnet Mask";
case 2: return "Banned Until " + QDateTime::fromSecsSinceEpoch(dat.banned_until).toUTC().toString();
}
}
//TODO: show different icons for IP vs Tor vs other kinds of connections
/*
if (role == Qt::DecorationRole && index.column() == 0) {
if (!dat.memo.isEmpty()) {
// If the memo is a Payment URI, then show a payment request icon
if (dat.memo.startsWith("hush:")) {
QIcon icon(":/icons/res/paymentreq.gif");
return QVariant(icon.pixmap(16, 16));
} else {
// Return the info pixmap to indicate memo
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation);
return QVariant(icon.pixmap(16, 16));
}
} else {
// Empty pixmap to make it align
QPixmap p(16, 16);
p.fill(Qt::white);
return QVariant(p);
}
}
*/
return QVariant();
}
QVariant BannedPeersTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
//if (role == Qt::TextAlignmentRole && section == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter);
if (role == Qt::TextAlignmentRole) return QVariant(Qt::AlignCenter | Qt::AlignVCenter);
if (role == Qt::FontRole) {
QFont f;
f.setBold(true);
return f;
}
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
return headers.at(section);
}
return QVariant();
}
QString BannedPeersTableModel::getAddress(int row) const {
return modeldata->at(row).address.trimmed();
}
QString BannedPeersTableModel::getSubnet(int row) const {
return modeldata->at(row).subnet;
}
qint64 BannedPeersTableModel::getBannedUntil(int row) const {
return modeldata->at(row).banned_until;
}

36
src/bannedpeerstablemodel.h

@ -0,0 +1,36 @@
// Copyright 2019-2021 The Hush developers
// Released under the GPLv3
#ifndef BANNEDPEERSTABLEMODEL_H
#define BANNEDPEERSTABLEMODEL_H
#include "precompiled.h"
struct BannedPeerItem;
class BannedPeersTableModel: public QAbstractTableModel
{
public:
BannedPeersTableModel(QObject* parent);
~BannedPeersTableModel();
QString getSubnet(int row) const;
QString getAddress(int row) const;
qint64 getBannedUntil(int row) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
void addData (const QList<BannedPeerItem>& data);
private:
void updateAllData();
QList<BannedPeerItem>* bannedPeers = nullptr;
QList<BannedPeerItem>* modeldata = nullptr;
QList<QString> headers;
};
#endif // BANNEDPEERSTABLEMODEL_H

93
src/mainwindow.cpp

@ -22,7 +22,6 @@
#include "requestdialog.h"
#include "websockets.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
@ -1128,16 +1127,62 @@ void MainWindow::setupBalancesTab() {
});
}
QString peer2ip(QString peer) {
QString ip = "";
if(peer.contains("[")) {
// this is actually ipv6, grab it all except the port
auto parts = peer.split(":");
parts[8]=""; // remove port
peer = parts.join(":");
peer.chop(1); // remove trailing :
} else {
ip = peer.split(":")[0];
}
return ip;
}
void MainWindow::setupPeersTab() {
qDebug() << __FUNCTION__;
// Set up context menu on transactions tab
ui->peersTable->setContextMenuPolicy(Qt::CustomContextMenu);
ui->bannedPeersTable->setContextMenuPolicy(Qt::CustomContextMenu);
// Table right click
QObject::connect(ui->bannedPeersTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) {
QModelIndex index = ui->peersTable->indexAt(pos);
if (index.row() < 0) return;
QMenu menu(this);
auto bannedPeerModel = dynamic_cast<BannedPeersTableModel *>(ui->bannedPeersTable->model());
QString addr = bannedPeerModel->getAddress(index.row());
QString ip = peer2ip(addr);
QString subnet = bannedPeerModel->getSubnet(index.row());
qint64 banned_until = bannedPeerModel->getBannedUntil(index.row());
if(!ip.isEmpty()) {
menu.addAction(tr("Copy banned peer IP"), [=] () {
QGuiApplication::clipboard()->setText(ip);
ui->statusBar->showMessage(tr("Copied to clipboard"), 3 * 1000);
});
}
// shodan only supports ipv4 addresses *and* we get ipv6 addresses
// in a different format, yay
if(!ip.isEmpty() && !ip.contains(":")) {
menu.addAction(tr("View banned host IP on shodan.io (3rd party service)"), [=] () {
QString url = "https://www.shodan.io/host/" + ip;
qDebug() << "opening " << url;
QDesktopServices::openUrl(QUrl(url));
});
}
menu.exec(ui->bannedPeersTable->viewport()->mapToGlobal(pos));
});
// Table right click
QObject::connect(ui->peersTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) {
QModelIndex index = ui->peersTable->indexAt(pos);
// we auto-sort by conntime
//ui->peersTable->setSortingEnabled(true);
if (index.row() < 0) return;
QMenu menu(this);
@ -1146,23 +1191,15 @@ void MainWindow::setupPeersTab() {
QString addr = peerModel->getAddress(index.row());
QString cipher = peerModel->getTLSCipher(index.row());
qint64 asn = peerModel->getASN(index.row());
QString ip = addr.split(":")[0];
QString ip = peer2ip(addr);
QString as = QString::number(asn);
if(ip.contains("[")) {
// this is actually ipv6, grab it all except the port
auto parts = addr.split(":");
parts[8]=""; // remove port
ip = parts.join(":");
ip.chop(1); // remove trailing :
}
menu.addAction(tr("Copy peer address+port"), [=] () {
QGuiApplication::clipboard()->setText(addr);
ui->statusBar->showMessage(tr("Copied to clipboard"), 3 * 1000);
});
//TODO: support Tor correctly
//TODO: support Tor correctly when v3 lands
menu.addAction(tr("Copy peer address"), [=] () {
QGuiApplication::clipboard()->setText(ip);
ui->statusBar->showMessage(tr("Copied to clipboard"), 3 * 1000);
@ -1197,6 +1234,36 @@ void MainWindow::setupPeersTab() {
menu.exec(ui->peersTable->viewport()->mapToGlobal(pos));
});
/*
//grep 'BAN THRESHOLD EXCEEDED' ~/.komodo/HUSH3/debug.log
//grep Disconnected ...
QFile debuglog = "";
#ifdef Q_OS_LINUX
debuglog = "~/.komodo/HUSH3/debug.log";
#elif defined(Q_OS_DARWIN)
debuglog = "~/Library/Application Support/Komodo/HUSH3/debug.log";
#elif defined(Q_OS_WIN64)
// "C:/Users/<USER>/AppData/Roaming/<APPNAME>",
// TODO: get current username
debuglog = "C:/Users/<USER>/AppData/Roaming/Komodo/HUSH3/debug.log";
#else
// Bless Your Heart, You Like Danger!
// There are open bounties to port HUSH softtware to OpenBSD and friends:
// git.hush.is/hush/tasks
debuglog = "~/.komodo/HUSH3/debug.log";
#endif // Q_OS_LINUX
if(debuglog.exists()) {
qDebug() << __func__ << ": Found debuglog at " << debuglog;
} else {
qDebug() << __func__ << ": No debug.log found";
}
*/
//ui->recentlyBannedPeers = "Could not open " + debuglog;
}
void MainWindow::setupHushTab() {

31
src/mainwindow.ui

@ -941,6 +941,17 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QLabel" name="currentPeersLabel">
<property name="text">
<string>
Current Peers
</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="peersTable">
<property name="selectionMode">
@ -950,18 +961,31 @@
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</item>
<item>
<widget class="QLabel" name="recentlyBannedPeers">
<widget class="QLabel" name="bannedPeersLabel">
<property name="text">
<string>
Looking For Recently Banned Peers ...
Banned Peers
</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="bannedPeersTable">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<!--
<widget class="QLabel" name="recentlyDisconnectedPeers">
<property name="text">
<string>
@ -970,6 +994,7 @@
</property>
</widget>
</item>
-->
</layout>

28
src/peerstablemodel.cpp

@ -20,34 +20,6 @@ void PeersTableModel::addData(const QList<PeerItem>& data) {
updateAllData();
}
bool PeersTableModel::exportToCsv(QString fileName) const {
if (!modeldata)
return false;
QFile file(fileName);
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate))
return false;
QTextStream out(&file); // we will serialize the data into the file
// Write headers
for (int i = 0; i < headers.length(); i++) {
out << "\"" << headers[i] << "\",";
}
out << "\"Memo\"";
out << endl;
// Write out each row
for (int row = 0; row < modeldata->length(); row++) {
for (int col = 0; col < headers.length(); col++) {
out << "\"" << data(index(row, col), Qt::DisplayRole).toString() << "\",";
}
}
file.close();
return true;
}
void PeersTableModel::updateAllData() {
auto newmodeldata = new QList<PeerItem>();

2
src/peerstablemodel.h

@ -25,8 +25,6 @@ public:
unsigned int getBanscore(int row) const;
unsigned int getProtocolVersion(int row) const;
bool exportToCsv(QString fileName) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;

49
src/rpc.cpp

@ -27,8 +27,14 @@ RPC::RPC(MainWindow* main) {
peersTableModel = new PeersTableModel(ui->peersTable);
main->ui->peersTable->setModel(peersTableModel);
// tls cipher is wide
main->ui->peersTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
// tls ciphersuite is wide
main->ui->peersTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
bannedPeersTableModel = new BannedPeersTableModel(ui->bannedPeersTable);
main->ui->bannedPeersTable->setModel(bannedPeersTableModel);
main->ui->bannedPeersTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
qDebug() << __func__ << "Done settings up TableModels";
// Set up timer to refresh Price
priceTimer = new QTimer(main);
@ -36,6 +42,7 @@ RPC::RPC(MainWindow* main) {
refreshPrice();
});
priceTimer->start(Settings::priceRefreshSpeed);
qDebug() << __func__ << ": started price refresh at speed=" << Settings::priceRefreshSpeed;
// Set up a timer to refresh the UI every few seconds
timer = new QTimer(main);
@ -53,6 +60,7 @@ RPC::RPC(MainWindow* main) {
});
txTimer->start(Settings::updateSpeed);
qDebug() << __func__ << "Done settings up all timers";
usedAddresses = new QMap<QString, bool>();
}
@ -64,6 +72,7 @@ RPC::~RPC() {
delete transactionsTableModel;
delete balancesTableModel;
delete peersTableModel;
delete bannedPeersTableModel;
delete utxos;
delete allBalances;
@ -246,6 +255,11 @@ void RPC::getPeerInfo(const std::function<void(QJsonValue)>& cb) {
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb);
}
void RPC::listBanned(const std::function<void(QJsonValue)>& cb) {
QString method = "listbanned";
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb);
}
void RPC::getTransactions(const std::function<void(QJsonValue)>& cb) {
QString method = "listtransactions";
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb);
@ -628,7 +642,7 @@ void RPC::getInfoThenRefresh(bool force) {
qint64 solrate = reply.toInt();
//TODO: format decimal
ui->solrate->setText(QString::number(solrate / 1000000) % " MegaSol/s");
ui->solrate->setText(QString::number((double)solrate / 1000000) % " MegaSol/s");
});
// Get network info
@ -861,6 +875,34 @@ void RPC::refreshPeers() {
if (conn == nullptr)
return noConnection();
/*
[
{
"address": "199.247.28.148/255.255.255.255",
"banned_until": 1612869516
},
{
"address": "2001:19f0:5001:d26:5400:3ff:fe18:f6c2/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"banned_until": 1612870431
}
]
*/
listBanned([=] (QJsonValue reply) {
QList<BannedPeerItem> peerdata;
for (const auto& it : reply.toArray()) {
auto addr = it.toObject()["address"].toString();
auto bantime = (qint64)it.toObject()["banned_until"].toInt();
auto parts = addr.split("/");
auto ip = parts[0];
auto subnet = parts[1];
BannedPeerItem peer { ip, subnet, bantime };
qDebug() << "Adding banned peer with address=" << addr;
peerdata.push_back(peer);
}
bannedPeersTableModel->addData(peerdata);
});
getPeerInfo([=] (QJsonValue reply) {
QList<PeerItem> peerdata;
@ -893,7 +935,6 @@ void RPC::refreshPeers() {
}
//qDebug() << peerdata;
// Update model data, which updates the table view
peersTableModel->addData(peerdata);
});

9
src/rpc.h

@ -7,6 +7,7 @@
#include "balancestablemodel.h"
#include "txtablemodel.h"
#include "peerstablemodel.h"
#include "bannedpeerstablemodel.h"
#include "ui_mainwindow.h"
#include "mainwindow.h"
#include "connection.h"
@ -22,6 +23,12 @@ struct TransactionItem {
QString memo;
};
struct BannedPeerItem {
QString address;
QString subnet;
qint64 banned_until;
};
struct PeerItem {
qint64 peerid;
QString type;
@ -125,6 +132,7 @@ private:
void getTransparentUnspent (const std::function<void(QJsonValue)>& cb);
void getZUnspent (const std::function<void(QJsonValue)>& cb);
void getTransactions (const std::function<void(QJsonValue)>& cb);
void listBanned (const std::function<void(QJsonValue)>& cb);
void getPeerInfo (const std::function<void(QJsonValue)>& cb);
void getZAddresses (const std::function<void(QJsonValue)>& cb);
void getTAddresses (const std::function<void(QJsonValue)>& cb);
@ -141,6 +149,7 @@ private:
TxTableModel* transactionsTableModel = nullptr;
PeersTableModel* peersTableModel = nullptr;
BannedPeersTableModel* bannedPeersTableModel = nullptr;
BalancesTableModel* balancesTableModel = nullptr;
QTimer* timer;

Loading…
Cancel
Save