Consolidate Unix and Windows implementations of QHostInfo::fromName

The implementations were practically identical, so we can just move the
code into qhostinfo.cpp, cleaning up things along the way.

Since QHostInfoAgent is an internal class, add the shared code as
additional static helper functions. And since half the code already used
QCoreApplication::translate anyway, we can remove the QObject
inheritance which was only added for getting a tr().

Change-Id: I58eafbdc3e7d06d2e898486a1add63cd63d98c96
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Volker Hilsheimer 2019-04-24 17:06:15 +02:00
parent d647cf85fa
commit dc3e5c4838
4 changed files with 178 additions and 247 deletions

View File

@ -37,8 +37,11 @@
**
****************************************************************************/
//#define QHOSTINFO_DEBUG
#include "qhostinfo.h"
#include "qhostinfo_p.h"
#include <qplatformdefs.h>
#include "QtCore/qscopedpointer.h"
#include <qabstracteventdispatcher.h>
@ -53,6 +56,15 @@
#ifdef Q_OS_UNIX
# include <unistd.h>
# include <netdb.h>
# include <netinet/in.h>
# if defined(AI_ADDRCONFIG)
# define Q_ADDRCONFIG AI_ADDRCONFIG
# endif
#elif defined Q_OS_WIN
# include <ws2tcpip.h>
# define QT_SOCKLEN_T int
#endif
QT_BEGIN_NAMESPACE
@ -412,6 +424,162 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetw
}
#endif
QHostInfo QHostInfoAgent::reverseLookup(const QHostAddress &address)
{
QHostInfo results;
// Reverse lookup
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa = 0;
QT_SOCKLEN_T saSize;
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
sa = reinterpret_cast<sockaddr *>(&sa4);
saSize = sizeof(sa4);
memset(&sa4, 0, sizeof(sa4));
sa4.sin_family = AF_INET;
sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
} else {
sa = reinterpret_cast<sockaddr *>(&sa6);
saSize = sizeof(sa6);
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
memcpy(&sa6.sin6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr));
}
char hbuf[NI_MAXHOST];
if (sa && getnameinfo(sa, saSize, hbuf, sizeof(hbuf), nullptr, 0, 0) == 0)
results.setHostName(QString::fromLatin1(hbuf));
if (results.hostName().isEmpty())
results.setHostName(address.toString());
results.setAddresses(QList<QHostAddress>() << address);
return results;
}
/*
Call getaddrinfo, and returns the results as QHostInfo::addresses
*/
QHostInfo QHostInfoAgent::lookup(const QString &hostName)
{
QHostInfo results;
// IDN support
QByteArray aceHostname = QUrl::toAce(hostName);
results.setHostName(hostName);
if (aceHostname.isEmpty()) {
results.setError(QHostInfo::HostNotFound);
results.setErrorString(hostName.isEmpty() ?
QCoreApplication::translate("QHostInfoAgent", "No host name given") :
QCoreApplication::translate("QHostInfoAgent", "Invalid hostname"));
return results;
}
addrinfo *res = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
#ifdef Q_ADDRCONFIG
hints.ai_flags = Q_ADDRCONFIG;
#endif
int result = getaddrinfo(aceHostname.constData(), nullptr, &hints, &res);
# ifdef Q_ADDRCONFIG
if (result == EAI_BADFLAGS) {
// if the lookup failed with AI_ADDRCONFIG set, try again without it
hints.ai_flags = 0;
result = getaddrinfo(aceHostname.constData(), nullptr, &hints, &res);
}
# endif
if (result == 0) {
addrinfo *node = res;
QList<QHostAddress> addresses;
while (node) {
#ifdef QHOSTINFO_DEBUG
qDebug() << "getaddrinfo node: flags:" << node->ai_flags << "family:" << node->ai_family
<< "ai_socktype:" << node->ai_socktype << "ai_protocol:" << node->ai_protocol
<< "ai_addrlen:" << node->ai_addrlen;
#endif
switch (node->ai_family) {
case AF_INET: {
QHostAddress addr;
addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr));
if (!addresses.contains(addr))
addresses.append(addr);
break;
}
case AF_INET6: {
QHostAddress addr;
sockaddr_in6 *sa6 = (sockaddr_in6 *) node->ai_addr;
addr.setAddress(sa6->sin6_addr.s6_addr);
if (sa6->sin6_scope_id)
addr.setScopeId(QString::number(sa6->sin6_scope_id));
if (!addresses.contains(addr))
addresses.append(addr);
break;
}
default:
results.setError(QHostInfo::UnknownError);
results.setErrorString(QCoreApplication::translate("QHostInfoAgent", "Unknown address type"));
}
node = node->ai_next;
}
if (addresses.isEmpty()) {
// Reached the end of the list, but no addresses were found; this
// means the list contains one or more unknown address types.
results.setError(QHostInfo::UnknownError);
results.setErrorString(QCoreApplication::translate("QHostInfoAgent", "Unknown address type"));
}
results.setAddresses(addresses);
freeaddrinfo(res);
} else {
switch (result) {
#ifdef Q_OS_WIN
case WSAHOST_NOT_FOUND: //authoritative not found
case WSATRY_AGAIN: //non authoritative not found
case WSANO_DATA: //valid name, no associated address
#else
case EAI_NONAME:
case EAI_FAIL:
# ifdef EAI_NODATA // EAI_NODATA is deprecated in RFC 3493
case EAI_NODATA:
# endif
#endif
results.setError(QHostInfo::HostNotFound);
results.setErrorString(QCoreApplication::translate("QHostInfoAgent", "Host not found"));
break;
default:
results.setError(QHostInfo::UnknownError);
#ifdef Q_OS_WIN
results.setErrorString(QString::fromWCharArray(gai_strerror(result)));
#else
results.setErrorString(QString::fromLocal8Bit(gai_strerror(result)));
#endif
break;
}
}
#if defined(QHOSTINFO_DEBUG)
if (results.error() != QHostInfo::NoError) {
qDebug("QHostInfoAgent::fromName(): error #%d %s",
h_errno, results.errorString().toLatin1().constData());
} else {
QString tmp;
QList<QHostAddress> addresses = results.addresses();
for (int i = 0; i < addresses.count(); ++i) {
if (i != 0) tmp += QLatin1String(", ");
tmp += addresses.at(i).toString();
}
qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}",
addresses.count(), aceHostname.constData(),
tmp.toLatin1().constData());
}
#endif
return results;
}
/*!
\enum QHostInfo::HostInfoError

View File

@ -127,15 +127,16 @@ Q_SIGNALS:
void resultsReady(const QHostInfo &info);
};
// needs to be QObject because fromName calls tr()
class QHostInfoAgent : public QObject
class QHostInfoAgent
{
Q_OBJECT
public:
static QHostInfo fromName(const QString &hostName);
#ifndef QT_NO_BEARERMANAGEMENT
static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession);
#endif
private:
static QHostInfo lookup(const QString &hostName);
static QHostInfo reverseLookup(const QHostAddress &address);
};
class QHostInfoPrivate

View File

@ -72,17 +72,6 @@
QT_BEGIN_NAMESPACE
// Almost always the same. If not, specify in qplatformdefs.h.
#if !defined(QT_SOCKOPTLEN_T)
# define QT_SOCKOPTLEN_T QT_SOCKLEN_T
#endif
// HP-UXi has a bug in getaddrinfo(3) that makes it thread-unsafe
// with this flag. So disable it in that platform.
#if defined(AI_ADDRCONFIG) && !defined(Q_OS_HPUX)
# define Q_ADDRCONFIG AI_ADDRCONFIG
#endif
enum LibResolvFeature {
NeedResInit,
NeedResNInit
@ -197,132 +186,10 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
local_res_init();
QHostAddress address;
if (address.setAddress(hostName)) {
// Reverse lookup
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa = 0;
QT_SOCKLEN_T saSize = 0;
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
sa = (sockaddr *)&sa4;
saSize = sizeof(sa4);
memset(&sa4, 0, sizeof(sa4));
sa4.sin_family = AF_INET;
sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
}
else {
sa = (sockaddr *)&sa6;
saSize = sizeof(sa6);
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
memcpy(sa6.sin6_addr.s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.s6_addr));
}
if (address.setAddress(hostName))
return reverseLookup(address);
char hbuf[NI_MAXHOST];
if (sa && getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
results.setHostName(QString::fromLatin1(hbuf));
if (results.hostName().isEmpty())
results.setHostName(address.toString());
results.setAddresses(QList<QHostAddress>() << address);
return results;
}
// IDN support
QByteArray aceHostname = QUrl::toAce(hostName);
results.setHostName(hostName);
if (aceHostname.isEmpty()) {
results.setError(QHostInfo::HostNotFound);
results.setErrorString(hostName.isEmpty() ?
QCoreApplication::translate("QHostInfoAgent", "No host name given") :
QCoreApplication::translate("QHostInfoAgent", "Invalid hostname"));
return results;
}
// Call getaddrinfo, and place all IPv4 addresses at the start and
// the IPv6 addresses at the end of the address list in results.
addrinfo *res = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
#ifdef Q_ADDRCONFIG
hints.ai_flags = Q_ADDRCONFIG;
#endif
int result = getaddrinfo(aceHostname.constData(), 0, &hints, &res);
# ifdef Q_ADDRCONFIG
if (result == EAI_BADFLAGS) {
// if the lookup failed with AI_ADDRCONFIG set, try again without it
hints.ai_flags = 0;
result = getaddrinfo(aceHostname.constData(), 0, &hints, &res);
}
# endif
if (result == 0) {
addrinfo *node = res;
QList<QHostAddress> addresses;
while (node) {
#ifdef QHOSTINFO_DEBUG
qDebug() << "getaddrinfo node: flags:" << node->ai_flags << "family:" << node->ai_family << "ai_socktype:" << node->ai_socktype << "ai_protocol:" << node->ai_protocol << "ai_addrlen:" << node->ai_addrlen;
#endif
if (node->ai_family == AF_INET) {
QHostAddress addr;
addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr));
if (!addresses.contains(addr))
addresses.append(addr);
}
else if (node->ai_family == AF_INET6) {
QHostAddress addr;
sockaddr_in6 *sa6 = (sockaddr_in6 *) node->ai_addr;
addr.setAddress(sa6->sin6_addr.s6_addr);
if (sa6->sin6_scope_id)
addr.setScopeId(QString::number(sa6->sin6_scope_id));
if (!addresses.contains(addr))
addresses.append(addr);
}
node = node->ai_next;
}
if (addresses.isEmpty() && node == 0) {
// Reached the end of the list, but no addresses were found; this
// means the list contains one or more unknown address types.
results.setError(QHostInfo::UnknownError);
results.setErrorString(tr("Unknown address type"));
}
results.setAddresses(addresses);
freeaddrinfo(res);
} else if (result == EAI_NONAME
|| result == EAI_FAIL
#ifdef EAI_NODATA
// EAI_NODATA is deprecated in RFC 3493
|| result == EAI_NODATA
#endif
) {
results.setError(QHostInfo::HostNotFound);
results.setErrorString(tr("Host not found"));
} else {
results.setError(QHostInfo::UnknownError);
results.setErrorString(QString::fromLocal8Bit(gai_strerror(result)));
}
#if defined(QHOSTINFO_DEBUG)
if (results.error() != QHostInfo::NoError) {
qDebug("QHostInfoAgent::fromName(): error #%d %s",
h_errno, results.errorString().toLatin1().constData());
} else {
QString tmp;
QList<QHostAddress> addresses = results.addresses();
for (int i = 0; i < addresses.count(); ++i) {
if (i != 0) tmp += ", ";
tmp += addresses.at(i).toString();
}
qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}",
addresses.count(), hostName.toLatin1().constData(),
tmp.toLatin1().constData());
}
#endif
return results;
return lookup(hostName);
}
QString QHostInfo::localDomainName()

View File

@ -51,27 +51,10 @@ QT_BEGIN_NAMESPACE
//#define QHOSTINFO_DEBUG
//###
#define QT_SOCKLEN_T int
#ifndef NI_MAXHOST // already defined to 1025 in ws2tcpip.h?
#define NI_MAXHOST 1024
#endif
static void translateWSAError(int error, QHostInfo *results)
{
switch (error) {
case WSAHOST_NOT_FOUND: //authoritative not found
case WSATRY_AGAIN: //non authoritative not found
case WSANO_DATA: //valid name, no associated address
results->setError(QHostInfo::HostNotFound);
results->setErrorString(QHostInfoAgent::tr("Host not found"));
return;
default:
results->setError(QHostInfo::UnknownError);
results->setErrorString(QHostInfoAgent::tr("Unknown error (%1)").arg(error));
return;
}
}
QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{
QSysInfo::machineHostName(); // this initializes ws2_32.dll
@ -84,98 +67,10 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
#endif
QHostAddress address;
if (address.setAddress(hostName)) {
// Reverse lookup
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa;
QT_SOCKLEN_T saSize;
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
sa = reinterpret_cast<sockaddr *>(&sa4);
saSize = sizeof(sa4);
memset(&sa4, 0, sizeof(sa4));
sa4.sin_family = AF_INET;
sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
} else {
sa = reinterpret_cast<sockaddr *>(&sa6);
saSize = sizeof(sa6);
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
memcpy(&sa6.sin6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr));
}
if (address.setAddress(hostName))
return reverseLookup(address);
char hbuf[NI_MAXHOST];
if (getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
results.setHostName(QString::fromLatin1(hbuf));
if (results.hostName().isEmpty())
results.setHostName(address.toString());
results.setAddresses(QList<QHostAddress>() << address);
return results;
}
// IDN support
QByteArray aceHostname = QUrl::toAce(hostName);
results.setHostName(hostName);
if (aceHostname.isEmpty()) {
results.setError(QHostInfo::HostNotFound);
results.setErrorString(hostName.isEmpty() ? tr("No host name given") : tr("Invalid hostname"));
return results;
}
addrinfo *res;
int err = getaddrinfo(aceHostname.constData(), 0, 0, &res);
if (err == 0) {
QList<QHostAddress> addresses;
for (addrinfo *p = res; p != 0; p = p->ai_next) {
#ifdef QHOSTINFO_DEBUG
qDebug() << "getaddrinfo node: flags:" << p->ai_flags << "family:" << p->ai_family
<< "ai_socktype:" << p->ai_socktype << "ai_protocol:" << p->ai_protocol
<< "ai_addrlen:" << p->ai_addrlen;
#endif
switch (p->ai_family) {
case AF_INET: {
QHostAddress addr;
addr.setAddress(ntohl(reinterpret_cast<sockaddr_in *>(p->ai_addr)->sin_addr.s_addr));
if (!addresses.contains(addr))
addresses.append(addr);
}
break;
case AF_INET6: {
QHostAddress addr;
addr.setAddress(reinterpret_cast<const sockaddr_in6 *>(p->ai_addr)->sin6_addr.s6_addr);
if (!addresses.contains(addr))
addresses.append(addr);
}
break;
default:
results.setError(QHostInfo::UnknownError);
results.setErrorString(tr("Unknown address type"));
}
}
results.setAddresses(addresses);
freeaddrinfo(res);
} else {
translateWSAError(WSAGetLastError(), &results);
}
#if defined(QHOSTINFO_DEBUG)
if (results.error() != QHostInfo::NoError) {
qDebug("QHostInfoAgent::run(): error (%s)",
results.errorString().toLatin1().constData());
} else {
QString tmp;
QList<QHostAddress> addresses = results.addresses();
for (int i = 0; i < addresses.count(); ++i) {
if (i != 0) tmp += QLatin1String(", ");
tmp += addresses.at(i).toString();
}
qDebug("QHostInfoAgent::run(): found %i entries: {%s}",
addresses.count(), tmp.toLatin1().constData());
}
#endif
return results;
return lookup(hostName);
}
// QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp