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 }