source: trunk/src/network/kernel/qhostinfo_unix.cpp@ 385

Last change on this file since 385 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 12.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42//#define QHOSTINFO_DEBUG
43
44static const int RESOLVER_TIMEOUT = 2000;
45
46#include "qplatformdefs.h"
47
48#include "qhostinfo_p.h"
49#include "qiodevice.h"
50#include <qbytearray.h>
51#include <qlibrary.h>
52#include <qurl.h>
53#include <qfile.h>
54#include <private/qmutexpool_p.h>
55
56extern "C" {
57#include <sys/types.h>
58#include <netdb.h>
59#include <arpa/inet.h>
60#include <resolv.h>
61}
62
63#if defined (QT_NO_GETADDRINFO)
64#include <qmutex.h>
65QT_BEGIN_NAMESPACE
66Q_GLOBAL_STATIC(QMutex, getHostByNameMutex)
67QT_END_NAMESPACE
68#endif
69
70QT_BEGIN_NAMESPACE
71
72// Almost always the same. If not, specify in qplatformdefs.h.
73#if !defined(QT_SOCKOPTLEN_T)
74# define QT_SOCKOPTLEN_T QT_SOCKLEN_T
75#endif
76
77// HP-UXi has a bug in getaddrinfo(3) that makes it thread-unsafe
78// with this flag. So disable it in that platform.
79#if defined(AI_ADDRCONFIG) && !defined(Q_OS_HPUX)
80# define Q_ADDRCONFIG AI_ADDRCONFIG
81#endif
82
83typedef struct __res_state *res_state_ptr;
84
85typedef int (*res_init_proto)(void);
86static res_init_proto local_res_init = 0;
87typedef int (*res_ninit_proto)(res_state_ptr);
88static res_ninit_proto local_res_ninit = 0;
89typedef void (*res_nclose_proto)(res_state_ptr);
90static res_nclose_proto local_res_nclose = 0;
91static res_state_ptr local_res = 0;
92
93static void resolveLibrary()
94{
95#ifndef QT_NO_LIBRARY
96 QLibrary lib(QLatin1String("resolv"));
97 if (!lib.load())
98 return;
99
100 local_res_init = res_init_proto(lib.resolve("__res_init"));
101 if (!local_res_init)
102 local_res_init = res_init_proto(lib.resolve("res_init"));
103
104 local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
105 if (!local_res_ninit)
106 local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
107
108 if (!local_res_ninit) {
109 // if we can't get a thread-safe context, we have to use the global _res state
110 local_res = res_state_ptr(lib.resolve("_res"));
111 } else {
112 local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
113 if (!local_res_nclose)
114 local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
115 if (!local_res_nclose)
116 local_res_ninit = 0;
117 }
118#endif
119}
120
121QHostInfo QHostInfoAgent::fromName(const QString &hostName)
122{
123 QHostInfo results;
124 results.setHostName(hostName);
125
126#if defined(QHOSTINFO_DEBUG)
127 qDebug("QHostInfoAgent::fromName(%s) looking up...",
128 hostName.toLatin1().constData());
129#endif
130
131 // Load res_init on demand.
132 static volatile bool triedResolve = false;
133 if (!triedResolve) {
134#ifndef QT_NO_THREAD
135 QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_init));
136#endif
137 if (!triedResolve) {
138 resolveLibrary();
139 triedResolve = true;
140 }
141 }
142
143 // If res_init is available, poll it.
144 if (local_res_init)
145 local_res_init();
146
147 QHostAddress address;
148 if (address.setAddress(hostName)) {
149 // Reverse lookup
150// Reverse lookups using getnameinfo are broken on darwin, use gethostbyaddr instead.
151#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN)
152 sockaddr_in sa4;
153#ifndef QT_NO_IPV6
154 sockaddr_in6 sa6;
155#endif
156 sockaddr *sa = 0;
157 QT_SOCKLEN_T saSize = 0;
158 if (address.protocol() == QAbstractSocket::IPv4Protocol) {
159 sa = (sockaddr *)&sa4;
160 saSize = sizeof(sa4);
161 memset(&sa4, 0, sizeof(sa4));
162 sa4.sin_family = AF_INET;
163 sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
164 }
165#ifndef QT_NO_IPV6
166 else {
167 sa = (sockaddr *)&sa6;
168 saSize = sizeof(sa6);
169 memset(&sa6, 0, sizeof(sa6));
170 sa6.sin6_family = AF_INET6;
171 memcpy(sa6.sin6_addr.s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.s6_addr));
172 }
173#endif
174
175 char hbuf[NI_MAXHOST];
176 if (!sa || getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) != 0) {
177 results.setError(QHostInfo::HostNotFound);
178 results.setErrorString(tr("Host not found"));
179 return results;
180 }
181 results.setHostName(QString::fromLatin1(hbuf));
182#else
183 in_addr_t inetaddr = inet_addr(hostName.toLatin1().constData());
184 struct hostent *ent = gethostbyaddr((const char *)&inetaddr, sizeof(inetaddr), AF_INET);
185 if (!ent) {
186 results.setError(QHostInfo::HostNotFound);
187 results.setErrorString(tr("Host not found"));
188 return results;
189 }
190 results.setHostName(QString::fromLatin1(ent->h_name));
191#endif
192 }
193
194#if !defined (QT_NO_GETADDRINFO)
195 // Call getaddrinfo, and place all IPv4 addresses at the start and
196 // the IPv6 addresses at the end of the address list in results.
197 addrinfo *res = 0;
198 struct addrinfo hints;
199 memset(&hints, 0, sizeof(hints));
200 hints.ai_family = PF_UNSPEC;
201#ifdef Q_ADDRCONFIG
202 hints.ai_flags = Q_ADDRCONFIG;
203#endif
204
205 int result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res);
206# ifdef Q_ADDRCONFIG
207 if (result == EAI_BADFLAGS) {
208 // if the lookup failed with AI_ADDRCONFIG set, try again without it
209 hints.ai_flags = 0;
210 result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res);
211 }
212# endif
213
214 if (result == 0) {
215 addrinfo *node = res;
216 QList<QHostAddress> addresses;
217 while (node) {
218 if (node->ai_family == AF_INET) {
219 QHostAddress addr;
220 addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr));
221 if (!addresses.contains(addr))
222 addresses.append(addr);
223 }
224#ifndef QT_NO_IPV6
225 else if (node->ai_family == AF_INET6) {
226 QHostAddress addr;
227 addr.setAddress(((sockaddr_in6 *) node->ai_addr)->sin6_addr.s6_addr);
228 if (!addresses.contains(addr))
229 addresses.append(addr);
230 }
231#endif
232 node = node->ai_next;
233 }
234 if (addresses.isEmpty() && node == 0) {
235 // Reached the end of the list, but no addresses were found; this
236 // means the list contains one or more unknown address types.
237 results.setError(QHostInfo::UnknownError);
238 results.setErrorString(tr("Unknown address type"));
239 }
240
241 results.setAddresses(addresses);
242 freeaddrinfo(res);
243 } else if (result == EAI_NONAME
244 || result == EAI_FAIL
245#ifdef EAI_NODATA
246 // EAI_NODATA is deprecated in RFC 3493
247 || result == EAI_NODATA
248#endif
249 ) {
250 results.setError(QHostInfo::HostNotFound);
251 results.setErrorString(tr("Host not found"));
252 } else {
253 results.setError(QHostInfo::UnknownError);
254 results.setErrorString(QString::fromLocal8Bit(gai_strerror(result)));
255 }
256
257#else
258 // Fall back to gethostbyname for platforms that don't define
259 // getaddrinfo. gethostbyname does not support IPv6, and it's not
260 // reentrant on all platforms. For now this is okay since we only
261 // use one QHostInfoAgent, but if more agents are introduced, locking
262 // must be provided.
263 QMutexLocker locker(::getHostByNameMutex());
264 hostent *result = gethostbyname(hostName.toLatin1().constData());
265 if (result) {
266 if (result->h_addrtype == AF_INET) {
267 QList<QHostAddress> addresses;
268 for (char **p = result->h_addr_list; *p != 0; p++) {
269 QHostAddress addr;
270 addr.setAddress(ntohl(*((quint32 *)*p)));
271 if (!addresses.contains(addr))
272 addresses.prepend(addr);
273 }
274 results.setAddresses(addresses);
275 } else {
276 results.setError(QHostInfo::UnknownError);
277 results.setErrorString(tr("Unknown address type"));
278 }
279 } else if (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA
280 || h_errno == NO_ADDRESS) {
281 results.setError(QHostInfo::HostNotFound);
282 results.setErrorString(tr("Host not found"));
283 } else {
284 results.setError(QHostInfo::UnknownError);
285 results.setErrorString(tr("Unknown error"));
286 }
287#endif // !defined (QT_NO_GETADDRINFO)
288
289#if defined(QHOSTINFO_DEBUG)
290 if (results.error() != QHostInfo::NoError) {
291 qDebug("QHostInfoAgent::fromName(): error #%d %s",
292 h_errno, results.errorString().toLatin1().constData());
293 } else {
294 QString tmp;
295 QList<QHostAddress> addresses = results.addresses();
296 for (int i = 0; i < addresses.count(); ++i) {
297 if (i != 0) tmp += ", ";
298 tmp += addresses.at(i).toString();
299 }
300 qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}",
301 addresses.count(), hostName.toLatin1().constData(),
302 tmp.toLatin1().constData());
303 }
304#endif
305 return results;
306}
307
308QString QHostInfo::localHostName()
309{
310 char hostName[512];
311 if (gethostname(hostName, sizeof(hostName)) == -1)
312 return QString();
313 hostName[sizeof(hostName) - 1] = '\0';
314 return QString::fromLocal8Bit(hostName);
315}
316
317QString QHostInfo::localDomainName()
318{
319 resolveLibrary();
320 if (local_res_ninit) {
321 // using thread-safe version
322 res_state_ptr state = res_state_ptr(qMalloc(sizeof(*state)));
323 memset(state, 0, sizeof(*state));
324 local_res_ninit(state);
325 QString domainName = QUrl::fromAce(state->defdname);
326 if (domainName.isEmpty())
327 domainName = QUrl::fromAce(state->dnsrch[0]);
328 local_res_nclose(state);
329 qFree(state);
330
331 return domainName;
332 }
333
334 if (local_res_init && local_res) {
335 // using thread-unsafe version
336
337#if defined(QT_NO_GETADDRINFO)
338 // We have to call res_init to be sure that _res was initialized
339 // So, for systems without getaddrinfo (which is thread-safe), we lock the mutex too
340 QMutexLocker locker(::getHostByNameMutex());
341#endif
342 local_res_init();
343 QString domainName = QUrl::fromAce(local_res->defdname);
344 if (domainName.isEmpty())
345 domainName = QUrl::fromAce(local_res->dnsrch[0]);
346 return domainName;
347 }
348
349 // nothing worked, try doing it by ourselves:
350 QFile resolvconf;
351#if defined(_PATH_RESCONF)
352 resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
353#else
354 resolvconf.setFileName(QLatin1String("/etc/resolv.conf"));
355#endif
356 if (!resolvconf.open(QIODevice::ReadOnly))
357 return QString(); // failure
358
359 QString domainName;
360 while (!resolvconf.atEnd()) {
361 QByteArray line = resolvconf.readLine().trimmed();
362 if (line.startsWith("domain "))
363 return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed());
364
365 // in case there's no "domain" line, fall back to the first "search" entry
366 if (domainName.isEmpty() && line.startsWith("search ")) {
367 QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed();
368 int pos = searchDomain.indexOf(' ');
369 if (pos != -1)
370 searchDomain.truncate(pos);
371 domainName = QUrl::fromAce(searchDomain);
372 }
373 }
374
375 // return the fallen-back-to searched domain
376 return domainName;
377}
378
379QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.