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(symbol: sym);
115
116#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
117 return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, name: sym));
118#else
119 return nullptr;
120#endif
121}
122
123LibResolv::LibResolv()
124{
125#ifdef LIBRESOLV_SO
126 lib.setFileName(QStringLiteral(LIBRESOLV_SO));
127 if (!lib.load())
128#endif
129 {
130 lib.setFileName(QLatin1String("resolv"));
131 lib.load();
132 }
133
134 // res_ninit is required for localDomainName()
135 local_res_ninit = res_ninit_proto(resolveSymbol(lib, sym: "__res_ninit"));
136 if (!local_res_ninit)
137 local_res_ninit = res_ninit_proto(resolveSymbol(lib, sym: "res_ninit"));
138 if (local_res_ninit) {
139 // we must now find res_nclose
140 local_res_nclose = res_nclose_proto(resolveSymbol(lib, sym: "res_nclose"));
141 if (!local_res_nclose)
142 local_res_nclose = res_nclose_proto(resolveSymbol(lib, sym: "__res_nclose"));
143 if (!local_res_nclose)
144 local_res_ninit = nullptr;
145 }
146
147 if (ReinitNecessary || !local_res_ninit) {
148 local_res_init = res_init_proto(resolveSymbol(lib, sym: "__res_init"));
149 if (!local_res_init)
150 local_res_init = res_init_proto(resolveSymbol(lib, sym: "res_init"));
151
152 if (local_res_init && !local_res_ninit) {
153 // if we can't get a thread-safe context, we have to use the global _res state
154 local_res = res_state_ptr(resolveSymbol(lib, sym: "_res"));
155 }
156 }
157}
158
159LibResolv* libResolv()
160{
161 static LibResolv* theLibResolv = nullptr;
162 static QBasicMutex theMutex;
163
164 const QMutexLocker locker(&theMutex);
165 if (theLibResolv == nullptr) {
166 theLibResolv = new LibResolv();
167 Q_ASSERT(QCoreApplication::instance());
168 QObject::connect(sender: QCoreApplication::instance(), signal: &QCoreApplication::destroyed, slot: [] {
169 const QMutexLocker locker(&theMutex);
170 delete theLibResolv;
171 theLibResolv = nullptr;
172 });
173 }
174
175 return theLibResolv;
176}
177
178static void resolveLibrary(LibResolvFeature f)
179{
180 if (LibResolv::ReinitNecessary || f == NeedResNInit)
181 libResolv();
182}
183#else // QT_CONFIG(library) || Q_OS_QNX
184static void resolveLibrary(LibResolvFeature)
185{
186}
187#endif // QT_CONFIG(library) || Q_OS_QNX
188
189QHostInfo QHostInfoAgent::fromName(const QString &hostName)
190{
191 QHostInfo results;
192
193#if defined(QHOSTINFO_DEBUG)
194 qDebug("QHostInfoAgent::fromName(%s) looking up...",
195 hostName.toLatin1().constData());
196#endif
197
198 // Load res_init on demand.
199 resolveLibrary(f: NeedResInit);
200
201 // If res_init is available, poll it.
202 if (local_res_init)
203 local_res_init();
204
205 QHostAddress address;
206 if (address.setAddress(hostName))
207 return reverseLookup(address);
208
209 return lookup(hostName);
210}
211
212QString QHostInfo::localDomainName()
213{
214#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID)
215 resolveLibrary(f: NeedResNInit);
216 if (local_res_ninit) {
217 // using thread-safe version
218 res_state_ptr state = res_state_ptr(malloc(size: sizeof(*state)));
219 Q_CHECK_PTR(state);
220 memset(s: state, c: 0, n: sizeof(*state));
221 local_res_ninit(state);
222 QString domainName = QUrl::fromAce(state->defdname);
223 if (domainName.isEmpty())
224 domainName = QUrl::fromAce(state->dnsrch[0]);
225 local_res_nclose(state);
226 free(ptr: state);
227
228 return domainName;
229 }
230
231 if (local_res_init && local_res) {
232 // using thread-unsafe version
233
234 local_res_init();
235 QString domainName = QUrl::fromAce(local_res->defdname);
236 if (domainName.isEmpty())
237 domainName = QUrl::fromAce(local_res->dnsrch[0]);
238 return domainName;
239 }
240#endif
241 // nothing worked, try doing it by ourselves:
242 QFile resolvconf;
243#if defined(_PATH_RESCONF)
244 resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
245#else
246 resolvconf.setFileName(QLatin1String("/etc/resolv.conf"));
247#endif
248 if (!resolvconf.open(flags: QIODevice::ReadOnly))
249 return QString(); // failure
250
251 QString domainName;
252 while (!resolvconf.atEnd()) {
253 QByteArray line = resolvconf.readLine().trimmed();
254 if (line.startsWith(c: "domain "))
255 return QUrl::fromAce(line.mid(index: sizeof "domain " - 1).trimmed());
256
257 // in case there's no "domain" line, fall back to the first "search" entry
258 if (domainName.isEmpty() && line.startsWith(c: "search ")) {
259 QByteArray searchDomain = line.mid(index: sizeof "search " - 1).trimmed();
260 int pos = searchDomain.indexOf(c: ' ');
261 if (pos != -1)
262 searchDomain.truncate(pos);
263 domainName = QUrl::fromAce(searchDomain);
264 }
265 }
266
267 // return the fallen-back-to searched domain
268 return domainName;
269}
270
271QT_END_NAMESPACE
272

source code of qtbase/src/network/kernel/qhostinfo_unix.cpp