1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4//#define QHOSTINFO_DEBUG
5
6#include "qhostinfo_p.h"
7
8#include <qbytearray.h>
9#include <qfile.h>
10#include <qplatformdefs.h>
11#include <qurl.h>
12
13#include <sys/types.h>
14#include <netdb.h>
15#include <netinet/in.h>
16
17#if QT_CONFIG(libresolv)
18# include <resolv.h>
19#endif
20
21#ifndef _PATH_RESCONF
22# define _PATH_RESCONF "/etc/resolv.conf"
23#endif
24
25QT_BEGIN_NAMESPACE
26
27using namespace Qt::StringLiterals;
28
29static void maybeRefreshResolver()
30{
31#if defined(RES_NORELOAD)
32 // If RES_NORELOAD is defined, then the libc is capable of watching
33 // /etc/resolv.conf for changes and reloading as necessary. So accept
34 // whatever is configured.
35 return;
36#elif defined(Q_OS_DARWIN)
37 // Apple's libsystem_info.dylib:getaddrinfo() uses the
38 // libsystem_dnssd.dylib to resolve hostnames. Using res_init() has no
39 // effect on it and is thread-unsafe.
40 return;
41#elif defined(Q_OS_FREEBSD)
42 // FreeBSD automatically refreshes:
43 // https://github.com/freebsd/freebsd-src/blob/b3fe5d932264445cbf9a1c4eab01afb6179b499b/lib/libc/resolv/res_state.c#L69
44 return;
45#elif defined(Q_OS_OPENBSD)
46 // OpenBSD automatically refreshes:
47 // https://github.com/ligurio/openbsd-src/blob/b1ce0da17da254cc15b8aff25b3d55d3c7a82cec/lib/libc/asr/asr.c#L367
48 return;
49#elif defined(Q_OS_QNX)
50 // res_init() is not thread-safe; executing it leads to state corruption.
51 // Whether it reloads resolv.conf on its own is unknown.
52 return;
53#endif
54
55#if QT_CONFIG(libresolv)
56 // OSes known or thought to reach here: AIX, NetBSD, Solaris,
57 // Linux with MUSL (though res_init() does nothing and is unnecessary)
58
59 Q_CONSTINIT static QT_STATBUF lastStat = {};
60 Q_CONSTINIT static QBasicMutex mutex = {};
61 if (QT_STATBUF st; QT_STAT(_PATH_RESCONF, buf: &st) == 0) {
62 QMutexLocker locker(&mutex);
63 bool refresh = false;
64 if ((_res.options & RES_INIT) == 0)
65 refresh = true;
66 else if (lastStat.st_ctime != st.st_ctime)
67 refresh = true; // file was updated
68 else if (lastStat.st_dev != st.st_dev || lastStat.st_ino != st.st_ino)
69 refresh = true; // file was replaced
70 if (refresh) {
71 lastStat = st;
72 res_init();
73 }
74 }
75#endif
76}
77
78QHostInfo QHostInfoAgent::fromName(const QString &hostName)
79{
80 QHostInfo results;
81
82#if defined(QHOSTINFO_DEBUG)
83 qDebug("QHostInfoAgent::fromName(%s) looking up...",
84 hostName.toLatin1().constData());
85#endif
86
87 maybeRefreshResolver();
88
89 QHostAddress address;
90 if (address.setAddress(hostName))
91 return reverseLookup(address);
92
93 return lookup(hostName);
94}
95
96QString QHostInfo::localDomainName()
97{
98#if QT_CONFIG(libresolv)
99 auto domainNameFromRes = [](res_state r) {
100 QString domainName;
101 if (r->defdname[0])
102 domainName = QUrl::fromAce(domain: r->defdname);
103 if (domainName.isEmpty())
104 domainName = QUrl::fromAce(domain: r->dnsrch[0]);
105 return domainName;
106 };
107 std::remove_pointer_t<res_state> state = {};
108 if (res_ninit(&state) == 0) {
109 // using thread-safe version
110 auto guard = qScopeGuard(f: [&] { res_nclose(&state); });
111 return domainNameFromRes(&state);
112 }
113
114 // using thread-unsafe version
115 maybeRefreshResolver();
116 return domainNameFromRes(&_res);
117#endif // !QT_CONFIG(libresolv)
118
119 // nothing worked, try doing it by ourselves:
120 QFile resolvconf;
121 resolvconf.setFileName(_PATH_RESCONF ""_L1);
122 if (!resolvconf.open(flags: QIODevice::ReadOnly))
123 return QString(); // failure
124
125 QString domainName;
126 while (!resolvconf.atEnd()) {
127 const QByteArray lineArray = resolvconf.readLine();
128 QByteArrayView line = QByteArrayView(lineArray).trimmed();
129 constexpr QByteArrayView domainWithSpace = "domain ";
130 if (line.startsWith(other: domainWithSpace))
131 return QUrl::fromAce(domain: line.mid(pos: domainWithSpace.size()).trimmed().toByteArray());
132
133 // in case there's no "domain" line, fall back to the first "search" entry
134 constexpr QByteArrayView searchWithSpace = "search ";
135 if (domainName.isEmpty() && line.startsWith(other: searchWithSpace)) {
136 QByteArrayView searchDomain = line.mid(pos: searchWithSpace.size()).trimmed();
137 int pos = searchDomain.indexOf(ch: ' ');
138 if (pos != -1)
139 searchDomain.truncate(n: pos);
140 domainName = QUrl::fromAce(domain: searchDomain.toByteArray());
141 }
142 }
143
144 // return the fallen-back-to searched domain
145 return domainName;
146}
147
148QT_END_NAMESPACE
149

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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