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

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