/* * Copyright (C) 2006,2007 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ // Note: JDnsShared supports multiple interfaces for multicast, but only one // for IPv4 and one for IPv6. Sharing multiple interfaces of the same IP // version for multicast is unfortunately not possible without reworking // the jdns subsystem. // // The reason for this limitation is that in order to do multi-interface // multicast, you have to do a single bind to Any, and then use special // functions to determine which interface a packet came from and to // specify which interface a packet should go out on. Again this is just // not possible with the current system and the assumptions made by jdns. // Note: When quering against multiple interfaces with multicast, it is // possible that different answers for a unique record may be reported // on each interface. We don't do anything about this. #include "jdnsshared.h" namespace { class SystemInfoCache { public: QJDns::SystemInfo info; QTime time; }; } Q_GLOBAL_STATIC(QMutex, jdnsshared_mutex) Q_GLOBAL_STATIC(SystemInfoCache, jdnsshared_infocache) static QJDns::SystemInfo get_sys_info() { QMutexLocker locker(jdnsshared_mutex()); SystemInfoCache *c = jdnsshared_infocache(); // cache info for 1/2 second, enough to prevent re-reading of sys // info 20 times because of all the different resolves if(c->time.isNull() || c->time.elapsed() >= 500) { c->info = QJDns::systemInfo(); c->time.start(); } return c->info; } static bool domainCompare(const QByteArray &a, const QByteArray &b) { return (qstricmp(a.data(), b.data()) == 0) ? true: false; } // adapted from jdns_mdnsd.c, _a_match() static bool matchRecordExceptTtl(const QJDns::Record &a, const QJDns::Record &b) { if(a.type != b.type || !domainCompare(a.owner, b.owner)) return false; if(a.type == QJDns::Srv) { if(domainCompare(a.name, b.name) && a.port == b.port && a.priority == b.priority && a.weight == b.weight) { return true; } } else if(a.type == QJDns::Ptr || a.type == QJDns::Ns || a.type == QJDns::Cname) { if(domainCompare(a.name, b.name)) return true; } else if(a.rdata == b.rdata) return true; return false; } //---------------------------------------------------------------------------- // Handle //---------------------------------------------------------------------------- namespace { // QJDns uses integer handle ids, but they are only unique within // the relevant QJDns instance. Since we want our handles to be // unique across all instances, we'll make an instance/id pair. class Handle { public: QJDns *jdns; int id; Handle() : jdns(0), id(-1) { } Handle(QJDns *_jdns, int _id) : jdns(_jdns), id(_id) { } bool operator==(const Handle &a) const { if(a.jdns == jdns && a.id == id) return true; return false; } bool operator!=(const Handle &a) const { return !(operator==(a)); } }; } inline uint qHash(const Handle &key) { return ((uint)key.jdns) ^ key.id; } //---------------------------------------------------------------------------- // JDnsShutdown //---------------------------------------------------------------------------- namespace { class JDnsShutdownAgent : public QObject { Q_OBJECT public: void start() { QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection); } signals: void started(); }; class JDnsShutdown : public QThread { Q_OBJECT public: QMutex m; QWaitCondition w; QList list; JDnsShutdownAgent *agent; int phase; void waitForShutdown(const QList _list) { list = _list; phase = 0; m.lock(); start(); w.wait(&m); foreach(JDnsShared *i, list) { i->setParent(0); i->moveToThread(this); } phase = 1; agent->start(); wait(); } protected: virtual void run() { m.lock(); agent = new JDnsShutdownAgent; connect(agent, SIGNAL(started()), SLOT(agent_started()), Qt::DirectConnection); agent->start(); exec(); delete agent; } private slots: void agent_started() { if(phase == 0) { w.wakeOne(); m.unlock(); } else { foreach(JDnsShared *i, list) { connect(i, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished()), Qt::DirectConnection); i->shutdown(); } } } void jdns_shutdownFinished() { JDnsShared *i = (JDnsShared *)sender(); delete i; list.removeAll(i); if(list.isEmpty()) quit(); } }; } //---------------------------------------------------------------------------- // JDnsSharedDebug //---------------------------------------------------------------------------- class JDnsSharedDebugPrivate : public QObject { Q_OBJECT public: JDnsSharedDebug *q; QMutex m; QStringList lines; bool dirty; JDnsSharedDebugPrivate(JDnsSharedDebug *_q) : QObject(_q), q(_q) { dirty = false; } void addDebug(const QString &name, const QStringList &_lines) { if(!_lines.isEmpty()) { QMutexLocker locker(&m); for(int n = 0; n < _lines.count(); ++n) lines += name + ": " + _lines[n]; if(!dirty) { dirty = true; QMetaObject::invokeMethod(this, "doUpdate", Qt::QueuedConnection); } } } private slots: void doUpdate() { { QMutexLocker locker(&m); if(!dirty) return; } emit q->readyRead(); } }; JDnsSharedDebug::JDnsSharedDebug(QObject *parent) :QObject(parent) { d = new JDnsSharedDebugPrivate(this); } JDnsSharedDebug::~JDnsSharedDebug() { delete d; } QStringList JDnsSharedDebug::readDebugLines() { QMutexLocker locker(&d->m); QStringList tmplines = d->lines; d->lines.clear(); d->dirty = false; return tmplines; } //---------------------------------------------------------------------------- // JDnsSharedRequest //---------------------------------------------------------------------------- class JDnsSharedPrivate : public QObject { Q_OBJECT public: class Instance { public: QJDns *jdns; QHostAddress addr; int index; Instance() : jdns(0) { } }; JDnsShared *q; JDnsShared::Mode mode; bool shutting_down; JDnsSharedDebug *db; QString dbname; QList instances; QHash instanceForQJDns; QSet requests; QHash requestForHandle; JDnsSharedPrivate(JDnsShared *_q) : QObject(_q), q(_q) { } JDnsSharedRequest *findRequest(QJDns *jdns, int id) const { Handle h(jdns, id); return requestForHandle.value(h); } void jdns_link(QJDns *jdns) { connect(jdns, SIGNAL(resultsReady(int, const QJDns::Response &)), SLOT(jdns_resultsReady(int, const QJDns::Response &))); connect(jdns, SIGNAL(published(int)), SLOT(jdns_published(int))); connect(jdns, SIGNAL(error(int, QJDns::Error)), SLOT(jdns_error(int, QJDns::Error))); connect(jdns, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished())); connect(jdns, SIGNAL(debugLinesReady()), SLOT(jdns_debugLinesReady())); } int getNewIndex() const { // find lowest unused value for(int n = 0;; ++n) { bool found = false; foreach(Instance *i, instances) { if(i->index == n) { found = true; break; } } if(!found) return n; } } void addDebug(int index, const QString &line) { if(db) db->d->addDebug(dbname + QString::number(index), QStringList() << line); } void doDebug(QJDns *jdns, int index) { QStringList lines = jdns->debugLines(); if(db) db->d->addDebug(dbname + QString::number(index), lines); } QJDns::Record manipulateRecord(const QJDns::Record &in) { // Note: since our implementation only allows 1 ipv4 and 1 ipv6 // interface to exist, it is safe to publish both kinds of // records on both interfaces, with the same values. For // example, an A record can be published on both interfaces, // with the value set to the ipv4 interface. If we supported // multiple ipv4 interfaces, then this wouldn't work, because // we wouldn't know which value to use for the A record when // publishing on the ipv6 interface. // publishing our own IP address? null address means the user // wants us to fill in the blank with our address. if((in.type == QJDns::Aaaa || in.type == QJDns::A) && in.address.isNull()) { QJDns::Record out = in; if(in.type == QJDns::Aaaa) { // are we operating on ipv6? foreach(Instance *i, instances) { if(i->addr.protocol() == QAbstractSocket::IPv6Protocol) { out.address = i->addr; break; } } } else // A { // are we operating on ipv4? foreach(Instance *i, instances) { if(i->addr.protocol() == QAbstractSocket::IPv4Protocol) { out.address = i->addr; break; } } } return out; } return in; } bool addInterface(const QHostAddress &addr); void removeInterface(const QHostAddress &addr); void queryStart(JDnsSharedRequest *obj, const QByteArray &name, int qType); void queryCancel(JDnsSharedRequest *obj); void publishStart(JDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record); void publishUpdate(JDnsSharedRequest *obj, const QJDns::Record &record); void publishCancel(JDnsSharedRequest *obj); public slots: void late_shutdown() { shutting_down = false; emit q->shutdownFinished(); } private slots: void jdns_resultsReady(int id, const QJDns::Response &results); void jdns_published(int id); void jdns_error(int id, QJDns::Error e); void jdns_shutdownFinished(); void jdns_debugLinesReady(); }; class JDnsSharedRequestPrivate : public QObject { Q_OBJECT public: JDnsSharedRequest *q; JDnsSharedPrivate *jsp; // current action JDnsSharedRequest::Type type; QByteArray name; int qType; QJDns::PublishMode pubmode; QJDns::Record pubrecord; // a single request might have to perform multiple QJDns operations QList handles; // keep a list of handles that successfully publish QList published; // use to weed out dups for multicast QList queryCache; bool success; JDnsSharedRequest::Error error; QList results; QTimer lateTimer; JDnsSharedRequestPrivate(JDnsSharedRequest *_q) : QObject(_q), q(_q), lateTimer(this) { connect(&lateTimer, SIGNAL(timeout()), SLOT(lateTimer_timeout())); } void resetSession() { name = QByteArray(); pubrecord = QJDns::Record(); handles.clear(); published.clear(); queryCache.clear(); } private slots: void lateTimer_timeout() { emit q->resultsReady(); } }; JDnsSharedRequest::JDnsSharedRequest(JDnsShared *jdnsShared, QObject *parent) :QObject(parent) { d = new JDnsSharedRequestPrivate(this); d->jsp = jdnsShared->d; } JDnsSharedRequest::~JDnsSharedRequest() { cancel(); delete d; } JDnsSharedRequest::Type JDnsSharedRequest::type() { return d->type; } void JDnsSharedRequest::query(const QByteArray &name, int type) { cancel(); d->jsp->queryStart(this, name, type); } void JDnsSharedRequest::publish(QJDns::PublishMode m, const QJDns::Record &record) { cancel(); d->jsp->publishStart(this, m, record); } void JDnsSharedRequest::publishUpdate(const QJDns::Record &record) { // only allowed to update if we have an active publish if(!d->handles.isEmpty() && d->type == Publish) d->jsp->publishUpdate(this, record); } void JDnsSharedRequest::cancel() { d->lateTimer.stop(); if(!d->handles.isEmpty()) { if(d->type == Query) d->jsp->queryCancel(this); else d->jsp->publishCancel(this); } d->resetSession(); } bool JDnsSharedRequest::success() const { return d->success; } JDnsSharedRequest::Error JDnsSharedRequest::error() const { return d->error; } QList JDnsSharedRequest::results() const { return d->results; } //---------------------------------------------------------------------------- // JDnsShared //---------------------------------------------------------------------------- JDnsShared::JDnsShared(Mode mode, QObject *parent) :QObject(parent) { d = new JDnsSharedPrivate(this); d->mode = mode; d->shutting_down = false; d->db = 0; } JDnsShared::~JDnsShared() { foreach(JDnsSharedPrivate::Instance *i, d->instances) { delete i->jdns; delete i; } delete d; } void JDnsShared::setDebug(JDnsSharedDebug *db, const QString &name) { d->db = db; d->dbname = name; } bool JDnsShared::addInterface(const QHostAddress &addr) { return d->addInterface(addr); } void JDnsShared::removeInterface(const QHostAddress &addr) { d->removeInterface(addr); } void JDnsShared::shutdown() { d->shutting_down = true; if(!d->instances.isEmpty()) { foreach(JDnsSharedPrivate::Instance *i, d->instances) i->jdns->shutdown(); } else QMetaObject::invokeMethod(d, "late_shutdown", Qt::QueuedConnection); } QList JDnsShared::domains() { return get_sys_info().domains; } void JDnsShared::waitForShutdown(const QList instances) { JDnsShutdown s; s.waitForShutdown(instances); } bool JDnsSharedPrivate::addInterface(const QHostAddress &addr) { if(shutting_down) return false; // make sure we don't have this one already foreach(Instance *i, instances) { if(i->addr == addr) return false; } int index = getNewIndex(); addDebug(index, QString("attempting to use interface %1").arg(addr.toString())); QJDns *jdns; if(mode == JDnsShared::UnicastInternet || mode == JDnsShared::UnicastLocal) { jdns = new QJDns(this); jdns_link(jdns); if(!jdns->init(QJDns::Unicast, addr)) { doDebug(jdns, index); delete jdns; return false; } if(mode == JDnsShared::UnicastLocal) { QJDns::NameServer host; host.address = QHostAddress("224.0.0.251"); host.port = 5353; jdns->setNameServers(QList() << host); } } else // Multicast { // only one multicast interface allowed per IP protocol version. // this is because we bind to INADDR_ANY. bool have_v6 = false; bool have_v4 = false; foreach(Instance *i, instances) { if(i->addr.protocol() == QAbstractSocket::IPv6Protocol) have_v6 = true; else have_v4 = true; } bool is_v6 = (addr.protocol() == QAbstractSocket::IPv6Protocol) ? true : false; if(is_v6 && have_v6) { addDebug(index, "already have an ipv6 interface"); return false; } if(!is_v6 && have_v4) { addDebug(index, "already have an ipv4 interface"); return false; } QHostAddress actualBindAddress; if(is_v6) actualBindAddress = QHostAddress::AnyIPv6; else actualBindAddress = QHostAddress::Any; jdns = new QJDns(this); jdns_link(jdns); if(!jdns->init(QJDns::Multicast, actualBindAddress)) { doDebug(jdns, index); delete jdns; return false; } } Instance *i = new Instance; i->jdns = jdns; i->addr = addr; i->index = index; instances += i; instanceForQJDns.insert(i->jdns, i); addDebug(index, "interface ready"); if(mode == JDnsShared::Multicast) { // extend active requests to this interface foreach(JDnsSharedRequest *obj, requests) { if(obj->d->type == JDnsSharedRequest::Query) { Handle h(i->jdns, i->jdns->queryStart(obj->d->name, obj->d->qType)); obj->d->handles += h; requestForHandle.insert(h, obj); } else // Publish { Handle h(i->jdns, i->jdns->publishStart(obj->d->pubmode, obj->d->pubrecord)); obj->d->handles += h; requestForHandle.insert(h, obj); } } } return true; } void JDnsSharedPrivate::removeInterface(const QHostAddress &addr) { Instance *i = 0; for(int n = 0; n < instances.count(); ++n) { if(instances[n]->addr == addr) { i = instances[n]; break; } } if(!i) return; int index = i->index; // we don't cancel operations or shutdown jdns, we simply // delete our references. this is because if the interface // is gone, then we have nothing to send on anyway. foreach(JDnsSharedRequest *obj, requests) { for(int n = 0; n < obj->d->handles.count(); ++n) { Handle h = obj->d->handles[n]; if(h.jdns == i->jdns) { // see above, no need to cancel the operation obj->d->handles.removeAt(n); requestForHandle.remove(h); break; } } // remove published reference if(obj->d->type == JDnsSharedRequest::Publish) { for(int n = 0; n < obj->d->published.count(); ++n) { Handle h = obj->d->published[n]; if(h.jdns == i->jdns) { obj->d->published.removeAt(n); break; } } } } // see above, no need to shutdown jdns instanceForQJDns.remove(i->jdns); instances.removeAll(i); delete i->jdns; delete i; // if that was the last interface to be removed, then there should // be no more handles left. let's take action with these // handleless requests. foreach(JDnsSharedRequest *obj, requests) { if(obj->d->handles.isEmpty()) { if(mode == JDnsShared::UnicastInternet || mode == JDnsShared::UnicastLocal) { // for unicast, we'll invalidate with ErrorNoNet obj->d->success = false; obj->d->error = JDnsSharedRequest::ErrorNoNet; obj->d->lateTimer.start(); } else // Multicast { // for multicast, we'll keep all requests alive. // activity will resume when an interface is // added. } } } addDebug(index, QString("removing from %1").arg(addr.toString())); } void JDnsSharedPrivate::queryStart(JDnsSharedRequest *obj, const QByteArray &name, int qType) { obj->d->type = JDnsSharedRequest::Query; obj->d->success = false; obj->d->results.clear(); obj->d->name = name; obj->d->qType = qType; // is the input an IP address and the qType is an address record? if(qType == QJDns::Aaaa || qType == QJDns::A) { QHostAddress addr; if(addr.setAddress(QString::fromLocal8Bit(name))) { if(qType == QJDns::Aaaa && addr.protocol() == QAbstractSocket::IPv6Protocol) { QJDns::Record rec; rec.owner = name; rec.type = QJDns::Aaaa; rec.ttl = 120; rec.haveKnown = true; rec.address = addr; obj->d->success = true; obj->d->results = QList() << rec; obj->d->lateTimer.start(); return; } else if(qType == QJDns::A && addr.protocol() == QAbstractSocket::IPv4Protocol) { QJDns::Record rec; rec.owner = name; rec.type = QJDns::A; rec.ttl = 120; rec.haveKnown = true; rec.address = addr; obj->d->success = true; obj->d->results = QList() << rec; obj->d->lateTimer.start(); return; } } } QJDns::SystemInfo sysInfo = get_sys_info(); // is the input name a known host and the qType is an address record? if(qType == QJDns::Aaaa || qType == QJDns::A) { QByteArray lname = name.toLower(); QList known = sysInfo.hosts; foreach(QJDns::DnsHost host, known) { if(((qType == QJDns::Aaaa && host.address.protocol() == QAbstractSocket::IPv6Protocol) || (qType == QJDns::A && host.address.protocol() == QAbstractSocket::IPv4Protocol)) && host.name.toLower() == lname) { QJDns::Record rec; rec.owner = name; rec.type = qType; rec.ttl = 120; rec.haveKnown = true; rec.address = host.address; obj->d->success = true; obj->d->results = QList() << rec; obj->d->lateTimer.start(); return; } } } // if we have no QJDns instances to operate on, then error if(instances.isEmpty()) { obj->d->error = JDnsSharedRequest::ErrorNoNet; obj->d->lateTimer.start(); return; } if(mode == JDnsShared::UnicastInternet) { // get latest nameservers, split into ipv6/v4, apply to jdns instances QList ns_v6; QList ns_v4; { QList nameServers = sysInfo.nameServers; foreach(QJDns::NameServer ns, nameServers) { if(ns.address.protocol() == QAbstractSocket::IPv6Protocol) ns_v6 += ns; else ns_v4 += ns; } } foreach(Instance *i, instances) { if(i->addr.protocol() == QAbstractSocket::IPv6Protocol) i->jdns->setNameServers(ns_v6); else i->jdns->setNameServers(ns_v4); } } // keep track of this request requests += obj; // query on all jdns instances foreach(Instance *i, instances) { Handle h(i->jdns, i->jdns->queryStart(name, qType)); obj->d->handles += h; // keep track of this handle for this request requestForHandle.insert(h, obj); } } void JDnsSharedPrivate::queryCancel(JDnsSharedRequest *obj) { if(!requests.contains(obj)) return; foreach(Handle h, obj->d->handles) { h.jdns->queryCancel(h.id); requestForHandle.remove(h); } obj->d->handles.clear(); requests.remove(obj); } void JDnsSharedPrivate::publishStart(JDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record) { obj->d->type = JDnsSharedRequest::Publish; obj->d->success = false; obj->d->results.clear(); obj->d->pubmode = m; obj->d->pubrecord = manipulateRecord(record); // if we have no QJDns instances to operate on, then error if(instances.isEmpty()) { obj->d->error = JDnsSharedRequest::ErrorNoNet; obj->d->lateTimer.start(); return; } // keep track of this request requests += obj; // attempt to publish on all jdns instances foreach(JDnsSharedPrivate::Instance *i, instances) { Handle h(i->jdns, i->jdns->publishStart(m, obj->d->pubrecord)); obj->d->handles += h; // keep track of this handle for this request requestForHandle.insert(h, obj); } } void JDnsSharedPrivate::publishUpdate(JDnsSharedRequest *obj, const QJDns::Record &record) { if(!requests.contains(obj)) return; obj->d->pubrecord = manipulateRecord(record); // publish update on all handles for this request foreach(Handle h, obj->d->handles) h.jdns->publishUpdate(h.id, obj->d->pubrecord); } void JDnsSharedPrivate::publishCancel(JDnsSharedRequest *obj) { if(!requests.contains(obj)) return; foreach(Handle h, obj->d->handles) { h.jdns->publishCancel(h.id); requestForHandle.remove(h); } obj->d->handles.clear(); obj->d->published.clear(); requests.remove(obj); } void JDnsSharedPrivate::jdns_resultsReady(int id, const QJDns::Response &results) { QJDns *jdns = (QJDns *)sender(); JDnsSharedRequest *obj = findRequest(jdns, id); obj->d->success = true; obj->d->results = results.answerRecords; if(mode == JDnsShared::UnicastInternet || mode == JDnsShared::UnicastLocal) { // only one response, so "cancel" it for(int n = 0; n < obj->d->handles.count(); ++n) { Handle h = obj->d->handles[n]; if(h.jdns == jdns && h.id == id) { obj->d->handles.removeAt(n); requestForHandle.remove(h); break; } } // cancel related handles foreach(Handle h, obj->d->handles) { h.jdns->queryCancel(h.id); requestForHandle.remove(h); } obj->d->handles.clear(); requests.remove(obj); } else // Multicast { // check our cache to see how we should report these results for(int n = 0; n < obj->d->results.count(); ++n) { QJDns::Record &r = obj->d->results[n]; // do we have this answer already in our cache? QJDns::Record *c = 0; int c_at = -1; for(int k = 0; k < obj->d->queryCache.count(); ++k) { QJDns::Record &tmp = obj->d->queryCache[k]; if(matchRecordExceptTtl(r, tmp)) { c = &tmp; c_at = k; break; } } // don't report duplicates or unknown removals if((c && r.ttl != 0) || (!c && r.ttl == 0)) { obj->d->results.removeAt(n); --n; // adjust position continue; } // if we have it, and it is removed, remove from cache if(c && r.ttl == 0) { obj->d->queryCache.removeAt(c_at); } // otherwise, if we don't have it, add it to the cache else if(!c) { obj->d->queryCache += r; } } if(obj->d->results.isEmpty()) return; } emit obj->resultsReady(); } void JDnsSharedPrivate::jdns_published(int id) { QJDns *jdns = (QJDns *)sender(); JDnsSharedRequest *obj = findRequest(jdns, id); // find handle Handle handle; for(int n = 0; n < obj->d->handles.count(); ++n) { Handle h = obj->d->handles[n]; if(h.jdns == jdns && h.id == id) { handle = h; break; } } obj->d->published += handle; // if this publish has already been considered successful, then // a publish has succeeded on a new interface and there's no // need to report success for this request again if(obj->d->success) return; // all handles published? if(obj->d->published.count() == obj->d->handles.count()) { obj->d->success = true; emit obj->resultsReady(); } } void JDnsSharedPrivate::jdns_error(int id, QJDns::Error e) { QJDns *jdns = (QJDns *)sender(); JDnsSharedRequest *obj = findRequest(jdns, id); // "cancel" it for(int n = 0; n < obj->d->handles.count(); ++n) { Handle h = obj->d->handles[n]; if(h.jdns == jdns && h.id == id) { obj->d->handles.removeAt(n); requestForHandle.remove(h); break; } } if(obj->d->type == JDnsSharedRequest::Query) { // ignore the error if it is not the last error if(!obj->d->handles.isEmpty()) return; requests.remove(obj); obj->d->success = false; JDnsSharedRequest::Error x = JDnsSharedRequest::ErrorGeneric; if(e == QJDns::ErrorNXDomain) x = JDnsSharedRequest::ErrorNXDomain; else if(e == QJDns::ErrorTimeout) x = JDnsSharedRequest::ErrorTimeout; else // ErrorGeneric x = JDnsSharedRequest::ErrorGeneric; obj->d->error = x; emit obj->resultsReady(); } else // Publish { // cancel related handles foreach(Handle h, obj->d->handles) { h.jdns->publishCancel(h.id); requestForHandle.remove(h); } obj->d->handles.clear(); obj->d->published.clear(); requests.remove(obj); obj->d->success = false; JDnsSharedRequest::Error x = JDnsSharedRequest::ErrorGeneric; if(e == QJDns::ErrorConflict) x = JDnsSharedRequest::ErrorConflict; else // ErrorGeneric x = JDnsSharedRequest::ErrorGeneric; obj->d->error = x; emit obj->resultsReady(); } } void JDnsSharedPrivate::jdns_shutdownFinished() { QJDns *jdns = (QJDns *)sender(); addDebug(instanceForQJDns.value(jdns)->index, "jdns_shutdownFinished, removing interface"); Instance *instance = instanceForQJDns.value(jdns); delete instance->jdns; delete instance; instanceForQJDns.remove(jdns); instances.removeAll(instance); if(instances.isEmpty()) late_shutdown(); } void JDnsSharedPrivate::jdns_debugLinesReady() { QJDns *jdns = (QJDns *)sender(); doDebug(jdns, instanceForQJDns.value(jdns)->index); } #include "jdnsshared.moc"