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