amnezia-client/client/mozilla/shared/ipaddress.cpp
2023-09-20 14:27:28 -04:00

284 lines
7.6 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ipaddress.h"
#include <QtMath>
#include "leakdetector.h"
IPAddress::IPAddress() { MZ_COUNT_CTOR(IPAddress); }
IPAddress::IPAddress(const QString& ip) {
MZ_COUNT_CTOR(IPAddress);
if (ip.contains("/")) {
QPair<QHostAddress, int> p = QHostAddress::parseSubnet(ip);
m_address = p.first;
m_prefixLength = p.second;
} else {
m_address = QHostAddress(ip);
m_prefixLength = 999999;
}
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
if (m_prefixLength >= 32) {
m_prefixLength = 32;
}
} else if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
if (m_prefixLength >= 128) {
m_prefixLength = 128;
}
}
}
IPAddress::IPAddress(const IPAddress& other) {
MZ_COUNT_CTOR(IPAddress);
*this = other;
}
IPAddress& IPAddress::operator=(const IPAddress& other) {
if (this == &other) return *this;
m_address = other.m_address;
m_prefixLength = other.m_prefixLength;
return *this;
}
IPAddress::IPAddress(const QHostAddress& address) : m_address(address) {
MZ_COUNT_CTOR(IPAddress);
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
m_prefixLength = 32;
} else {
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
m_prefixLength = 128;
}
}
IPAddress::IPAddress(const QHostAddress& address, int prefixLength)
: m_address(address), m_prefixLength(prefixLength) {
MZ_COUNT_CTOR(IPAddress);
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
Q_ASSERT(prefixLength >= 0 && prefixLength <= 32);
} else {
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
Q_ASSERT(prefixLength >= 0 && prefixLength <= 128);
}
}
IPAddress::~IPAddress() { MZ_COUNT_DTOR(IPAddress); }
QAbstractSocket::NetworkLayerProtocol IPAddress::type() const {
return m_address.protocol();
}
QHostAddress IPAddress::netmask() const {
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
Q_IPV6ADDR rawNetmask = {0};
Q_ASSERT(m_prefixLength <= 128);
memset(&rawNetmask, 0xff, m_prefixLength / 8);
if (m_prefixLength % 8) {
rawNetmask[m_prefixLength / 8] = 0xFF ^ (0xFF >> (m_prefixLength % 8));
}
return QHostAddress(rawNetmask);
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
quint32 rawNetmask = 0xffffffff;
Q_ASSERT(m_prefixLength <= 32);
if (m_prefixLength < 32) {
rawNetmask ^= (0xffffffff >> m_prefixLength);
}
return QHostAddress(rawNetmask);
} else {
return QHostAddress();
}
}
QHostAddress IPAddress::hostmask() const {
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
Q_IPV6ADDR rawHostmask = {0};
int offset = (m_prefixLength + 7) / 8;
Q_ASSERT(m_prefixLength <= 128);
memset(&rawHostmask[offset], 0xff, sizeof(rawHostmask) - offset);
if (m_prefixLength % 8) {
rawHostmask[m_prefixLength / 8] = 0xFF >> (m_prefixLength % 8);
}
return QHostAddress(rawHostmask);
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
if (m_prefixLength < 32) {
return QHostAddress(0xffffffff >> m_prefixLength);
} else {
quint32 zero = 0;
return QHostAddress(zero);
}
} else {
return QHostAddress();
}
}
QHostAddress IPAddress::broadcastAddress() const {
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
Q_IPV6ADDR rawAddress = m_address.toIPv6Address();
int offset = (m_prefixLength + 7) / 8;
memset(&rawAddress[offset], 0xff, sizeof(rawAddress) - offset);
if (m_prefixLength % 8) {
rawAddress[m_prefixLength / 8] |= 0xFF >> (m_prefixLength % 8);
}
return QHostAddress(rawAddress);
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
quint32 rawAddress = m_address.toIPv4Address();
if (m_prefixLength < 32) {
rawAddress |= (0xffffffff >> m_prefixLength);
}
return QHostAddress(rawAddress);
} else {
return QHostAddress();
}
}
bool IPAddress::overlaps(const IPAddress& other) const {
if (m_prefixLength < other.m_prefixLength) {
return contains(other.m_address);
} else {
return other.contains(m_address);
}
}
bool IPAddress::contains(const QHostAddress& address) const {
if (address.protocol() != m_address.protocol()) {
return false;
}
if (m_prefixLength == 0) {
return true;
}
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
Q_IPV6ADDR a = m_address.toIPv6Address();
Q_IPV6ADDR b = address.toIPv6Address();
int bytes = m_prefixLength / 8;
if (bytes > 0) {
if (memcmp(&a, &b, bytes) != 0) {
return false;
}
}
if (m_prefixLength % 8) {
quint8 diff = (a[bytes] ^ b[bytes]) >> (8 - m_prefixLength % 8);
return (diff == 0);
}
return true;
}
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
quint32 diff = m_address.toIPv4Address() ^ address.toIPv4Address();
if (m_prefixLength < 32) {
diff >>= (32 - m_prefixLength);
}
return (diff == 0);
}
return false;
}
bool IPAddress::operator==(const IPAddress& other) const {
return m_address == other.m_address && m_prefixLength == other.m_prefixLength;
}
bool IPAddress::subnetOf(const IPAddress& other) const {
if (other.m_address.protocol() != m_address.protocol()) {
return false;
}
if (m_prefixLength < other.m_prefixLength) {
return false;
}
return other.contains(m_address);
}
QList<IPAddress> IPAddress::subnets() const {
QList<IPAddress> list;
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
if (m_prefixLength >= 32) {
list.append(*this);
return list;
}
quint32 rawAddress = m_address.toIPv4Address();
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
rawAddress |= (0x80000000 >> m_prefixLength);
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
return list;
}
Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol);
if (m_prefixLength >= 128) {
list.append(*this);
return list;
}
Q_IPV6ADDR rawAddress = m_address.toIPv6Address();
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
rawAddress[m_prefixLength / 8] |= (0x80 >> (m_prefixLength % 8));
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
return list;
}
// static
QList<IPAddress> IPAddress::excludeAddresses(
const QList<IPAddress>& sourceList, const QList<IPAddress>& excludeList) {
QList<IPAddress> results = sourceList;
for (const IPAddress& exclude : excludeList) {
QList<IPAddress> newResults;
for (const IPAddress& ip : results) {
if (!ip.overlaps(exclude)) {
newResults.append(ip);
} else if (exclude.subnetOf(ip) && exclude != ip) {
QList<IPAddress> range = ip.excludeAddresses(exclude);
newResults.append(range);
}
}
results = newResults;
}
return results;
}
QList<IPAddress> IPAddress::excludeAddresses(const IPAddress& ip) const {
QList<IPAddress> sn = subnets();
Q_ASSERT(sn.length() >= 2);
QList<IPAddress> result;
while (sn[0] != ip && sn[1] != ip) {
if (ip.subnetOf(sn[0])) {
result.append(sn[1]);
sn = sn[0].subnets();
} else if (ip.subnetOf(sn[1])) {
result.append(sn[0]);
sn = sn[1].subnets();
} else {
Q_ASSERT(false);
}
}
if (sn[0] == ip) {
result.append(sn[1]);
} else if (sn[1] == ip) {
result.append(sn[0]);
} else {
Q_ASSERT(false);
}
return result;
}