1// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
2// Copyright (C) 2023 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#ifndef QDNSLOOKUP_P_H
7#define QDNSLOOKUP_P_H
8
9//
10// W A R N I N G
11// -------------
12//
13// This file is not part of the Qt API. It exists for the convenience
14// of the QDnsLookup class. This header file may change from
15// version to version without notice, or even be removed.
16//
17// We mean it.
18//
19
20#include <QtNetwork/private/qtnetworkglobal_p.h>
21#include "QtCore/qmutex.h"
22#include "QtCore/qrunnable.h"
23#if QT_CONFIG(thread)
24#include "QtCore/qthreadpool.h"
25#endif
26#include "QtNetwork/qdnslookup.h"
27#include "QtNetwork/qhostaddress.h"
28#include "private/qobject_p.h"
29#include "private/qurl_p.h"
30
31#if QT_CONFIG(ssl)
32# include "qsslconfiguration.h"
33#endif
34
35QT_REQUIRE_CONFIG(dnslookup);
36
37QT_BEGIN_NAMESPACE
38
39//#define QDNSLOOKUP_DEBUG
40
41constexpr qsizetype MaxDomainNameLength = 255;
42constexpr quint16 DnsPort = 53;
43constexpr quint16 DnsOverTlsPort = 853;
44
45class QDnsLookupRunnable;
46QDebug operator<<(QDebug &, QDnsLookupRunnable *);
47
48class QDnsLookupReply
49{
50public:
51 QDnsLookup::Error error = QDnsLookup::NoError;
52 bool authenticData = false;
53 QString errorString;
54
55 QList<QDnsDomainNameRecord> canonicalNameRecords;
56 QList<QDnsHostAddressRecord> hostAddressRecords;
57 QList<QDnsMailExchangeRecord> mailExchangeRecords;
58 QList<QDnsDomainNameRecord> nameServerRecords;
59 QList<QDnsDomainNameRecord> pointerRecords;
60 QList<QDnsServiceRecord> serviceRecords;
61 QList<QDnsTlsAssociationRecord> tlsAssociationRecords;
62 QList<QDnsTextRecord> textRecords;
63
64#if QT_CONFIG(ssl)
65 std::optional<QSslConfiguration> sslConfiguration;
66#endif
67
68 // helper methods
69 void setError(QDnsLookup::Error err, QString &&msg)
70 {
71 error = err;
72 errorString = std::move(msg);
73 }
74
75 void makeResolverSystemError(int code = -1)
76 {
77 Q_ASSERT(allAreEmpty());
78 setError(err: QDnsLookup::ResolverError, msg: qt_error_string(errorCode: code));
79 }
80
81 void makeTimeoutError()
82 {
83 Q_ASSERT(allAreEmpty());
84 setError(err: QDnsLookup::TimeoutError, msg: QDnsLookup::tr(s: "Request timed out"));
85 }
86
87 void makeDnsRcodeError(quint8 rcode)
88 {
89 Q_ASSERT(allAreEmpty());
90 switch (rcode) {
91 case 1: // FORMERR
92 error = QDnsLookup::InvalidRequestError;
93 errorString = QDnsLookup::tr(s: "Server could not process query");
94 return;
95 case 2: // SERVFAIL
96 case 4: // NOTIMP
97 error = QDnsLookup::ServerFailureError;
98 errorString = QDnsLookup::tr(s: "Server failure");
99 return;
100 case 3: // NXDOMAIN
101 error = QDnsLookup::NotFoundError;
102 errorString = QDnsLookup::tr(s: "Non existent domain");
103 return;
104 case 5: // REFUSED
105 error = QDnsLookup::ServerRefusedError;
106 errorString = QDnsLookup::tr(s: "Server refused to answer");
107 return;
108 default:
109 error = QDnsLookup::InvalidReplyError;
110 errorString = QDnsLookup::tr(s: "Invalid reply received (rcode %1)")
111 .arg(a: rcode);
112 return;
113 }
114 }
115
116 void makeInvalidReplyError(QString &&msg = QString())
117 {
118 if (msg.isEmpty())
119 msg = QDnsLookup::tr(s: "Invalid reply received");
120 else
121 msg = QDnsLookup::tr(s: "Invalid reply received (%1)").arg(a: std::move(msg));
122 *this = QDnsLookupReply(); // empty our lists
123 setError(err: QDnsLookup::InvalidReplyError, msg: std::move(msg));
124 }
125
126private:
127 bool allAreEmpty() const
128 {
129 return canonicalNameRecords.isEmpty()
130 && hostAddressRecords.isEmpty()
131 && mailExchangeRecords.isEmpty()
132 && nameServerRecords.isEmpty()
133 && pointerRecords.isEmpty()
134 && serviceRecords.isEmpty()
135 && tlsAssociationRecords.isEmpty()
136 && textRecords.isEmpty();
137 }
138};
139
140class QDnsLookupPrivate : public QObjectPrivate
141{
142public:
143 QDnsLookupPrivate()
144 : type(QDnsLookup::A)
145 , port(0)
146 , protocol(QDnsLookup::Standard)
147 { }
148
149 void nameChanged()
150 {
151 emit q_func()->nameChanged(name);
152 }
153 Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QString, name,
154 &QDnsLookupPrivate::nameChanged);
155
156 void nameserverChanged()
157 {
158 emit q_func()->nameserverChanged(nameserver);
159 }
160 Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
161 &QDnsLookupPrivate::nameserverChanged);
162
163 void typeChanged()
164 {
165 emit q_func()->typeChanged(type);
166 }
167
168 Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Type,
169 type, &QDnsLookupPrivate::typeChanged);
170
171 void nameserverPortChanged()
172 {
173 emit q_func()->nameserverPortChanged(port);
174 }
175
176 Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, quint16,
177 port, &QDnsLookupPrivate::nameserverPortChanged);
178
179 void nameserverProtocolChanged()
180 {
181 emit q_func()->nameserverProtocolChanged(protocol);
182 }
183
184 Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Protocol,
185 protocol, &QDnsLookupPrivate::nameserverProtocolChanged);
186
187 QDnsLookupReply reply;
188 QDnsLookupRunnable *runnable = nullptr;
189 bool isFinished = false;
190
191#if QT_CONFIG(ssl)
192 std::optional<QSslConfiguration> sslConfiguration;
193#endif
194
195 Q_DECLARE_PUBLIC(QDnsLookup)
196};
197
198class QDnsLookupRunnable : public QObject, public QRunnable
199{
200 Q_OBJECT
201
202public:
203#ifdef Q_OS_WIN
204 using EncodedLabel = QString;
205#else
206 using EncodedLabel = QByteArray;
207#endif
208 // minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
209 static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
210 using ReplyBuffer = QVarLengthArray<unsigned char, ReplyBufferSize>;
211
212 QDnsLookupRunnable(const QDnsLookupPrivate *d);
213 void run() override;
214 bool sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query, ReplyBuffer &response);
215
216signals:
217 void finished(const QDnsLookupReply &reply);
218
219private:
220 template <typename T> static QString decodeLabel(T encodedLabel)
221 {
222 return qt_ACE_do(encodedLabel.toString(), NormalizeAce, ForbidLeadingDot);
223 }
224 void query(QDnsLookupReply *reply);
225
226 EncodedLabel requestName;
227 QHostAddress nameserver;
228 QDnsLookup::Type requestType;
229 quint16 port;
230 QDnsLookup::Protocol protocol;
231
232#if QT_CONFIG(ssl)
233 std::optional<QSslConfiguration> sslConfiguration;
234#endif
235 friend QDebug operator<<(QDebug &, QDnsLookupRunnable *);
236};
237
238class QDnsRecordPrivate : public QSharedData
239{
240public:
241 QDnsRecordPrivate()
242 : timeToLive(0)
243 { }
244
245 QString name;
246 quint32 timeToLive;
247};
248
249class QDnsDomainNameRecordPrivate : public QDnsRecordPrivate
250{
251public:
252 QDnsDomainNameRecordPrivate()
253 { }
254
255 QString value;
256};
257
258class QDnsHostAddressRecordPrivate : public QDnsRecordPrivate
259{
260public:
261 QDnsHostAddressRecordPrivate()
262 { }
263
264 QHostAddress value;
265};
266
267class QDnsMailExchangeRecordPrivate : public QDnsRecordPrivate
268{
269public:
270 QDnsMailExchangeRecordPrivate()
271 : preference(0)
272 { }
273
274 QString exchange;
275 quint16 preference;
276};
277
278class QDnsServiceRecordPrivate : public QDnsRecordPrivate
279{
280public:
281 QDnsServiceRecordPrivate()
282 : port(0),
283 priority(0),
284 weight(0)
285 { }
286
287 QString target;
288 quint16 port;
289 quint16 priority;
290 quint16 weight;
291};
292
293class QDnsTextRecordPrivate : public QDnsRecordPrivate
294{
295public:
296 QDnsTextRecordPrivate()
297 { }
298
299 QList<QByteArray> values;
300};
301
302class QDnsTlsAssociationRecordPrivate : public QDnsRecordPrivate
303{
304public:
305 QDnsTlsAssociationRecord::CertificateUsage usage;
306 QDnsTlsAssociationRecord::Selector selector;
307 QDnsTlsAssociationRecord::MatchingType matchType;
308 QByteArray value;
309};
310
311QT_END_NAMESPACE
312
313#endif // QDNSLOOKUP_P_H
314

source code of qtbase/src/network/kernel/qdnslookup_p.h