/* * srvresolver.cpp - class to simplify SRV lookups * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "srvresolver.h" #include #ifndef NO_NDNS # include "ndns.h" #endif // CS_NAMESPACE_BEGIN bool serverLessThan(const Q3Dns::Server &s1, const Q3Dns::Server &s2) { int a = s1.priority; int b = s2.priority; int j = s1.weight; int k = s2.weight; return a < b || (a == b && j < k); } static void sortSRVList(QList &list) { qStableSort(list.begin(), list.end(), serverLessThan); } class SrvResolver::Private { public: Private() {} XMPP::NameResolver nndns; XMPP::NameRecord::Type nntype; bool nndns_busy; #ifndef NO_NDNS NDns ndns; #endif bool failed; QHostAddress resultAddress; quint16 resultPort; bool srvonly; QString srv; QList servers; bool aaaa; QTimer t; }; SrvResolver::SrvResolver(QObject *parent) :QObject(parent) { d = new Private; d->nndns_busy = false; connect(&d->nndns, SIGNAL(resultsReady(const QList &)), SLOT(nndns_resultsReady(const QList &))); connect(&d->nndns, SIGNAL(error(XMPP::NameResolver::Error)), SLOT(nndns_error(XMPP::NameResolver::Error))); #ifndef NO_NDNS connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done())); #endif connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout())); stop(); } SrvResolver::~SrvResolver() { stop(); delete d; } void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto, bool srvOnly) { stop(); d->failed = false; d->srvonly = srvOnly; d->srv = QString("_") + type + "._" + proto + '.' + server; d->t.setSingleShot(true); d->t.start(15000); d->nndns_busy = true; d->nntype = XMPP::NameRecord::Srv; d->nndns.start(d->srv.toLatin1(), d->nntype); } void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto) { resolve(server, type, proto, false); } void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto) { resolve(server, type, proto, true); } void SrvResolver::next() { if(d->servers.isEmpty()) return; tryNext(); } void SrvResolver::stop() { if(d->t.isActive()) d->t.stop(); if(d->nndns_busy) { d->nndns.stop(); d->nndns_busy = false; } #ifndef NO_NDNS if(d->ndns.isBusy()) d->ndns.stop(); #endif d->resultAddress = QHostAddress(); d->resultPort = 0; d->servers.clear(); d->srv = ""; d->failed = true; } bool SrvResolver::isBusy() const { #ifndef NO_NDNS if(d->nndns_busy || d->ndns.isBusy()) #else if(d->nndns_busy) #endif return true; else return false; } QList SrvResolver::servers() const { return d->servers; } bool SrvResolver::failed() const { return d->failed; } QHostAddress SrvResolver::resultAddress() const { return d->resultAddress; } quint16 SrvResolver::resultPort() const { return d->resultPort; } void SrvResolver::tryNext() { #ifndef NO_NDNS d->ndns.resolve(d->servers.first().name); #else d->nndns_busy = true; d->nntype = d->aaaa ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A; d->nndns.start(d->servers.first().name.toLatin1(), d->nntype); #endif } void SrvResolver::nndns_resultsReady(const QList &results) { if(!d->nndns_busy) return; d->t.stop(); if(d->nntype == XMPP::NameRecord::Srv) { // grab the server list and destroy the qdns object QList list; for(int n = 0; n < results.count(); ++n) { list += Q3Dns::Server(QString::fromLatin1(results[n].name()), results[n].priority(), results[n].weight(), results[n].port()); } d->nndns_busy = false; d->nndns.stop(); if(list.isEmpty()) { stop(); resultsReady(); return; } sortSRVList(list); d->servers = list; if(d->srvonly) resultsReady(); else { // kick it off d->aaaa = true; tryNext(); } } else { // grab the address list and destroy the qdns object QList list; if(d->nntype == XMPP::NameRecord::A || d->nntype == XMPP::NameRecord::Aaaa) { for(int n = 0; n < results.count(); ++n) { list += results[n].address(); } } d->nndns_busy = false; d->nndns.stop(); if(!list.isEmpty()) { int port = d->servers.first().port; d->servers.removeFirst(); d->aaaa = true; d->resultAddress = list.first(); d->resultPort = port; resultsReady(); } else { if(!d->aaaa) d->servers.removeFirst(); d->aaaa = !d->aaaa; // failed? bail if last one if(d->servers.isEmpty()) { stop(); resultsReady(); return; } // otherwise try the next tryNext(); } } } void SrvResolver::nndns_error(XMPP::NameResolver::Error) { nndns_resultsReady(QList()); } void SrvResolver::ndns_done() { #ifndef NO_NDNS QHostAddress r = d->ndns.result(); int port = d->servers.first().port; d->servers.removeFirst(); if(!r.isNull()) { d->resultAddress = r; d->resultPort = port; resultsReady(); } else { // failed? bail if last one if(d->servers.isEmpty()) { stop(); resultsReady(); return; } // otherwise try the next tryNext(); } #endif } void SrvResolver::t_timeout() { stop(); resultsReady(); } // CS_NAMESPACE_END