Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Qt] allow banning and unbanning over UI->peers table #6315

Merged
merged 20 commits into from
Sep 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions qa/rpc-tests/nodehandling.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ def run_test(self):
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
listBeforeShutdown = self.nodes[2].listbanned();
assert_equal("192.168.0.1/255.255.255.255", listBeforeShutdown[2]['address']) #must be here
assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here
time.sleep(2) #make 100% sure we expired 192.168.0.1 node time

#stop node
stop_node(self.nodes[2], 2)

self.nodes[2] = start_node(2, self.options.tmpdir)
listAfterShutdown = self.nodes[2].listbanned();
assert_equal("127.0.0.0/255.255.255.0", listAfterShutdown[0]['address'])
assert_equal("127.0.0.0/255.255.255.255", listAfterShutdown[1]['address'])
assert_equal("2001:4000::/ffff:e000:0:0:0:0:0:0", listAfterShutdown[2]['address'])
assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
assert_equal("/19" in listAfterShutdown[2]['address'], True)

###########################
# RPC disconnectnode test #
Expand Down
3 changes: 3 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ QT_MOC_CPP = \
qt/moc_addressbookpage.cpp \
qt/moc_addresstablemodel.cpp \
qt/moc_askpassphrasedialog.cpp \
qt/moc_bantablemodel.cpp \
qt/moc_bitcoinaddressvalidator.cpp \
qt/moc_bitcoinamountfield.cpp \
qt/moc_bitcoingui.cpp \
Expand Down Expand Up @@ -162,6 +163,7 @@ BITCOIN_QT_H = \
qt/addressbookpage.h \
qt/addresstablemodel.h \
qt/askpassphrasedialog.h \
qt/bantablemodel.h \
qt/bitcoinaddressvalidator.h \
qt/bitcoinamountfield.h \
qt/bitcoingui.h \
Expand Down Expand Up @@ -260,6 +262,7 @@ RES_ICONS = \
qt/res/icons/verify.png

BITCOIN_QT_CPP = \
qt/bantablemodel.cpp \
qt/bitcoinaddressvalidator.cpp \
qt/bitcoinamountfield.cpp \
qt/bitcoingui.cpp \
Expand Down
56 changes: 48 additions & 8 deletions src/netbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1311,17 +1311,57 @@ bool CSubNet::Match(const CNetAddr &addr) const
return true;
}

static inline int NetmaskBits(uint8_t x)
{
switch(x) {
case 0x00: return 0; break;
case 0x80: return 1; break;
case 0xc0: return 2; break;
case 0xe0: return 3; break;
case 0xf0: return 4; break;
case 0xf8: return 5; break;
case 0xfc: return 6; break;
case 0xfe: return 7; break;
case 0xff: return 8; break;
default: return -1; break;
}
}

std::string CSubNet::ToString() const
{
/* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */
int cidr = 0;
bool valid_cidr = true;
int n = network.IsIPv4() ? 12 : 0;
for (; n < 16 && netmask[n] == 0xff; ++n)
cidr += 8;
if (n < 16) {
int bits = NetmaskBits(netmask[n]);
if (bits < 0)
valid_cidr = false;
else
cidr += bits;
++n;
}
for (; n < 16 && valid_cidr; ++n)
if (netmask[n] != 0x00)
valid_cidr = false;

/* Format output */
std::string strNetmask;
if (network.IsIPv4())
strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
else
strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
if (valid_cidr) {
strNetmask = strprintf("%u", cidr);
} else {
if (network.IsIPv4())
strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
else
strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
}

return network.ToString() + "/" + strNetmask;
}

Expand Down
181 changes: 181 additions & 0 deletions src/qt/bantablemodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright (c) 2011-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bantablemodel.h"

#include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"

#include "sync.h"
#include "utiltime.h"

#include <QDebug>
#include <QList>

bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan& right) const
{
const CCombinedBan* pLeft = &left;
const CCombinedBan* pRight = &right;

if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);

switch(column)
{
case BanTableModel::Address:
return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0;
case BanTableModel::Bantime:
return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil;
}

return false;
}

