File indexing completed on 2024-04-28 16:52:16
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2022 Daniel Vrátil <dvratil@kde.org> 0003 0004 #include "ipvalidator.h" 0005 0006 #include <QRegularExpression> 0007 0008 // AF_INET + inet_pton() for Linux, FreeBSD 0009 #include <arpa/inet.h> 0010 #include <netinet/in.h> 0011 #include <sys/socket.h> 0012 #include <sys/types.h> 0013 0014 namespace 0015 { 0016 0017 /// Whether @c netmask is within range for the given IP version 0018 QValidator::State checkNetmaskInRange(IPValidator::IPVersion ipVersion, int netmask) 0019 { 0020 const auto maxNetmask = ipVersion == IPValidator::IPVersion::IPv4 ? 32 : 128; 0021 if (netmask >= 0 && netmask <= maxNetmask) { 0022 return QValidator::Acceptable; 0023 } 0024 0025 return QValidator::Invalid; 0026 } 0027 0028 /// Whether @c nmStr contains a valid netmask 0029 QValidator::State checkNetmaskValid(IPValidator::IPVersion ipVersion, const QString &nmStr) 0030 { 0031 if (nmStr.isEmpty()) { 0032 return QValidator::Intermediate; 0033 } 0034 0035 bool valid = false; 0036 const auto netmask = nmStr.toInt(&valid); 0037 if (!valid) { 0038 return QValidator::Invalid; 0039 } 0040 return checkNetmaskInRange(ipVersion, netmask); 0041 } 0042 0043 /// Whether @c addr contains a valid IP address 0044 QValidator::State checkAddressValid(IPValidator::IPVersion ipVersion, const QString &addr) 0045 { 0046 const auto addrChar = addr.toLatin1(); 0047 const int af = ipVersion == IPValidator::IPVersion::IPv4 ? AF_INET : AF_INET6; 0048 char buf[sizeof(struct in6_addr)]; // Enough for both ipv4 and v6 0049 // Ask standard library to convert the string representation to a binary representation 0050 // and check whether it succeeded or not, rather than using a regular expression. 0051 if (inet_pton(af, addrChar.constData(), buf) == 1) { 0052 return QValidator::Acceptable; 0053 } 0054 0055 return QValidator::Intermediate; 0056 } 0057 0058 QRegularExpression regexForAddress(IPValidator::IPVersion version) 0059 { 0060 if (version == IPValidator::IPVersion::IPv4) { 0061 return QRegularExpression{QStringLiteral(R"(^[0-9\./]+$)")}; 0062 } else { 0063 return QRegularExpression{QStringLiteral(R"(^[a-fA-F0-9:\./]+$)")}; 0064 } 0065 } 0066 0067 } // namespace 0068 0069 IPValidator::IPValidator(QObject *parent) 0070 : QValidator(parent) 0071 { 0072 } 0073 0074 IPValidator::IPVersion IPValidator::ipVersion() const 0075 { 0076 return mIPVersion; 0077 } 0078 0079 void IPValidator::setIPVersion(IPVersion version) 0080 { 0081 if (mIPVersion != version) { 0082 mIPVersion = version; 0083 Q_EMIT ipVersionChanged(mIPVersion); 0084 } 0085 } 0086 0087 QValidator::State IPValidator::validate(QString &arg, int &pos) const 0088 { 0089 Q_UNUSED(pos); 0090 0091 const auto rx = regexForAddress(mIPVersion); 0092 // Quick regex check to immediatelly reject anything that does not look like an IP address. 0093 if (!rx.match(arg).hasMatch()) { 0094 return QValidator::Invalid; 0095 } 0096 0097 const auto parts = arg.split(QLatin1Char('/')); 0098 // Check there's at most one split 0099 if (parts.size() < 1 || parts.size() > 2) { 0100 return QValidator::Invalid; 0101 } 0102 0103 // Validate the address 0104 const auto addressValidity = checkAddressValid(mIPVersion, parts[0]); 0105 if (addressValidity == QValidator::Invalid) { 0106 return QValidator::Invalid; 0107 } 0108 0109 // Validate netmask, if present. 0110 const auto netmaskValidity = parts.size() == 2 ? checkNetmaskValid(mIPVersion, parts[1]) : QValidator::Acceptable; 0111 if (netmaskValidity == QValidator::Invalid) { 0112 return QValidator::Invalid; 0113 } 0114 0115 return std::min(addressValidity, netmaskValidity); 0116 }