2017-08-28 09:03:45 +00:00
|
|
|
#include "ip4range.h"
|
|
|
|
|
|
|
|
#include <QHash>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
|
|
|
#include "netutil.h"
|
|
|
|
|
|
|
|
Ip4Range::Ip4Range(QObject *parent) :
|
|
|
|
QObject(parent),
|
|
|
|
m_errorLineNo(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ip4Range::setErrorLineNo(int lineNo)
|
|
|
|
{
|
|
|
|
if (m_errorLineNo != lineNo) {
|
|
|
|
m_errorLineNo = lineNo;
|
|
|
|
emit errorLineNoChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ip4Range::setErrorMessage(const QString &errorMessage)
|
|
|
|
{
|
|
|
|
if (m_errorMessage != errorMessage) {
|
|
|
|
m_errorMessage = errorMessage;
|
|
|
|
emit errorMessageChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-29 08:40:23 +00:00
|
|
|
QString Ip4Range::errorLineAndMessage() const
|
|
|
|
{
|
|
|
|
return tr("Error at line %1: %2")
|
|
|
|
.arg(QString::number(m_errorLineNo),
|
|
|
|
m_errorMessage);
|
|
|
|
}
|
|
|
|
|
2017-08-28 09:03:45 +00:00
|
|
|
QString Ip4Range::toText()
|
|
|
|
{
|
|
|
|
QString text;
|
|
|
|
|
2017-08-29 08:40:23 +00:00
|
|
|
const int n = size();
|
2017-08-28 09:03:45 +00:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2017-08-29 08:40:23 +00:00
|
|
|
const Ip4Pair ip = at(i);
|
2017-08-28 09:03:45 +00:00
|
|
|
|
|
|
|
text += QString("%1-%2\n")
|
|
|
|
.arg(NetUtil::ip4ToText(ip.from),
|
|
|
|
NetUtil::ip4ToText(ip.to));
|
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Ip4Range::fromText(const QString &text)
|
|
|
|
{
|
2017-08-29 08:40:23 +00:00
|
|
|
m_fromArray.clear();
|
|
|
|
m_toArray.clear();
|
2017-08-28 09:03:45 +00:00
|
|
|
|
|
|
|
ip4range_map_t ipRangeMap;
|
|
|
|
|
|
|
|
int lineNo = 0;
|
2017-08-29 08:40:23 +00:00
|
|
|
foreach (const QStringRef &line,
|
|
|
|
text.splitRef(QLatin1Char('\n'))) {
|
2017-08-28 09:03:45 +00:00
|
|
|
++lineNo;
|
|
|
|
|
2017-08-29 08:40:23 +00:00
|
|
|
if (line.isEmpty())
|
|
|
|
continue;
|
|
|
|
|
2017-08-28 09:03:45 +00:00
|
|
|
quint32 from, to;
|
|
|
|
if (!parseAddressMask(line, from, to)) {
|
|
|
|
setErrorLineNo(lineNo);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipRangeMap.insert(from, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
fillRange(ipRangeMap);
|
|
|
|
|
|
|
|
setErrorLineNo(0);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-19 03:21:34 +00:00
|
|
|
// Parse "127.0.0.0-127.255.255.255" or "127.0.0.0/24" or "127.0.0.0"
|
2017-08-28 09:03:45 +00:00
|
|
|
bool Ip4Range::parseAddressMask(const QStringRef &line,
|
|
|
|
quint32 &from, quint32 &to)
|
|
|
|
{
|
2017-09-19 03:21:34 +00:00
|
|
|
const QRegularExpression re("([\\d.]+)\\s*([/-]?)\\s*(\\S*)");
|
2017-08-28 09:03:45 +00:00
|
|
|
const QRegularExpressionMatch match = re.match(line);
|
|
|
|
|
|
|
|
if (!match.hasMatch()) {
|
|
|
|
setErrorMessage(tr("Bad format"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString ip = match.captured(1);
|
2017-09-19 03:21:34 +00:00
|
|
|
const QString sepStr = match.captured(2);
|
|
|
|
const QChar sep = sepStr.isEmpty() ? QLatin1Char('/') : sepStr.at(0);
|
2017-08-28 09:03:45 +00:00
|
|
|
const QString mask = match.captured(3);
|
|
|
|
|
2017-09-19 03:21:34 +00:00
|
|
|
if (sepStr.isEmpty() != mask.isEmpty()) {
|
|
|
|
setErrorMessage(tr("Bad mask"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-28 09:03:45 +00:00
|
|
|
bool ok;
|
|
|
|
|
|
|
|
from = NetUtil::textToIp4(ip, &ok);
|
|
|
|
if (!ok) {
|
2017-08-29 08:40:23 +00:00
|
|
|
setErrorMessage(tr("Bad IP address"));
|
2017-08-28 09:03:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sep == QLatin1Char('-')) { // e.g. "127.0.0.0-127.255.255.255"
|
|
|
|
to = NetUtil::textToIp4(mask, &ok);
|
|
|
|
if (!ok) {
|
2017-08-29 08:40:23 +00:00
|
|
|
setErrorMessage(tr("Bad second IP address"));
|
2017-08-28 09:03:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (from > to) {
|
|
|
|
setErrorMessage(tr("Bad range"));
|
|
|
|
return false;
|
|
|
|
}
|
2017-09-19 03:21:34 +00:00
|
|
|
} else if (sep == QLatin1Char('/')) { // e.g. "127.0.0.0/24", "127.0.0.0"
|
|
|
|
bool ok = true;
|
|
|
|
const int nbits = mask.isEmpty() ? 24 : mask.toInt(&ok);
|
2017-08-28 09:03:45 +00:00
|
|
|
|
|
|
|
if (!ok || nbits < 0 || nbits > 32) {
|
|
|
|
setErrorMessage(tr("Bad mask"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-19 03:50:49 +00:00
|
|
|
to = from | (nbits == 32 ? 0 : ((1 << (32 - nbits)) - 1));
|
2017-08-28 09:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ip4Range::fillRange(const ip4range_map_t &ipRangeMap)
|
|
|
|
{
|
|
|
|
ip4range_map_t::const_iterator it = ipRangeMap.constBegin();
|
|
|
|
ip4range_map_t::const_iterator end = ipRangeMap.constEnd();
|
|
|
|
|
2017-08-29 08:40:23 +00:00
|
|
|
const int mapSize = ipRangeMap.size();
|
|
|
|
m_fromArray.reserve(mapSize);
|
|
|
|
m_toArray.reserve(mapSize);
|
2017-08-28 09:03:45 +00:00
|
|
|
|
2017-08-29 08:40:23 +00:00
|
|
|
Ip4Pair prevIp;
|
|
|
|
int prevIndex = -1;
|
2017-08-28 09:03:45 +00:00
|
|
|
|
|
|
|
for (; it != end; ++it) {
|
|
|
|
Ip4Pair ip{it.key(), it.value()};
|
|
|
|
|
|
|
|
// try to merge colliding adresses
|
2017-09-19 03:51:16 +00:00
|
|
|
if (prevIndex >= 0 && ip.from <= prevIp.to + 1) {
|
2017-08-29 08:40:23 +00:00
|
|
|
if (ip.to > prevIp.to) {
|
|
|
|
m_toArray.replace(prevIndex, ip.to);
|
|
|
|
|
|
|
|
prevIp.to = ip.to;
|
2017-08-28 09:03:45 +00:00
|
|
|
}
|
|
|
|
// else skip it
|
|
|
|
} else {
|
2017-08-29 08:40:23 +00:00
|
|
|
m_fromArray.append(ip.from);
|
|
|
|
m_toArray.append(ip.to);
|
|
|
|
|
|
|
|
prevIp = ip;
|
|
|
|
++prevIndex;
|
2017-08-28 09:03:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|