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