Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtldurl.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include <QtNetwork/private/qtnetworkglobal_p.h>
6
7#if QT_CONFIG(topleveldomain)
8
9#include "QtCore/qfile.h"
10#include "QtCore/qloggingcategory.h"
11#include "QtCore/qstandardpaths.h"
12#include "QtCore/qstring.h"
13
14#if !QT_CONFIG(publicsuffix_qt) && !QT_CONFIG(publicsuffix_system)
15# error Enable at least one feature: publicsuffix-qt, publicsuffix-system
16#endif
17
18#if QT_CONFIG(publicsuffix_qt)
19# include "psl_data.cpp"
20#endif
21
22// Defined in src/3rdparty/libpsl/src/lookup_string_in_fixed_set.c
23extern "C" int LookupStringInFixedSet(const unsigned char *graph, std::size_t length,
24 const char *key, std::size_t key_length);
25
26QT_BEGIN_NAMESPACE
27
28using namespace Qt::StringLiterals;
29
30#if QT_CONFIG(publicsuffix_system)
31Q_STATIC_LOGGING_CATEGORY(lcTld, "qt.network.tld")
32#endif
33
34static constexpr int PSL_NOT_FOUND = -1;
35static constexpr int PSL_FLAG_EXCEPTION = 1 << 0;
36static constexpr int PSL_FLAG_WILDCARD = 1 << 1;
37
38class QPublicSuffixDatabase final
39{
40public:
41#if QT_CONFIG(publicsuffix_system)
42 QPublicSuffixDatabase();
43#endif // QT_CONFIG(publicsuffix_system)
44
45 int lookupDomain(QByteArrayView domain) const;
46
47private:
48 QByteArrayView m_data
49#if QT_CONFIG(publicsuffix_qt)
50 {
51 kDafsa, sizeof(kDafsa)
52 }
53#endif // QT_CONFIG(publicsuffix_qt)
54 ;
55
56#if QT_CONFIG(publicsuffix_system)
57 std::unique_ptr<QFile> m_dev;
58 QByteArray m_storage;
59 bool loadFile(const QString &fileName);
60#endif // QT_CONFIG(publicsuffix_system)
61};
62
63int QPublicSuffixDatabase::lookupDomain(QByteArrayView domain) const
64{
65 return LookupStringInFixedSet(reinterpret_cast<const unsigned char *>(m_data.constData()),
66 m_data.size(), domain.data(), domain.size());
67}
68
69#if QT_CONFIG(publicsuffix_system)
70
71static QStringList locatePublicSuffixFiles()
72{
73 return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
74 u"publicsuffix/public_suffix_list.dafsa"_s);
75}
76
77QPublicSuffixDatabase::QPublicSuffixDatabase()
78{
79 const auto fileNames = locatePublicSuffixFiles();
80 for (const auto &fileName : fileNames) {
81 if (loadFile(fileName))
82 return;
83 }
84
85#if QT_CONFIG(publicsuffix_qt)
86 qCDebug(lcTld, "Using builtin publicsuffix list");
87#else
88 qCWarning(lcTld, "No usable publicsuffix file found");
89#endif
90}
91
92bool QPublicSuffixDatabase::loadFile(const QString &fileName)
93{
94 static const QByteArrayView DafsaFileHeader = ".DAFSA@PSL_0 \n";
95
96 qCDebug(lcTld, "Loading publicsuffix file: %s", qUtf8Printable(fileName));
97
98 auto systemFile = std::make_unique<QFile>(fileName);
99
100 if (!systemFile->open(QIODevice::ReadOnly)) {
101 qCDebug(lcTld, "Failed to open publicsuffix file: %s",
102 qUtf8Printable(systemFile->errorString()));
103 return false;
104 }
105
106 auto fileSize = systemFile->size();
107 // Check if there is enough data for header, version byte and some data
108 if (fileSize < DafsaFileHeader.size() + 2) {
109 qCWarning(lcTld, "publicsuffix file is too small: %zu", std::size_t(fileSize));
110 return false;
111 }
112
113 auto header = systemFile->read(DafsaFileHeader.size());
114 if (header != DafsaFileHeader) {
115 qCWarning(lcTld, "Invalid publicsuffix file header: %s", header.toHex().constData());
116 return false;
117 }
118
119 // Check if the file is UTF-8 compatible
120 if (!systemFile->seek(fileSize - 1)) {
121 qCWarning(lcTld, "Failed to seek to the end of file: %s",
122 qUtf8Printable(systemFile->errorString()));
123 return false;
124 }
125
126 char version;
127 if (systemFile->read(&version, 1) != 1) {
128 qCWarning(lcTld, "Failed to read publicsuffix version");
129 return false;
130 }
131
132 if (version != 0x01) {
133 qCWarning(lcTld, "Unsupported publicsuffix version: %d", int(version));
134 return false;
135 }
136
137 const auto dataSize = fileSize - DafsaFileHeader.size() - 1;
138 // Try to map the file first
139 auto mappedData = systemFile->map(DafsaFileHeader.size(), dataSize);
140 if (mappedData) {
141 qCDebug(lcTld, "Using mapped system publicsuffix data");
142 systemFile->close();
143 m_data = QByteArrayView(mappedData, dataSize);
144 m_dev = std::move(systemFile);
145 return true;
146 }
147
148 qCDebug(lcTld, "Failed to map publicsuffix file: %s",
149 qUtf8Printable(systemFile->errorString()));
150
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");
155 m_storage.clear();
156 return false;
157 }
158
159 qCDebug(lcTld, "Using system publicsuffix data");
160 m_data = m_storage;
161
162 return true;
163}
164
165Q_GLOBAL_STATIC(QPublicSuffixDatabase, publicSuffix);
166
167#else
168
169static const QPublicSuffixDatabase m_publicSuffix;
170
171#endif // QT_CONFIG(publicsuffix_system)
172
173/*!
174 \internal
175
176 Return true if \a domain is a top-level-domain per Qt's copy of the Mozilla public suffix list.
177
178 The \a domain must be in lower-case format (as per QString::toLower()).
179*/
180
181Q_NETWORK_EXPORT bool qIsEffectiveTLD(QStringView domain)
182{
183 // for domain 'foo.bar.com':
184 // 1. return false if TLD table contains '!foo.bar.com'
185 // 2. return true if TLD table contains 'foo.bar.com'
186 // 3. return true if the table contains '*.bar.com'
187
188 QByteArray decodedDomain = domain.toUtf8();
189 QByteArrayView domainView(decodedDomain);
190
191#if QT_CONFIG(publicsuffix_system)
192 if (publicSuffix.isDestroyed())
193 return false;
194#else
195 auto publicSuffix = &m_publicSuffix;
196#endif // QT_CONFIG(publicsuffix_system)
197
198 auto ret = publicSuffix->lookupDomain(domainView);
199 if (ret != PSL_NOT_FOUND) {
200 if (ret & PSL_FLAG_EXCEPTION) // 1
201 return false;
202 if ((ret & PSL_FLAG_WILDCARD) == 0) // 2
203 return true;
204 }
205
206 const auto dot = domainView.indexOf('.');
207 if (dot < 0) // Actual TLD: may be effective if the subject of a wildcard rule:
208 return ret != PSL_NOT_FOUND;
209 ret = publicSuffix->lookupDomain(domainView.sliced(dot + 1)); // 3
210 if (ret == PSL_NOT_FOUND)
211 return false;
212 return (ret & PSL_FLAG_WILDCARD) != 0;
213}
214
215QT_END_NAMESPACE
216
217#endif // QT_CONFIG(topleveldomain)