5#include <QtNetwork/private/qtnetworkglobal_p.h>
7#if QT_CONFIG(topleveldomain)
9#include "QtCore/qfile.h"
10#include "QtCore/qloggingcategory.h"
11#include "QtCore/qstandardpaths.h"
12#include "QtCore/qstring.h"
14#if !QT_CONFIG(publicsuffix_qt) && !QT_CONFIG(publicsuffix_system)
15# error Enable at least one feature: publicsuffix-qt, publicsuffix-system
18#if QT_CONFIG(publicsuffix_qt)
19# include "psl_data.cpp"
23extern "C" int LookupStringInFixedSet(
const unsigned char *graph, std::size_t length,
24 const char *key, std::size_t key_length);
28using namespace Qt::StringLiterals;
30#if QT_CONFIG(publicsuffix_system)
31Q_STATIC_LOGGING_CATEGORY(lcTld,
"qt.network.tld")
34static constexpr int PSL_NOT_FOUND = -1;
35static constexpr int PSL_FLAG_EXCEPTION = 1 << 0;
36static constexpr int PSL_FLAG_WILDCARD = 1 << 1;
38class QPublicSuffixDatabase final
41#if QT_CONFIG(publicsuffix_system)
42 QPublicSuffixDatabase();
45 int lookupDomain(QByteArrayView domain)
const;
49#if QT_CONFIG(publicsuffix_qt)
51 kDafsa,
sizeof(kDafsa)
56#if QT_CONFIG(publicsuffix_system)
57 std::unique_ptr<QFile> m_dev;
59 bool loadFile(
const QString &fileName);
63int QPublicSuffixDatabase::lookupDomain(QByteArrayView domain)
const
65 return LookupStringInFixedSet(
reinterpret_cast<
const unsigned char *>(m_data.constData()),
66 m_data.size(), domain.data(), domain.size());
69#if QT_CONFIG(publicsuffix_system)
71static QStringList locatePublicSuffixFiles()
73 return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
74 u"publicsuffix/public_suffix_list.dafsa"_s);
77QPublicSuffixDatabase::QPublicSuffixDatabase()
79 const auto fileNames = locatePublicSuffixFiles();
80 for (
const auto &fileName : fileNames) {
81 if (loadFile(fileName))
85#if QT_CONFIG(publicsuffix_qt)
86 qCDebug(lcTld,
"Using builtin publicsuffix list");
88 qCWarning(lcTld,
"No usable publicsuffix file found");
92bool QPublicSuffixDatabase::loadFile(
const QString &fileName)
94 static const QByteArrayView DafsaFileHeader =
".DAFSA@PSL_0 \n";
96 qCDebug(lcTld,
"Loading publicsuffix file: %s", qUtf8Printable(fileName));
98 auto systemFile = std::make_unique<QFile>(fileName);
100 if (!systemFile->open(QIODevice::ReadOnly)) {
101 qCDebug(lcTld,
"Failed to open publicsuffix file: %s",
102 qUtf8Printable(systemFile->errorString()));
106 auto fileSize = systemFile->size();
108 if (fileSize < DafsaFileHeader.size() + 2) {
109 qCWarning(lcTld,
"publicsuffix file is too small: %zu", std::size_t(fileSize));
113 auto header = systemFile->read(DafsaFileHeader.size());
114 if (header != DafsaFileHeader) {
115 qCWarning(lcTld,
"Invalid publicsuffix file header: %s", header.toHex().constData());
120 if (!systemFile->seek(fileSize - 1)) {
121 qCWarning(lcTld,
"Failed to seek to the end of file: %s",
122 qUtf8Printable(systemFile->errorString()));
127 if (systemFile->read(&version, 1) != 1) {
128 qCWarning(lcTld,
"Failed to read publicsuffix version");
132 if (version != 0x01) {
133 qCWarning(lcTld,
"Unsupported publicsuffix version: %d",
int(version));
137 const auto dataSize = fileSize - DafsaFileHeader.size() - 1;
139 auto mappedData = systemFile->map(DafsaFileHeader.size(), dataSize);
141 qCDebug(lcTld,
"Using mapped system publicsuffix data");
143 m_data = QByteArrayView(mappedData, dataSize);
144 m_dev = std::move(systemFile);
148 qCDebug(lcTld,
"Failed to map publicsuffix file: %s",
149 qUtf8Printable(systemFile->errorString()));
151 systemFile->seek(DafsaFileHeader.size());
152 m_storage = systemFile->read(dataSize);
153 if (m_storage.size() != dataSize) {
154 qCWarning(lcTld,
"Failed to read publicsuffix file");
159 qCDebug(lcTld,
"Using system publicsuffix data");
165Q_GLOBAL_STATIC(QPublicSuffixDatabase, publicSuffix);
169static const QPublicSuffixDatabase m_publicSuffix;
174
175
176
177
178
179
181Q_NETWORK_EXPORT
bool qIsEffectiveTLD(QStringView domain)
188 QByteArray decodedDomain = domain.toUtf8();
189 QByteArrayView domainView(decodedDomain);
191#if QT_CONFIG(publicsuffix_system)
192 if (publicSuffix.isDestroyed())
195 auto publicSuffix = &m_publicSuffix;
198 auto ret = publicSuffix->lookupDomain(domainView);
199 if (ret != PSL_NOT_FOUND) {
200 if (ret & PSL_FLAG_EXCEPTION)
202 if ((ret & PSL_FLAG_WILDCARD) == 0)
206 const auto dot = domainView.indexOf(
'.');
208 return ret != PSL_NOT_FOUND;
209 ret = publicSuffix->lookupDomain(domainView.sliced(dot + 1));
210 if (ret == PSL_NOT_FOUND)
212 return (ret & PSL_FLAG_WILDCARD) != 0;