QDnsLookup: Add support for custom DNS server

Implemented the use of the new QDnsLookup property "nameserver".

On the Linux platform, we can specify both IPv4 and IPv6 addresses
for the nameserver.

On Windows since we are using DnsQuery_W(), which does not have a
way of accepting IPv6 addresses, passing IPv6 nameserver address
is not supported.

On OSX/BSD platforms, specifying IPv6 addresses for nameserver
require access to the __res_state_ext structure which is in a
private header of libresolv (this header is different for BSDs and
OSX). If this feature has to be enabled in the future, we have to
figure out a way to access this struct by either accessing the
private header or by specifying one of our own. Currently, I'm
disabling it till such a solution is arrived at.

Nameserver support on different platforms:

  Platform   |      IPv4     |     IPv6
-------------+---------------+---------------
  Linux/X11  |   supported   |  supported
-------------+---------------+---------------
  Windows    |   supported   |  not supported
-------------+---------------+---------------
  OSX        |   supported   |  not supported
-------------+---------------+---------------
  WinRT      | not supported |  not supported
-------------+---------------+---------------
  Others     |   supported   |  not supported
             |  (not tested) |
-------------+---------------+---------------

Task-number: QTBUG-30166
Change-Id: Iedbddf15b9a62738ce4c2cfa0fce051514d64766
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Mandeep Sandhu 2013-09-27 15:03:04 +05:30 committed by The Qt Project
parent 44ee7984fc
commit 62f01d581b
6 changed files with 72 additions and 9 deletions

View File

@ -482,7 +482,7 @@ void QDnsLookup::lookup()
Q_D(QDnsLookup);
d->isFinished = false;
d->reply = QDnsLookupReply();
d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name));
d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name), d->nameserver);
connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
this, SLOT(_q_lookupFinished(QDnsLookupReply)),
Qt::BlockingQueuedConnection);
@ -990,7 +990,7 @@ void QDnsLookupRunnable::run()
}
// Perform request.
query(requestType, requestName, &reply);
query(requestType, requestName, nameserver, &reply);
// Sort results.
if (!theDnsLookupSeedStorage()->hasLocalData()) {

View File

@ -43,10 +43,11 @@
QT_BEGIN_NAMESPACE
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
Q_UNUSED(requestType);
Q_UNUSED(requestName);
Q_UNUSED(nameserver);
Q_UNUSED(reply);
qWarning() << Q_FUNC_INFO << "Not yet supported on Android";
reply->error = QDnsLookup::ResolverError;

View File

@ -112,9 +112,10 @@ class QDnsLookupRunnable : public QObject, public QRunnable
Q_OBJECT
public:
QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name)
QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name, const QHostAddress &nameserver)
: requestType(type)
, requestName(name)
, nameserver(nameserver)
{ }
void run();
@ -122,9 +123,10 @@ signals:
void finished(const QDnsLookupReply &reply);
private:
static void query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply);
static void query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply);
QDnsLookup::Type requestType;
QByteArray requestName;
QHostAddress nameserver;
};
class QDnsLookupThreadPool : public QThreadPool

View File

@ -115,7 +115,7 @@ static void resolveLibrary()
local_res_nquery = res_nquery_proto(lib.resolve("res_nquery"));
}
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
// Load dn_expand, res_ninit and res_nquery on demand.
static QBasicAtomicInt triedResolve = Q_BASIC_ATOMIC_INITIALIZER(false);
@ -142,6 +142,43 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
reply->errorString = tr("Resolver initialization failed");
return;
}
//Check if a nameserver was set. If so, use it
if (!nameserver.isNull()) {
if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
state.nsaddr_list[0].sin_addr.s_addr = htonl(nameserver.toIPv4Address());
state.nscount = 1;
} else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
#if defined(Q_OS_LINUX)
struct sockaddr_in6 *ns;
ns = state._u._ext.nsaddrs[0];
// nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
if (!ns) {
// Memory allocated here will be free'd in res_close() as we
// have done res_init() above.
ns = (struct sockaddr_in6*) calloc(1, sizeof(struct sockaddr_in6));
Q_CHECK_PTR(ns);
state._u._ext.nsaddrs[0] = ns;
}
// Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
// See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
state._u._ext.nsmap[0] = MAXNS + 1;
state._u._ext.nscount6 = 1;
ns->sin6_family = AF_INET6;
ns->sin6_port = htons(53);
Q_IPV6ADDR ipv6Address = nameserver.toIPv6Address();
for (int i=0; i<16; i++) {
ns->sin6_addr.s6_addr[i] = ipv6Address[i];
}
#else
qWarning() << Q_FUNC_INFO << "IPv6 addresses for nameservers is currently not supported";
reply->error = QDnsLookup::ResolverError;
reply->errorString = tr("IPv6 addresses for nameservers is currently not supported");
return;
#endif
}
}
#ifdef QDNSLOOKUP_DEBUG
state.options |= RES_DEBUG;
#endif

View File

@ -48,15 +48,33 @@
#include <qt_windows.h>
#include <windns.h>
#include <memory.h>
QT_BEGIN_NAMESPACE
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
// Perform DNS query.
PDNS_RECORD dns_records = 0;
const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size());
const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, NULL, &dns_records, NULL);
IP4_ARRAY srvList;
memset(&srvList, 0, sizeof(IP4_ARRAY));
if (!nameserver.isNull()) {
if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
// The below code is referenced from: http://support.microsoft.com/kb/831226
srvList.AddrCount = 1;
srvList.AddrArray[0] = htonl(nameserver.toIPv4Address());
} else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
// For supoprting IPv6 nameserver addresses, we'll need to switch
// from DnsQuey() to DnsQueryEx() as it supports passing an IPv6
// address in the nameserver list
qWarning() << Q_FUNC_INFO << "IPv6 addresses for nameservers is currently not supported";
reply->error = QDnsLookup::ResolverError;
reply->errorString = tr("IPv6 addresses for nameservers is currently not supported");
return;
}
}
const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL);
switch (status) {
case ERROR_SUCCESS:
break;

View File

@ -42,6 +42,7 @@
#include "qdnslookup_p.h"
#include <qurl.h>
#include <qdebug.h>
#include <wrl.h>
#include <windows.foundation.h>
@ -59,8 +60,12 @@ using namespace ABI::Windows::Networking::Sockets;
QT_BEGIN_NAMESPACE
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
// TODO: Add nameserver support for winRT
if (!nameserver.isNull())
qWarning() << "Ignoring nameserver as its currently not supported on WinRT";
// TODO: is there any way to do "proper" dns lookup?
if (requestType != QDnsLookup::A && requestType != QDnsLookup::AAAA
&& requestType != QDnsLookup::ANY) {