File indexing completed on 2024-05-12 04:05:31

0001 /*
0002  * Copyright (C) 2006  Justin Karneges
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * either version 2
0008    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public
0016  * License along with this library; if not, write to the Free Software
0017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0018  * 02110-1301  USA
0019  *
0020  */
0021 
0022 #include <stdio.h>
0023 #include <iris/processquit.h>
0024 #include <iris/netinterface.h>
0025 #include <iris/netavailability.h>
0026 #include <iris/netnames.h>
0027 
0028 using namespace XMPP;
0029 
0030 class NetMonitor : public QObject
0031 {
0032     Q_OBJECT
0033 public:
0034     NetInterfaceManager *man;
0035     QList<NetInterface*> ifaces;
0036     NetAvailability *netavail;
0037 
0038     ~NetMonitor()
0039     {
0040         delete netavail;
0041         qDeleteAll(ifaces);
0042         delete man;
0043     }
0044 
0045 signals:
0046     void quit();
0047 
0048 public slots:
0049     void start()
0050     {
0051         connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit()));
0052 
0053         man = new NetInterfaceManager;
0054         connect(man, SIGNAL(interfaceAvailable(QString)),
0055             SLOT(here(QString)));
0056         QStringList list = man->interfaces();
0057         for(int n = 0; n < list.count(); ++n)
0058             here(list[n]);
0059 
0060         netavail = new NetAvailability;
0061         connect(netavail, SIGNAL(changed(bool)), SLOT(avail(bool)));
0062         avail(netavail->isAvailable());
0063     }
0064 
0065     void here(const QString &id)
0066     {
0067         NetInterface *iface = new NetInterface(id, man);
0068         connect(iface, SIGNAL(unavailable()), SLOT(gone()));
0069         printf("HERE: %s name=[%s]\n", qPrintable(iface->id()), qPrintable(iface->name()));
0070         QList<QHostAddress> addrs = iface->addresses();
0071         for(int n = 0; n < addrs.count(); ++n)
0072             printf("  address: %s\n", qPrintable(addrs[n].toString()));
0073         if(!iface->gateway().isNull())
0074             printf("  gateway: %s\n", qPrintable(iface->gateway().toString()));
0075         ifaces += iface;
0076     }
0077 
0078     void gone()
0079     {
0080         NetInterface *iface = (NetInterface *)sender();
0081         printf("GONE: %s\n", qPrintable(iface->id()));
0082         ifaces.removeAll(iface);
0083         delete iface;
0084     }
0085 
0086     void avail(bool available)
0087     {
0088         if(available)
0089             printf("** Network available\n");
0090         else
0091             printf("** Network unavailable\n");
0092     }
0093 };
0094 
0095 static QString dataToString(const QByteArray &buf)
0096 {
0097     QString out;
0098     for(int n = 0; n < buf.size(); ++n)
0099     {
0100         unsigned char c = (unsigned char)buf[n];
0101         if(c == '\\')
0102             out += "\\\\";
0103         else if(c >= 0x20 || c < 0x7f)
0104             out += c;
0105         else
0106             out += QString("\\x%1").arg((uint)c, 2, 16);
0107     }
0108     return out;
0109 }
0110 
0111 static void print_record(const NameRecord &r)
0112 {
0113     switch(r.type())
0114     {
0115         case NameRecord::A:
0116             printf("A: [%s] (ttl=%d)\n", qPrintable(r.address().toString()), r.ttl());
0117             break;
0118         case NameRecord::Aaaa:
0119             printf("AAAA: [%s] (ttl=%d)\n", qPrintable(r.address().toString()), r.ttl());
0120             break;
0121         case NameRecord::Mx:
0122             printf("MX: [%s] priority=%d (ttl=%d)\n", r.name().data(), r.priority(), r.ttl());
0123             break;
0124         case NameRecord::Srv:
0125             printf("SRV: [%s] port=%d priority=%d weight=%d (ttl=%d)\n", r.name().data(), r.port(), r.priority(), r.weight(), r.ttl());
0126             break;
0127         case NameRecord::Ptr:
0128             printf("PTR: [%s] (ttl=%d)\n", r.name().data(), r.ttl());
0129             break;
0130         case NameRecord::Txt:
0131         {
0132             QList<QByteArray> texts = r.texts();
0133             printf("TXT: count=%d (ttl=%d)\n", texts.count(), r.ttl());
0134             for(int n = 0; n < texts.count(); ++n)
0135                 printf("  len=%d [%s]\n", texts[n].size(), qPrintable(dataToString(texts[n])));
0136             break;
0137         }
0138         case NameRecord::Hinfo:
0139             printf("HINFO: [%s] [%s] (ttl=%d)\n", r.cpu().data(), r.os().data(), r.ttl());
0140             break;
0141         case NameRecord::Null:
0142             printf("NULL: %d bytes (ttl=%d)\n", r.rawData().size(), r.ttl());
0143             break;
0144         default:
0145             printf("(Unknown): type=%d (ttl=%d)\n", r.type(), r.ttl());
0146             break;
0147     }
0148 }
0149 
0150 static int str2rtype(const QString &in)
0151 {
0152     QString str = in.toLower();
0153     if(str == "a")
0154         return NameRecord::A;
0155     else if(str == "aaaa")
0156         return NameRecord::Aaaa;
0157     else if(str == "ptr")
0158         return NameRecord::Ptr;
0159     else if(str == "srv")
0160         return NameRecord::Srv;
0161     else if(str == "mx")
0162         return NameRecord::Mx;
0163     else if(str == "txt")
0164         return NameRecord::Txt;
0165     else if(str == "hinfo")
0166         return NameRecord::Hinfo;
0167     else if(str == "null")
0168         return NameRecord::Null;
0169     else
0170         return -1;
0171 }
0172 
0173 class ResolveName : public QObject
0174 {
0175     Q_OBJECT
0176 public:
0177     QString name;
0178     NameRecord::Type type;
0179     bool longlived;
0180     NameResolver dns;
0181     bool null_dump;
0182 
0183     ResolveName()
0184     {
0185         null_dump = false;
0186     }
0187 
0188 public slots:
0189     void start()
0190     {
0191         connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit()));
0192 
0193         connect(&dns, SIGNAL(resultsReady(QList<XMPP::NameRecord>)),
0194             SLOT(dns_resultsReady(QList<XMPP::NameRecord>)));
0195         connect(&dns, SIGNAL(error(XMPP::NameResolver::Error)),
0196             SLOT(dns_error(XMPP::NameResolver::Error)));
0197 
0198         dns.start(name.toLatin1(), type, longlived ? NameResolver::LongLived : NameResolver::Single);
0199     }
0200 
0201 signals:
0202     void quit();
0203 
0204 private slots:
0205     void dns_resultsReady(const QList<XMPP::NameRecord> &list)
0206     {
0207         if(null_dump && list[0].type() == NameRecord::Null)
0208         {
0209             QByteArray buf = list[0].rawData();
0210             fwrite(buf.data(), buf.size(), 1, stdout);
0211         }
0212         else
0213         {
0214             for(int n = 0; n < list.count(); ++n)
0215                 print_record(list[n]);
0216         }
0217         if(!longlived)
0218         {
0219             dns.stop();
0220             emit quit();
0221         }
0222     }
0223 
0224     void dns_error(XMPP::NameResolver::Error e)
0225     {
0226         QString str;
0227         if(e == NameResolver::ErrorNoName)
0228             str = "ErrorNoName";
0229         else if(e == NameResolver::ErrorTimeout)
0230             str = "ErrorTimeout";
0231         else if(e == NameResolver::ErrorNoLocal)
0232             str = "ErrorNoLocal";
0233         else if(e == NameResolver::ErrorNoLongLived)
0234             str = "ErrorNoLongLived";
0235         else // ErrorGeneric, or anything else
0236             str = "ErrorGeneric";
0237 
0238         printf("Error: %s\n", qPrintable(str));
0239         emit quit();
0240     }
0241 };
0242 
0243 class BrowseServices : public QObject
0244 {
0245     Q_OBJECT
0246 public:
0247     QString type, domain;
0248     ServiceBrowser browser;
0249 
0250 public slots:
0251     void start()
0252     {
0253         connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit()));
0254 
0255         connect(&browser, SIGNAL(instanceAvailable(XMPP::ServiceInstance)),
0256             SLOT(browser_instanceAvailable(XMPP::ServiceInstance)));
0257         connect(&browser, SIGNAL(instanceUnavailable(XMPP::ServiceInstance)),
0258             SLOT(browser_instanceUnavailable(XMPP::ServiceInstance)));
0259         connect(&browser, SIGNAL(error()), SLOT(browser_error()));
0260 
0261         browser.start(type, domain);
0262     }
0263 
0264 signals:
0265     void quit();
0266 
0267 private slots:
0268     void browser_instanceAvailable(const XMPP::ServiceInstance &i)
0269     {
0270         printf("HERE: [%s] (%d attributes)\n", qPrintable(i.instance()), i.attributes().count());
0271         QMap<QString,QByteArray> attribs = i.attributes();
0272         QMapIterator<QString,QByteArray> it(attribs);
0273         while(it.hasNext())
0274         {
0275             it.next();
0276             printf("  [%s] = [%s]\n", qPrintable(it.key()), qPrintable(dataToString(it.value())));
0277         }
0278     }
0279 
0280     void browser_instanceUnavailable(const XMPP::ServiceInstance &i)
0281     {
0282         printf("GONE: [%s]\n", qPrintable(i.instance()));
0283     }
0284 
0285     void browser_error()
0286     {
0287     }
0288 };
0289 
0290 class ResolveService : public QObject
0291 {
0292     Q_OBJECT
0293 public:
0294     int mode;
0295     QString instance;
0296     QString type;
0297     QString domain;
0298     int port;
0299 
0300     ServiceResolver dns;
0301 
0302 public slots:
0303     void start()
0304     {
0305         connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit()));
0306 
0307         connect(&dns, SIGNAL(resultsReady(QHostAddress,int)),
0308             SLOT(dns_resultsReady(QHostAddress,int)));
0309         connect(&dns, SIGNAL(finished()), SLOT(dns_finished()));
0310         connect(&dns, SIGNAL(error()), SLOT(dns_error()));
0311 
0312         if(mode == 0)
0313             dns.startFromInstance(instance.toLatin1() + '.' + type.toLatin1() + ".local.");
0314         else if(mode == 1)
0315             dns.startFromDomain(domain, type);
0316         else // 2
0317             dns.startFromPlain(domain, port);
0318     }
0319 
0320 signals:
0321     void quit();
0322 
0323 private slots:
0324     void dns_resultsReady(const QHostAddress &addr, int port)
0325     {
0326         printf("[%s] port=%d\n", qPrintable(addr.toString()), port);
0327         dns.tryNext();
0328     }
0329 
0330     void dns_finished()
0331     {
0332         emit quit();
0333     }
0334 
0335     void dns_error()
0336     {
0337     }
0338 };
0339 
0340 class PublishService : public QObject
0341 {
0342     Q_OBJECT
0343 public:
0344     QString instance;
0345     QString type;
0346     int port;
0347     QMap<QString,QByteArray> attribs;
0348     QByteArray extra_null;
0349 
0350     ServiceLocalPublisher pub;
0351 
0352 public slots:
0353     void start()
0354     {
0355         //NetInterfaceManager::instance();
0356 
0357         connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit()));
0358 
0359         connect(&pub, SIGNAL(published()), SLOT(pub_published()));
0360         connect(&pub, SIGNAL(error(XMPP::ServiceLocalPublisher::Error)),
0361             SLOT(pub_error(XMPP::ServiceLocalPublisher::Error)));
0362 
0363         pub.publish(instance, type, port, attribs);
0364     }
0365 
0366 signals:
0367     void quit();
0368 
0369 private slots:
0370     void pub_published()
0371     {
0372         printf("Published\n");
0373         if(!extra_null.isEmpty())
0374         {
0375             NameRecord rec;
0376             rec.setNull(extra_null);
0377             pub.addRecord(rec);
0378         }
0379     }
0380 
0381     void pub_error(XMPP::ServiceLocalPublisher::Error e)
0382     {
0383         printf("Error: [%d]\n", e);
0384         emit quit();
0385     }
0386 };
0387 
0388 
0389 
0390 void usage()
0391 {
0392     printf("nettool: simple testing utility\n");
0393     printf("usage: nettool [command]\n");
0394     printf("\n");
0395     printf(" netmon                                          monitor network interfaces\n");
0396     printf(" rname (-r) [domain] (record type)               look up record (default = a)\n");
0397     printf(" rnamel [domain] [record type]                   look up record (long-lived)\n");
0398     printf(" browse [service type]                           browse for local services\n");
0399     printf(" rservi [instance] [service type]                look up browsed instance\n");
0400     printf(" rservd [domain] [service type]                  look up normal SRV\n");
0401     printf(" rservp [domain] [port]                          look up non-SRV\n");
0402     printf(" pserv  [inst] [type] [port] (attr) (-a [rec])   publish service instance\n");
0403     printf("\n");
0404     printf("record types: a aaaa ptr srv mx txt hinfo null\n");
0405     printf("service types: _service._proto format (e.g. \"_xmpp-client._tcp\")\n");
0406     printf("attributes: var0[=val0],...,varn[=valn]\n");
0407     printf("rname -r: for null type, dump raw record data to stdout\n");
0408     printf("pub -a: add extra record.  format: null:filename.dat\n");
0409     printf("\n");
0410 }
0411 
0412 int main(int argc, char **argv)
0413 {
0414     QCoreApplication app(argc, argv);
0415     if(argc < 2)
0416     {
0417         usage();
0418         return 1;
0419     }
0420 
0421     QStringList args;
0422     for(int n = 1; n < argc; ++n)
0423         args += argv[n];
0424 
0425     if(args[0] == "netmon")
0426     {
0427         NetMonitor a;
0428         QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
0429         QTimer::singleShot(0, &a, SLOT(start()));
0430         app.exec();
0431     }
0432     else if(args[0] == "rname" || args[0] == "rnamel")
0433     {
0434         bool null_dump = false;
0435         for(int n = 1; n < args.count(); ++n)
0436         {
0437             if(args[n] == "-r")
0438             {
0439                 null_dump = true;
0440                 args.removeAt(n);
0441                 --n;
0442             }
0443         }
0444 
0445         if(args.count() < 2)
0446         {
0447             usage();
0448             return 1;
0449         }
0450         if(args[0] == "rnamel" && args.count() < 3)
0451         {
0452             usage();
0453             return 1;
0454         }
0455         int x = NameRecord::A;
0456         if(args.count() >= 3)
0457         {
0458             x = str2rtype(args[2]);
0459             if(x == -1)
0460             {
0461                 usage();
0462                 return 1;
0463             }
0464         }
0465         ResolveName a;
0466         a.name = args[1];
0467         a.type = (NameRecord::Type)x;
0468         a.longlived = (args[0] == "rnamel") ? true : false;
0469         if(args[0] == "rname" && null_dump)
0470             a.null_dump = true;
0471         QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
0472         QTimer::singleShot(0, &a, SLOT(start()));
0473         app.exec();
0474     }
0475     else if(args[0] == "browse")
0476     {
0477         if(args.count() < 2)
0478         {
0479             usage();
0480             return 1;
0481         }
0482 
0483         BrowseServices a;
0484         a.type = args[1];
0485         QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
0486         QTimer::singleShot(0, &a, SLOT(start()));
0487         app.exec();
0488     }
0489     else if(args[0] == "rservi" || args[0] == "rservd" || args[0] == "rservp")
0490     {
0491         // they all take 2 params
0492         if(args.count() < 3)
0493         {
0494             usage();
0495             return 1;
0496         }
0497 
0498         ResolveService a;
0499         if(args[0] == "rservi")
0500         {
0501             a.mode = 0;
0502             a.instance = args[1];
0503             a.type = args[2];
0504         }
0505         else if(args[0] == "rservd")
0506         {
0507             a.mode = 1;
0508             a.domain = args[1];
0509             a.type = args[2];
0510         }
0511         else // rservp
0512         {
0513             a.mode = 2;
0514             a.domain = args[1];
0515             a.port = args[2].toInt();
0516         }
0517         QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
0518         QTimer::singleShot(0, &a, SLOT(start()));
0519         app.exec();
0520     }
0521     else if(args[0] == "pserv")
0522     {
0523         QStringList addrecs;
0524         for(int n = 1; n < args.count(); ++n)
0525         {
0526             if(args[n] == "-a")
0527             {
0528                 if(n + 1 < args.count())
0529                 {
0530                     addrecs += args[n + 1];
0531                     args.removeAt(n);
0532                     args.removeAt(n);
0533                     --n;
0534                 }
0535                 else
0536                 {
0537                     usage();
0538                     return 1;
0539                 }
0540             }
0541         }
0542 
0543         QByteArray extra_null;
0544         for(int n = 0; n < addrecs.count(); ++n)
0545         {
0546             const QString &str = addrecs[n];
0547             int x = str.indexOf(':');
0548             if(x == -1 || str.mid(0, x) != "null")
0549             {
0550                 usage();
0551                 return 1;
0552             }
0553 
0554             QString null_file = str.mid(x + 1);
0555 
0556             if(!null_file.isEmpty())
0557             {
0558                 QFile f(null_file);
0559                 if(!f.open(QFile::ReadOnly))
0560                 {
0561                     printf("can't read file\n");
0562                     return 1;
0563                 }
0564                 extra_null = f.readAll();
0565             }
0566         }
0567 
0568         if(args.count() < 4)
0569         {
0570             usage();
0571             return 1;
0572         }
0573 
0574         QMap<QString,QByteArray> attribs;
0575         if(args.count() > 4)
0576         {
0577             QStringList parts = args[4].split(',');
0578             for(int n = 0; n < parts.count(); ++n)
0579             {
0580                 const QString &str = parts[n];
0581                 int x = str.indexOf('=');
0582                 if(x != -1)
0583                     attribs.insert(str.mid(0, x), str.mid(x + 1).toUtf8());
0584                 else
0585                     attribs.insert(str, QByteArray());
0586             }
0587         }
0588 
0589         PublishService a;
0590         a.instance = args[1];
0591         a.type = args[2];
0592         a.port = args[3].toInt();
0593         a.attribs = attribs;
0594         a.extra_null = extra_null;
0595         QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
0596         QTimer::singleShot(0, &a, SLOT(start()));
0597         app.exec();
0598     }
0599     else
0600     {
0601         usage();
0602         return 1;
0603     }
0604     return 0;
0605 }