~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/network/kernel/qdnslookup_unix.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Jeremy LainĆ© <jeremy.laine@m4x.org>
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtNetwork module of the Qt Toolkit.
 
7
**
 
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.
 
16
**
 
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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qdnslookup_p.h"
 
43
 
 
44
#include <qlibrary.h>
 
45
#include <qscopedpointer.h>
 
46
#include <qurl.h>
 
47
#include <private/qmutexpool_p.h>
 
48
 
 
49
#include <sys/types.h>
 
50
#include <netinet/in.h>
 
51
#include <arpa/nameser.h>
 
52
#include <arpa/nameser_compat.h>
 
53
#include <resolv.h>
 
54
 
 
55
QT_BEGIN_NAMESPACE
 
56
 
 
57
#ifndef QT_NO_LIBRARY
 
58
 
 
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;
 
67
 
 
68
// Custom deleter to close resolver state.
 
69
 
 
70
struct QDnsLookupStateDeleter
 
71
{
 
72
    static inline void cleanup(struct __res_state *pointer)
 
73
    {
 
74
        local_res_nclose(pointer);
 
75
    }
 
76
};
 
77
 
 
78
static void resolveLibrary()
 
79
{
 
80
    QLibrary lib(QLatin1String("resolv"));
 
81
    if (!lib.load())
 
82
        return;
 
83
 
 
84
    local_dn_expand = dn_expand_proto(lib.resolve("__dn_expand"));
 
85
    if (!local_dn_expand)
 
86
        local_dn_expand = dn_expand_proto(lib.resolve("dn_expand"));
 
87
 
 
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"));
 
93
 
 
94
    local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
 
95
    if (!local_res_ninit)
 
96
        local_res_ninit = res_ninit_proto(lib.resolve("res_9_ninit"));
 
97
    if (!local_res_ninit)
 
98
        local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
 
99
 
 
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"));
 
105
}
 
106
 
 
107
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
 
108
{
 
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()) {
 
114
            resolveLibrary();
 
115
            triedResolve.storeRelease(true);
 
116
        }
 
117
    }
 
118
 
 
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");
 
123
        return;
 
124
    }
 
125
 
 
126
    // Initialize state.
 
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");
 
132
        return;
 
133
    }
 
134
#ifdef QDNSLOOKUP_DEBUG
 
135
    state.options |= RES_DEBUG;
 
136
#endif
 
137
    QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
 
138
 
 
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));
 
143
 
 
144
    // Check the response header.
 
145
    HEADER *header = (HEADER*)response;
 
146
    const int answerCount = ntohs(header->ancount);
 
147
    switch (header->rcode) {
 
148
    case NOERROR:
 
149
        break;
 
150
    case FORMERR:
 
151
        reply->error = QDnsLookup::InvalidRequestError;
 
152
        reply->errorString = tr("Server could not process query");
 
153
        return;
 
154
    case SERVFAIL:
 
155
        reply->error = QDnsLookup::ServerFailureError;
 
156
        reply->errorString = tr("Server failure");
 
157
        return;
 
158
    case NXDOMAIN:
 
159
        reply->error = QDnsLookup::NotFoundError;
 
160
        reply->errorString = tr("Non existent domain");
 
161
        return;
 
162
    case REFUSED:
 
163
        reply->error = QDnsLookup::ServerRefusedError;
 
164
        reply->errorString = tr("Server refused to answer");
 
165
        return;
 
166
    default:
 
167
        reply->error = QDnsLookup::InvalidReplyError;
 
168
        reply->errorString = tr("Invalid reply received");
 
169
        return;
 
170
    }
 
171
 
 
172
    // Check the reply is valid.
 
173
    if (responseLength < int(sizeof(HEADER))) {
 
174
        reply->error = QDnsLookup::InvalidReplyError;
 
175
        reply->errorString = tr("Invalid reply received");
 
176
        return;
 
177
    }
 
178
 
 
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));
 
183
    if (status < 0) {
 
184
        reply->error = QDnsLookup::InvalidReplyError;
 
185
        reply->errorString = tr("Could not expand domain name");
 
186
        return;
 
187
    }
 
188
    p += status + 4;
 
189
 
 
190
    // Extract results.
 
191
    int answerIndex = 0;
 
192
    while ((p < response + responseLength) && (answerIndex < answerCount)) {
 
193
        status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
 
194
        if (status < 0) {
 
195
            reply->error = QDnsLookup::InvalidReplyError;
 
196
            reply->errorString = tr("Could not expand domain name");
 
197
            return;
 
198
        }
 
199
        const QString name = QUrl::fromAce(host);
 
200
 
 
201
        p += status;
 
202
        const quint16 type = (p[0] << 8) | p[1];
 
203
        p += 2; // RR type
 
204
        p += 2; // RR class
 
205
        const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
 
206
        p += 4;
 
207
        const quint16 size = (p[0] << 8) | p[1];
 
208
        p += 2;
 
209
 
 
210
        if (type == QDnsLookup::A) {
 
211
            if (size != 4) {
 
212
                reply->error = QDnsLookup::InvalidReplyError;
 
213
                reply->errorString = tr("Invalid IPv4 address record");
 
214
                return;
 
215
            }
 
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) {
 
223
            if (size != 16) {
 
224
                reply->error = QDnsLookup::InvalidReplyError;
 
225
                reply->errorString = tr("Invalid IPv6 address record");
 
226
                return;
 
227
            }
 
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));
 
235
            if (status < 0) {
 
236
                reply->error = QDnsLookup::InvalidReplyError;
 
237
                reply->errorString = tr("Invalid canonical name record");
 
238
                return;
 
239
            }
 
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));
 
247
            if (status < 0) {
 
248
                reply->error = QDnsLookup::InvalidReplyError;
 
249
                reply->errorString = tr("Invalid name server record");
 
250
                return;
 
251
            }
 
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));
 
259
            if (status < 0) {
 
260
                reply->error = QDnsLookup::InvalidReplyError;
 
261
                reply->errorString = tr("Invalid pointer record");
 
262
                return;
 
263
            }
 
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));
 
272
            if (status < 0) {
 
273
                reply->error = QDnsLookup::InvalidReplyError;
 
274
                reply->errorString = tr("Invalid mail exchange record");
 
275
                return;
 
276
            }
 
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));
 
288
            if (status < 0) {
 
289
                reply->error = QDnsLookup::InvalidReplyError;
 
290
                reply->errorString = tr("Invalid service record");
 
291
                return;
 
292
            }
 
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;
 
308
                txt++;
 
309
                if (txt + length > p + size) {
 
310
                    reply->error = QDnsLookup::InvalidReplyError;
 
311
                    reply->errorString = tr("Invalid text record");
 
312
                    return;
 
313
                }
 
314
                record.d->values << QByteArray((char*)txt, length);
 
315
                txt += length;
 
316
            }
 
317
            reply->textRecords.append(record);
 
318
        }
 
319
        p += size;
 
320
        answerIndex++;
 
321
    }
 
322
}
 
323
 
 
324
#else
 
325
 
 
326
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
 
327
{
 
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");
 
332
    return;
 
333
}
 
334
 
 
335
#endif /* ifndef QT_NO_LIBRARY */
 
336
 
 
337
QT_END_NAMESPACE