Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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