/* * 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 #include #include #include"safedelete.h" #ifndef NO_NDNS #include"ndns.h" #endif // CS_NAMESPACE_BEGIN static void sortSRVList(QValueList &list) { QValueList tmp = list; list.clear(); while(!tmp.isEmpty()) { QValueList::Iterator p = tmp.end(); for(QValueList::Iterator it = tmp.begin(); it != tmp.end(); ++it) { if(p == tmp.end()) p = it; else { int a = (*it).priority; int b = (*p).priority; int j = (*it).weight; int k = (*p).weight; if(a < b || (a == b && j < k)) p = it; } } list.append(*p); tmp.remove(p); } } class SrvResolver::Private { public: Private() {} QDns *qdns; #ifndef NO_NDNS NDns ndns; #endif bool failed; QHostAddress resultAddress; Q_UINT16 resultPort; bool srvonly; QString srv; QValueList servers; bool aaaa; QTimer t; SafeDelete sd; }; SrvResolver::SrvResolver(QObject *parent) :QObject(parent) { d = new Private; d->qdns = 0; #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) { stop(); d->failed = false; d->srvonly = false; d->srv = QString("_") + type + "._" + proto + '.' + server; d->t.start(15000, true); d->qdns = new QDns; connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done())); d->qdns->setRecordType(QDns::Srv); d->qdns->setLabel(d->srv); } void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto) { stop(); d->failed = false; d->srvonly = true; d->srv = QString("_") + type + "._" + proto + '.' + server; d->t.start(15000, true); d->qdns = new QDns; connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done())); d->qdns->setRecordType(QDns::Srv); d->qdns->setLabel(d->srv); } void SrvResolver::next() { if(d->servers.isEmpty()) return; tryNext(); } void SrvResolver::stop() { if(d->t.isActive()) d->t.stop(); if(d->qdns) { d->qdns->disconnect(this); d->sd.deleteLater(d->qdns); d->qdns = 0; } #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->qdns || d->ndns.isBusy()) #else if(d->qdns) #endif return true; else return false; } QValueList SrvResolver::servers() const { return d->servers; } bool SrvResolver::failed() const { return d->failed; } QHostAddress SrvResolver::resultAddress() const { return d->resultAddress; } Q_UINT16 SrvResolver::resultPort() const { return d->resultPort; } void SrvResolver::tryNext() { #ifndef NO_NDNS d->ndns.resolve(d->servers.first().name); #else d->qdns = new QDns; connect(d->qdns, SIGNAL(resultsReady()), SLOT(ndns_done())); if(d->aaaa) d->qdns->setRecordType(QDns::Aaaa); // IPv6 else d->qdns->setRecordType(QDns::A); // IPv4 d->qdns->setLabel(d->servers.first().name); #endif } void SrvResolver::qdns_done() { if(!d->qdns) return; // apparently we sometimes get this signal even though the results aren't ready if(d->qdns->isWorking()) return; d->t.stop(); SafeDeleteLock s(&d->sd); // grab the server list and destroy the qdns object QValueList list; if(d->qdns->recordType() == QDns::Srv) list = d->qdns->servers(); d->qdns->disconnect(this); d->sd.deleteLater(d->qdns); d->qdns = 0; if(list.isEmpty()) { stop(); resultsReady(); return; } sortSRVList(list); d->servers = list; if(d->srvonly) resultsReady(); else { // kick it off d->aaaa = true; tryNext(); } } void SrvResolver::ndns_done() { #ifndef NO_NDNS SafeDeleteLock s(&d->sd); uint r = d->ndns.result(); int port = d->servers.first().port; d->servers.remove(d->servers.begin()); if(r) { d->resultAddress = QHostAddress(d->ndns.result()); d->resultPort = port; resultsReady(); } else { // failed? bail if last one if(d->servers.isEmpty()) { stop(); resultsReady(); return; } // otherwise try the next tryNext(); } #else if(!d->qdns) return; // apparently we sometimes get this signal even though the results aren't ready if(d->qdns->isWorking()) return; SafeDeleteLock s(&d->sd); // grab the address list and destroy the qdns object QValueList list; if(d->qdns->recordType() == QDns::A || d->qdns->recordType() == QDns::Aaaa) list = d->qdns->addresses(); d->qdns->disconnect(this); d->sd.deleteLater(d->qdns); d->qdns = 0; if(!list.isEmpty()) { int port = d->servers.first().port; d->servers.remove(d->servers.begin()); d->aaaa = true; d->resultAddress = list.first(); d->resultPort = port; resultsReady(); } else { if(!d->aaaa) d->servers.remove(d->servers.begin()); d->aaaa = !d->aaaa; // failed? bail if last one if(d->servers.isEmpty()) { stop(); resultsReady(); return; } // otherwise try the next tryNext(); } #endif } void SrvResolver::t_timeout() { SafeDeleteLock s(&d->sd); stop(); resultsReady(); } // CS_NAMESPACE_END #include "srvresolver.moc"