QHostInfo: Make getaddrinfo() mandatory

All systems must implement it by now. If there's any system still
without it, that means it has no IPv6 support, so they can disable
QtNetwork entirely.

[ChangeLog][Deprecation Notice] Starting with Qt 5.10, IPv6 support is
mandatory for all platforms. Systems without proper IPv6 support, such
as the getaddrinfo() function or the proper socket address structures,
will not be able to build QtNetwork anymore.

Change-Id: I3868166e5efc45538544fffd14d8c28046f9191b
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Thiago Macieira 2017-08-07 20:52:11 -07:00 committed by Edward Welbourne
parent 3760bc7590
commit 3fe74b76fd
8 changed files with 55 additions and 223 deletions

View File

@ -89,31 +89,6 @@
},
"tests": {
"getaddrinfo": {
"label": "getaddrinfo()",
"type": "compile",
"test": {
"head": [
"#include <stdio.h>",
"#include <stdlib.h>",
"#ifdef __MINGW32__",
"# include <winsock2.h>",
"# include <ws2tcpip.h>",
"#else",
"# include <sys/types.h>",
"# include <sys/socket.h>",
"# include <netdb.h>",
"#endif"
],
"main": [
"addrinfo *res = 0;",
"(void) getaddrinfo(\"foo\", 0, 0, &res);",
"freeaddrinfo(res);",
"gai_strerror(0);"
]
},
"use": "network"
},
"getifaddrs": {
"label": "getifaddrs()",
"type": "compile",
@ -170,11 +145,6 @@
"emitIf": "config.darwin",
"output": [ "feature", "privateFeature" ]
},
"getaddrinfo": {
"label": "getaddrinfo()",
"condition": "tests.getaddrinfo",
"output": [ "feature" ]
},
"getifaddrs": {
"label": "getifaddrs()",
"condition": "tests.getifaddrs",
@ -337,7 +307,7 @@ For example:
"args": "corewlan",
"condition": "config.darwin"
},
"getaddrinfo", "getifaddrs", "ipv6ifname", "libproxy",
"getifaddrs", "ipv6ifname", "libproxy",
{
"type": "feature",
"args": "securetransport",

View File

@ -66,10 +66,6 @@
# include <gnu/lib-names.h>
#endif
#if defined (QT_NO_GETADDRINFO)
static QBasicMutex getHostByNameMutex;
#endif
QT_BEGIN_NAMESPACE
// Almost always the same. If not, specify in qplatformdefs.h.
@ -150,7 +146,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QHostAddress address;
if (address.setAddress(hostName)) {
// Reverse lookup
#if !defined (QT_NO_GETADDRINFO)
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa = 0;
@ -173,12 +168,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
char hbuf[NI_MAXHOST];
if (sa && getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
results.setHostName(QString::fromLatin1(hbuf));
#else
in_addr_t inetaddr = qt_safe_inet_addr(hostName.toLatin1().constData());
struct hostent *ent = gethostbyaddr((const char *)&inetaddr, sizeof(inetaddr), AF_INET);
if (ent)
results.setHostName(QString::fromLatin1(ent->h_name));
#endif
if (results.hostName().isEmpty())
results.setHostName(address.toString());
@ -197,7 +186,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
return results;
}
#if !defined (QT_NO_GETADDRINFO)
// 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;
@ -264,39 +252,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
results.setErrorString(QString::fromLocal8Bit(gai_strerror(result)));
}
#else
// Fall back to gethostbyname for platforms that don't define
// getaddrinfo. gethostbyname does not support IPv6, and it's not
// reentrant on all platforms. For now this is okay since we only
// use one QHostInfoAgent, but if more agents are introduced, locking
// must be provided.
QMutexLocker locker(&getHostByNameMutex);
hostent *result = gethostbyname(aceHostname.constData());
if (result) {
if (result->h_addrtype == AF_INET) {
QList<QHostAddress> addresses;
for (char **p = result->h_addr_list; *p != 0; p++) {
QHostAddress addr;
addr.setAddress(ntohl(*((quint32 *)*p)));
if (!addresses.contains(addr))
addresses.prepend(addr);
}
results.setAddresses(addresses);
} else {
results.setError(QHostInfo::UnknownError);
results.setErrorString(tr("Unknown address type"));
}
#if !defined(Q_OS_VXWORKS)
} else if (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA
|| h_errno == NO_ADDRESS) {
results.setError(QHostInfo::HostNotFound);
results.setErrorString(tr("Host not found"));
#endif
} else {
results.setError(QHostInfo::UnknownError);
results.setErrorString(tr("Unknown error"));
}
#endif // !defined (QT_NO_GETADDRINFO)
#if defined(QHOSTINFO_DEBUG)
if (results.error() != QHostInfo::NoError) {
@ -339,11 +294,6 @@ QString QHostInfo::localDomainName()
if (local_res_init && local_res) {
// using thread-unsafe version
#if defined(QT_NO_GETADDRINFO)
// We have to call res_init to be sure that _res was initialized
// So, for systems without getaddrinfo (which is thread-safe), we lock the mutex too
QMutexLocker locker(&getHostByNameMutex);
#endif
local_res_init();
QString domainName = QUrl::fromAce(local_res->defdname);
if (domainName.isEmpty())

View File

@ -50,50 +50,12 @@ QT_BEGIN_NAMESPACE
//#define QHOSTINFO_DEBUG
// Older SDKs do not include the addrinfo struct declaration, so we
// include a copy of it here.
struct qt_addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char *ai_canonname;
sockaddr *ai_addr;
qt_addrinfo *ai_next;
};
//###
#define QT_SOCKLEN_T int
#ifndef NI_MAXHOST // already defined to 1025 in ws2tcpip.h?
#define NI_MAXHOST 1024
#endif
typedef int (__stdcall *getnameinfoProto)(const sockaddr *, QT_SOCKLEN_T, const char *, DWORD, const char *, DWORD, int);
typedef int (__stdcall *getaddrinfoProto)(const char *, const char *, const qt_addrinfo *, qt_addrinfo **);
typedef int (__stdcall *freeaddrinfoProto)(qt_addrinfo *);
static getnameinfoProto local_getnameinfo = 0;
static getaddrinfoProto local_getaddrinfo = 0;
static freeaddrinfoProto local_freeaddrinfo = 0;
static bool resolveLibraryInternal()
{
// Attempt to resolve getaddrinfo(); without it we'll have to fall
// back to gethostbyname(), which has no IPv6 support.
#if defined (Q_OS_WINRT)
local_getaddrinfo = (getaddrinfoProto) &getaddrinfo;
local_freeaddrinfo = (freeaddrinfoProto) &freeaddrinfo;
local_getnameinfo = (getnameinfoProto) getnameinfo;
#else
local_getaddrinfo = (getaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2_32"), "getaddrinfo");
local_freeaddrinfo = (freeaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2_32"), "freeaddrinfo");
local_getnameinfo = (getnameinfoProto) QSystemLibrary::resolve(QLatin1String("ws2_32"), "getnameinfo");
#endif
return true;
}
Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal()))
static void translateWSAError(int error, QHostInfo *results)
{
switch (error) {
@ -114,49 +76,39 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{
QSysInfo::machineHostName(); // this initializes ws2_32.dll
// Load res_init on demand.
resolveLibrary();
QHostInfo results;
#if defined(QHOSTINFO_DEBUG)
qDebug("QHostInfoAgent::fromName(): looking up \"%s\" (IPv6 support is %s)",
hostName.toLatin1().constData(),
(local_getaddrinfo && local_freeaddrinfo) ? "enabled" : "disabled");
(getaddrinfo && freeaddrinfo) ? "enabled" : "disabled");
#endif
QHostAddress address;
if (address.setAddress(hostName)) {
// Reverse lookup
if (local_getnameinfo) {
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa;
QT_SOCKLEN_T saSize;
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, address.toIPv6Address().c, sizeof(sa6.sin6_addr));
}
char hbuf[NI_MAXHOST];
if (local_getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
results.setHostName(QString::fromLatin1(hbuf));
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa;
QT_SOCKLEN_T saSize;
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 {
unsigned long addr = inet_addr(hostName.toLatin1().constData());
struct hostent *ent = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
if (ent)
results.setHostName(QString::fromLatin1(ent->h_name));
sa = (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 (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);
@ -172,64 +124,35 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
return results;
}
if (local_getaddrinfo && local_freeaddrinfo) {
// Call getaddrinfo, and place all IPv4 addresses at the start
// and the IPv6 addresses at the end of the address list in
// results.
qt_addrinfo *res;
int err = local_getaddrinfo(aceHostname.constData(), 0, 0, &res);
if (err == 0) {
QList<QHostAddress> addresses;
for (qt_addrinfo *p = res; p != 0; p = p->ai_next) {
switch (p->ai_family) {
case AF_INET: {
QHostAddress addr;
addr.setAddress(ntohl(((sockaddr_in *) p->ai_addr)->sin_addr.s_addr));
if (!addresses.contains(addr))
addresses.append(addr);
}
break;
case AF_INET6: {
QHostAddress addr;
addr.setAddress(((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"));
}
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) {
switch (p->ai_family) {
case AF_INET: {
QHostAddress addr;
addr.setAddress(ntohl(((sockaddr_in *) p->ai_addr)->sin_addr.s_addr));
if (!addresses.contains(addr))
addresses.append(addr);
}
break;
case AF_INET6: {
QHostAddress addr;
addr.setAddress(((sockaddr_in6 *) p->ai_addr)->sin6_addr.s6_addr);
if (!addresses.contains(addr))
addresses.append(addr);
}
results.setAddresses(addresses);
local_freeaddrinfo(res);
} else {
translateWSAError(WSAGetLastError(), &results);
}
} else {
// Fall back to gethostbyname, which only supports IPv4.
hostent *ent = gethostbyname(aceHostname.constData());
if (ent) {
char **p;
QList<QHostAddress> addresses;
switch (ent->h_addrtype) {
case AF_INET:
for (p = ent->h_addr_list; *p != 0; p++) {
long *ip4Addr = (long *) *p;
QHostAddress temp;
temp.setAddress(ntohl(*ip4Addr));
addresses << temp;
}
break;
default:
results.setError(QHostInfo::UnknownError);
results.setErrorString(tr("Unknown address type"));
break;
}
results.setAddresses(addresses);
} else {
translateWSAError(WSAGetLastError(), &results);
}
results.setAddresses(addresses);
freeaddrinfo(res);
} else {
translateWSAError(WSAGetLastError(), &results);
}
#if defined(QHOSTINFO_DEBUG)

View File

@ -174,16 +174,6 @@ static inline int qt_safe_ioctl(int sockfd, unsigned long request, T arg)
#endif
}
// VxWorks' headers do not specify any const modifiers
static inline in_addr_t qt_safe_inet_addr(const char *cp)
{
#ifdef Q_OS_VXWORKS
return ::inet_addr((char *) cp);
#else
return ::inet_addr(cp);
#endif
}
static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
#ifdef MSG_NOSIGNAL

View File

@ -0,0 +1,6 @@
# These tests fail due to a DNS server issue
# (this is not a Qt bug)
[lookupIPv6:a-plus-aaaa]
windows ci
[blockingLookup:a-plus-aaaa]
windows ci

View File

@ -64,14 +64,10 @@
#include <qhostinfo.h>
#include "private/qhostinfo_p.h"
#if !defined(QT_NO_GETADDRINFO)
# include <sys/types.h>
# if defined(Q_OS_UNIX)
#include <sys/types.h>
#if defined(Q_OS_UNIX)
# include <sys/socket.h>
# endif
# if !defined(Q_OS_WIN)
# include <netdb.h>
# endif
#endif
#include "../../../network-settings.h"
@ -204,15 +200,13 @@ void tst_QHostInfo::initTestCase()
ipv6Available = true;
}
// HP-UX 11i does not support IPv6 reverse lookups.
#if !defined(QT_NO_GETADDRINFO) && !(defined(Q_OS_HPUX) && defined(__ia64))
// check if the system getaddrinfo can do IPv6 lookups
struct addrinfo hint, *result = 0;
memset(&hint, 0, sizeof hint);
hint.ai_family = AF_UNSPEC;
# ifdef AI_ADDRCONFIG
#ifdef AI_ADDRCONFIG
hint.ai_flags = AI_ADDRCONFIG;
# endif
#endif
int res = getaddrinfo("::1", "80", &hint, &result);
if (res == 0) {
@ -224,7 +218,6 @@ void tst_QHostInfo::initTestCase()
ipv6LookupsAvailable = true;
}
}
#endif
// run each testcase with and without test enabled
QTest::addColumn<bool>("cache");

View File

@ -156,7 +156,7 @@ void tst_NetworkRemoteStressTest::clearManager()
bool nativeLookup(const char *hostname, int port, QByteArray &buf)
{
#if !defined(QT_NO_GETADDRINFO) && 0
#if 0
addrinfo *res = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));

View File

@ -147,7 +147,7 @@ void tst_NetworkStressTest::clearManager()
bool nativeLookup(const char *hostname, int port, QByteArray &buf)
{
#if !defined(QT_NO_GETADDRINFO) && 0
#if 0
addrinfo *res = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));