// private implementation
class BanTablePriv
{
public:
/** Local cache of peer information */
QList<CCombinedBan> cachedBanlist;
/** Column to sort nodes by */
int sortColumn;
/** Order (ascending or descending) to sort nodes by */
Qt::SortOrder sortOrder;

/** Pull a full list of banned nodes from CNode into our cache */
void refreshBanlist()
{
banmap_t banMap;
CNode::GetBanned(banMap);

cachedBanlist.clear();
#if QT_VERSION >= 0x040700
cachedBanlist.reserve(banMap.size());
#endif
for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
{
CCombinedBan banEntry;
banEntry.subnet = (*it).first;
banEntry.banEntry = (*it).second;
cachedBanlist.append(banEntry);
}

if (sortColumn >= 0)
// sort cachedBanlist (use stable sort to prevent rows jumping around unneceesarily)
qStableSort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder));
}

int size() const
{
return cachedBanlist.size();
}

CCombinedBan *index(int idx)
{
if (idx >= 0 && idx < cachedBanlist.size())
return &cachedBanlist[idx];

return 0;
}
};

BanTableModel::BanTableModel(ClientModel *parent) :
QAbstractTableModel(parent),
clientModel(parent)
{
columns << tr("IP/Netmask") << tr("Banned Until");
priv = new BanTablePriv();
// default to unsorted
priv->sortColumn = -1;

// load initial data
refresh();
}

int BanTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return priv->size();
}

int BanTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return columns.length();;
}

QVariant BanTableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();

CCombinedBan *rec = static_cast<CCombinedBan*>(index.internalPointer());

if (role == Qt::DisplayRole) {
switch(index.column())
{
case Address:
return QString::fromStdString(rec->subnet.ToString());
case Bantime:
QDateTime date = QDateTime::fromMSecsSinceEpoch(0);
date = date.addSecs(rec->banEntry.nBanUntil);
return date.toString(Qt::SystemLocaleLongDate);
}
}

return QVariant();
}

QVariant BanTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal)
{
if(role == Qt::DisplayRole && section < columns.size())
{
return columns[section];
}
}
return QVariant();
}

Qt::ItemFlags BanTableModel::flags(const QModelIndex &index) const
{
if(!index.isValid())
return 0;

Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return retval;
}

QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
CCombinedBan *data = priv->index(row);

if (data)
return createIndex(row, column, data);
return QModelIndex();
}

void BanTableModel::refresh()
{
Q_EMIT layoutAboutToBeChanged();
priv->refreshBanlist();
Q_EMIT layoutChanged();
}

void BanTableModel::sort(int column, Qt::SortOrder order)
{
priv->sortColumn = column;
priv->sortOrder = order;
refresh();
}

bool BanTableModel::shouldShow()
{
if (priv->size() > 0)
return true;
return false;
}
72 changes: 72 additions & 0 deletions src/qt/bantablemodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2011-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_QT_BANTABLEMODEL_H
#define BITCOIN_QT_BANTABLEMODEL_H

#include "net.h"

#include <QAbstractTableModel>
#include <QStringList>

class ClientModel;
class BanTablePriv;

struct CCombinedBan {
CSubNet subnet;
CBanEntry banEntry;
};

class BannedNodeLessThan
{
public:
BannedNodeLessThan(int nColumn, Qt::SortOrder fOrder) :
column(nColumn), order(fOrder) {}
bool operator()(const CCombinedBan& left, const CCombinedBan& right) const;

private:
int column;
Qt::SortOrder order;
};

/**
Qt model providing information about connected peers, similar to the
"getpeerinfo" RPC call. Used by the rpc console UI.
*/
class BanTableModel : public QAbstractTableModel
{
Q_OBJECT

public:
explicit BanTableModel(ClientModel *parent = 0);
void startAutoRefresh();
void stopAutoRefresh();

enum ColumnIndex {
Address = 0,
Bantime = 1
};

/** @name Methods overridden from QAbstractTableModel
@{*/
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;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
void sort(int column, Qt::SortOrder order);
bool shouldShow();
/*@}*/

public Q_SLOTS:
void refresh();

private:
ClientModel *clientModel;
QStringList columns;
BanTablePriv *priv;
};

#endif // BITCOIN_QT_BANTABLEMODEL_H
Loading