1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40//#define QHOSTINFO_DEBUG
41
42#include "qplatformdefs.h"
43
44#include "qhostinfo_p.h"
45#include "private/qnativesocketengine_p.h"
46#include "qiodevice.h"
47#include <qbytearray.h>
48#if QT_CONFIG(library)
49#include <qlibrary.h>
50#endif
51#include <qbasicatomic.h>
52#include <qurl.h>
53#include <qfile.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(__GNU_LIBRARY__) && !defined(__UCLIBC__)
66# include <gnu/lib-names.h>
67#endif
68
69#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
70# include <dlfcn.h>
71#endif
72
73QT_BEGIN_NAMESPACE
74
75enum LibResolvFeature {
76 NeedResInit,
77 NeedResNInit
78};
79
80typedef struct __res_state *res_state_ptr;
81
82typedef int (*res_init_proto)(void);
83static res_init_proto local_res_init = nullptr;
84typedef int (*res_ninit_proto)(res_state_ptr);
85static res_ninit_proto local_res_ninit = nullptr;
86typedef void (*res_nclose_proto)(res_state_ptr);
87static res_nclose_proto local_res_nclose = nullptr;
88static res_state_ptr local_res = nullptr;
89
90#if QT_CONFIG(library) && !defined(Q_OS_QNX)
91namespace {
92struct LibResolv
93{
94 enum {
95#ifdef RES_NORELOAD
96 // If RES_NORELOAD is defined, then the libc is capable of watching
97 // /etc/resolv.conf for changes and reloading as necessary. So accept
98 // whatever is configured.
99 ReinitNecessary = false
100#else
101 ReinitNecessary = true
102#endif
103 };
104
105 QLibrary lib;
106 LibResolv();
107 ~LibResolv() { lib.unload(); }
108};
109}
110
111static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
112{
113 if (lib.isLoaded())
114 return lib.resolve(sym);
115
116#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
117 return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
118#else
119 return nullptr;
120#endif
121}
122
123LibResolv::LibResolv()
124{
125 QLibrary lib;
126#ifdef LIBRESOLV_SO
127 lib.setFileName(QStringLiteral(LIBRESOLV_SO));
128 if (!lib.load())
129#endif
130 {
131 lib.setFileName(QLatin1String("resolv"));
132 lib.load();
133 }
134
135 // res_ninit is required for localDomainName()
136 local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
137 if (!local_res_ninit)
138 local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
139 if (local_res_ninit) {
140 // we must now find res_nclose
141 local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
142 if (!local_res_nclose)
143 local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
144 if (!local_res_nclose)
145 local_res_ninit = nullptr;
146 }
147
148 if (ReinitNecessary || !local_res_ninit) {
149 local_res_init = res_init_proto(resolveSymbol(lib, "__res_init"));
150 if (!local_res_init)
151 local_res_init = res_init_proto(resolveSymbol(lib, "res_init"));
152
153 if (local_res_init && !local_res_ninit) {
154 // if we can't get a thread-safe context, we have to use the global _res state
155 local_res = res_state_ptr(resolveSymbol(lib, "_res"));
156 }
157 }
158}
159
160LibResolv* libResolv()
161{
162 static LibResolv* theLibResolv = nullptr;
163 static QBasicMutex theMutex;
164
165 const QMutexLocker locker(&theMutex);
166 if (theLibResolv == nullptr) {
167 theLibResolv = new LibResolv();
168 Q_ASSERT(QCoreApplication::instance());
169 QObject::connect(QCoreApplication::instance(), &QCoreApplication::destroyed, [] {
170 const QMutexLocker locker(&theMutex);
171 delete theLibResolv;
172 theLibResolv = nullptr;
173 });
174 }
175
176 return theLibResolv;
177}
178
179static void resolveLibrary(LibResolvFeature f)
180{
181 if (LibResolv::ReinitNecessary || f == NeedResNInit)
182 libResolv();
183}
184#else // QT_CONFIG(library) || Q_OS_QNX
185static void resolveLibrary(LibResolvFeature)
186{
187}
188#endif // QT_CONFIG(library) || Q_OS_QNX
189
190QHostInfo QHostInfoAgent::fromName(const QString &hostName)
191{
192 QHostInfo results;
193
194#if defined(QHOSTINFO_DEBUG)
195 qDebug("QHostInfoAgent::fromName(%s) looking up...",
196 hostName.toLatin1().constData());
197#endif
198
199 // Load res_init on demand.
200 resolveLibrary(NeedResInit);
201
202 // If res_init is available, poll it.
203 if (local_res_init)
204 local_res_init();
205
206 QHostAddress address;
207 if (address.setAddress(hostName))
208 return reverseLookup(address);
209
210 return lookup(hostName);
211}
212
213QString QHostInfo::localDomainName()
214{
215#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID)
216 resolveLibrary(NeedResNInit);
217 if (local_res_ninit) {
218 // using thread-safe version
219 res_state_ptr state = res_state_ptr(malloc(sizeof(*state)));
220 Q_CHECK_PTR(state);
221 memset(state, 0, sizeof(*state));
222 local_res_ninit(state);
223 QString domainName = QUrl::fromAce(state->defdname);
224 if (domainName.isEmpty())
225 domainName = QUrl::fromAce(state->dnsrch[0]);
226 local_res_nclose(state);
227 free(state);
228
229 return domainName;
230 }
231
232 if (local_res_init && local_res) {
233 // using thread-unsafe version
234
235 local_res_init();
236 QString domainName = QUrl::fromAce(local_res->defdname);
237 if (domainName.isEmpty())
238 domainName = QUrl::fromAce(local_res->dnsrch[0]);
239 return domainName;
240 }
241#endif
242 // nothing worked, try doing it by ourselves:
243 QFile resolvconf;
244#if defined(_PATH_RESCONF)
245 resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
246#else
247 resolvconf.setFileName(QLatin1String("/etc/resolv.conf"));
248#endif
249 if (!resolvconf.open(QIODevice::ReadOnly))
250 return QString(); // failure
251
252 QString domainName;
253 while (!resolvconf.atEnd()) {
254 QByteArray line = resolvconf.readLine().trimmed();
255 if (line.startsWith("domain "))
256 return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed());
257
258 // in case there's no "domain" line, fall back to the first "search" entry
259 if (domainName.isEmpty() && line.startsWith("search ")) {
260 QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed();
261 int pos = searchDomain.indexOf(' ');
262 if (pos != -1)
263 searchDomain.truncate(pos);
264 domainName = QUrl::fromAce(searchDomain);
265 }
266 }
267
268 // return the fallen-back-to searched domain
269 return domainName;
270}
271
272QT_END_NAMESPACE
273