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

0001 /*
0002  * Copyright (C) 2005  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 <QtCore>
0025 #include <QtNetwork>
0026 #include "qjdns.h"
0027 
0028 QString dataToString(const QByteArray &buf)
0029 {
0030     QString out;
0031     for(int n = 0; n < buf.size(); ++n)
0032     {
0033         unsigned char c = (unsigned char)buf[n];
0034         if(c == '\\')
0035             out += "\\\\";
0036         else if(c >= 0x20 && c < 0x7f)
0037             out += c;
0038         else
0039             out += QString().sprintf("\\x%02x", (unsigned int)c);
0040     }
0041     return out;
0042 }
0043 
0044 void print_record(const QJDns::Record &r)
0045 {
0046     switch(r.type)
0047     {
0048         case QJDns::A:
0049             printf("  A: [%s] (ttl=%d)\n", qPrintable(r.address.toString()), r.ttl);
0050             break;
0051         case QJDns::Aaaa:
0052             printf("  AAAA: [%s] (ttl=%d)\n", qPrintable(r.address.toString()), r.ttl);
0053             break;
0054         case QJDns::Mx:
0055             printf("  MX: [%s] priority=%d (ttl=%d)\n", r.name.data(), r.priority, r.ttl);
0056             break;
0057         case QJDns::Srv:
0058             printf("  SRV: [%s] port=%d priority=%d weight=%d (ttl=%d)\n", r.name.data(), r.port, r.priority, r.weight, r.ttl);
0059             break;
0060         case QJDns::Cname:
0061             printf("  CNAME: [%s] (ttl=%d)\n", r.name.data(), r.ttl);
0062             break;
0063         case QJDns::Ptr:
0064             printf("  PTR: [%s] (ttl=%d)\n", r.name.data(), r.ttl);
0065             break;
0066         case QJDns::Txt:
0067         {
0068             printf("  TXT: count=%d (ttl=%d)\n", r.texts.count(), r.ttl);
0069             for(int n = 0; n < r.texts.count(); ++n)
0070                 printf("    len=%d [%s]\n", r.texts[n].size(), qPrintable(dataToString(r.texts[n])));
0071             break;
0072         }
0073         case QJDns::Hinfo:
0074             printf("  HINFO: [%s] [%s] (ttl=%d)\n", r.cpu.data(), r.os.data(), r.ttl);
0075             break;
0076         case QJDns::Ns:
0077             printf("  NS: [%s] (ttl=%d)\n", r.name.data(), r.ttl);
0078             break;
0079         default:
0080             printf("  (Unknown): type=%d, size=%d (ttl=%d)\n", r.type, r.rdata.size(), r.ttl);
0081             break;
0082     }
0083 }
0084 
0085 class App : public QObject
0086 {
0087     Q_OBJECT
0088 public:
0089     bool opt_debug, opt_ipv6, opt_quit;
0090     int quit_time;
0091     QString mode, type, name, ipaddr;
0092     QStringList nslist;
0093     QList<QJDns::Record> pubitems;
0094     QJDns jdns;
0095     int req_id;
0096 
0097     App()
0098     {
0099         connect(&jdns, SIGNAL(resultsReady(int,QJDns::Response)), SLOT(jdns_resultsReady(int,QJDns::Response)));
0100         connect(&jdns, SIGNAL(published(int)), SLOT(jdns_published(int)));
0101         connect(&jdns, SIGNAL(error(int,QJDns::Error)), SLOT(jdns_error(int,QJDns::Error)));
0102         connect(&jdns, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished()));
0103         connect(&jdns, SIGNAL(debugLinesReady()), SLOT(jdns_debugLinesReady()));
0104     }
0105 
0106     ~App()
0107     {
0108     }
0109 
0110 public slots:
0111     void start()
0112     {
0113         if(mode == "uni")
0114         {
0115             if(!jdns.init(QJDns::Unicast, opt_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::Any))
0116             {
0117                 jdns_debugLinesReady();
0118                 printf("unable to bind\n");
0119                 emit quit();
0120                 return;
0121             }
0122 
0123             QList<QJDns::NameServer> addrs;
0124             for(int n = 0; n < nslist.count(); ++n)
0125             {
0126                 QJDns::NameServer host;
0127                 QString str = nslist[n];
0128                 if(str == "mul")
0129                 {
0130                     if(opt_ipv6)
0131                         host.address = QHostAddress("FF02::FB");
0132                     else
0133                         host.address = QHostAddress("224.0.0.251");
0134                     host.port = 5353;
0135                 }
0136                 else
0137                 {
0138                     int at = str.indexOf(';');
0139                     if(at != -1)
0140                     {
0141                         host.address = QHostAddress(str.mid(0, at));
0142                         host.port = str.mid(at + 1).toInt();
0143                     }
0144                     else
0145                     {
0146                         host.address = QHostAddress(str);
0147                     }
0148                 }
0149 
0150                 if(host.address.isNull() || host.port <= 0)
0151                 {
0152                     printf("bad nameserver: [%s]\n", qPrintable(nslist[n]));
0153                     emit quit();
0154                     return;
0155                 }
0156                 addrs += host;
0157             }
0158 
0159             if(addrs.isEmpty())
0160                 addrs = QJDns::systemInfo().nameServers;
0161 
0162             if(addrs.isEmpty())
0163             {
0164                 printf("no nameservers were detected or specified\n");
0165                 emit quit();
0166                 return;
0167             }
0168 
0169             jdns.setNameServers(addrs);
0170         }
0171         else
0172         {
0173             if(!jdns.init(QJDns::Multicast, opt_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::Any))
0174             {
0175                 jdns_debugLinesReady();
0176                 printf("unable to bind\n");
0177                 emit quit();
0178                 return;
0179             }
0180         }
0181 
0182         if(mode == "uni" || mode == "mul")
0183         {
0184             int x = QJDns::A;
0185             if(type == "ptr")
0186                 x = QJDns::Ptr;
0187             else if(type == "srv")
0188                 x = QJDns::Srv;
0189             else if(type == "a")
0190                 x = QJDns::A;
0191             else if(type == "aaaa")
0192                 x = QJDns::Aaaa;
0193             else if(type == "mx")
0194                 x = QJDns::Mx;
0195             else if(type == "txt")
0196                 x = QJDns::Txt;
0197             else if(type == "hinfo")
0198                 x = QJDns::Hinfo;
0199             else if(type == "cname")
0200                 x = QJDns::Cname;
0201             else if(type == "any")
0202                 x = QJDns::Any;
0203             else
0204             {
0205                 bool ok;
0206                 int y = type.toInt(&ok);
0207                 if(ok)
0208                     x = y;
0209             }
0210 
0211             req_id = jdns.queryStart(name.toLatin1(), x);
0212             printf("[%d] Querying for [%s] type=%d ...\n", req_id, qPrintable(name), x);
0213         }
0214         else // publish
0215         {
0216             for(int n = 0; n < pubitems.count(); ++n)
0217             {
0218                 const QJDns::Record &rr = pubitems[n];
0219                 QJDns::PublishMode m = QJDns::Unique;
0220                 if(rr.type == QJDns::Ptr)
0221                     m = QJDns::Shared;
0222                 int id = jdns.publishStart(m, rr);
0223                 printf("[%d] Publishing [%s] type=%d ...\n", id, rr.owner.data(), rr.type);
0224             }
0225         }
0226 
0227         if(opt_quit)
0228             QTimer::singleShot(quit_time * 1000, this, SLOT(doShutdown()));
0229     }
0230 
0231 signals:
0232     void quit();
0233 
0234 private slots:
0235     void jdns_resultsReady(int id, const QJDns::Response &results)
0236     {
0237         printf("[%d] Results\n", id);
0238         for(int n = 0; n < results.answerRecords.count(); ++n)
0239             print_record(results.answerRecords[n]);
0240 
0241         if(mode == "uni")
0242             jdns.shutdown();
0243     }
0244 
0245     void jdns_published(int id)
0246     {
0247         printf("[%d] Published\n", id);
0248     }
0249 
0250     void jdns_error(int id, QJDns::Error e)
0251     {
0252         QString str;
0253         if(e == QJDns::ErrorGeneric)
0254             str = "Generic";
0255         else if(e == QJDns::ErrorNXDomain)
0256             str = "NXDomain";
0257         else if(e == QJDns::ErrorTimeout)
0258             str = "Timeout";
0259         else if(e == QJDns::ErrorConflict)
0260             str = "Conflict";
0261         printf("[%d] Error: %s\n", id, qPrintable(str));
0262         jdns.shutdown();
0263     }
0264 
0265     void jdns_shutdownFinished()
0266     {
0267         emit quit();
0268     }
0269 
0270     void jdns_debugLinesReady()
0271     {
0272         QStringList lines = jdns.debugLines();
0273         if(opt_debug)
0274         {
0275             for(int n = 0; n < lines.count(); ++n)
0276                 printf("jdns: %s\n", qPrintable(lines[n]));
0277         }
0278     }
0279 
0280     void doShutdown()
0281     {
0282         jdns.shutdown();
0283     }
0284 };
0285 
0286 #include "main.moc"
0287 
0288 void usage()
0289 {
0290     printf("usage: jdns (options) uni [type] [name] (nameserver(;port)|mul ...)\n");
0291     printf("       jdns (options) mul [type] [name]\n");
0292     printf("       jdns (options) pub [items ...]\n");
0293     printf("       jdns sys\n");
0294     printf("\n");
0295     printf("options:\n");
0296     printf("  -d     show debug output\n");
0297     printf("  -6     use ipv6\n");
0298     printf("  -q x   quit x seconds after starting\n");
0299     printf("\n");
0300     printf("uni/mul types: a aaaa ptr srv mx txt hinfo cname any\n");
0301     printf("pub items: ptr:name,answer srv:name,answer,port a:name,ipaddr\n");
0302     printf("           txt:name,str0,...,strn aaaa:name,ipaddr\n");
0303     printf("\n");
0304     printf("examples:\n");
0305     printf("  jdns uni a jabber.org 192.168.0.1\n");
0306     printf("  jdns uni srv _xmpp-client._tcp.jabber.org 192.168.0.1;53\n");
0307     printf("  jdns uni 10 user@host._presence._tcp.local mul\n");
0308     printf("  jdns mul a foobar.local\n");
0309     printf("  jdns mul ptr _services._dns-sd._udp.local\n");
0310     printf("  jdns pub a:mybox.local.,192.168.0.55\n");
0311     printf("\n");
0312 }
0313 
0314 int main(int argc, char **argv)
0315 {
0316     QCoreApplication app(argc, argv);
0317 
0318     if(argc < 2)
0319     {
0320         usage();
0321         return 1;
0322     }
0323 
0324     // get args
0325     QStringList args;
0326     for(int n = 1; n < argc; ++n)
0327         args += QString(argv[n]);
0328 
0329     bool opt_debug = false;
0330     bool opt_ipv6 = false;
0331     bool opt_quit = false;
0332     int quit_time = 0;
0333     QString mode, type, name, ipaddr;
0334     QStringList nslist;
0335     QList<QJDns::Record> pubitems;
0336 
0337     // options
0338     for(int n = 0; n < args.count(); ++n)
0339     {
0340         if(args[n].left(1) == "-")
0341         {
0342             if(args[n] == "-d")
0343                 opt_debug = true;
0344             else if(args[n] == "-6")
0345                 opt_ipv6 = true;
0346             else if(args[n] == "-q")
0347             {
0348                 if(n + 1 >= args.count())
0349                 {
0350                     printf("need to specify number of seconds\n");
0351                     usage();
0352                     return 1;
0353                 }
0354 
0355                 int x = args[n + 1].toInt();
0356                 if(x < 1)
0357                     x = 30;
0358 
0359                 opt_quit = true;
0360                 quit_time = x;
0361 
0362                 args.removeAt(n + 1);
0363             }
0364             else
0365             {
0366                 printf("bad option\n");
0367                 usage();
0368                 return 1;
0369             }
0370             args.removeAt(n);
0371             --n; // adjust position
0372         }
0373     }
0374 
0375     mode = args[0];
0376     if(mode == "uni" || mode == "mul")
0377     {
0378         if(args.count() < 3)
0379         {
0380             printf("not enough args\n");
0381             usage();
0382             return 1;
0383         }
0384         type = args[1];
0385         name = args[2];
0386         if(mode == "uni")
0387         {
0388             for(int n = 3; n < args.count(); ++n)
0389                 nslist += QString(args[n]);
0390         }
0391     }
0392     else if(mode == "pub")
0393     {
0394         if(args.count() < 2)
0395         {
0396             printf("not enough args\n");
0397             usage();
0398             return 1;
0399         }
0400         for(int n = 1; n < args.count(); ++n)
0401         {
0402             QString arg = args[n];
0403             int at = arg.indexOf(':');
0404             if(at == -1)
0405             {
0406                 printf("missing colon\n");
0407                 usage();
0408                 return 1;
0409             }
0410             QString type = arg.mid(0, at).toLower();
0411             QString val = arg.mid(at + 1);
0412             if(type == "a")
0413             {
0414                 QStringList list = val.split(',');
0415                 if(list.count() != 2)
0416                 {
0417                     printf("bad format for A type\n");
0418                     usage();
0419                     return 1;
0420                 }
0421                 QHostAddress host(list[1]);
0422                 if(host.isNull() || host.protocol() != QAbstractSocket::IPv4Protocol)
0423                 {
0424                     printf("bad format for A type IP address\n");
0425                     usage();
0426                     return 1;
0427                 }
0428 
0429                 QJDns::Record rec;
0430                 rec.owner = list[0].toLatin1();
0431                 rec.type = QJDns::A;
0432                 rec.ttl = 120;
0433                 rec.haveKnown = true;
0434                 rec.address = host;
0435                 pubitems += rec;
0436             }
0437             else if(type == "aaaa")
0438             {
0439                 QStringList list = val.split(',');
0440                 if(list.count() != 2)
0441                 {
0442                     printf("bad format for AAAA type\n");
0443                     usage();
0444                     return 1;
0445                 }
0446                 QHostAddress host(list[1]);
0447                 if(host.isNull() || host.protocol() != QAbstractSocket::IPv6Protocol)
0448                 {
0449                     printf("bad format for AAAA type IP address\n");
0450                     usage();
0451                     return 1;
0452                 }
0453 
0454                 QJDns::Record rec;
0455                 rec.owner = list[0].toLatin1();
0456                 rec.type = QJDns::Aaaa;
0457                 rec.ttl = 120;
0458                 rec.haveKnown = true;
0459                 rec.address = host;
0460                 pubitems += rec;
0461             }
0462             else if(type == "srv")
0463             {
0464                 QStringList list = val.split(',');
0465                 if(list.count() != 3)
0466                 {
0467                     printf("bad format for SRV type\n");
0468                     usage();
0469                     return 1;
0470                 }
0471 
0472                 QJDns::Record rec;
0473                 rec.owner = list[0].toLatin1();
0474                 rec.type = QJDns::Srv;
0475                 rec.ttl = 120;
0476                 rec.haveKnown = true;
0477                 rec.name = list[1].toLatin1();
0478                 rec.priority = 0;
0479                 rec.weight = 0;
0480                 rec.port = list[2].toInt();
0481                 pubitems += rec;
0482             }
0483             else if(type == "ptr")
0484             {
0485                 QStringList list = val.split(',');
0486                 if(list.count() != 2)
0487                 {
0488                     printf("bad format for PTR type\n");
0489                     usage();
0490                     return 1;
0491                 }
0492 
0493                 QJDns::Record rec;
0494                 rec.owner = list[0].toLatin1();
0495                 rec.type = QJDns::Ptr;
0496                 rec.ttl = 120;
0497                 rec.haveKnown = true;
0498                 rec.name = list[1].toLatin1();
0499                 pubitems += rec;
0500             }
0501             else if(type == "txt")
0502             {
0503                 QStringList list = val.split(',');
0504                 QList<QByteArray> texts;
0505                 for(int n = 1; n < list.count(); ++n)
0506                     texts += list[n].toLatin1();
0507 
0508                 QJDns::Record rec;
0509                 rec.owner = list[0].toLatin1();
0510                 rec.type = QJDns::Txt;
0511                 rec.ttl = 120;
0512                 rec.haveKnown = true;
0513                 rec.texts = texts;
0514                 pubitems += rec;
0515             }
0516             else
0517             {
0518                 printf("bad record type [%s]\n", qPrintable(type));
0519                 usage();
0520                 return 1;
0521             }
0522         }
0523     }
0524     else if(mode == "sys")
0525     {
0526         QJDns::SystemInfo info = QJDns::systemInfo();
0527 
0528         printf("DNS System Information\n");
0529         printf("  Name Servers:\n");
0530         if(!info.nameServers.isEmpty())
0531         {
0532             for(int n = 0; n < info.nameServers.count(); ++n)
0533                 printf("    %s\n", qPrintable(info.nameServers[n].address.toString()));
0534         }
0535         else
0536             printf("    (None)\n");
0537 
0538         printf("  Domains:\n");
0539         if(!info.domains.isEmpty())
0540         {
0541             for(int n = 0; n < info.domains.count(); ++n)
0542                 printf("    [%s]\n", info.domains[n].data());
0543         }
0544         else
0545             printf("    (None)\n");
0546 
0547         printf("  Hosts:\n");
0548         if(!info.hosts.isEmpty())
0549         {
0550             for(int n = 0; n < info.hosts.count(); ++n)
0551             {
0552                 const QJDns::DnsHost &h = info.hosts[n];
0553                 printf("    [%s] -> %s\n", h.name.data(), qPrintable(h.address.toString()));
0554             }
0555         }
0556         else
0557             printf("    (None)\n");
0558 
0559         QHostAddress addr;
0560         printf("Primary IPv4 Multicast Address: ");
0561         addr = QJDns::detectPrimaryMulticast(QHostAddress::Any);
0562         if(!addr.isNull())
0563             printf("%s\n", qPrintable(addr.toString()));
0564         else
0565             printf("(None)\n");
0566         printf("Primary IPv6 Multicast Address: ");
0567         addr = QJDns::detectPrimaryMulticast(QHostAddress::AnyIPv6);
0568         if(!addr.isNull())
0569             printf("%s\n", qPrintable(addr.toString()));
0570         else
0571             printf("(None)\n");
0572 
0573         return 0;
0574     }
0575     else
0576     {
0577         usage();
0578         return 1;
0579     }
0580 
0581     App a;
0582     a.opt_debug = opt_debug;
0583     a.opt_ipv6 = opt_ipv6;
0584     a.opt_quit = opt_quit;
0585     a.quit_time = quit_time;
0586     a.mode = mode;
0587     a.type = type.toLower();
0588     a.name = name;
0589     a.ipaddr = ipaddr;
0590     a.nslist = nslist;
0591     a.pubitems = pubitems;
0592     QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
0593     QTimer::singleShot(0, &a, SLOT(start()));
0594     app.exec();
0595     return 0;
0596 }