File indexing completed on 2024-05-19 04:06:39

0001 /*
0002  * Copyright (C) 2005-2008  Justin Karneges
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the
0006  * "Software"), to deal in the Software without restriction, including
0007  * without limitation the rights to use, copy, modify, merge, publish,
0008  * distribute, sublicense, and/or sell copies of the Software, and to
0009  * permit persons to whom the Software is furnished to do so, subject to
0010  * the following conditions:
0011  *
0012  * The above copyright notice and this permission notice shall be included
0013  * in all copies or substantial portions of the Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
0016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
0018  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
0019  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
0020  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
0021  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0022  */
0023 
0024 #include "qjdns.h"
0025 
0026 #include <time.h>
0027 #include "qjdns_sock.h"
0028 #include "jdns.h"
0029 
0030 // for fprintf
0031 #include <stdio.h>
0032 
0033 namespace {
0034 
0035 // safeobj stuff, from qca
0036 
0037 void releaseAndDeleteLater(QObject *owner, QObject *obj)
0038 {
0039     obj->disconnect(owner);
0040     obj->setParent(0);
0041     obj->deleteLater();
0042 }
0043 
0044 class SafeTimer : public QObject
0045 {
0046     Q_OBJECT
0047 public:
0048     SafeTimer(QObject *parent = 0) :
0049         QObject(parent)
0050     {
0051         t = new QTimer(this);
0052         connect(t, &QTimer::timeout, this, &SafeTimer::timeout);
0053     }
0054 
0055     ~SafeTimer() override
0056     {
0057         releaseAndDeleteLater(this, t);
0058     }
0059 
0060     int interval() const                { return t->interval(); }
0061     bool isActive() const               { return t->isActive(); }
0062     bool isSingleShot() const           { return t->isSingleShot(); }
0063     void setInterval(int msec)          { t->setInterval(msec); }
0064     void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); }
0065     int timerId() const                 { return t->timerId(); }
0066 
0067 public slots:
0068     void start(int msec)                { t->start(msec); }
0069     void start()                        { t->start(); }
0070     void stop()                         { t->stop(); }
0071 
0072 signals:
0073     void timeout();
0074 
0075 private:
0076     QTimer *t;
0077 };
0078 
0079 }
0080 
0081 static jdns_string_t *qt2str(const QByteArray &in)
0082 {
0083     jdns_string_t *out = jdns_string_new();
0084     jdns_string_set(out, (const unsigned char *)in.data(), in.size());
0085     return out;
0086 }
0087 
0088 static QByteArray str2qt(const jdns_string_t *in)
0089 {
0090     return QByteArray((const char *)in->data, in->size);
0091 }
0092 
0093 static void qt2addr_set(jdns_address_t *addr, const QHostAddress &host)
0094 {
0095     if(host.protocol() == QAbstractSocket::IPv6Protocol)
0096         jdns_address_set_ipv6(addr, host.toIPv6Address().c);
0097     else
0098         jdns_address_set_ipv4(addr, host.toIPv4Address());
0099 }
0100 
0101 static jdns_address_t *qt2addr(const QHostAddress &host)
0102 {
0103     jdns_address_t *addr = jdns_address_new();
0104     qt2addr_set(addr, host);
0105     return addr;
0106 }
0107 
0108 static QHostAddress addr2qt(const jdns_address_t *addr)
0109 {
0110     if(addr->isIpv6)
0111         return QHostAddress(addr->addr.v6);
0112     else
0113         return QHostAddress(addr->addr.v4);
0114 }
0115 
0116 static QJDns::Record import_record(const jdns_rr_t *in)
0117 {
0118     QJDns::Record out;
0119 
0120     out.owner = QByteArray((const char *)in->owner);
0121     out.ttl = in->ttl;
0122     out.type = in->type;
0123     out.rdata = QByteArray((const char *)in->rdata, in->rdlength);
0124 
0125     // known
0126     if(in->haveKnown)
0127     {
0128         int type = in->type;
0129 
0130         if(type == QJDns::A || type == QJDns::Aaaa)
0131         {
0132             out.haveKnown = true;
0133             out.address = addr2qt(in->data.address);
0134         }
0135         else if(type == QJDns::Mx)
0136         {
0137             out.haveKnown = true;
0138             out.name = QByteArray((const char *)in->data.server->name);
0139             out.priority = in->data.server->priority;
0140         }
0141         else if(type == QJDns::Srv)
0142         {
0143             out.haveKnown = true;
0144             out.name = QByteArray((const char *)in->data.server->name);
0145             out.priority = in->data.server->priority;
0146             out.weight = in->data.server->weight;
0147             out.port = in->data.server->port;
0148         }
0149         else if(type == QJDns::Cname || type == QJDns::Ptr || type == QJDns::Ns)
0150         {
0151             out.haveKnown = true;
0152             out.name = QByteArray((const char *)in->data.name);
0153         }
0154         else if(type == QJDns::Txt)
0155         {
0156             out.haveKnown = true;
0157             out.texts.clear();
0158             for(int n = 0; n < in->data.texts->count; ++n)
0159                 out.texts += str2qt(in->data.texts->item[n]);
0160         }
0161         else if(type == QJDns::Hinfo)
0162         {
0163             out.haveKnown = true;
0164             out.cpu = str2qt(in->data.hinfo.cpu);
0165             out.os = str2qt(in->data.hinfo.os);
0166         }
0167     }
0168 
0169     return out;
0170 }
0171 
0172 static jdns_rr_t *export_record(const QJDns::Record &in)
0173 {
0174     jdns_rr_t *out = jdns_rr_new();
0175 
0176     jdns_rr_set_owner(out, (const unsigned char *)in.owner.data());
0177     out->ttl = in.ttl;
0178 
0179     // if we have known, use that
0180     if(in.haveKnown)
0181     {
0182         int type = in.type;
0183 
0184         if(type == QJDns::A)
0185         {
0186             jdns_address_t *addr = qt2addr(in.address);
0187             jdns_rr_set_A(out, addr);
0188             jdns_address_delete(addr);
0189         }
0190         else if(type == QJDns::Aaaa)
0191         {
0192             jdns_address_t *addr = qt2addr(in.address);
0193             jdns_rr_set_AAAA(out, addr);
0194             jdns_address_delete(addr);
0195         }
0196         else if(type == QJDns::Mx)
0197         {
0198             jdns_rr_set_MX(out, (const unsigned char *)in.name.data(), in.priority);
0199         }
0200         else if(type == QJDns::Srv)
0201         {
0202             jdns_rr_set_SRV(out, (const unsigned char *)in.name.data(), in.port, in.priority, in.weight);
0203         }
0204         else if(type == QJDns::Cname)
0205         {
0206             jdns_rr_set_CNAME(out, (const unsigned char *)in.name.data());
0207         }
0208         else if(type == QJDns::Ptr)
0209         {
0210             jdns_rr_set_PTR(out, (const unsigned char *)in.name.data());
0211         }
0212         else if(type == QJDns::Txt)
0213         {
0214             jdns_stringlist_t *list = jdns_stringlist_new();
0215             for(int n = 0; n < in.texts.count(); ++n)
0216             {
0217                 jdns_string_t *str = qt2str(in.texts[n]);
0218                 jdns_stringlist_append(list, str);
0219                 jdns_string_delete(str);
0220             }
0221             jdns_rr_set_TXT(out, list);
0222             jdns_stringlist_delete(list);
0223         }
0224         else if(type == QJDns::Hinfo)
0225         {
0226             jdns_string_t *cpu = qt2str(in.cpu);
0227             jdns_string_t *os = qt2str(in.os);
0228             jdns_rr_set_HINFO(out, cpu, os);
0229             jdns_string_delete(cpu);
0230             jdns_string_delete(os);
0231         }
0232         else if(type == QJDns::Ns)
0233         {
0234             jdns_rr_set_NS(out, (const unsigned char *)in.name.data());
0235         }
0236     }
0237     else
0238         jdns_rr_set_record(out, in.type, (const unsigned char *)in.rdata.data(), in.rdata.size());
0239 
0240     return out;
0241 }
0242 
0243 //----------------------------------------------------------------------------
0244 // QJDns::NameServer
0245 //----------------------------------------------------------------------------
0246 QJDns::NameServer::NameServer()
0247 {
0248     port = JDNS_UNICAST_PORT;
0249 }
0250 
0251 //----------------------------------------------------------------------------
0252 // QJDns::Record
0253 //----------------------------------------------------------------------------
0254 QJDns::Record::Record()
0255 {
0256     ttl = 0;
0257     type = -1;
0258     haveKnown = false;
0259 }
0260 
0261 bool QJDns::Record::verify() const
0262 {
0263     jdns_rr_t *rr = export_record(*this);
0264     int ok = jdns_rr_verify(rr);
0265     jdns_rr_delete(rr);
0266     return (ok ? true : false);
0267 }
0268 
0269 //----------------------------------------------------------------------------
0270 // QJDns
0271 //----------------------------------------------------------------------------
0272 static int my_srand_done = 0;
0273 
0274 static void my_srand()
0275 {
0276     if(my_srand_done)
0277         return;
0278 
0279     // lame attempt at randomizing without srand
0280     int count = ::time(NULL) % 128;
0281     for(int n = 0; n < count; ++n)
0282         rand();
0283 
0284     my_srand_done = 1;
0285 }
0286 
0287 class QJDns::Private : public QObject
0288 {
0289     Q_OBJECT
0290 public:
0291     class LateError
0292     {
0293     public:
0294         int source_type; // 0 for query, 1 for publish
0295         int id;
0296         Error error;
0297     };
0298 
0299     class LateResponse
0300     {
0301     public:
0302         int id;
0303         QJDns::Response response;
0304         bool do_cancel;
0305     };
0306 
0307     QJDns *q;
0308     QJDns::Mode mode;
0309     jdns_session_t *sess;
0310     bool shutting_down;
0311     SafeTimer stepTrigger, debugTrigger;
0312     SafeTimer stepTimeout;
0313     QElapsedTimer clock;
0314     QStringList debug_strings;
0315     bool new_debug_strings;
0316     int next_handle;
0317     bool need_handle;
0318     QHash<int,QUdpSocket*> socketForHandle;
0319     QHash<QUdpSocket*,int> handleForSocket;
0320     int pending;
0321     bool pending_wait;
0322     bool complete_shutdown;
0323 
0324     // pointers that will point to things we are currently signalling
0325     //   about.  when a query or publish is cancelled, we can use these
0326     //   pointers to extract anything we shouldn't signal.
0327     QList<LateError> *pErrors;
0328     QList<int> *pPublished;
0329     QList<LateResponse> *pResponses;
0330 
0331     Private(QJDns *_q) :
0332         QObject(_q),
0333         q(_q),
0334         stepTrigger(this),
0335         debugTrigger(this),
0336         stepTimeout(this),
0337         pErrors(0),
0338         pPublished(0),
0339         pResponses(0)
0340     {
0341         sess = 0;
0342         shutting_down = false;
0343         new_debug_strings = false;
0344         pending = 0;
0345 
0346         connect(&stepTrigger, &SafeTimer::timeout, this, &Private::doNextStepSlot);
0347         stepTrigger.setSingleShot(true);
0348 
0349         connect(&debugTrigger, &SafeTimer::timeout, this, &Private::doDebug);
0350         debugTrigger.setSingleShot(true);
0351 
0352         connect(&stepTimeout, &SafeTimer::timeout, this, &Private::st_timeout);
0353         stepTimeout.setSingleShot(true);
0354 
0355         my_srand();
0356 
0357         clock.start();
0358     }
0359 
0360     ~Private() override
0361     {
0362         cleanup();
0363     }
0364 
0365     void cleanup()
0366     {
0367         if(sess)
0368         {
0369             jdns_session_delete(sess);
0370             sess = 0;
0371         }
0372 
0373         shutting_down = false;
0374         pending = 0;
0375 
0376         // it is safe to delete the QUdpSocket objects here without
0377         //   deleteLater, since this code path never occurs when
0378         //   a signal from those objects is on the stack
0379         qDeleteAll(socketForHandle);
0380         socketForHandle.clear();
0381         handleForSocket.clear();
0382 
0383         stepTrigger.stop();
0384         stepTimeout.stop();
0385         need_handle = 0;
0386     }
0387 
0388     bool init(QJDns::Mode _mode, const QHostAddress &address)
0389     {
0390         mode = _mode;
0391 
0392         jdns_callbacks_t callbacks;
0393         callbacks.app = this;
0394         callbacks.time_now = cb_time_now;
0395         callbacks.rand_int = cb_rand_int;
0396         callbacks.debug_line = cb_debug_line;
0397         callbacks.udp_bind = cb_udp_bind;
0398         callbacks.udp_unbind = cb_udp_unbind;
0399         callbacks.udp_read = cb_udp_read;
0400         callbacks.udp_write = cb_udp_write;
0401         sess = jdns_session_new(&callbacks);
0402         jdns_set_hold_ids_enabled(sess, 1);
0403         next_handle = 1;
0404         need_handle = false;
0405 
0406         int ret;
0407 
0408         jdns_address_t *baddr = qt2addr(address);
0409         if(mode == Unicast)
0410         {
0411             ret = jdns_init_unicast(sess, baddr, 0);
0412         }
0413         else
0414         {
0415             jdns_address_t *maddr;
0416             if(address.protocol() == QAbstractSocket::IPv6Protocol)
0417                 maddr = jdns_address_multicast6_new();
0418             else
0419                 maddr = jdns_address_multicast4_new();
0420             ret = jdns_init_multicast(sess, baddr, JDNS_MULTICAST_PORT, maddr);
0421             jdns_address_delete(maddr);
0422         }
0423         jdns_address_delete(baddr);
0424 
0425         if(!ret)
0426         {
0427             jdns_session_delete(sess);
0428             sess = 0;
0429             return false;
0430         }
0431         return true;
0432     }
0433 
0434     void setNameServers(const QList<NameServer> &nslist)
0435     {
0436         jdns_nameserverlist_t *addrs = jdns_nameserverlist_new();
0437         for(int n = 0; n < nslist.count(); ++n)
0438         {
0439             jdns_address_t *addr = qt2addr(nslist[n].address);
0440             jdns_nameserverlist_append(addrs, addr, nslist[n].port);
0441             jdns_address_delete(addr);
0442         }
0443         jdns_set_nameservers(sess, addrs);
0444         jdns_nameserverlist_delete(addrs);
0445     }
0446 
0447     void process()
0448     {
0449         if(!stepTrigger.isActive())
0450         {
0451             stepTimeout.stop();
0452             stepTrigger.start();
0453         }
0454     }
0455 
0456     void processDebug()
0457     {
0458         new_debug_strings = true;
0459         if(!debugTrigger.isActive())
0460             debugTrigger.start();
0461     }
0462 
0463     void doNextStep()
0464     {
0465         if(shutting_down && complete_shutdown)
0466         {
0467             cleanup();
0468             emit q->shutdownFinished();
0469             return;
0470         }
0471 
0472         QPointer<QObject> self = this;
0473 
0474         int ret = jdns_step(sess);
0475 
0476         QList<LateError> errors;
0477         QList<int> published;
0478         QList<LateResponse> responses;
0479         bool finish_shutdown = false;
0480 
0481         pErrors = &errors;
0482         pPublished = &published;
0483         pResponses = &responses;
0484 
0485         while(1)
0486         {
0487             jdns_event_t *e = jdns_next_event(sess);
0488             if(!e)
0489                 break;
0490 
0491             if(e->type == JDNS_EVENT_SHUTDOWN)
0492             {
0493                 finish_shutdown = true;
0494             }
0495             else if(e->type == JDNS_EVENT_PUBLISH)
0496             {
0497                 if(e->status != JDNS_STATUS_SUCCESS)
0498                 {
0499                     QJDns::Error error;
0500                     if(e->status == JDNS_STATUS_CONFLICT)
0501                         error = QJDns::ErrorConflict;
0502                     else
0503                         error = QJDns::ErrorGeneric;
0504                     LateError le;
0505                     le.source_type = 1;
0506                     le.id = e->id;
0507                     le.error = error;
0508                     errors += le;
0509                 }
0510                 else
0511                 {
0512                     published += e->id;
0513                 }
0514             }
0515             else if(e->type == JDNS_EVENT_RESPONSE)
0516             {
0517                 if(e->status != JDNS_STATUS_SUCCESS)
0518                 {
0519                     QJDns::Error error;
0520                     if(e->status == JDNS_STATUS_NXDOMAIN)
0521                         error = QJDns::ErrorNXDomain;
0522                     else if(e->status == JDNS_STATUS_TIMEOUT)
0523                         error = QJDns::ErrorTimeout;
0524                     else
0525                         error = QJDns::ErrorGeneric;
0526                     LateError le;
0527                     le.source_type = 0;
0528                     le.id = e->id;
0529                     le.error = error;
0530                     errors += le;
0531                 }
0532                 else
0533                 {
0534                     QJDns::Response out_response;
0535                     for(int n = 0; n < e->response->answerCount; ++n)
0536                         out_response.answerRecords += import_record(e->response->answerRecords[n]);
0537                     LateResponse lr;
0538                     lr.id = e->id;
0539                     lr.response = out_response;
0540                     if(mode == Unicast)
0541                         lr.do_cancel = true;
0542                     else
0543                         lr.do_cancel = false;
0544                     responses += lr;
0545                 }
0546             }
0547 
0548             jdns_event_delete(e);
0549         }
0550 
0551         if(ret & JDNS_STEP_TIMER)
0552             stepTimeout.start(jdns_next_timer(sess));
0553         else
0554             stepTimeout.stop();
0555 
0556         need_handle = (ret & JDNS_STEP_HANDLE);
0557 
0558         // read the lists safely enough so that items can be deleted
0559         //   behind our back
0560 
0561         while(!errors.isEmpty())
0562         {
0563             LateError i = errors.takeFirst();
0564             if(i.source_type == 0)
0565                 jdns_cancel_query(sess, i.id);
0566             else
0567                 jdns_cancel_publish(sess, i.id);
0568             emit q->error(i.id, i.error);
0569             if(!self)
0570                 return;
0571         }
0572 
0573         while(!published.isEmpty())
0574         {
0575             int i = published.takeFirst();
0576             emit q->published(i);
0577             if(!self)
0578                 return;
0579         }
0580 
0581         while(!responses.isEmpty())
0582         {
0583             LateResponse i = responses.takeFirst();
0584             if(i.do_cancel)
0585                 jdns_cancel_query(sess, i.id);
0586             emit q->resultsReady(i.id, i.response);
0587             if(!self)
0588                 return;
0589         }
0590 
0591         if(finish_shutdown)
0592         {
0593             // if we have pending udp packets to write, stick around
0594             if(pending > 0)
0595             {
0596                 pending_wait = true;
0597             }
0598             else
0599             {
0600                 complete_shutdown = true;
0601                 process();
0602             }
0603         }
0604 
0605         pErrors = 0;
0606         pPublished = 0;
0607         pResponses = 0;
0608     }
0609 
0610     void removeCancelled(int id)
0611     {
0612         if(pErrors)
0613         {
0614             for(int n = 0; n < pErrors->count(); ++n)
0615             {
0616                 if(pErrors->at(n).id == id)
0617                 {
0618                     pErrors->removeAt(n);
0619                     --n; // adjust position
0620                 }
0621             }
0622         }
0623 
0624         if(pPublished)
0625         {
0626             for(int n = 0; n < pPublished->count(); ++n)
0627             {
0628                 if(pPublished->at(n) == id)
0629                 {
0630                     pPublished->removeAt(n);
0631                     --n; // adjust position
0632                 }
0633             }
0634         }
0635 
0636         if(pResponses)
0637         {
0638             for(int n = 0; n < pResponses->count(); ++n)
0639             {
0640                 if(pResponses->at(n).id == id)
0641                 {
0642                     pResponses->removeAt(n);
0643                     --n; // adjust position
0644                 }
0645             }
0646         }
0647     }
0648 
0649 private slots:
0650     void udp_readyRead()
0651     {
0652         QUdpSocket *sock = (QUdpSocket *)sender();
0653         int handle = handleForSocket.value(sock);
0654 
0655         if(need_handle)
0656         {
0657             jdns_set_handle_readable(sess, handle);
0658             process();
0659         }
0660         else
0661         {
0662             // eat packet
0663             QByteArray buf(4096, 0);
0664             QHostAddress from_addr;
0665             quint16 from_port;
0666             sock->readDatagram(buf.data(), buf.size(), &from_addr, &from_port);
0667         }
0668     }
0669 
0670     void udp_bytesWritten(qint64)
0671     {
0672         if(pending > 0)
0673         {
0674             --pending;
0675             if(shutting_down && pending_wait && pending == 0)
0676             {
0677                 pending_wait = false;
0678                 complete_shutdown = true;
0679                 process();
0680             }
0681         }
0682     }
0683 
0684     void st_timeout()
0685     {
0686         doNextStep();
0687     }
0688 
0689     void doNextStepSlot()
0690     {
0691         doNextStep();
0692     }
0693 
0694     void doDebug()
0695     {
0696         if(new_debug_strings)
0697         {
0698             new_debug_strings = false;
0699             if(!debug_strings.isEmpty())
0700                 emit q->debugLinesReady();
0701         }
0702     }
0703 
0704 private:
0705     // jdns callbacks
0706     static int cb_time_now(jdns_session_t *, void *app)
0707     {
0708         QJDns::Private *self = (QJDns::Private *)app;
0709 
0710         return self->clock.elapsed();
0711     }
0712 
0713     static int cb_rand_int(jdns_session_t *, void *)
0714     {
0715         return rand() % 65536;
0716     }
0717 
0718     static void cb_debug_line(jdns_session_t *, void *app, const char *str)
0719     {
0720         QJDns::Private *self = (QJDns::Private *)app;
0721 
0722         self->debug_strings += QString::fromLatin1(str);
0723         self->processDebug();
0724     }
0725 
0726     static int cb_udp_bind(jdns_session_t *, void *app, const jdns_address_t *addr, int port, const jdns_address_t *maddr)
0727     {
0728         QJDns::Private *self = (QJDns::Private *)app;
0729 
0730         // we always pass non-null to jdns_init, so this should be a valid address
0731         QHostAddress host = addr2qt(addr);
0732 
0733         QUdpSocket *sock = new QUdpSocket(self);
0734         self->connect(sock, &QIODevice::readyRead, self, &Private::udp_readyRead);
0735 
0736         // use queued for bytesWritten, since qt is evil and emits before writeDatagram returns
0737         qRegisterMetaType<qint64>("qint64");
0738         self->connect(sock, &QIODevice::bytesWritten, self, &Private::udp_bytesWritten, Qt::QueuedConnection);
0739 
0740         QUdpSocket::BindMode mode;
0741         mode |= QUdpSocket::ShareAddress;
0742         mode |= QUdpSocket::ReuseAddressHint;
0743         if(!sock->bind(host, port, mode))
0744         {
0745             delete sock;
0746             return 0;
0747         }
0748 
0749         if(maddr)
0750         {
0751             int sd = sock->socketDescriptor();
0752             bool ok;
0753             int errorCode;
0754             if(maddr->isIpv6)
0755                 ok = qjdns_sock_setMulticast6(sd, maddr->addr.v6, &errorCode);
0756             else
0757                 ok = qjdns_sock_setMulticast4(sd, maddr->addr.v4, &errorCode);
0758 
0759             if(!ok)
0760             {
0761                 delete sock;
0762 
0763                 self->debug_strings += QStringLiteral("failed to setup multicast on the socket (errorCode=%1)").arg(errorCode);
0764                 self->processDebug();
0765                 return 0;
0766             }
0767 
0768             if(maddr->isIpv6)
0769             {
0770                 qjdns_sock_setTTL6(sd, 255);
0771                 qjdns_sock_setIPv6Only(sd);
0772             }
0773             else
0774                 qjdns_sock_setTTL4(sd, 255);
0775         }
0776 
0777         int handle = self->next_handle++;
0778         self->socketForHandle.insert(handle, sock);
0779         self->handleForSocket.insert(sock, handle);
0780         return handle;
0781     }
0782 
0783     static void cb_udp_unbind(jdns_session_t *, void *app, int handle)
0784     {
0785         QJDns::Private *self = (QJDns::Private *)app;
0786 
0787         QUdpSocket *sock = self->socketForHandle.value(handle);
0788         if(!sock)
0789             return;
0790 
0791         self->socketForHandle.remove(handle);
0792         self->handleForSocket.remove(sock);
0793         delete sock;
0794     }
0795 
0796     static int cb_udp_read(jdns_session_t *, void *app, int handle, jdns_address_t *addr, int *port, unsigned char *buf, int *bufsize)
0797     {
0798         QJDns::Private *self = (QJDns::Private *)app;
0799 
0800         QUdpSocket *sock = self->socketForHandle.value(handle);
0801         if(!sock)
0802             return 0;
0803 
0804         // nothing to read?
0805         if(!sock->hasPendingDatagrams())
0806             return 0;
0807 
0808         QHostAddress from_addr;
0809         quint16 from_port;
0810         int ret = sock->readDatagram((char *)buf, *bufsize, &from_addr, &from_port);
0811         if(ret == -1)
0812             return 0;
0813 
0814         qt2addr_set(addr, from_addr);
0815         *port = (int)from_port;
0816         *bufsize = ret;
0817         return 1;
0818     }
0819 
0820     static int cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize)
0821     {
0822         QJDns::Private *self = (QJDns::Private *)app;
0823 
0824         QUdpSocket *sock = self->socketForHandle.value(handle);
0825         if(!sock)
0826             return 0;
0827 
0828         QHostAddress host = addr2qt(addr);
0829         int ret = sock->writeDatagram((const char *)buf, bufsize, host, port);
0830         if(ret == -1)
0831         {
0832             // this can happen if the datagram to send is too big.  i'm not sure what else
0833             //   may cause this.  if we return 0, then jdns may try to resend the packet,
0834             //   which might not work if it is too large (causing the same error over and
0835             //   over).  we'll return success to jdns, so the result is as if the packet
0836             //   was dropped.
0837             return 1;
0838         }
0839 
0840         ++self->pending;
0841         return 1;
0842     }
0843 };
0844 
0845 QJDns::QJDns(QObject *parent)
0846 :QObject(parent)
0847 {
0848     d = new Private(this);
0849 }
0850 
0851 QJDns::~QJDns()
0852 {
0853     delete d;
0854 }
0855 
0856 bool QJDns::init(Mode mode, const QHostAddress &address)
0857 {
0858     return d->init(mode, address);
0859 }
0860 
0861 void QJDns::shutdown()
0862 {
0863     d->shutting_down = true;
0864     d->pending_wait = false;
0865     d->complete_shutdown = false;
0866     jdns_shutdown(d->sess);
0867     d->process();
0868 }
0869 
0870 QStringList QJDns::debugLines()
0871 {
0872     QStringList tmp = d->debug_strings;
0873     d->debug_strings.clear();
0874     return tmp;
0875 }
0876 
0877 QJDns::SystemInfo QJDns::systemInfo()
0878 {
0879     SystemInfo out;
0880     jdns_dnsparams_t *params = jdns_system_dnsparams();
0881     for(int n = 0; n < params->nameservers->count; ++n)
0882     {
0883         NameServer ns;
0884         ns.address = addr2qt(params->nameservers->item[n]->address);
0885         out.nameServers += ns;
0886     }
0887     for(int n = 0; n < params->domains->count; ++n)
0888         out.domains += str2qt(params->domains->item[n]);
0889     for(int n = 0; n < params->hosts->count; ++n)
0890     {
0891         DnsHost h;
0892         h.name = str2qt(params->hosts->item[n]->name);
0893         h.address = addr2qt(params->hosts->item[n]->address);
0894         out.hosts += h;
0895     }
0896     jdns_dnsparams_delete(params);
0897     return out;
0898 }
0899 
0900 #define PROBE_BASE  20000
0901 #define PROBE_RANGE   100
0902 
0903 QHostAddress QJDns::detectPrimaryMulticast(const QHostAddress &address)
0904 {
0905     my_srand();
0906 
0907     QUdpSocket *sock = new QUdpSocket;
0908     QUdpSocket::BindMode mode;
0909     mode |= QUdpSocket::ShareAddress;
0910     mode |= QUdpSocket::ReuseAddressHint;
0911     int port = -1;
0912     for(int n = 0; n < PROBE_RANGE; ++n)
0913     {
0914         if(sock->bind(address, PROBE_BASE + n, mode))
0915         {
0916             port = PROBE_BASE + n;
0917             break;
0918         }
0919     }
0920     if(port == -1)
0921     {
0922         delete sock;
0923         return QHostAddress();
0924     }
0925 
0926     jdns_address_t *a;
0927     if(address.protocol() == QAbstractSocket::IPv6Protocol)
0928         a = jdns_address_multicast6_new();
0929     else
0930         a = jdns_address_multicast4_new();
0931     QHostAddress maddr = addr2qt(a);
0932     jdns_address_delete(a);
0933 
0934     if(address.protocol() == QAbstractSocket::IPv6Protocol)
0935     {
0936         int x;
0937         if(!qjdns_sock_setMulticast6(sock->socketDescriptor(), maddr.toIPv6Address().c, &x))
0938         {
0939             delete sock;
0940             return QHostAddress();
0941         }
0942         qjdns_sock_setTTL6(sock->socketDescriptor(), 0);
0943     }
0944     else
0945     {
0946         int x;
0947         if(!qjdns_sock_setMulticast4(sock->socketDescriptor(), maddr.toIPv4Address(), &x))
0948         {
0949             delete sock;
0950             return QHostAddress();
0951         }
0952         qjdns_sock_setTTL4(sock->socketDescriptor(), 0);
0953     }
0954 
0955     QHostAddress result;
0956     QByteArray out(128, 0);
0957     for(int n = 0; n < out.size(); ++n)
0958         out[n] = rand();
0959     if(sock->writeDatagram(out.data(), out.size(), maddr, port) == -1)
0960     {
0961         delete sock;
0962         return QHostAddress();
0963     }
0964     while(1)
0965     {
0966         if(!sock->waitForReadyRead(1000))
0967         {
0968             fprintf(stderr, "QJDns::detectPrimaryMulticast: timeout while checking %s\n", qPrintable(address.toString()));
0969             delete sock;
0970             return QHostAddress();
0971         }
0972         QByteArray in(128, 0);
0973         QHostAddress from_addr;
0974         quint16 from_port;
0975         int ret = sock->readDatagram(in.data(), in.size(), &from_addr, &from_port);
0976         if(ret == -1)
0977         {
0978             delete sock;
0979             return QHostAddress();
0980         }
0981 
0982         if(from_port != port)
0983             continue;
0984         in.resize(ret);
0985         if(in != out)
0986             continue;
0987 
0988         result = from_addr;
0989         break;
0990     }
0991     delete sock;
0992 
0993     return result;
0994 }
0995 
0996 void QJDns::setNameServers(const QList<NameServer> &list)
0997 {
0998     d->setNameServers(list);
0999 }
1000 
1001 int QJDns::queryStart(const QByteArray &name, int type)
1002 {
1003     int id = jdns_query(d->sess, (const unsigned char *)name.data(), type);
1004     d->process();
1005     return id;
1006 }
1007 
1008 void QJDns::queryCancel(int id)
1009 {
1010     jdns_cancel_query(d->sess, id);
1011     d->removeCancelled(id);
1012     d->process();
1013 }
1014 
1015 int QJDns::publishStart(PublishMode m, const Record &record)
1016 {
1017     jdns_rr_t *rr = export_record(record);
1018 
1019     int pubmode;
1020     if(m == QJDns::Unique)
1021         pubmode = JDNS_PUBLISH_UNIQUE;
1022     else
1023         pubmode = JDNS_PUBLISH_SHARED;
1024 
1025     int id = jdns_publish(d->sess, pubmode, rr);
1026     jdns_rr_delete(rr);
1027     d->process();
1028     return id;
1029 }
1030 
1031 void QJDns::publishUpdate(int id, const Record &record)
1032 {
1033     jdns_rr_t *rr = export_record(record);
1034 
1035     jdns_update_publish(d->sess, id, rr);
1036     jdns_rr_delete(rr);
1037     d->process();
1038 }
1039 
1040 void QJDns::publishCancel(int id)
1041 {
1042     jdns_cancel_publish(d->sess, id);
1043     d->removeCancelled(id);
1044     d->process();
1045 }
1046 
1047 #include "qjdns.moc"