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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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