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 
73 QT_BEGIN_NAMESPACE
74 
75 enum LibResolvFeature {
76     NeedResInit,
77     NeedResNInit
78 };
79 
80 typedef struct __res_state *res_state_ptr;
81 
82 typedef int (*res_init_proto)(void);
83 static res_init_proto local_res_init = nullptr;
84 typedef int (*res_ninit_proto)(res_state_ptr);
85 static res_ninit_proto local_res_ninit = nullptr;
86 typedef void (*res_nclose_proto)(res_state_ptr);
87 static res_nclose_proto local_res_nclose = nullptr;
88 static res_state_ptr local_res = nullptr;
89 
90 #if QT_CONFIG(library) && !defined(Q_OS_QNX)
91 namespace {
92 struct 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();
~LibResolv__anon498529d70111::LibResolv107     ~LibResolv() { lib.unload(); }
108 };
109 }
110 
resolveSymbol(QLibrary & lib,const char * sym)111 static 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 
LibResolv()123 LibResolv::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 
libResolv()160 LibResolv* 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 
resolveLibrary(LibResolvFeature f)179 static void resolveLibrary(LibResolvFeature f)
180 {
181     if (LibResolv::ReinitNecessary || f == NeedResNInit)
182         libResolv();
183 }
184 #else // QT_CONFIG(library) || Q_OS_QNX
resolveLibrary(LibResolvFeature)185 static void resolveLibrary(LibResolvFeature)
186 {
187 }
188 #endif // QT_CONFIG(library) || Q_OS_QNX
189 
fromName(const QString & hostName)190 QHostInfo 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 
localDomainName()213 QString 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 
272 QT_END_NAMESPACE
273