2
* Copyright (C) 2005-2008 Justin Karneges
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the
6
* "Software"), to deal in the Software without restriction, including
7
* without limitation the rights to use, copy, modify, merge, publish,
8
* distribute, sublicense, and/or sell copies of the Software, and to
9
* permit persons to whom the Software is furnished to do so, subject to
10
* the following conditions:
12
* The above copyright notice and this permission notice shall be included
13
* in all copies or substantial portions of the Software.
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
#include "qjdns_sock.h"
35
// safeobj stuff, from qca
37
void releaseAndDeleteLater(QObject *owner, QObject *obj)
39
obj->disconnect(owner);
44
class SafeTimer : public QObject
48
SafeTimer(QObject *parent = 0) :
52
connect(t, SIGNAL(timeout()), SIGNAL(timeout()));
57
releaseAndDeleteLater(this, t);
60
int interval() const { return t->interval(); }
61
bool isActive() const { return t->isActive(); }
62
bool isSingleShot() const { return t->isSingleShot(); }
63
void setInterval(int msec) { t->setInterval(msec); }
64
void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); }
65
int timerId() const { return t->timerId(); }
68
void start(int msec) { t->start(msec); }
69
void start() { t->start(); }
70
void stop() { t->stop(); }
81
static jdns_string_t *qt2str(const QByteArray &in)
83
jdns_string_t *out = jdns_string_new();
84
jdns_string_set(out, (const unsigned char *)in.data(), in.size());
88
static QByteArray str2qt(const jdns_string_t *in)
90
return QByteArray((const char *)in->data, in->size);
93
static void qt2addr_set(jdns_address_t *addr, const QHostAddress &host)
95
if(host.protocol() == QAbstractSocket::IPv6Protocol)
96
jdns_address_set_ipv6(addr, host.toIPv6Address().c);
98
jdns_address_set_ipv4(addr, host.toIPv4Address());
101
static jdns_address_t *qt2addr(const QHostAddress &host)
103
jdns_address_t *addr = jdns_address_new();
104
qt2addr_set(addr, host);
108
static QHostAddress addr2qt(const jdns_address_t *addr)
111
return QHostAddress(addr->addr.v6);
113
return QHostAddress(addr->addr.v4);
116
static QJDns::Record import_record(const jdns_rr_t *in)
120
out.owner = QByteArray((const char *)in->owner);
123
out.rdata = QByteArray((const char *)in->rdata, in->rdlength);
130
if(type == QJDns::A || type == QJDns::Aaaa)
132
out.haveKnown = true;
133
out.address = addr2qt(in->data.address);
135
else if(type == QJDns::Mx)
137
out.haveKnown = true;
138
out.name = QByteArray((const char *)in->data.server->name);
139
out.priority = in->data.server->priority;
141
else if(type == QJDns::Srv)
143
out.haveKnown = true;
144
out.name = QByteArray((const char *)in->data.server->name);
145
out.priority = in->data.server->priority;
146
out.weight = in->data.server->weight;
147
out.port = in->data.server->port;
149
else if(type == QJDns::Cname || type == QJDns::Ptr || type == QJDns::Ns)
151
out.haveKnown = true;
152
out.name = QByteArray((const char *)in->data.name);
154
else if(type == QJDns::Txt)
156
out.haveKnown = true;
158
for(int n = 0; n < in->data.texts->count; ++n)
159
out.texts += str2qt(in->data.texts->item[n]);
161
else if(type == QJDns::Hinfo)
163
out.haveKnown = true;
164
out.cpu = str2qt(in->data.hinfo.cpu);
165
out.os = str2qt(in->data.hinfo.os);
172
static jdns_rr_t *export_record(const QJDns::Record &in)
174
jdns_rr_t *out = jdns_rr_new();
176
jdns_rr_set_owner(out, (const unsigned char *)in.owner.data());
179
// if we have known, use that
186
jdns_address_t *addr = qt2addr(in.address);
187
jdns_rr_set_A(out, addr);
188
jdns_address_delete(addr);
190
else if(type == QJDns::Aaaa)
192
jdns_address_t *addr = qt2addr(in.address);
193
jdns_rr_set_AAAA(out, addr);
194
jdns_address_delete(addr);
196
else if(type == QJDns::Mx)
198
jdns_rr_set_MX(out, (const unsigned char *)in.name.data(), in.priority);
200
else if(type == QJDns::Srv)
202
jdns_rr_set_SRV(out, (const unsigned char *)in.name.data(), in.port, in.priority, in.weight);
204
else if(type == QJDns::Cname)
206
jdns_rr_set_CNAME(out, (const unsigned char *)in.name.data());
208
else if(type == QJDns::Ptr)
210
jdns_rr_set_PTR(out, (const unsigned char *)in.name.data());
212
else if(type == QJDns::Txt)
214
jdns_stringlist_t *list = jdns_stringlist_new();
215
for(int n = 0; n < in.texts.count(); ++n)
217
jdns_string_t *str = qt2str(in.texts[n]);
218
jdns_stringlist_append(list, str);
219
jdns_string_delete(str);
221
jdns_rr_set_TXT(out, list);
222
jdns_stringlist_delete(list);
224
else if(type == QJDns::Hinfo)
226
jdns_string_t *cpu = qt2str(in.cpu);
227
jdns_string_t *os = qt2str(in.os);
228
jdns_rr_set_HINFO(out, cpu, os);
229
jdns_string_delete(cpu);
230
jdns_string_delete(os);
232
else if(type == QJDns::Ns)
234
jdns_rr_set_NS(out, (const unsigned char *)in.name.data());
238
jdns_rr_set_record(out, in.type, (const unsigned char *)in.rdata.data(), in.rdata.size());
243
//----------------------------------------------------------------------------
245
//----------------------------------------------------------------------------
246
QJDns::NameServer::NameServer()
248
port = JDNS_UNICAST_PORT;
251
//----------------------------------------------------------------------------
253
//----------------------------------------------------------------------------
254
QJDns::Record::Record()
261
bool QJDns::Record::verify() const
263
jdns_rr_t *rr = export_record(*this);
264
int ok = jdns_rr_verify(rr);
266
return (ok ? true : false);
269
//----------------------------------------------------------------------------
271
//----------------------------------------------------------------------------
272
static int my_srand_done = 0;
274
static void my_srand()
279
// lame attempt at randomizing without srand
280
int count = ::time(NULL) % 128;
281
for(int n = 0; n < count; ++n)
287
class QJDns::Private : public QObject
294
int source_type; // 0 for query, 1 for publish
303
QJDns::Response response;
309
jdns_session_t *sess;
311
SafeTimer stepTrigger, debugTrigger;
312
SafeTimer stepTimeout;
314
QStringList debug_strings;
315
bool new_debug_strings;
318
QHash<int,QUdpSocket*> socketForHandle;
319
QHash<QUdpSocket*,int> handleForSocket;
322
bool complete_shutdown;
324
// pointers that will point to things we are currently signalling
325
// about. when a query or publish is cancelled, we can use these
326
// pointers to extract anything we shouldn't signal.
327
QList<LateError> *pErrors;
328
QList<int> *pPublished;
329
QList<LateResponse> *pResponses;
342
shutting_down = false;
343
new_debug_strings = false;
346
connect(&stepTrigger, SIGNAL(timeout()), SLOT(doNextStepSlot()));
347
stepTrigger.setSingleShot(true);
349
connect(&debugTrigger, SIGNAL(timeout()), SLOT(doDebug()));
350
debugTrigger.setSingleShot(true);
352
connect(&stepTimeout, SIGNAL(timeout()), SLOT(st_timeout()));
353
stepTimeout.setSingleShot(true);
369
jdns_session_delete(sess);
373
shutting_down = false;
376
// it is safe to delete the QUdpSocket objects here without
377
// deleteLater, since this code path never occurs when
378
// a signal from those objects is on the stack
379
qDeleteAll(socketForHandle);
380
socketForHandle.clear();
381
handleForSocket.clear();
388
bool init(QJDns::Mode _mode, const QHostAddress &address)
392
jdns_callbacks_t callbacks;
393
callbacks.app = this;
394
callbacks.time_now = cb_time_now;
395
callbacks.rand_int = cb_rand_int;
396
callbacks.debug_line = cb_debug_line;
397
callbacks.udp_bind = cb_udp_bind;
398
callbacks.udp_unbind = cb_udp_unbind;
399
callbacks.udp_read = cb_udp_read;
400
callbacks.udp_write = cb_udp_write;
401
sess = jdns_session_new(&callbacks);
402
jdns_set_hold_ids_enabled(sess, 1);
408
jdns_address_t *baddr = qt2addr(address);
411
ret = jdns_init_unicast(sess, baddr, 0);
415
jdns_address_t *maddr;
416
if(address.protocol() == QAbstractSocket::IPv6Protocol)
417
maddr = jdns_address_multicast6_new();
419
maddr = jdns_address_multicast4_new();
420
ret = jdns_init_multicast(sess, baddr, JDNS_MULTICAST_PORT, maddr);
421
jdns_address_delete(maddr);
423
jdns_address_delete(baddr);
427
jdns_session_delete(sess);
434
void setNameServers(const QList<NameServer> &nslist)
436
jdns_nameserverlist_t *addrs = jdns_nameserverlist_new();
437
for(int n = 0; n < nslist.count(); ++n)
439
jdns_address_t *addr = qt2addr(nslist[n].address);
440
jdns_nameserverlist_append(addrs, addr, nslist[n].port);
441
jdns_address_delete(addr);
443
jdns_set_nameservers(sess, addrs);
444
jdns_nameserverlist_delete(addrs);
449
if(!stepTrigger.isActive())
458
new_debug_strings = true;
459
if(!debugTrigger.isActive())
460
debugTrigger.start();
465
if(shutting_down && complete_shutdown)
468
emit q->shutdownFinished();
472
QPointer<QObject> self = this;
474
int ret = jdns_step(sess);
476
QList<LateError> errors;
477
QList<int> published;
478
QList<LateResponse> responses;
479
bool finish_shutdown = false;
482
pPublished = &published;
483
pResponses = &responses;
487
jdns_event_t *e = jdns_next_event(sess);
491
if(e->type == JDNS_EVENT_SHUTDOWN)
493
finish_shutdown = true;
495
else if(e->type == JDNS_EVENT_PUBLISH)
497
if(e->status != JDNS_STATUS_SUCCESS)
500
if(e->status == JDNS_STATUS_CONFLICT)
501
error = QJDns::ErrorConflict;
503
error = QJDns::ErrorGeneric;
515
else if(e->type == JDNS_EVENT_RESPONSE)
517
if(e->status != JDNS_STATUS_SUCCESS)
520
if(e->status == JDNS_STATUS_NXDOMAIN)
521
error = QJDns::ErrorNXDomain;
522
else if(e->status == JDNS_STATUS_TIMEOUT)
523
error = QJDns::ErrorTimeout;
525
error = QJDns::ErrorGeneric;
534
QJDns::Response out_response;
535
for(int n = 0; n < e->response->answerCount; ++n)
536
out_response.answerRecords += import_record(e->response->answerRecords[n]);
539
lr.response = out_response;
543
lr.do_cancel = false;
548
jdns_event_delete(e);
551
if(ret & JDNS_STEP_TIMER)
552
stepTimeout.start(jdns_next_timer(sess));
556
need_handle = (ret & JDNS_STEP_HANDLE);
558
// read the lists safely enough so that items can be deleted
561
while(!errors.isEmpty())
563
LateError i = errors.takeFirst();
564
if(i.source_type == 0)
565
jdns_cancel_query(sess, i.id);
567
jdns_cancel_publish(sess, i.id);
568
emit q->error(i.id, i.error);
573
while(!published.isEmpty())
575
int i = published.takeFirst();
576
emit q->published(i);
581
while(!responses.isEmpty())
583
LateResponse i = responses.takeFirst();
585
jdns_cancel_query(sess, i.id);
586
emit q->resultsReady(i.id, i.response);
593
// if we have pending udp packets to write, stick around
600
complete_shutdown = true;
610
void removeCancelled(int id)
614
for(int n = 0; n < pErrors->count(); ++n)
616
if(pErrors->at(n).id == id)
618
pErrors->removeAt(n);
619
--n; // adjust position
626
for(int n = 0; n < pPublished->count(); ++n)
628
if(pPublished->at(n) == id)
630
pPublished->removeAt(n);
631
--n; // adjust position
638
for(int n = 0; n < pResponses->count(); ++n)
640
if(pResponses->at(n).id == id)
642
pResponses->removeAt(n);
643
--n; // adjust position
652
QUdpSocket *sock = (QUdpSocket *)sender();
653
int handle = handleForSocket.value(sock);
657
jdns_set_handle_readable(sess, handle);
663
QByteArray buf(4096, 0);
664
QHostAddress from_addr;
666
sock->readDatagram(buf.data(), buf.size(), &from_addr, &from_port);
670
void udp_bytesWritten(qint64)
675
if(shutting_down && pending_wait && pending == 0)
677
pending_wait = false;
678
complete_shutdown = true;
689
void doNextStepSlot()
696
if(new_debug_strings)
698
new_debug_strings = false;
699
if(!debug_strings.isEmpty())
700
emit q->debugLinesReady();
706
static int cb_time_now(jdns_session_t *, void *app)
708
QJDns::Private *self = (QJDns::Private *)app;
710
return self->clock.elapsed();
713
static int cb_rand_int(jdns_session_t *, void *)
715
return rand() % 65536;
718
static void cb_debug_line(jdns_session_t *, void *app, const char *str)
720
QJDns::Private *self = (QJDns::Private *)app;
722
self->debug_strings += QString::fromLatin1(str);
723
self->processDebug();
726
static int cb_udp_bind(jdns_session_t *, void *app, const jdns_address_t *addr, int port, const jdns_address_t *maddr)
728
QJDns::Private *self = (QJDns::Private *)app;
730
// we always pass non-null to jdns_init, so this should be a valid address
731
QHostAddress host = addr2qt(addr);
733
QUdpSocket *sock = new QUdpSocket(self);
734
self->connect(sock, SIGNAL(readyRead()), SLOT(udp_readyRead()));
736
// use queued for bytesWritten, since qt is evil and emits before writeDatagram returns
737
qRegisterMetaType<qint64>("qint64");
738
self->connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(udp_bytesWritten(qint64)), Qt::QueuedConnection);
740
QUdpSocket::BindMode mode;
741
mode |= QUdpSocket::ShareAddress;
742
mode |= QUdpSocket::ReuseAddressHint;
743
if(!sock->bind(host, port, mode))
751
int sd = sock->socketDescriptor();
755
ok = qjdns_sock_setMulticast6(sd, maddr->addr.v6, &errorCode);
757
ok = qjdns_sock_setMulticast4(sd, maddr->addr.v4, &errorCode);
763
self->debug_strings += QString("failed to setup multicast on the socket (errorCode=%1)").arg(errorCode);
764
self->processDebug();
770
qjdns_sock_setTTL6(sd, 255);
771
qjdns_sock_setIPv6Only(sd);
774
qjdns_sock_setTTL4(sd, 255);
777
int handle = self->next_handle++;
778
self->socketForHandle.insert(handle, sock);
779
self->handleForSocket.insert(sock, handle);
783
static void cb_udp_unbind(jdns_session_t *, void *app, int handle)
785
QJDns::Private *self = (QJDns::Private *)app;
787
QUdpSocket *sock = self->socketForHandle.value(handle);
791
self->socketForHandle.remove(handle);
792
self->handleForSocket.remove(sock);
796
static int cb_udp_read(jdns_session_t *, void *app, int handle, jdns_address_t *addr, int *port, unsigned char *buf, int *bufsize)
798
QJDns::Private *self = (QJDns::Private *)app;
800
QUdpSocket *sock = self->socketForHandle.value(handle);
805
if(!sock->hasPendingDatagrams())
808
QHostAddress from_addr;
810
int ret = sock->readDatagram((char *)buf, *bufsize, &from_addr, &from_port);
814
qt2addr_set(addr, from_addr);
815
*port = (int)from_port;
820
static int cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize)
822
QJDns::Private *self = (QJDns::Private *)app;
824
QUdpSocket *sock = self->socketForHandle.value(handle);
828
QHostAddress host = addr2qt(addr);
829
int ret = sock->writeDatagram((const char *)buf, bufsize, host, port);
832
// this can happen if the datagram to send is too big. i'm not sure what else
833
// may cause this. if we return 0, then jdns may try to resend the packet,
834
// which might not work if it is too large (causing the same error over and
835
// over). we'll return success to jdns, so the result is as if the packet
845
QJDns::QJDns(QObject *parent)
848
d = new Private(this);
856
bool QJDns::init(Mode mode, const QHostAddress &address)
858
return d->init(mode, address);
861
void QJDns::shutdown()
863
d->shutting_down = true;
864
d->pending_wait = false;
865
d->complete_shutdown = false;
866
jdns_shutdown(d->sess);
870
QStringList QJDns::debugLines()
872
QStringList tmp = d->debug_strings;
873
d->debug_strings.clear();
877
QJDns::SystemInfo QJDns::systemInfo()
880
jdns_dnsparams_t *params = jdns_system_dnsparams();
881
for(int n = 0; n < params->nameservers->count; ++n)
884
ns.address = addr2qt(params->nameservers->item[n]->address);
885
out.nameServers += ns;
887
for(int n = 0; n < params->domains->count; ++n)
888
out.domains += str2qt(params->domains->item[n]);
889
for(int n = 0; n < params->hosts->count; ++n)
892
h.name = str2qt(params->hosts->item[n]->name);
893
h.address = addr2qt(params->hosts->item[n]->address);
896
jdns_dnsparams_delete(params);
900
#define PROBE_BASE 20000
901
#define PROBE_RANGE 100
903
QHostAddress QJDns::detectPrimaryMulticast(const QHostAddress &address)
907
QUdpSocket *sock = new QUdpSocket;
908
QUdpSocket::BindMode mode;
909
mode |= QUdpSocket::ShareAddress;
910
mode |= QUdpSocket::ReuseAddressHint;
912
for(int n = 0; n < PROBE_RANGE; ++n)
914
if(sock->bind(address, PROBE_BASE + n, mode))
916
port = PROBE_BASE + n;
923
return QHostAddress();
927
if(address.protocol() == QAbstractSocket::IPv6Protocol)
928
a = jdns_address_multicast6_new();
930
a = jdns_address_multicast4_new();
931
QHostAddress maddr = addr2qt(a);
932
jdns_address_delete(a);
934
if(address.protocol() == QAbstractSocket::IPv6Protocol)
937
if(!qjdns_sock_setMulticast6(sock->socketDescriptor(), maddr.toIPv6Address().c, &x))
940
return QHostAddress();
942
qjdns_sock_setTTL6(sock->socketDescriptor(), 0);
947
if(!qjdns_sock_setMulticast4(sock->socketDescriptor(), maddr.toIPv4Address(), &x))
950
return QHostAddress();
952
qjdns_sock_setTTL4(sock->socketDescriptor(), 0);
956
QByteArray out(128, 0);
957
for(int n = 0; n < out.size(); ++n)
959
if(sock->writeDatagram(out.data(), out.size(), maddr, port) == -1)
962
return QHostAddress();
966
if(!sock->waitForReadyRead(1000))
968
fprintf(stderr, "QJDns::detectPrimaryMulticast: timeout while checking %s\n", qPrintable(address.toString()));
970
return QHostAddress();
972
QByteArray in(128, 0);
973
QHostAddress from_addr;
975
int ret = sock->readDatagram(in.data(), in.size(), &from_addr, &from_port);
979
return QHostAddress();
982
if(from_port != port)
996
void QJDns::setNameServers(const QList<NameServer> &list)
998
d->setNameServers(list);
1001
int QJDns::queryStart(const QByteArray &name, int type)
1003
int id = jdns_query(d->sess, (const unsigned char *)name.data(), type);
1008
void QJDns::queryCancel(int id)
1010
jdns_cancel_query(d->sess, id);
1011
d->removeCancelled(id);
1015
int QJDns::publishStart(PublishMode m, const Record &record)
1017
jdns_rr_t *rr = export_record(record);
1020
if(m == QJDns::Unique)
1021
pubmode = JDNS_PUBLISH_UNIQUE;
1023
pubmode = JDNS_PUBLISH_SHARED;
1025
int id = jdns_publish(d->sess, pubmode, rr);
1031
void QJDns::publishUpdate(int id, const Record &record)
1033
jdns_rr_t *rr = export_record(record);
1035
jdns_update_publish(d->sess, id, rr);
1040
void QJDns::publishCancel(int id)
1042
jdns_cancel_publish(d->sess, id);
1043
d->removeCancelled(id);
1047
#include "qjdns.moc"