File indexing completed on 2024-10-06 12:22:03
0001 /* 0002 SPDX-FileCopyrightText: 2003 Malte Starostik <malte@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <config-kpac.h> 0008 // #include <config-prefix.h> 0009 #include <netdb.h> 0010 #include <unistd.h> 0011 0012 #include <sys/types.h> 0013 0014 #if HAVE_NETINET_IN_H 0015 #include <netinet/in.h> 0016 #endif 0017 #include <arpa/nameser.h> 0018 #if HAVE_ARPA_NAMESER8_COMPAT_H 0019 #include <arpa/nameser8_compat.h> 0020 #else 0021 #if HAVE_ARPA_NAMESER_COMPAT_H 0022 #include <arpa/nameser_compat.h> 0023 #endif 0024 #endif 0025 #if HAVE_SYS_PARAM_H 0026 // Basically, the BSDs need this before resolv.h 0027 #include <sys/param.h> 0028 #endif 0029 0030 #include <resolv.h> 0031 #include <sys/utsname.h> 0032 0033 #include <QHostInfo> 0034 #include <QProcess> 0035 #include <QStandardPaths> 0036 #include <QTimer> 0037 #include <QUrl> 0038 0039 #include "moc_discovery.cpp" 0040 #include <KLocalizedString> 0041 0042 namespace KPAC 0043 { 0044 Discovery::Discovery(QObject *parent) 0045 : Downloader(parent) 0046 , m_helper(new QProcess(this)) 0047 { 0048 m_helper->setProcessChannelMode(QProcess::SeparateChannels); 0049 connect(m_helper, &QProcess::readyReadStandardOutput, this, &Discovery::helperOutput); 0050 connect(m_helper, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, &Discovery::failed); 0051 m_helper->start(QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/kpac_dhcp_helper"), QStringList()); 0052 if (!m_helper->waitForStarted()) { 0053 QTimer::singleShot(0, this, &Discovery::failed); 0054 } 0055 } 0056 0057 bool Discovery::initDomainName() 0058 { 0059 m_domainName = QHostInfo::localDomainName(); 0060 return !m_domainName.isEmpty(); 0061 } 0062 0063 bool Discovery::checkDomain() const 0064 { 0065 // If a domain has a SOA record, don't traverse any higher. 0066 // Returns true if no SOA can be found (domain is "ok" to use) 0067 // Stick to old resolver interface for portability reasons. 0068 union { 0069 HEADER header; 0070 unsigned char buf[PACKETSZ]; 0071 } response; 0072 0073 int len = res_query(m_domainName.toLocal8Bit().constData(), C_IN, T_SOA, response.buf, sizeof(response.buf)); 0074 if (len <= int(sizeof(response.header)) || ntohs(response.header.ancount) != 1) { 0075 return true; 0076 } 0077 0078 unsigned char *pos = response.buf + sizeof(response.header); 0079 unsigned char *end = response.buf + len; 0080 0081 // skip query section 0082 pos += dn_skipname(pos, end) + QFIXEDSZ; 0083 if (pos >= end) { 0084 return true; 0085 } 0086 0087 // skip answer domain 0088 pos += dn_skipname(pos, end); 0089 short type; 0090 GETSHORT(type, pos); 0091 return type != T_SOA; 0092 } 0093 0094 void Discovery::failed() 0095 { 0096 setError(i18n("Could not find a usable proxy configuration script")); 0097 0098 // If this is the first DNS query, initialize our host name or abort 0099 // on failure. Otherwise abort if the current domain (which was already 0100 // queried for a host called "wpad" contains a SOA record) 0101 const bool firstQuery = m_domainName.isEmpty(); 0102 if ((firstQuery && !initDomainName()) || (!firstQuery && !checkDomain())) { 0103 Q_EMIT result(false); 0104 return; 0105 } 0106 0107 const int dot = m_domainName.indexOf(QLatin1Char('.')); 0108 if (dot > -1 || firstQuery) { 0109 const QString address = QLatin1String("http://wpad.") + m_domainName + QLatin1String("/wpad.dat"); 0110 if (dot > -1) { 0111 m_domainName.remove(0, dot + 1); // remove one domain level 0112 } 0113 download(QUrl(address)); 0114 return; 0115 } 0116 0117 Q_EMIT result(false); 0118 } 0119 0120 void Discovery::helperOutput() 0121 { 0122 m_helper->disconnect(this); 0123 const QByteArray line = m_helper->readLine(); 0124 const QUrl url(QString::fromLocal8Bit(line.constData(), line.length()).trimmed()); 0125 download(url); 0126 } 0127 }