diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 27a38ef61..965330da3 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -258,6 +258,7 @@ set( libHeaders playlist/dynamic/widgets/MiscControlWidgets.h playlist/dynamic/widgets/CollapsibleControls.h + utils/tomahawkutils.h utils/querylabel.h utils/elidedlabel.h utils/animatedcounterlabel.h @@ -296,6 +297,9 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ../../qxt/qxtweb-standalone/qxtweb ../../rtaudio ../../alsa-playback + ../../thirdparty/jdns + ../../thirdparty/jdns/jdns + ../../thirdparty/jdns/jdnsshared playlist ) @@ -359,7 +363,7 @@ target_link_libraries( tomahawklib vorbisfile ogg FLAC++ - jdns + tomahawk_jdns clucene ) diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index 312459328..d298ffc23 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -16,8 +16,11 @@ #ifdef Q_WS_MAC #include #include +#include #endif +#include +#include namespace TomahawkUtils { @@ -305,4 +308,62 @@ setProxy( QNetworkProxy* proxy ) s_proxy = proxy; } + +///////////////// DNSResolver ///////////////// + +static DNSResolver* s_dnsResolver = 0; + +DNSResolver* +dnsResolver() +{ + if( !s_dnsResolver ) + s_dnsResolver = new DNSResolver(); + + return s_dnsResolver; +} + +DNSResolver::DNSResolver() +{ + m_dnsShared = new JDnsShared(JDnsShared::UnicastInternet); + m_dnsShared->addInterface(QHostAddress::Any); + m_dnsShared->addInterface(QHostAddress::AnyIPv6); + m_dnsSharedRequest = new JDnsSharedRequest(m_dnsShared); +} + +void +DNSResolver::resolve( QString &host, QString &type ) +{ + connect(m_dnsSharedRequest, SIGNAL(resultsReady()), SLOT(resultsReady())); + if( type == "SRV" ) + { + // For the moment, assume we are looking for XMPP... + m_dnsSharedRequest->query( "_xmpp-client._tcp." + host.toUtf8(), QJDns::Srv ); + } + else + { + QString badResult( "NONE" ); + emit result( badResult ); + } +} + +void +DNSResolver::resultsReady() +{ + if( m_dnsSharedRequest->success() ) + { + QList results = m_dnsSharedRequest->results(); + foreach( QJDns::Record r, results ) + { + if( r.type == QJDns::Srv ) + { + QString foundResult( r.name ); + emit result( foundResult ); + return; + } + } + } + QString badResult( "NONE" ); + emit result( badResult ); +} + } // ns diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index 6fdd14bc7..2b8555fa6 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -2,6 +2,7 @@ #define TOMAHAWKUTILS_H #include "dllmacro.h" +#include #define RESPATH ":/data/" @@ -12,8 +13,31 @@ class QPixmap; class QNetworkAccessManager; class QNetworkProxy; +class JDnsShared; +class JDnsSharedRequest; + namespace TomahawkUtils { + class DLLEXPORT DNSResolver : public QObject + { + Q_OBJECT + public: + explicit DNSResolver(); + ~DNSResolver() {} + + void resolve( QString &host, QString &type ); + + signals: + void result( QString &result ); + + public slots: + void resultsReady(); + + private: + JDnsShared* m_dnsShared; + JDnsSharedRequest* m_dnsSharedRequest; + }; + DLLEXPORT QDir appConfigDir(); DLLEXPORT QDir appDataDir(); @@ -28,6 +52,8 @@ namespace TomahawkUtils DLLEXPORT void setNam( QNetworkAccessManager* nam ); DLLEXPORT void setProxy( QNetworkProxy* proxy ); + + DLLEXPORT DNSResolver* dnsResolver(); } #endif // TOMAHAWKUTILS_H diff --git a/thirdparty/jdns/CMakeLists.txt b/thirdparty/jdns/CMakeLists.txt index 8111be46e..573c9e994 100644 --- a/thirdparty/jdns/CMakeLists.txt +++ b/thirdparty/jdns/CMakeLists.txt @@ -6,39 +6,46 @@ SET(CMAKE_VERBOSE_MAKEFILE ON) SET( QT_USE_QTNETWORK TRUE ) INCLUDE( ${QT_USE_FILE} ) +add_definitions( ${QT_DEFINITIONS} ) +add_definitions( -DQT_SHARED ) + if(WIN32) - SET(PLATFORM_SPECIFIC_LIBS "ws2_32.dll" "advapi32.dll" ) + set(PLATFORM_SPECIFIC_LIBS "ws2_32.dll" "advapi32.dll" ) endif(WIN32) -set(JDNS_SOURCES - jdns_util.c - jdns_packet.c - jdns_mdnsd.c - jdns_sys.c - jdns.c - qjdns_sock.cpp - qjdns.cpp +set(TOMAHAWK_JDNS_SOURCES + jdns/jdns_util.c + jdns/jdns_packet.c + jdns/jdns_mdnsd.c + jdns/jdns_sys.c + jdns/jdns.c + jdns/qjdns_sock.cpp + jdns/qjdns.cpp + jdnsshared/jdnsshared.cpp ) -set(JDNS_HEADERS - qjdns.h +set(TOMAHAWK_JDNS_HEADERS + jdns/qjdns.h + jdns/qjdns_helpers.h + jdnsshared/jdnsshared.h + jdnsshared/jdnsshared_helpers.h ) include_directories( + . ${QT_INCLUDE_DIR} ${QT_INCLUDES} + jdns ) -qt4_wrap_cpp( JDNS_MOC ${JDNS_HEADERS} ) +qt4_wrap_cpp( TOMAHAWK_JDNS_MOC ${TOMAHAWK_JDNS_HEADERS} ) -if(WIN32) - ADD_LIBRARY(jdns SHARED ${JDNS_SOURCES} ${JDNS_MOC}) -else() - ADD_LIBRARY(jdns STATIC ${JDNS_SOURCES} ${JDNS_MOC}) -endif() +ADD_LIBRARY(tomahawk_jdns SHARED ${TOMAHAWK_JDNS_SOURCES} ${TOMAHAWK_JDNS_MOC}) -target_link_libraries(jdns +target_link_libraries(tomahawk_jdns ${QT_LIBRARIES} ) -INSTALL(TARGETS jdns ARCHIVE DESTINATION lib) +SET_TARGET_PROPERTIES( tomahawk_jdns PROPERTIES DEFINE_SYMBOL MAKE_JDNS_LIB ) + +INSTALL(TARGETS tomahawk_jdns DESTINATION lib) diff --git a/thirdparty/jdns/README b/thirdparty/jdns/jdns/README similarity index 100% rename from thirdparty/jdns/README rename to thirdparty/jdns/jdns/README diff --git a/thirdparty/jdns/TODO b/thirdparty/jdns/jdns/TODO similarity index 100% rename from thirdparty/jdns/TODO rename to thirdparty/jdns/jdns/TODO diff --git a/thirdparty/jdns/jdns.c b/thirdparty/jdns/jdns/jdns.c similarity index 100% rename from thirdparty/jdns/jdns.c rename to thirdparty/jdns/jdns/jdns.c diff --git a/thirdparty/jdns/jdns.h b/thirdparty/jdns/jdns/jdns.h similarity index 100% rename from thirdparty/jdns/jdns.h rename to thirdparty/jdns/jdns/jdns.h diff --git a/thirdparty/jdns/jdns.pri b/thirdparty/jdns/jdns/jdns.pri similarity index 100% rename from thirdparty/jdns/jdns.pri rename to thirdparty/jdns/jdns/jdns.pri diff --git a/thirdparty/jdns/jdns.pro b/thirdparty/jdns/jdns/jdns.pro similarity index 100% rename from thirdparty/jdns/jdns.pro rename to thirdparty/jdns/jdns/jdns.pro diff --git a/thirdparty/jdns/jdns_mdnsd.c b/thirdparty/jdns/jdns/jdns_mdnsd.c similarity index 100% rename from thirdparty/jdns/jdns_mdnsd.c rename to thirdparty/jdns/jdns/jdns_mdnsd.c diff --git a/thirdparty/jdns/jdns_mdnsd.h b/thirdparty/jdns/jdns/jdns_mdnsd.h similarity index 100% rename from thirdparty/jdns/jdns_mdnsd.h rename to thirdparty/jdns/jdns/jdns_mdnsd.h diff --git a/thirdparty/jdns/jdns_p.h b/thirdparty/jdns/jdns/jdns_p.h similarity index 100% rename from thirdparty/jdns/jdns_p.h rename to thirdparty/jdns/jdns/jdns_p.h diff --git a/thirdparty/jdns/jdns_packet.c b/thirdparty/jdns/jdns/jdns_packet.c similarity index 100% rename from thirdparty/jdns/jdns_packet.c rename to thirdparty/jdns/jdns/jdns_packet.c diff --git a/thirdparty/jdns/jdns_packet.h b/thirdparty/jdns/jdns/jdns_packet.h similarity index 100% rename from thirdparty/jdns/jdns_packet.h rename to thirdparty/jdns/jdns/jdns_packet.h diff --git a/thirdparty/jdns/jdns_sys.c b/thirdparty/jdns/jdns/jdns_sys.c similarity index 100% rename from thirdparty/jdns/jdns_sys.c rename to thirdparty/jdns/jdns/jdns_sys.c diff --git a/thirdparty/jdns/jdns_util.c b/thirdparty/jdns/jdns/jdns_util.c similarity index 100% rename from thirdparty/jdns/jdns_util.c rename to thirdparty/jdns/jdns/jdns_util.c diff --git a/thirdparty/jdns/main.cpp b/thirdparty/jdns/jdns/main.cpp similarity index 100% rename from thirdparty/jdns/main.cpp rename to thirdparty/jdns/jdns/main.cpp diff --git a/thirdparty/jdns/jdns/qjdns.cpp b/thirdparty/jdns/jdns/qjdns.cpp new file mode 100644 index 000000000..a65219bcb --- /dev/null +++ b/thirdparty/jdns/jdns/qjdns.cpp @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2005-2008 Justin Karneges + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "qjdns.h" + +#include +#include "qjdns_sock.h" +#include "qjdns_helpers.h" +#include "jdns.h" + +// for fprintf +#include + + +//---------------------------------------------------------------------------- +// QJDns::NameServer +//---------------------------------------------------------------------------- +QJDns::NameServer::NameServer() +{ + port = JDNS_UNICAST_PORT; +} + +//---------------------------------------------------------------------------- +// QJDns::Record +//---------------------------------------------------------------------------- +QJDns::Record::Record() +{ + ttl = 0; + type = -1; + haveKnown = false; +} + +bool QJDns::Record::verify() const +{ + jdns_rr_t *rr = export_record(*this); + int ok = jdns_rr_verify(rr); + jdns_rr_delete(rr); + return (ok ? true : false); +} + +//---------------------------------------------------------------------------- +// QJDns +//---------------------------------------------------------------------------- +QJDns::Private::Private(QJDns *_q) : + QObject(_q), + q(_q), + stepTrigger(this), + debugTrigger(this), + stepTimeout(this), + pErrors(0), + pPublished(0), + pResponses(0) +{ + sess = 0; + shutting_down = false; + new_debug_strings = false; + pending = 0; + + connect(&stepTrigger, SIGNAL(timeout()), SLOT(doNextStepSlot())); + stepTrigger.setSingleShot(true); + + connect(&debugTrigger, SIGNAL(timeout()), SLOT(doDebug())); + debugTrigger.setSingleShot(true); + + connect(&stepTimeout, SIGNAL(timeout()), SLOT(st_timeout())); + stepTimeout.setSingleShot(true); + + my_srand(); + + clock.start(); +} + +QJDns::Private::~Private() +{ + cleanup(); +} + +void QJDns::Private::cleanup() +{ + if(sess) + { + jdns_session_delete(sess); + sess = 0; + } + + shutting_down = false; + pending = 0; + + // it is safe to delete the QUdpSocket objects here without + // deleteLater, since this code path never occurs when + // a signal from those objects is on the stack + qDeleteAll(socketForHandle); + socketForHandle.clear(); + handleForSocket.clear(); + + stepTrigger.stop(); + stepTimeout.stop(); + need_handle = 0; +} + +bool QJDns::Private::init(QJDns::Mode _mode, const QHostAddress &address) +{ + mode = _mode; + + jdns_callbacks_t callbacks; + callbacks.app = this; + callbacks.time_now = cb_time_now; + callbacks.rand_int = cb_rand_int; + callbacks.debug_line = cb_debug_line; + callbacks.udp_bind = cb_udp_bind; + callbacks.udp_unbind = cb_udp_unbind; + callbacks.udp_read = cb_udp_read; + callbacks.udp_write = cb_udp_write; + sess = jdns_session_new(&callbacks); + jdns_set_hold_ids_enabled(sess, 1); + next_handle = 1; + need_handle = false; + + int ret; + + jdns_address_t *baddr = qt2addr(address); + if(mode == Unicast) + { + ret = jdns_init_unicast(sess, baddr, 0); + } + else + { + jdns_address_t *maddr; + if(address.protocol() == QAbstractSocket::IPv6Protocol) + maddr = jdns_address_multicast6_new(); + else + maddr = jdns_address_multicast4_new(); + ret = jdns_init_multicast(sess, baddr, JDNS_MULTICAST_PORT, maddr); + jdns_address_delete(maddr); + } + jdns_address_delete(baddr); + + if(!ret) + { + jdns_session_delete(sess); + sess = 0; + return false; + } + return true; +} + +void QJDns::Private::setNameServers(const QList &nslist) +{ + jdns_nameserverlist_t *addrs = jdns_nameserverlist_new(); + for(int n = 0; n < nslist.count(); ++n) + { + jdns_address_t *addr = qt2addr(nslist[n].address); + jdns_nameserverlist_append(addrs, addr, nslist[n].port); + jdns_address_delete(addr); + } + jdns_set_nameservers(sess, addrs); + jdns_nameserverlist_delete(addrs); +} + +void QJDns::Private::process() +{ + if(!stepTrigger.isActive()) + { + stepTimeout.stop(); + stepTrigger.start(); + } +} + +void QJDns::Private::processDebug() +{ + new_debug_strings = true; + if(!debugTrigger.isActive()) + debugTrigger.start(); +} + +void QJDns::Private::doNextStep() +{ + if(shutting_down && complete_shutdown) + { + cleanup(); + emit q->shutdownFinished(); + return; + } + + QPointer self = this; + + int ret = jdns_step(sess); + + QList errors; + QList published; + QList responses; + bool finish_shutdown = false; + + pErrors = &errors; + pPublished = &published; + pResponses = &responses; + + while(1) + { + jdns_event_t *e = jdns_next_event(sess); + if(!e) + break; + + if(e->type == JDNS_EVENT_SHUTDOWN) + { + finish_shutdown = true; + } + else if(e->type == JDNS_EVENT_PUBLISH) + { + if(e->status != JDNS_STATUS_SUCCESS) + { + QJDns::Error error; + if(e->status == JDNS_STATUS_CONFLICT) + error = QJDns::ErrorConflict; + else + error = QJDns::ErrorGeneric; + LateError le; + le.source_type = 1; + le.id = e->id; + le.error = error; + errors += le; + } + else + { + published += e->id; + } + } + else if(e->type == JDNS_EVENT_RESPONSE) + { + if(e->status != JDNS_STATUS_SUCCESS) + { + QJDns::Error error; + if(e->status == JDNS_STATUS_NXDOMAIN) + error = QJDns::ErrorNXDomain; + else if(e->status == JDNS_STATUS_TIMEOUT) + error = QJDns::ErrorTimeout; + else + error = QJDns::ErrorGeneric; + LateError le; + le.source_type = 0; + le.id = e->id; + le.error = error; + errors += le; + } + else + { + QJDns::Response out_response; + for(int n = 0; n < e->response->answerCount; ++n) + out_response.answerRecords += import_record(e->response->answerRecords[n]); + LateResponse lr; + lr.id = e->id; + lr.response = out_response; + if(mode == Unicast) + lr.do_cancel = true; + else + lr.do_cancel = false; + responses += lr; + } + } + + jdns_event_delete(e); + } + + if(ret & JDNS_STEP_TIMER) + stepTimeout.start(jdns_next_timer(sess)); + else + stepTimeout.stop(); + + need_handle = (ret & JDNS_STEP_HANDLE); + + // read the lists safely enough so that items can be deleted + // behind our back + + while(!errors.isEmpty()) + { + LateError i = errors.takeFirst(); + if(i.source_type == 0) + jdns_cancel_query(sess, i.id); + else + jdns_cancel_publish(sess, i.id); + emit q->error(i.id, i.error); + if(!self) + return; + } + + while(!published.isEmpty()) + { + int i = published.takeFirst(); + emit q->published(i); + if(!self) + return; + } + + while(!responses.isEmpty()) + { + LateResponse i = responses.takeFirst(); + if(i.do_cancel) + jdns_cancel_query(sess, i.id); + emit q->resultsReady(i.id, i.response); + if(!self) + return; + } + + if(finish_shutdown) + { + // if we have pending udp packets to write, stick around + if(pending > 0) + { + pending_wait = true; + } + else + { + complete_shutdown = true; + process(); + } + } + + pErrors = 0; + pPublished = 0; + pResponses = 0; +} + +void QJDns::Private::removeCancelled(int id) +{ + if(pErrors) + { + for(int n = 0; n < pErrors->count(); ++n) + { + if(pErrors->at(n).id == id) + { + pErrors->removeAt(n); + --n; // adjust position + } + } + } + + if(pPublished) + { + for(int n = 0; n < pPublished->count(); ++n) + { + if(pPublished->at(n) == id) + { + pPublished->removeAt(n); + --n; // adjust position + } + } + } + + if(pResponses) + { + for(int n = 0; n < pResponses->count(); ++n) + { + if(pResponses->at(n).id == id) + { + pResponses->removeAt(n); + --n; // adjust position + } + } + } +} + +void QJDns::Private::udp_readyRead() +{ + QUdpSocket *sock = (QUdpSocket *)sender(); + int handle = handleForSocket.value(sock); + + if(need_handle) + { + jdns_set_handle_readable(sess, handle); + process(); + } + else + { + // eat packet + QByteArray buf(4096, 0); + QHostAddress from_addr; + quint16 from_port; + sock->readDatagram(buf.data(), buf.size(), &from_addr, &from_port); + } +} + +void QJDns::Private::udp_bytesWritten(qint64) +{ + if(pending > 0) + { + --pending; + if(shutting_down && pending_wait && pending == 0) + { + pending_wait = false; + complete_shutdown = true; + process(); + } + } +} + +void QJDns::Private::st_timeout() +{ + doNextStep(); +} + +void QJDns::Private::doNextStepSlot() +{ + doNextStep(); +} + +void QJDns::Private::doDebug() +{ + if(new_debug_strings) + { + new_debug_strings = false; + if(!debug_strings.isEmpty()) + emit q->debugLinesReady(); + } +} + +QJDns::QJDns(QObject *parent) +:QObject(parent) +{ + d = new Private(this); +} + +QJDns::~QJDns() +{ + delete d; +} + +bool QJDns::init(Mode mode, const QHostAddress &address) +{ + return d->init(mode, address); +} + +void QJDns::shutdown() +{ + d->shutting_down = true; + d->pending_wait = false; + d->complete_shutdown = false; + jdns_shutdown(d->sess); + d->process(); +} + +QStringList QJDns::debugLines() +{ + QStringList tmp = d->debug_strings; + d->debug_strings.clear(); + return tmp; +} + +QJDns::SystemInfo QJDns::systemInfo() +{ + SystemInfo out; + jdns_dnsparams_t *params = jdns_system_dnsparams(); + for(int n = 0; n < params->nameservers->count; ++n) + { + NameServer ns; + ns.address = addr2qt(params->nameservers->item[n]->address); + out.nameServers += ns; + } + for(int n = 0; n < params->domains->count; ++n) + out.domains += str2qt(params->domains->item[n]); + for(int n = 0; n < params->hosts->count; ++n) + { + DnsHost h; + h.name = str2qt(params->hosts->item[n]->name); + h.address = addr2qt(params->hosts->item[n]->address); + out.hosts += h; + } + jdns_dnsparams_delete(params); + return out; +} + +#define PROBE_BASE 20000 +#define PROBE_RANGE 100 + +QHostAddress QJDns::detectPrimaryMulticast(const QHostAddress &address) +{ + my_srand(); + + QUdpSocket *sock = new QUdpSocket; + QUdpSocket::BindMode mode; + mode |= QUdpSocket::ShareAddress; + mode |= QUdpSocket::ReuseAddressHint; + int port = -1; + for(int n = 0; n < PROBE_RANGE; ++n) + { + if(sock->bind(address, PROBE_BASE + n, mode)) + { + port = PROBE_BASE + n; + break; + } + } + if(port == -1) + { + delete sock; + return QHostAddress(); + } + + jdns_address_t *a; + if(address.protocol() == QAbstractSocket::IPv6Protocol) + a = jdns_address_multicast6_new(); + else + a = jdns_address_multicast4_new(); + QHostAddress maddr = addr2qt(a); + jdns_address_delete(a); + + if(address.protocol() == QAbstractSocket::IPv6Protocol) + { + int x; + if(!qjdns_sock_setMulticast6(sock->socketDescriptor(), maddr.toIPv6Address().c, &x)) + { + delete sock; + return QHostAddress(); + } + qjdns_sock_setTTL6(sock->socketDescriptor(), 0); + } + else + { + int x; + if(!qjdns_sock_setMulticast4(sock->socketDescriptor(), maddr.toIPv4Address(), &x)) + { + delete sock; + return QHostAddress(); + } + qjdns_sock_setTTL4(sock->socketDescriptor(), 0); + } + + QHostAddress result; + QByteArray out(128, 0); + for(int n = 0; n < out.size(); ++n) + out[n] = rand(); + if(sock->writeDatagram(out.data(), out.size(), maddr, port) == -1) + { + delete sock; + return QHostAddress(); + } + while(1) + { + if(!sock->waitForReadyRead(1000)) + { + fprintf(stderr, "QJDns::detectPrimaryMulticast: timeout while checking %s\n", qPrintable(address.toString())); + delete sock; + return QHostAddress(); + } + QByteArray in(128, 0); + QHostAddress from_addr; + quint16 from_port; + int ret = sock->readDatagram(in.data(), in.size(), &from_addr, &from_port); + if(ret == -1) + { + delete sock; + return QHostAddress(); + } + + if(from_port != port) + continue; + in.resize(ret); + if(in != out) + continue; + + result = from_addr; + break; + } + delete sock; + + return result; +} + +void QJDns::setNameServers(const QList &list) +{ + d->setNameServers(list); +} + +int QJDns::queryStart(const QByteArray &name, int type) +{ + int id = jdns_query(d->sess, (const unsigned char *)name.data(), type); + d->process(); + return id; +} + +void QJDns::queryCancel(int id) +{ + jdns_cancel_query(d->sess, id); + d->removeCancelled(id); + d->process(); +} + +int QJDns::publishStart(PublishMode m, const Record &record) +{ + jdns_rr_t *rr = export_record(record); + + int pubmode; + if(m == QJDns::Unique) + pubmode = JDNS_PUBLISH_UNIQUE; + else + pubmode = JDNS_PUBLISH_SHARED; + + int id = jdns_publish(d->sess, pubmode, rr); + jdns_rr_delete(rr); + d->process(); + return id; +} + +void QJDns::publishUpdate(int id, const Record &record) +{ + jdns_rr_t *rr = export_record(record); + + jdns_update_publish(d->sess, id, rr); + jdns_rr_delete(rr); + d->process(); +} + +void QJDns::publishCancel(int id) +{ + jdns_cancel_publish(d->sess, id); + d->removeCancelled(id); + d->process(); +} + +//#include "qjdns.moc" diff --git a/thirdparty/jdns/qjdns.h b/thirdparty/jdns/jdns/qjdns.h similarity index 100% rename from thirdparty/jdns/qjdns.h rename to thirdparty/jdns/jdns/qjdns.h diff --git a/thirdparty/jdns/qjdns_sock.cpp b/thirdparty/jdns/jdns/qjdns_sock.cpp similarity index 100% rename from thirdparty/jdns/qjdns_sock.cpp rename to thirdparty/jdns/jdns/qjdns_sock.cpp diff --git a/thirdparty/jdns/qjdns_sock.h b/thirdparty/jdns/jdns/qjdns_sock.h similarity index 100% rename from thirdparty/jdns/qjdns_sock.h rename to thirdparty/jdns/jdns/qjdns_sock.h diff --git a/thirdparty/jdns/jdns_export.h b/thirdparty/jdns/jdns_export.h new file mode 100644 index 000000000..540d8c463 --- /dev/null +++ b/thirdparty/jdns/jdns_export.h @@ -0,0 +1,17 @@ +#ifndef JDNS_EXPORT_H +#define JDNS_EXPORT_H + +#include + +#ifdef Q_WS_WIN +# if defined(MAKE_JDNS_LIB) +# define JDNS_EXPORT Q_DECL_EXPORT +# else +# define JDNS_EXPORT Q_DECL_IMPORT +# endif +#else +# define JDNS_EXPORT Q_DECL_EXPORT +#endif + +#endif + diff --git a/src/libtomahawk/utils/jdnsshared.cpp b/thirdparty/jdns/jdnsshared/jdnsshared.cpp similarity index 65% rename from src/libtomahawk/utils/jdnsshared.cpp rename to thirdparty/jdns/jdnsshared/jdnsshared.cpp index b6e8a1c31..9f907d425 100644 --- a/src/libtomahawk/utils/jdnsshared.cpp +++ b/thirdparty/jdns/jdnsshared/jdnsshared.cpp @@ -34,71 +34,18 @@ // on each interface. We don't do anything about this. #include "jdnsshared.h" - -namespace { +#include "jdns_export.h" +#include "jdnsshared_helpers.h" // safeobj stuff, from qca -void releaseAndDeleteLater(QObject *owner, QObject *obj) -{ - obj->disconnect(owner); - obj->setParent(0); - obj->deleteLater(); -} - -class SafeTimer : public QObject -{ - Q_OBJECT -public: - SafeTimer(QObject *parent = 0) : - QObject(parent) - { - t = new QTimer(this); - connect(t, SIGNAL(timeout()), SIGNAL(timeout())); - } - - ~SafeTimer() - { - releaseAndDeleteLater(this, t); - } - - int interval() const { return t->interval(); } - bool isActive() const { return t->isActive(); } - bool isSingleShot() const { return t->isSingleShot(); } - void setInterval(int msec) { t->setInterval(msec); } - void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); } - int timerId() const { return t->timerId(); } - -public slots: - void start(int msec) { t->start(msec); } - void start() { t->start(); } - void stop() { t->stop(); } - -signals: - void timeout(); - -private: - QTimer *t; -}; - -// for caching system info - -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(); + 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 @@ -193,42 +140,6 @@ static QByteArray makeReverseName(const QHostAddress &addr) return out; } -//---------------------------------------------------------------------------- -// 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)); - } -}; - // adapted from qHash static inline uint qHash(const Handle &key) { @@ -237,166 +148,122 @@ static inline uint qHash(const Handle &key) return ((h1 << 16) | (h1 >> 16)) ^ h2; } +JDnsShutdownAgent::JDnsShutdownAgent() + : QObject() +{ } -//---------------------------------------------------------------------------- -// JDnsShutdown -//---------------------------------------------------------------------------- -namespace { - -class JDnsShutdownAgent : public QObject +void JDnsShutdownAgent::start() { - Q_OBJECT -public: - void start() - { - QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection); - } - -signals: - void started(); -}; - -class JDnsShutdownWorker : public QObject -{ - Q_OBJECT -public: - QList list; - - JDnsShutdownWorker(const QList &_list) : QObject(0), list(_list) - { - foreach(JDnsShared *i, list) - { - connect(i, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished())); - i->shutdown(); // MUST support DOR-DS, and it does - } - } - -signals: - void finished(); - -private slots: - void jdns_shutdownFinished() - { - JDnsShared *i = (JDnsShared *)sender(); - list.removeAll(i); - delete i; - if(list.isEmpty()) - emit finished(); - } -}; - -class JDnsShutdown : public QThread -{ - Q_OBJECT -public: - QMutex m; - QWaitCondition w; - QList list; - JDnsShutdownAgent *agent; - JDnsShutdownWorker *worker; - 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 - { - worker = new JDnsShutdownWorker(list); - connect(worker, SIGNAL(finished()), SLOT(worker_finished()), Qt::DirectConnection); - } - } - - void worker_finished() - { - delete worker; - worker = 0; - - quit(); - } -}; - + QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection); } -//---------------------------------------------------------------------------- -// JDnsSharedDebug -//---------------------------------------------------------------------------- -class JDnsSharedDebugPrivate : public QObject + +JDnsShutdownWorker::JDnsShutdownWorker(const QList &_list) : QObject(0), list(_list) { - Q_OBJECT -public: - JDnsSharedDebug *q; - QMutex m; - QStringList lines; - bool dirty; - - JDnsSharedDebugPrivate(JDnsSharedDebug *_q) : QObject(_q), q(_q) + foreach(JDnsShared *i, list) { - dirty = false; + connect(i, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished())); + i->shutdown(); // MUST support DOR-DS, and it does + } +} + +void JDnsShutdownWorker::jdns_shutdownFinished() +{ + JDnsShared *i = (JDnsShared *)sender(); + list.removeAll(i); + delete i; + if(list.isEmpty()) + emit finished(); +} + +JDnsShutdown::JDnsShutdown() + : QThread() +{ +} + +void JDnsShutdown::waitForShutdown(const QList &_list) +{ + list = _list; + phase = 0; + + m.lock(); + start(); + w.wait(&m); + + foreach(JDnsShared *i, list) + { + i->setParent(0); + i->moveToThread(this); } - void addDebug(const QString &name, const QStringList &_lines) + phase = 1; + agent->start(); + wait(); +} + +void JDnsShutdown::run() +{ + m.lock(); + agent = new JDnsShutdownAgent; + connect(agent, SIGNAL(started()), SLOT(agent_started()), Qt::DirectConnection); + agent->start(); + exec(); + delete agent; +} + +void JDnsShutdown::agent_started() +{ + if(phase == 0) { - if(!_lines.isEmpty()) + w.wakeOne(); + m.unlock(); + } + else + { + worker = new JDnsShutdownWorker(list); + connect(worker, SIGNAL(finished()), SLOT(worker_finished()), Qt::DirectConnection); + } +} + +void JDnsShutdown::worker_finished() +{ + delete worker; + worker = 0; + + quit(); +} + +JDnsSharedDebugPrivate::JDnsSharedDebugPrivate(JDnsSharedDebug *_q) + : QObject(_q), q(_q) +{ + dirty = false; +} + +void JDnsSharedDebugPrivate::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) { - 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); - } + dirty = true; + QMetaObject::invokeMethod(this, "doUpdate", Qt::QueuedConnection); } } +} -private slots: - void doUpdate() +void JDnsSharedDebugPrivate::doUpdate() +{ { - { - QMutexLocker locker(&m); - if(!dirty) - return; - } - emit q->readyRead(); + QMutexLocker locker(&m); + if(!dirty) + return; } -}; + emit q->readyRead(); +} JDnsSharedDebug::JDnsSharedDebug(QObject *parent) :QObject(parent) @@ -418,281 +285,195 @@ QStringList JDnsSharedDebug::readDebugLines() return tmplines; } -//---------------------------------------------------------------------------- -// JDnsSharedRequest -//---------------------------------------------------------------------------- -class JDnsSharedPrivate : public QObject +JDnsSharedPrivate::JDnsSharedPrivate(JDnsShared *_q) + : QObject(_q), q(_q) { - Q_OBJECT -public: - class Instance - { - public: - QJDns *jdns; - QHostAddress addr; - int index; +} - Instance() : jdns(0) +JDnsSharedRequest *JDnsSharedPrivate::findRequest(QJDns *jdns, int id) const +{ + Handle h(jdns, id); + return requestForHandle.value(h); +} + +void JDnsSharedPrivate::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 JDnsSharedPrivate::getNewIndex() const +{ + // find lowest unused value + for(int n = 0;; ++n) + { + bool found = false; + foreach(Instance *i, instances) { - } - }; - - enum PreprocessMode - { - None, // don't muck with anything - FillInAddress, // for A/AAAA - FillInPtrOwner6, // for PTR, IPv6 - FillInPtrOwner4, // for PTR, IPv4 - }; - - 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) { - if(i->index == n) - { - found = true; - break; - } + found = true; + break; } - if(!found) - return n; } + if(!found) + return n; + } +} + +void JDnsSharedPrivate::addDebug(int index, const QString &line) +{ + if(db) + db->d->addDebug(dbname + QString::number(index), QStringList() << line); +} + +void JDnsSharedPrivate::doDebug(QJDns *jdns, int index) +{ + QStringList lines = jdns->debugLines(); + if(db) + db->d->addDebug(dbname + QString::number(index), lines); +} + +JDnsSharedPrivate::PreprocessMode JDnsSharedPrivate::determinePpMode(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()) + { + return FillInAddress; + } + // publishing our own reverse lookup? partial owner means + // user wants us to fill in the rest. + else if(in.type == QJDns::Ptr && in.owner == ".ip6.arpa.") + { + return FillInPtrOwner6; + } + else if(in.type == QJDns::Ptr && in.owner == ".in-addr.arpa.") + { + return FillInPtrOwner4; } - void addDebug(int index, const QString &line) + return None; +} + +QJDns::Record JDnsSharedPrivate::manipulateRecord(const QJDns::Record &in, PreprocessMode ppmode, bool *modified) +{ + if(ppmode == FillInAddress) { - if(db) - db->d->addDebug(dbname + QString::number(index), QStringList() << line); - } + QJDns::Record out = in; - void doDebug(QJDns *jdns, int index) - { - QStringList lines = jdns->debugLines(); - if(db) - db->d->addDebug(dbname + QString::number(index), lines); - } - - PreprocessMode determinePpMode(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()) + if(in.type == QJDns::Aaaa) { - return FillInAddress; - } - // publishing our own reverse lookup? partial owner means - // user wants us to fill in the rest. - else if(in.type == QJDns::Ptr && in.owner == ".ip6.arpa.") - { - return FillInPtrOwner6; - } - else if(in.type == QJDns::Ptr && in.owner == ".in-addr.arpa.") - { - return FillInPtrOwner4; - } - - return None; - } - - QJDns::Record manipulateRecord(const QJDns::Record &in, PreprocessMode ppmode, bool *modified = 0) - { - if(ppmode == FillInAddress) - { - QJDns::Record out = in; - - if(in.type == QJDns::Aaaa) - { - // are we operating on ipv6? - foreach(Instance *i, instances) - { - if(i->addr.protocol() == QAbstractSocket::IPv6Protocol) - { - if(modified && !(out.address == i->addr)) - *modified = true; - out.address = i->addr; - break; - } - } - } - else // A - { - // are we operating on ipv4? - foreach(Instance *i, instances) - { - if(i->addr.protocol() == QAbstractSocket::IPv4Protocol) - { - if(modified && !(out.address == i->addr)) - *modified = true; - out.address = i->addr; - break; - } - } - } - - return out; - } - else if(ppmode == FillInPtrOwner6) - { - QJDns::Record out = in; - // are we operating on ipv6? foreach(Instance *i, instances) { if(i->addr.protocol() == QAbstractSocket::IPv6Protocol) { - QByteArray newOwner = makeReverseName(i->addr); - if(modified && !(out.owner == newOwner)) + if(modified && !(out.address == i->addr)) *modified = true; - out.owner = newOwner; + out.address = i->addr; break; } } - - return out; } - else if(ppmode == FillInPtrOwner4) + else // A { - QJDns::Record out = in; - // are we operating on ipv4? foreach(Instance *i, instances) { if(i->addr.protocol() == QAbstractSocket::IPv4Protocol) { - QByteArray newOwner = makeReverseName(i->addr); - if(modified && !(out.owner == newOwner)) + if(modified && !(out.address == i->addr)) *modified = true; - out.owner = newOwner; + out.address = i->addr; break; } } - - return out; } - if(modified) - *modified = false; - return in; + return out; } - - 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() + else if(ppmode == FillInPtrOwner6) { - shutting_down = false; - emit q->shutdownFinished(); + QJDns::Record out = in; + + // are we operating on ipv6? + foreach(Instance *i, instances) + { + if(i->addr.protocol() == QAbstractSocket::IPv6Protocol) + { + QByteArray newOwner = makeReverseName(i->addr); + if(modified && !(out.owner == newOwner)) + *modified = true; + out.owner = newOwner; + break; + } + } + + return out; + } + else if(ppmode == FillInPtrOwner4) + { + QJDns::Record out = in; + + // are we operating on ipv4? + foreach(Instance *i, instances) + { + if(i->addr.protocol() == QAbstractSocket::IPv4Protocol) + { + QByteArray newOwner = makeReverseName(i->addr); + if(modified && !(out.owner == newOwner)) + *modified = true; + out.owner = newOwner; + break; + } + } + + return out; } -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(); -}; + if(modified) + *modified = false; + return in; +} -class JDnsSharedRequestPrivate : public QObject +void JDnsSharedPrivate::late_shutdown() { - Q_OBJECT -public: - JDnsSharedRequest *q; - JDnsSharedPrivate *jsp; + shutting_down = false; + emit q->shutdownFinished(); +} - // current action - JDnsSharedRequest::Type type; - QByteArray name; - int qType; - QJDns::PublishMode pubmode; - JDnsSharedPrivate::PreprocessMode ppmode; - QJDns::Record pubrecord; +JDnsSharedRequestPrivate::JDnsSharedRequestPrivate(JDnsSharedRequest *_q) + : QObject(_q), q(_q), lateTimer(this) +{ + connect(&lateTimer, SIGNAL(timeout()), SLOT(lateTimer_timeout())); +} - // a single request might have to perform multiple QJDns operations - QList handles; +void JDnsSharedRequestPrivate::resetSession() +{ + name = QByteArray(); + pubrecord = QJDns::Record(); + handles.clear(); + published.clear(); + queryCache.clear(); +} - // 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; - SafeTimer 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(); - } -}; +void JDnsSharedRequestPrivate::lateTimer_timeout() +{ + emit q->resultsReady(); +} JDnsSharedRequest::JDnsSharedRequest(JDnsShared *jdnsShared, QObject *parent) :QObject(parent) @@ -816,7 +597,7 @@ QList JDnsShared::domains() void JDnsShared::waitForShutdown(const QList &instances) { - JDnsShutdown s; + JDnsShutdown s; s.waitForShutdown(instances); } @@ -920,7 +701,7 @@ bool JDnsSharedPrivate::addInterface(const QHostAddress &addr) { if(obj->d->type == JDnsSharedRequest::Query) { - Handle h(i->jdns, i->jdns->queryStart(obj->d->name, obj->d->qType)); + Handle h(i->jdns, i->jdns->queryStart(obj->d->name, obj->d->qType)); obj->d->handles += h; requestForHandle.insert(h, obj); } @@ -936,7 +717,7 @@ bool JDnsSharedPrivate::addInterface(const QHostAddress &addr) } // publish the record on the new interface - Handle h(i->jdns, i->jdns->publishStart(obj->d->pubmode, obj->d->pubrecord)); + Handle h(i->jdns, i->jdns->publishStart(obj->d->pubmode, obj->d->pubrecord)); obj->d->handles += h; requestForHandle.insert(h, obj); } @@ -970,7 +751,7 @@ void JDnsSharedPrivate::removeInterface(const QHostAddress &addr) { for(int n = 0; n < obj->d->handles.count(); ++n) { - Handle h = obj->d->handles[n]; + Handle h = obj->d->handles[n]; if(h.jdns == i->jdns) { // see above, no need to cancel the operation @@ -985,7 +766,7 @@ void JDnsSharedPrivate::removeInterface(const QHostAddress &addr) { for(int n = 0; n < obj->d->published.count(); ++n) { - Handle h = obj->d->published[n]; + Handle h = obj->d->published[n]; if(h.jdns == i->jdns) { obj->d->published.removeAt(n); @@ -1135,7 +916,7 @@ void JDnsSharedPrivate::queryStart(JDnsSharedRequest *obj, const QByteArray &nam // query on all jdns instances foreach(Instance *i, instances) { - Handle h(i->jdns, i->jdns->queryStart(name, qType)); + Handle h(i->jdns, i->jdns->queryStart(name, qType)); obj->d->handles += h; // keep track of this handle for this request @@ -1181,7 +962,7 @@ void JDnsSharedPrivate::publishStart(JDnsSharedRequest *obj, QJDns::PublishMode // attempt to publish on all jdns instances foreach(JDnsSharedPrivate::Instance *i, instances) { - Handle h(i->jdns, i->jdns->publishStart(m, obj->d->pubrecord)); + Handle h(i->jdns, i->jdns->publishStart(m, obj->d->pubrecord)); obj->d->handles += h; // keep track of this handle for this request @@ -1232,7 +1013,7 @@ void JDnsSharedPrivate::jdns_resultsReady(int id, const QJDns::Response &results // only one response, so "cancel" it for(int n = 0; n < obj->d->handles.count(); ++n) { - Handle h = obj->d->handles[n]; + Handle h = obj->d->handles[n]; if(h.jdns == jdns && h.id == id) { obj->d->handles.removeAt(n); @@ -1306,10 +1087,10 @@ void JDnsSharedPrivate::jdns_published(int id) Q_ASSERT(obj); // find handle - Handle handle; + Handle handle; for(int n = 0; n < obj->d->handles.count(); ++n) { - Handle h = obj->d->handles[n]; + Handle h = obj->d->handles[n]; if(h.jdns == jdns && h.id == id) { handle = h; @@ -1342,7 +1123,7 @@ void JDnsSharedPrivate::jdns_error(int id, QJDns::Error e) // "cancel" it for(int n = 0; n < obj->d->handles.count(); ++n) { - Handle h = obj->d->handles[n]; + Handle h = obj->d->handles[n]; if(h.jdns == jdns && h.id == id) { obj->d->handles.removeAt(n); @@ -1416,5 +1197,3 @@ void JDnsSharedPrivate::jdns_debugLinesReady() doDebug(jdns, instanceForQJDns.value(jdns)->index); } - -#include "jdnsshared.moc" diff --git a/src/libtomahawk/utils/jdnsshared.h b/thirdparty/jdns/jdnsshared/jdnsshared.h similarity index 99% rename from src/libtomahawk/utils/jdnsshared.h rename to thirdparty/jdns/jdnsshared/jdnsshared.h index 81ac215cb..701c7115e 100644 --- a/src/libtomahawk/utils/jdnsshared.h +++ b/thirdparty/jdns/jdnsshared/jdnsshared.h @@ -73,7 +73,7 @@ public: /** \brief Destroys the object */ - ~JDnsSharedDebug(); + virtual ~JDnsSharedDebug(); /** \brief Read the available debug information diff --git a/thirdparty/jdns/jdnsshared/jdnsshared_helpers.h b/thirdparty/jdns/jdnsshared/jdnsshared_helpers.h new file mode 100644 index 000000000..3898918cb --- /dev/null +++ b/thirdparty/jdns/jdnsshared/jdnsshared_helpers.h @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2006-2008 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" +#include "jdns_export.h" + +// safeobj stuff, from qca + +class JDNS_EXPORT JDnsSharedSafeTimer : public QObject +{ + Q_OBJECT +public: + + void JDnsSharedReleaseAndDeleteLater(QObject *owner, QObject *obj) + { + obj->disconnect(owner); + obj->setParent(0); + obj->deleteLater(); + } + + JDnsSharedSafeTimer(QObject *parent = 0) : + QObject(parent) + { + t = new QTimer(this); + connect(t, SIGNAL(timeout()), SIGNAL(timeout())); + } + + ~JDnsSharedSafeTimer() + { + JDnsSharedReleaseAndDeleteLater(this, t); + } + + int interval() const { return t->interval(); } + bool isActive() const { return t->isActive(); } + bool isSingleShot() const { return t->isSingleShot(); } + void setInterval(int msec) { t->setInterval(msec); } + void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); } + int timerId() const { return t->timerId(); } + +public slots: + void start(int msec) { t->start(msec); } + void start() { t->start(); } + void stop() { t->stop(); } + +signals: + void timeout(); + +private: + QTimer *t; +}; + +// for caching system info + +class SystemInfoCache +{ +public: + QJDns::SystemInfo info; + QTime time; +}; + + +//---------------------------------------------------------------------------- +// Handle +//---------------------------------------------------------------------------- + +// 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) + { + } + + ~Handle() {} + + 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)); + } +}; + +//---------------------------------------------------------------------------- +// JDnsShutdown +//---------------------------------------------------------------------------- +class JDnsShutdownAgent : public QObject +{ + Q_OBJECT +public: + JDnsShutdownAgent(); + ~JDnsShutdownAgent() {} + void start(); + +signals: + void started(); +}; + + +class JDnsShutdownWorker : public QObject +{ + Q_OBJECT +public: + QList list; + + JDnsShutdownWorker(const QList &_list); + virtual ~JDnsShutdownWorker() {} + +signals: + void finished(); + +private slots: + void jdns_shutdownFinished(); +}; + +class JDnsShutdown : public QThread +{ + Q_OBJECT +public: + + QMutex m; + QWaitCondition w; + QList list; + JDnsShutdownAgent *agent; + JDnsShutdownWorker *worker; + int phase; + + JDnsShutdown(); + ~JDnsShutdown() {} + void waitForShutdown(const QList &_list); + +protected: + virtual void run(); + +private slots: + void agent_started(); + + void worker_finished(); +}; + +//---------------------------------------------------------------------------- +// JDnsSharedDebug +//---------------------------------------------------------------------------- +class JDnsSharedDebugPrivate : public QObject +{ + Q_OBJECT +public: + JDnsSharedDebug *q; + QMutex m; + QStringList lines; + bool dirty; + + JDnsSharedDebugPrivate(JDnsSharedDebug *_q); + ~JDnsSharedDebugPrivate() {} + + void addDebug(const QString &name, const QStringList &_lines); + +private slots: + void doUpdate(); +}; + +//---------------------------------------------------------------------------- +// JDnsSharedRequest +//---------------------------------------------------------------------------- +class JDnsSharedPrivate : public QObject +{ + Q_OBJECT +public: + class Instance + { + public: + QJDns *jdns; + QHostAddress addr; + int index; + + Instance() : jdns(0) + { + } + }; + + enum PreprocessMode + { + None, // don't muck with anything + FillInAddress, // for A/AAAA + FillInPtrOwner6, // for PTR, IPv6 + FillInPtrOwner4, // for PTR, IPv4 + }; + + JDnsShared *q; + JDnsShared::Mode mode; + bool shutting_down; + JDnsSharedDebug *db; + QString dbname; + + QList instances; + QHash instanceForQJDns; + + QSet requests; + QHash requestForHandle; + + JDnsSharedPrivate(JDnsShared *_q); + + ~JDnsSharedPrivate() {} + + JDnsSharedRequest *findRequest(QJDns *jdns, int id) const; + + void jdns_link(QJDns *jdns); + + int getNewIndex() const; + + void addDebug(int index, const QString &line); + + void doDebug(QJDns *jdns, int index); + + PreprocessMode determinePpMode(const QJDns::Record &in); + + QJDns::Record manipulateRecord(const QJDns::Record &in, PreprocessMode ppmode, bool *modified = 0); + + 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(); + +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; + JDnsSharedPrivate::PreprocessMode ppmode; + 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; + JDnsSharedSafeTimer lateTimer; + + JDnsSharedRequestPrivate(JDnsSharedRequest *_q); + ~JDnsSharedRequestPrivate() {}; + + void resetSession(); + +private slots: + void lateTimer_timeout(); +}; + diff --git a/thirdparty/jdns/qjdns.cpp b/thirdparty/jdns/qjdns.cpp deleted file mode 100644 index 158068536..000000000 --- a/thirdparty/jdns/qjdns.cpp +++ /dev/null @@ -1,1047 +0,0 @@ -/* - * Copyright (C) 2005-2008 Justin Karneges - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "qjdns.h" - -#include -#include "qjdns_sock.h" -#include "jdns.h" - -// for fprintf -#include - -namespace { - -// safeobj stuff, from qca - -void releaseAndDeleteLater(QObject *owner, QObject *obj) -{ - obj->disconnect(owner); - obj->setParent(0); - obj->deleteLater(); -} - -class SafeTimer : public QObject -{ - Q_OBJECT -public: - SafeTimer(QObject *parent = 0) : - QObject(parent) - { - t = new QTimer(this); - connect(t, SIGNAL(timeout()), SIGNAL(timeout())); - } - - ~SafeTimer() - { - releaseAndDeleteLater(this, t); - } - - int interval() const { return t->interval(); } - bool isActive() const { return t->isActive(); } - bool isSingleShot() const { return t->isSingleShot(); } - void setInterval(int msec) { t->setInterval(msec); } - void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); } - int timerId() const { return t->timerId(); } - -public slots: - void start(int msec) { t->start(msec); } - void start() { t->start(); } - void stop() { t->stop(); } - -signals: - void timeout(); - -private: - QTimer *t; -}; - -} - -static jdns_string_t *qt2str(const QByteArray &in) -{ - jdns_string_t *out = jdns_string_new(); - jdns_string_set(out, (const unsigned char *)in.data(), in.size()); - return out; -} - -static QByteArray str2qt(const jdns_string_t *in) -{ - return QByteArray((const char *)in->data, in->size); -} - -static void qt2addr_set(jdns_address_t *addr, const QHostAddress &host) -{ - if(host.protocol() == QAbstractSocket::IPv6Protocol) - jdns_address_set_ipv6(addr, host.toIPv6Address().c); - else - jdns_address_set_ipv4(addr, host.toIPv4Address()); -} - -static jdns_address_t *qt2addr(const QHostAddress &host) -{ - jdns_address_t *addr = jdns_address_new(); - qt2addr_set(addr, host); - return addr; -} - -static QHostAddress addr2qt(const jdns_address_t *addr) -{ - if(addr->isIpv6) - return QHostAddress(addr->addr.v6); - else - return QHostAddress(addr->addr.v4); -} - -static QJDns::Record import_record(const jdns_rr_t *in) -{ - QJDns::Record out; - - out.owner = QByteArray((const char *)in->owner); - out.ttl = in->ttl; - out.type = in->type; - out.rdata = QByteArray((const char *)in->rdata, in->rdlength); - - // known - if(in->haveKnown) - { - int type = in->type; - - if(type == QJDns::A || type == QJDns::Aaaa) - { - out.haveKnown = true; - out.address = addr2qt(in->data.address); - } - else if(type == QJDns::Mx) - { - out.haveKnown = true; - out.name = QByteArray((const char *)in->data.server->name); - out.priority = in->data.server->priority; - } - else if(type == QJDns::Srv) - { - out.haveKnown = true; - out.name = QByteArray((const char *)in->data.server->name); - out.priority = in->data.server->priority; - out.weight = in->data.server->weight; - out.port = in->data.server->port; - } - else if(type == QJDns::Cname || type == QJDns::Ptr || type == QJDns::Ns) - { - out.haveKnown = true; - out.name = QByteArray((const char *)in->data.name); - } - else if(type == QJDns::Txt) - { - out.haveKnown = true; - out.texts.clear(); - for(int n = 0; n < in->data.texts->count; ++n) - out.texts += str2qt(in->data.texts->item[n]); - } - else if(type == QJDns::Hinfo) - { - out.haveKnown = true; - out.cpu = str2qt(in->data.hinfo.cpu); - out.os = str2qt(in->data.hinfo.os); - } - } - - return out; -} - -static jdns_rr_t *export_record(const QJDns::Record &in) -{ - jdns_rr_t *out = jdns_rr_new(); - - jdns_rr_set_owner(out, (const unsigned char *)in.owner.data()); - out->ttl = in.ttl; - - // if we have known, use that - if(in.haveKnown) - { - int type = in.type; - - if(type == QJDns::A) - { - jdns_address_t *addr = qt2addr(in.address); - jdns_rr_set_A(out, addr); - jdns_address_delete(addr); - } - else if(type == QJDns::Aaaa) - { - jdns_address_t *addr = qt2addr(in.address); - jdns_rr_set_AAAA(out, addr); - jdns_address_delete(addr); - } - else if(type == QJDns::Mx) - { - jdns_rr_set_MX(out, (const unsigned char *)in.name.data(), in.priority); - } - else if(type == QJDns::Srv) - { - jdns_rr_set_SRV(out, (const unsigned char *)in.name.data(), in.port, in.priority, in.weight); - } - else if(type == QJDns::Cname) - { - jdns_rr_set_CNAME(out, (const unsigned char *)in.name.data()); - } - else if(type == QJDns::Ptr) - { - jdns_rr_set_PTR(out, (const unsigned char *)in.name.data()); - } - else if(type == QJDns::Txt) - { - jdns_stringlist_t *list = jdns_stringlist_new(); - for(int n = 0; n < in.texts.count(); ++n) - { - jdns_string_t *str = qt2str(in.texts[n]); - jdns_stringlist_append(list, str); - jdns_string_delete(str); - } - jdns_rr_set_TXT(out, list); - jdns_stringlist_delete(list); - } - else if(type == QJDns::Hinfo) - { - jdns_string_t *cpu = qt2str(in.cpu); - jdns_string_t *os = qt2str(in.os); - jdns_rr_set_HINFO(out, cpu, os); - jdns_string_delete(cpu); - jdns_string_delete(os); - } - else if(type == QJDns::Ns) - { - jdns_rr_set_NS(out, (const unsigned char *)in.name.data()); - } - } - else - jdns_rr_set_record(out, in.type, (const unsigned char *)in.rdata.data(), in.rdata.size()); - - return out; -} - -//---------------------------------------------------------------------------- -// QJDns::NameServer -//---------------------------------------------------------------------------- -QJDns::NameServer::NameServer() -{ - port = JDNS_UNICAST_PORT; -} - -//---------------------------------------------------------------------------- -// QJDns::Record -//---------------------------------------------------------------------------- -QJDns::Record::Record() -{ - ttl = 0; - type = -1; - haveKnown = false; -} - -bool QJDns::Record::verify() const -{ - jdns_rr_t *rr = export_record(*this); - int ok = jdns_rr_verify(rr); - jdns_rr_delete(rr); - return (ok ? true : false); -} - -//---------------------------------------------------------------------------- -// QJDns -//---------------------------------------------------------------------------- -static int my_srand_done = 0; - -static void my_srand() -{ - if(my_srand_done) - return; - - // lame attempt at randomizing without srand - int count = ::time(NULL) % 128; - for(int n = 0; n < count; ++n) - rand(); - - my_srand_done = 1; -} - -class QJDns::Private : public QObject -{ - Q_OBJECT -public: - class LateError - { - public: - int source_type; // 0 for query, 1 for publish - int id; - Error error; - }; - - class LateResponse - { - public: - int id; - QJDns::Response response; - bool do_cancel; - }; - - QJDns *q; - QJDns::Mode mode; - jdns_session_t *sess; - bool shutting_down; - SafeTimer stepTrigger, debugTrigger; - SafeTimer stepTimeout; - QTime clock; - QStringList debug_strings; - bool new_debug_strings; - int next_handle; - bool need_handle; - QHash socketForHandle; - QHash handleForSocket; - int pending; - bool pending_wait; - bool complete_shutdown; - - // pointers that will point to things we are currently signalling - // about. when a query or publish is cancelled, we can use these - // pointers to extract anything we shouldn't signal. - QList *pErrors; - QList *pPublished; - QList *pResponses; - - Private(QJDns *_q) : - QObject(_q), - q(_q), - stepTrigger(this), - debugTrigger(this), - stepTimeout(this), - pErrors(0), - pPublished(0), - pResponses(0) - { - sess = 0; - shutting_down = false; - new_debug_strings = false; - pending = 0; - - connect(&stepTrigger, SIGNAL(timeout()), SLOT(doNextStepSlot())); - stepTrigger.setSingleShot(true); - - connect(&debugTrigger, SIGNAL(timeout()), SLOT(doDebug())); - debugTrigger.setSingleShot(true); - - connect(&stepTimeout, SIGNAL(timeout()), SLOT(st_timeout())); - stepTimeout.setSingleShot(true); - - my_srand(); - - clock.start(); - } - - ~Private() - { - cleanup(); - } - - void cleanup() - { - if(sess) - { - jdns_session_delete(sess); - sess = 0; - } - - shutting_down = false; - pending = 0; - - // it is safe to delete the QUdpSocket objects here without - // deleteLater, since this code path never occurs when - // a signal from those objects is on the stack - qDeleteAll(socketForHandle); - socketForHandle.clear(); - handleForSocket.clear(); - - stepTrigger.stop(); - stepTimeout.stop(); - need_handle = 0; - } - - bool init(QJDns::Mode _mode, const QHostAddress &address) - { - mode = _mode; - - jdns_callbacks_t callbacks; - callbacks.app = this; - callbacks.time_now = cb_time_now; - callbacks.rand_int = cb_rand_int; - callbacks.debug_line = cb_debug_line; - callbacks.udp_bind = cb_udp_bind; - callbacks.udp_unbind = cb_udp_unbind; - callbacks.udp_read = cb_udp_read; - callbacks.udp_write = cb_udp_write; - sess = jdns_session_new(&callbacks); - jdns_set_hold_ids_enabled(sess, 1); - next_handle = 1; - need_handle = false; - - int ret; - - jdns_address_t *baddr = qt2addr(address); - if(mode == Unicast) - { - ret = jdns_init_unicast(sess, baddr, 0); - } - else - { - jdns_address_t *maddr; - if(address.protocol() == QAbstractSocket::IPv6Protocol) - maddr = jdns_address_multicast6_new(); - else - maddr = jdns_address_multicast4_new(); - ret = jdns_init_multicast(sess, baddr, JDNS_MULTICAST_PORT, maddr); - jdns_address_delete(maddr); - } - jdns_address_delete(baddr); - - if(!ret) - { - jdns_session_delete(sess); - sess = 0; - return false; - } - return true; - } - - void setNameServers(const QList &nslist) - { - jdns_nameserverlist_t *addrs = jdns_nameserverlist_new(); - for(int n = 0; n < nslist.count(); ++n) - { - jdns_address_t *addr = qt2addr(nslist[n].address); - jdns_nameserverlist_append(addrs, addr, nslist[n].port); - jdns_address_delete(addr); - } - jdns_set_nameservers(sess, addrs); - jdns_nameserverlist_delete(addrs); - } - - void process() - { - if(!stepTrigger.isActive()) - { - stepTimeout.stop(); - stepTrigger.start(); - } - } - - void processDebug() - { - new_debug_strings = true; - if(!debugTrigger.isActive()) - debugTrigger.start(); - } - - void doNextStep() - { - if(shutting_down && complete_shutdown) - { - cleanup(); - emit q->shutdownFinished(); - return; - } - - QPointer self = this; - - int ret = jdns_step(sess); - - QList errors; - QList published; - QList responses; - bool finish_shutdown = false; - - pErrors = &errors; - pPublished = &published; - pResponses = &responses; - - while(1) - { - jdns_event_t *e = jdns_next_event(sess); - if(!e) - break; - - if(e->type == JDNS_EVENT_SHUTDOWN) - { - finish_shutdown = true; - } - else if(e->type == JDNS_EVENT_PUBLISH) - { - if(e->status != JDNS_STATUS_SUCCESS) - { - QJDns::Error error; - if(e->status == JDNS_STATUS_CONFLICT) - error = QJDns::ErrorConflict; - else - error = QJDns::ErrorGeneric; - LateError le; - le.source_type = 1; - le.id = e->id; - le.error = error; - errors += le; - } - else - { - published += e->id; - } - } - else if(e->type == JDNS_EVENT_RESPONSE) - { - if(e->status != JDNS_STATUS_SUCCESS) - { - QJDns::Error error; - if(e->status == JDNS_STATUS_NXDOMAIN) - error = QJDns::ErrorNXDomain; - else if(e->status == JDNS_STATUS_TIMEOUT) - error = QJDns::ErrorTimeout; - else - error = QJDns::ErrorGeneric; - LateError le; - le.source_type = 0; - le.id = e->id; - le.error = error; - errors += le; - } - else - { - QJDns::Response out_response; - for(int n = 0; n < e->response->answerCount; ++n) - out_response.answerRecords += import_record(e->response->answerRecords[n]); - LateResponse lr; - lr.id = e->id; - lr.response = out_response; - if(mode == Unicast) - lr.do_cancel = true; - else - lr.do_cancel = false; - responses += lr; - } - } - - jdns_event_delete(e); - } - - if(ret & JDNS_STEP_TIMER) - stepTimeout.start(jdns_next_timer(sess)); - else - stepTimeout.stop(); - - need_handle = (ret & JDNS_STEP_HANDLE); - - // read the lists safely enough so that items can be deleted - // behind our back - - while(!errors.isEmpty()) - { - LateError i = errors.takeFirst(); - if(i.source_type == 0) - jdns_cancel_query(sess, i.id); - else - jdns_cancel_publish(sess, i.id); - emit q->error(i.id, i.error); - if(!self) - return; - } - - while(!published.isEmpty()) - { - int i = published.takeFirst(); - emit q->published(i); - if(!self) - return; - } - - while(!responses.isEmpty()) - { - LateResponse i = responses.takeFirst(); - if(i.do_cancel) - jdns_cancel_query(sess, i.id); - emit q->resultsReady(i.id, i.response); - if(!self) - return; - } - - if(finish_shutdown) - { - // if we have pending udp packets to write, stick around - if(pending > 0) - { - pending_wait = true; - } - else - { - complete_shutdown = true; - process(); - } - } - - pErrors = 0; - pPublished = 0; - pResponses = 0; - } - - void removeCancelled(int id) - { - if(pErrors) - { - for(int n = 0; n < pErrors->count(); ++n) - { - if(pErrors->at(n).id == id) - { - pErrors->removeAt(n); - --n; // adjust position - } - } - } - - if(pPublished) - { - for(int n = 0; n < pPublished->count(); ++n) - { - if(pPublished->at(n) == id) - { - pPublished->removeAt(n); - --n; // adjust position - } - } - } - - if(pResponses) - { - for(int n = 0; n < pResponses->count(); ++n) - { - if(pResponses->at(n).id == id) - { - pResponses->removeAt(n); - --n; // adjust position - } - } - } - } - -private slots: - void udp_readyRead() - { - QUdpSocket *sock = (QUdpSocket *)sender(); - int handle = handleForSocket.value(sock); - - if(need_handle) - { - jdns_set_handle_readable(sess, handle); - process(); - } - else - { - // eat packet - QByteArray buf(4096, 0); - QHostAddress from_addr; - quint16 from_port; - sock->readDatagram(buf.data(), buf.size(), &from_addr, &from_port); - } - } - - void udp_bytesWritten(qint64) - { - if(pending > 0) - { - --pending; - if(shutting_down && pending_wait && pending == 0) - { - pending_wait = false; - complete_shutdown = true; - process(); - } - } - } - - void st_timeout() - { - doNextStep(); - } - - void doNextStepSlot() - { - doNextStep(); - } - - void doDebug() - { - if(new_debug_strings) - { - new_debug_strings = false; - if(!debug_strings.isEmpty()) - emit q->debugLinesReady(); - } - } - -private: - // jdns callbacks - static int cb_time_now(jdns_session_t *, void *app) - { - QJDns::Private *self = (QJDns::Private *)app; - - return self->clock.elapsed(); - } - - static int cb_rand_int(jdns_session_t *, void *) - { - return rand() % 65536; - } - - static void cb_debug_line(jdns_session_t *, void *app, const char *str) - { - QJDns::Private *self = (QJDns::Private *)app; - - self->debug_strings += QString::fromLatin1(str); - self->processDebug(); - } - - static int cb_udp_bind(jdns_session_t *, void *app, const jdns_address_t *addr, int port, const jdns_address_t *maddr) - { - QJDns::Private *self = (QJDns::Private *)app; - - // we always pass non-null to jdns_init, so this should be a valid address - QHostAddress host = addr2qt(addr); - - QUdpSocket *sock = new QUdpSocket(self); - self->connect(sock, SIGNAL(readyRead()), SLOT(udp_readyRead())); - - // use queued for bytesWritten, since qt is evil and emits before writeDatagram returns - qRegisterMetaType("qint64"); - self->connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(udp_bytesWritten(qint64)), Qt::QueuedConnection); - - QUdpSocket::BindMode mode; - mode |= QUdpSocket::ShareAddress; - mode |= QUdpSocket::ReuseAddressHint; - if(!sock->bind(host, port, mode)) - { - delete sock; - return 0; - } - - if(maddr) - { - int sd = sock->socketDescriptor(); - bool ok; - int errorCode; - if(maddr->isIpv6) - ok = qjdns_sock_setMulticast6(sd, maddr->addr.v6, &errorCode); - else - ok = qjdns_sock_setMulticast4(sd, maddr->addr.v4, &errorCode); - - if(!ok) - { - delete sock; - - self->debug_strings += QString("failed to setup multicast on the socket (errorCode=%1)").arg(errorCode); - self->processDebug(); - return 0; - } - - if(maddr->isIpv6) - { - qjdns_sock_setTTL6(sd, 255); - qjdns_sock_setIPv6Only(sd); - } - else - qjdns_sock_setTTL4(sd, 255); - } - - int handle = self->next_handle++; - self->socketForHandle.insert(handle, sock); - self->handleForSocket.insert(sock, handle); - return handle; - } - - static void cb_udp_unbind(jdns_session_t *, void *app, int handle) - { - QJDns::Private *self = (QJDns::Private *)app; - - QUdpSocket *sock = self->socketForHandle.value(handle); - if(!sock) - return; - - self->socketForHandle.remove(handle); - self->handleForSocket.remove(sock); - delete sock; - } - - static int cb_udp_read(jdns_session_t *, void *app, int handle, jdns_address_t *addr, int *port, unsigned char *buf, int *bufsize) - { - QJDns::Private *self = (QJDns::Private *)app; - - QUdpSocket *sock = self->socketForHandle.value(handle); - if(!sock) - return 0; - - // nothing to read? - if(!sock->hasPendingDatagrams()) - return 0; - - QHostAddress from_addr; - quint16 from_port; - int ret = sock->readDatagram((char *)buf, *bufsize, &from_addr, &from_port); - if(ret == -1) - return 0; - - qt2addr_set(addr, from_addr); - *port = (int)from_port; - *bufsize = ret; - return 1; - } - - static int cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize) - { - QJDns::Private *self = (QJDns::Private *)app; - - QUdpSocket *sock = self->socketForHandle.value(handle); - if(!sock) - return 0; - - QHostAddress host = addr2qt(addr); - int ret = sock->writeDatagram((const char *)buf, bufsize, host, port); - if(ret == -1) - { - // this can happen if the datagram to send is too big. i'm not sure what else - // may cause this. if we return 0, then jdns may try to resend the packet, - // which might not work if it is too large (causing the same error over and - // over). we'll return success to jdns, so the result is as if the packet - // was dropped. - return 1; - } - - ++self->pending; - return 1; - } -}; - -QJDns::QJDns(QObject *parent) -:QObject(parent) -{ - d = new Private(this); -} - -QJDns::~QJDns() -{ - delete d; -} - -bool QJDns::init(Mode mode, const QHostAddress &address) -{ - return d->init(mode, address); -} - -void QJDns::shutdown() -{ - d->shutting_down = true; - d->pending_wait = false; - d->complete_shutdown = false; - jdns_shutdown(d->sess); - d->process(); -} - -QStringList QJDns::debugLines() -{ - QStringList tmp = d->debug_strings; - d->debug_strings.clear(); - return tmp; -} - -QJDns::SystemInfo QJDns::systemInfo() -{ - SystemInfo out; - jdns_dnsparams_t *params = jdns_system_dnsparams(); - for(int n = 0; n < params->nameservers->count; ++n) - { - NameServer ns; - ns.address = addr2qt(params->nameservers->item[n]->address); - out.nameServers += ns; - } - for(int n = 0; n < params->domains->count; ++n) - out.domains += str2qt(params->domains->item[n]); - for(int n = 0; n < params->hosts->count; ++n) - { - DnsHost h; - h.name = str2qt(params->hosts->item[n]->name); - h.address = addr2qt(params->hosts->item[n]->address); - out.hosts += h; - } - jdns_dnsparams_delete(params); - return out; -} - -#define PROBE_BASE 20000 -#define PROBE_RANGE 100 - -QHostAddress QJDns::detectPrimaryMulticast(const QHostAddress &address) -{ - my_srand(); - - QUdpSocket *sock = new QUdpSocket; - QUdpSocket::BindMode mode; - mode |= QUdpSocket::ShareAddress; - mode |= QUdpSocket::ReuseAddressHint; - int port = -1; - for(int n = 0; n < PROBE_RANGE; ++n) - { - if(sock->bind(address, PROBE_BASE + n, mode)) - { - port = PROBE_BASE + n; - break; - } - } - if(port == -1) - { - delete sock; - return QHostAddress(); - } - - jdns_address_t *a; - if(address.protocol() == QAbstractSocket::IPv6Protocol) - a = jdns_address_multicast6_new(); - else - a = jdns_address_multicast4_new(); - QHostAddress maddr = addr2qt(a); - jdns_address_delete(a); - - if(address.protocol() == QAbstractSocket::IPv6Protocol) - { - int x; - if(!qjdns_sock_setMulticast6(sock->socketDescriptor(), maddr.toIPv6Address().c, &x)) - { - delete sock; - return QHostAddress(); - } - qjdns_sock_setTTL6(sock->socketDescriptor(), 0); - } - else - { - int x; - if(!qjdns_sock_setMulticast4(sock->socketDescriptor(), maddr.toIPv4Address(), &x)) - { - delete sock; - return QHostAddress(); - } - qjdns_sock_setTTL4(sock->socketDescriptor(), 0); - } - - QHostAddress result; - QByteArray out(128, 0); - for(int n = 0; n < out.size(); ++n) - out[n] = rand(); - if(sock->writeDatagram(out.data(), out.size(), maddr, port) == -1) - { - delete sock; - return QHostAddress(); - } - while(1) - { - if(!sock->waitForReadyRead(1000)) - { - fprintf(stderr, "QJDns::detectPrimaryMulticast: timeout while checking %s\n", qPrintable(address.toString())); - delete sock; - return QHostAddress(); - } - QByteArray in(128, 0); - QHostAddress from_addr; - quint16 from_port; - int ret = sock->readDatagram(in.data(), in.size(), &from_addr, &from_port); - if(ret == -1) - { - delete sock; - return QHostAddress(); - } - - if(from_port != port) - continue; - in.resize(ret); - if(in != out) - continue; - - result = from_addr; - break; - } - delete sock; - - return result; -} - -void QJDns::setNameServers(const QList &list) -{ - d->setNameServers(list); -} - -int QJDns::queryStart(const QByteArray &name, int type) -{ - int id = jdns_query(d->sess, (const unsigned char *)name.data(), type); - d->process(); - return id; -} - -void QJDns::queryCancel(int id) -{ - jdns_cancel_query(d->sess, id); - d->removeCancelled(id); - d->process(); -} - -int QJDns::publishStart(PublishMode m, const Record &record) -{ - jdns_rr_t *rr = export_record(record); - - int pubmode; - if(m == QJDns::Unique) - pubmode = JDNS_PUBLISH_UNIQUE; - else - pubmode = JDNS_PUBLISH_SHARED; - - int id = jdns_publish(d->sess, pubmode, rr); - jdns_rr_delete(rr); - d->process(); - return id; -} - -void QJDns::publishUpdate(int id, const Record &record) -{ - jdns_rr_t *rr = export_record(record); - - jdns_update_publish(d->sess, id, rr); - jdns_rr_delete(rr); - d->process(); -} - -void QJDns::publishCancel(int id) -{ - jdns_cancel_publish(d->sess, id); - d->removeCancelled(id); - d->process(); -} - -//#include "qjdns.moc"