1
/****************************************************************************
3
** Copyright (C) 2012 Jeremy LainƩ <jeremy.laine@m4x.org>
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtNetwork module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qdnslookup_p.h"
45
#include <qscopedpointer.h>
47
#include <private/qmutexpool_p.h>
49
#include <sys/types.h>
50
#include <netinet/in.h>
51
#include <arpa/nameser.h>
52
#include <arpa/nameser_compat.h>
59
typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
60
static dn_expand_proto local_dn_expand = 0;
61
typedef void (*res_nclose_proto)(res_state);
62
static res_nclose_proto local_res_nclose = 0;
63
typedef int (*res_ninit_proto)(res_state);
64
static res_ninit_proto local_res_ninit = 0;
65
typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
66
static res_nquery_proto local_res_nquery = 0;
68
// Custom deleter to close resolver state.
70
struct QDnsLookupStateDeleter
72
static inline void cleanup(struct __res_state *pointer)
74
local_res_nclose(pointer);
78
static void resolveLibrary()
80
QLibrary lib(QLatin1String("resolv"));
84
local_dn_expand = dn_expand_proto(lib.resolve("__dn_expand"));
86
local_dn_expand = dn_expand_proto(lib.resolve("dn_expand"));
88
local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
89
if (!local_res_nclose)
90
local_res_nclose = res_nclose_proto(lib.resolve("res_9_nclose"));
91
if (!local_res_nclose)
92
local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
94
local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
96
local_res_ninit = res_ninit_proto(lib.resolve("res_9_ninit"));
98
local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
100
local_res_nquery = res_nquery_proto(lib.resolve("__res_nquery"));
101
if (!local_res_nquery)
102
local_res_nquery = res_nquery_proto(lib.resolve("res_9_nquery"));
103
if (!local_res_nquery)
104
local_res_nquery = res_nquery_proto(lib.resolve("res_nquery"));
107
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
109
// Load dn_expand, res_ninit and res_nquery on demand.
110
static QBasicAtomicInt triedResolve = Q_BASIC_ATOMIC_INITIALIZER(false);
111
if (!triedResolve.loadAcquire()) {
112
QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_ninit));
113
if (!triedResolve.load()) {
115
triedResolve.storeRelease(true);
119
// If dn_expand, res_ninit or res_nquery is missing, fail.
120
if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
121
reply->error = QDnsLookup::ResolverError;
122
reply->errorString = tr("Resolver functions not found");
127
struct __res_state state;
128
memset(&state, 0, sizeof(state));
129
if (local_res_ninit(&state) < 0) {
130
reply->error = QDnsLookup::ResolverError;
131
reply->errorString = tr("Resolver initialization failed");
134
#ifdef QDNSLOOKUP_DEBUG
135
state.options |= RES_DEBUG;
137
QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
139
// Perform DNS query.
140
unsigned char response[PACKETSZ];
141
memset(response, 0, sizeof(response));
142
const int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, response, sizeof(response));
144
// Check the response header.
145
HEADER *header = (HEADER*)response;
146
const int answerCount = ntohs(header->ancount);
147
switch (header->rcode) {
151
reply->error = QDnsLookup::InvalidRequestError;
152
reply->errorString = tr("Server could not process query");
155
reply->error = QDnsLookup::ServerFailureError;
156
reply->errorString = tr("Server failure");
159
reply->error = QDnsLookup::NotFoundError;
160
reply->errorString = tr("Non existent domain");
163
reply->error = QDnsLookup::ServerRefusedError;
164
reply->errorString = tr("Server refused to answer");
167
reply->error = QDnsLookup::InvalidReplyError;
168
reply->errorString = tr("Invalid reply received");
172
// Check the reply is valid.
173
if (responseLength < int(sizeof(HEADER))) {
174
reply->error = QDnsLookup::InvalidReplyError;
175
reply->errorString = tr("Invalid reply received");
179
// Skip the query host, type (2 bytes) and class (2 bytes).
180
char host[PACKETSZ], answer[PACKETSZ];
181
unsigned char *p = response + sizeof(HEADER);
182
int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
184
reply->error = QDnsLookup::InvalidReplyError;
185
reply->errorString = tr("Could not expand domain name");
192
while ((p < response + responseLength) && (answerIndex < answerCount)) {
193
status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
195
reply->error = QDnsLookup::InvalidReplyError;
196
reply->errorString = tr("Could not expand domain name");
199
const QString name = QUrl::fromAce(host);
202
const quint16 type = (p[0] << 8) | p[1];
205
const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
207
const quint16 size = (p[0] << 8) | p[1];
210
if (type == QDnsLookup::A) {
212
reply->error = QDnsLookup::InvalidReplyError;
213
reply->errorString = tr("Invalid IPv4 address record");
216
const quint32 addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
217
QDnsHostAddressRecord record;
218
record.d->name = name;
219
record.d->timeToLive = ttl;
220
record.d->value = QHostAddress(addr);
221
reply->hostAddressRecords.append(record);
222
} else if (type == QDnsLookup::AAAA) {
224
reply->error = QDnsLookup::InvalidReplyError;
225
reply->errorString = tr("Invalid IPv6 address record");
228
QDnsHostAddressRecord record;
229
record.d->name = name;
230
record.d->timeToLive = ttl;
231
record.d->value = QHostAddress(p);
232
reply->hostAddressRecords.append(record);
233
} else if (type == QDnsLookup::CNAME) {
234
status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
236
reply->error = QDnsLookup::InvalidReplyError;
237
reply->errorString = tr("Invalid canonical name record");
240
QDnsDomainNameRecord record;
241
record.d->name = name;
242
record.d->timeToLive = ttl;
243
record.d->value = QUrl::fromAce(answer);
244
reply->canonicalNameRecords.append(record);
245
} else if (type == QDnsLookup::NS) {
246
status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
248
reply->error = QDnsLookup::InvalidReplyError;
249
reply->errorString = tr("Invalid name server record");
252
QDnsDomainNameRecord record;
253
record.d->name = name;
254
record.d->timeToLive = ttl;
255
record.d->value = QUrl::fromAce(answer);
256
reply->nameServerRecords.append(record);
257
} else if (type == QDnsLookup::PTR) {
258
status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
260
reply->error = QDnsLookup::InvalidReplyError;
261
reply->errorString = tr("Invalid pointer record");
264
QDnsDomainNameRecord record;
265
record.d->name = name;
266
record.d->timeToLive = ttl;
267
record.d->value = QUrl::fromAce(answer);
268
reply->pointerRecords.append(record);
269
} else if (type == QDnsLookup::MX) {
270
const quint16 preference = (p[0] << 8) | p[1];
271
status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
273
reply->error = QDnsLookup::InvalidReplyError;
274
reply->errorString = tr("Invalid mail exchange record");
277
QDnsMailExchangeRecord record;
278
record.d->exchange = QUrl::fromAce(answer);
279
record.d->name = name;
280
record.d->preference = preference;
281
record.d->timeToLive = ttl;
282
reply->mailExchangeRecords.append(record);
283
} else if (type == QDnsLookup::SRV) {
284
const quint16 priority = (p[0] << 8) | p[1];
285
const quint16 weight = (p[2] << 8) | p[3];
286
const quint16 port = (p[4] << 8) | p[5];
287
status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
289
reply->error = QDnsLookup::InvalidReplyError;
290
reply->errorString = tr("Invalid service record");
293
QDnsServiceRecord record;
294
record.d->name = name;
295
record.d->target = QUrl::fromAce(answer);
296
record.d->port = port;
297
record.d->priority = priority;
298
record.d->timeToLive = ttl;
299
record.d->weight = weight;
300
reply->serviceRecords.append(record);
301
} else if (type == QDnsLookup::TXT) {
302
unsigned char *txt = p;
303
QDnsTextRecord record;
304
record.d->name = name;
305
record.d->timeToLive = ttl;
306
while (txt < p + size) {
307
const unsigned char length = *txt;
309
if (txt + length > p + size) {
310
reply->error = QDnsLookup::InvalidReplyError;
311
reply->errorString = tr("Invalid text record");
314
record.d->values << QByteArray((char*)txt, length);
317
reply->textRecords.append(record);
326
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
328
Q_UNUSED(requestType)
329
Q_UNUSED(requestName)
330
reply->error = QDnsLookup::ResolverError;
331
reply->errorString = tr("Resolver library can't be loaded: No runtime library loading support");
335
#endif /* ifndef QT_NO_LIBRARY */