/* * Copyright (C) 2006 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace XMPP; static QString prompt(const QString &s) { printf("* %s ", qPrintable(s)); fflush(stdout); char line[256]; fgets(line, 255, stdin); QString result = line; if(result[result.length()-1] == '\n') result.truncate(result.length()-1); return result; } class NetMonitor : public QObject { Q_OBJECT public: NetInterfaceManager *man; QList ifaces; NetAvailability *netavail; ~NetMonitor() { delete netavail; qDeleteAll(ifaces); delete man; } signals: void quit(); public slots: void start() { connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit())); man = new NetInterfaceManager; connect(man, SIGNAL(interfaceAvailable(const QString &)), SLOT(here(const QString &))); QStringList list = man->interfaces(); for(int n = 0; n < list.count(); ++n) here(list[n]); netavail = new NetAvailability; connect(netavail, SIGNAL(changed(bool)), SLOT(avail(bool))); avail(netavail->isAvailable()); } void here(const QString &id) { NetInterface *iface = new NetInterface(id, man); connect(iface, SIGNAL(unavailable()), SLOT(gone())); printf("HERE: %s name=[%s]\n", qPrintable(iface->id()), qPrintable(iface->name())); QList addrs = iface->addresses(); for(int n = 0; n < addrs.count(); ++n) printf(" address: %s\n", qPrintable(addrs[n].toString())); if(!iface->gateway().isNull()) printf(" gateway: %s\n", qPrintable(iface->gateway().toString())); ifaces += iface; } void gone() { NetInterface *iface = (NetInterface *)sender(); printf("GONE: %s\n", qPrintable(iface->id())); ifaces.removeAll(iface); delete iface; } void avail(bool available) { if(available) printf("** Network available\n"); else printf("** Network unavailable\n"); } }; static QString dataToString(const QByteArray &buf) { QString out; for(int n = 0; n < buf.size(); ++n) { unsigned char c = (unsigned char)buf[n]; if(c == '\\') out += "\\\\"; else if(c >= 0x20 || c < 0x7f) out += c; else out += QString("\\x%1").arg((uint)c, 2, 16); } return out; } static void print_record(const NameRecord &r) { switch(r.type()) { case NameRecord::A: printf("A: [%s] (ttl=%d)\n", qPrintable(r.address().toString()), r.ttl()); break; case NameRecord::Aaaa: printf("AAAA: [%s] (ttl=%d)\n", qPrintable(r.address().toString()), r.ttl()); break; case NameRecord::Mx: printf("MX: [%s] priority=%d (ttl=%d)\n", r.name().data(), r.priority(), r.ttl()); break; case NameRecord::Srv: printf("SRV: [%s] port=%d priority=%d weight=%d (ttl=%d)\n", r.name().data(), r.port(), r.priority(), r.weight(), r.ttl()); break; case NameRecord::Ptr: printf("PTR: [%s] (ttl=%d)\n", r.name().data(), r.ttl()); break; case NameRecord::Txt: { QList texts = r.texts(); printf("TXT: count=%d (ttl=%d)\n", texts.count(), r.ttl()); for(int n = 0; n < texts.count(); ++n) printf(" len=%d [%s]\n", texts[n].size(), qPrintable(dataToString(texts[n]))); break; } case NameRecord::Hinfo: printf("HINFO: [%s] [%s] (ttl=%d)\n", r.cpu().data(), r.os().data(), r.ttl()); break; case NameRecord::Null: printf("NULL: %d bytes (ttl=%d)\n", r.rawData().size(), r.ttl()); break; default: printf("(Unknown): type=%d (ttl=%d)\n", r.type(), r.ttl()); break; } } static int str2rtype(const QString &in) { QString str = in.toLower(); if(str == "a") return NameRecord::A; else if(str == "aaaa") return NameRecord::Aaaa; else if(str == "ptr") return NameRecord::Ptr; else if(str == "srv") return NameRecord::Srv; else if(str == "mx") return NameRecord::Mx; else if(str == "txt") return NameRecord::Txt; else if(str == "hinfo") return NameRecord::Hinfo; else if(str == "null") return NameRecord::Null; else return -1; } class ResolveName : public QObject { Q_OBJECT public: QString name; NameRecord::Type type; bool longlived; NameResolver dns; bool null_dump; ResolveName() { null_dump = false; } public slots: void start() { connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit())); connect(&dns, SIGNAL(resultsReady(const QList &)), SLOT(dns_resultsReady(const QList &))); connect(&dns, SIGNAL(error(XMPP::NameResolver::Error)), SLOT(dns_error(XMPP::NameResolver::Error))); dns.start(name.toLatin1(), type, longlived ? NameResolver::LongLived : NameResolver::Single); } signals: void quit(); private slots: void dns_resultsReady(const QList &list) { if(null_dump && list[0].type() == NameRecord::Null) { QByteArray buf = list[0].rawData(); if(fwrite(buf.data(), buf.size(), 1, stdout) < 1) { fprintf(stderr, "Error: unable to write raw record to stdout\n"); emit quit(); return; } } else { for(int n = 0; n < list.count(); ++n) print_record(list[n]); } if(!longlived) { dns.stop(); emit quit(); } } void dns_error(XMPP::NameResolver::Error e) { QString str; if(e == NameResolver::ErrorNoName) str = "ErrorNoName"; else if(e == NameResolver::ErrorTimeout) str = "ErrorTimeout"; else if(e == NameResolver::ErrorNoLocal) str = "ErrorNoLocal"; else if(e == NameResolver::ErrorNoLongLived) str = "ErrorNoLongLived"; else // ErrorGeneric, or anything else str = "ErrorGeneric"; printf("Error: %s\n", qPrintable(str)); emit quit(); } }; class ResolveAddr : public QObject { Q_OBJECT public: QString name; AddressResolver dns; public slots: void start() { connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit())); connect(&dns, SIGNAL(resultsReady(const QList &)), SLOT(dns_resultsReady(const QList &))); connect(&dns, SIGNAL(error(XMPP::AddressResolver::Error)), SLOT(dns_error(XMPP::AddressResolver::Error))); dns.start(name.toLatin1()); } signals: void quit(); private slots: void dns_resultsReady(const QList &list) { for(int n = 0; n < list.count(); ++n) printf("%s\n", qPrintable(list[n].toString())); emit quit(); } void dns_error(XMPP::AddressResolver::Error e) { Q_UNUSED(e); QString str; //else // ErrorGeneric, or anything else str = "ErrorGeneric"; printf("Error: %s\n", qPrintable(str)); emit quit(); } }; class BrowseServices : public QObject { Q_OBJECT public: QString type, domain; ServiceBrowser browser; public slots: void start() { connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit())); connect(&browser, SIGNAL(instanceAvailable(const XMPP::ServiceInstance &)), SLOT(browser_instanceAvailable(const XMPP::ServiceInstance &))); connect(&browser, SIGNAL(instanceUnavailable(const XMPP::ServiceInstance &)), SLOT(browser_instanceUnavailable(const XMPP::ServiceInstance &))); connect(&browser, SIGNAL(error()), SLOT(browser_error())); browser.start(type, domain); } signals: void quit(); private slots: void browser_instanceAvailable(const XMPP::ServiceInstance &i) { printf("HERE: [%s] (%d attributes)\n", qPrintable(i.instance()), i.attributes().count()); QMap attribs = i.attributes(); QMapIterator it(attribs); while(it.hasNext()) { it.next(); printf(" [%s] = [%s]\n", qPrintable(it.key()), qPrintable(dataToString(it.value()))); } } void browser_instanceUnavailable(const XMPP::ServiceInstance &i) { printf("GONE: [%s]\n", qPrintable(i.instance())); } void browser_error() { } }; class ResolveService : public QObject { Q_OBJECT public: int mode; QString instance; QString type; QString domain; int port; ServiceResolver dns; public slots: void start() { connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit())); connect(&dns, SIGNAL(resultsReady(const QHostAddress &, int)), SLOT(dns_resultsReady(const QHostAddress &, int))); connect(&dns, SIGNAL(finished()), SLOT(dns_finished())); connect(&dns, SIGNAL(error()), SLOT(dns_error())); if(mode == 0) dns.startFromInstance(instance.toLatin1() + '.' + type.toLatin1() + ".local."); else if(mode == 1) dns.startFromDomain(domain, type); else // 2 dns.startFromPlain(domain, port); } signals: void quit(); private slots: void dns_resultsReady(const QHostAddress &addr, int port) { printf("[%s] port=%d\n", qPrintable(addr.toString()), port); dns.tryNext(); } void dns_finished() { emit quit(); } void dns_error() { printf("Error\n"); emit quit(); } }; class PublishService : public QObject { Q_OBJECT public: QString instance; QString type; int port; QMap attribs; QByteArray extra_null; ServiceLocalPublisher pub; public slots: void start() { //NetInterfaceManager::instance(); connect(ProcessQuit::instance(), SIGNAL(quit()), SIGNAL(quit())); connect(&pub, SIGNAL(published()), SLOT(pub_published())); connect(&pub, SIGNAL(error(XMPP::ServiceLocalPublisher::Error)), SLOT(pub_error(XMPP::ServiceLocalPublisher::Error))); pub.publish(instance, type, port, attribs); } signals: void quit(); private slots: void pub_published() { printf("Published\n"); if(!extra_null.isEmpty()) { NameRecord rec; rec.setNull(extra_null); pub.addRecord(rec); } } void pub_error(XMPP::ServiceLocalPublisher::Error e) { printf("Error: [%d]\n", e); emit quit(); } }; class StunBind : public QObject { Q_OBJECT public: bool debug; QHostAddress addr; int port; int localPort; QUdpSocket *sock; StunTransactionPool *pool; StunBinding *binding; ~StunBind() { // make sure transactions are always deleted before the pool delete binding; } public slots: void start() { sock = new QUdpSocket(this); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); pool = new StunTransactionPool(StunTransaction::Udp, this); if(debug) pool->setDebugLevel(StunTransactionPool::DL_Packet); else pool->setDebugLevel(StunTransactionPool::DL_Info); connect(pool, SIGNAL(outgoingMessage(const QByteArray &, const QHostAddress &, int)), SLOT(pool_outgoingMessage(const QByteArray &, const QHostAddress &, int))); connect(pool, SIGNAL(debugLine(const QString &)), SLOT(pool_debugLine(const QString &))); if(!sock->bind(localPort != -1 ? localPort : 0)) { printf("Error binding to local port.\n"); emit quit(); return; } printf("Bound to local port %d.\n", sock->localPort()); binding = new StunBinding(pool); connect(binding, SIGNAL(success()), SLOT(binding_success())); connect(binding, SIGNAL(error(XMPP::StunBinding::Error)), SLOT(binding_error(XMPP::StunBinding::Error))); binding->start(); } signals: void quit(); private slots: void sock_readyRead() { while(sock->hasPendingDatagrams()) { QByteArray buf(sock->pendingDatagramSize(), 0); QHostAddress from; quint16 fromPort; sock->readDatagram(buf.data(), buf.size(), &from, &fromPort); if(from == addr && fromPort == port) { processDatagram(buf); } else { printf("Response from unknown sender %s:%d, dropping.\n", qPrintable(from.toString()), fromPort); } } } void pool_outgoingMessage(const QByteArray &packet, const QHostAddress &toAddress, int toPort) { // in this example, we aren't using IP-associated transactions Q_UNUSED(toAddress); Q_UNUSED(toPort); sock->writeDatagram(packet, addr, port); } void pool_debugLine(const QString &line) { printf("%s\n", qPrintable(line)); } void binding_success() { QHostAddress saddr = binding->reflexiveAddress(); quint16 sport = binding->reflexivePort(); printf("Server says we are %s;%d\n", qPrintable(saddr.toString()), sport); emit quit(); } void binding_error(XMPP::StunBinding::Error e) { Q_UNUSED(e); printf("Error: %s\n", qPrintable(binding->errorString())); emit quit(); } private: void processDatagram(const QByteArray &buf) { StunMessage message = StunMessage::fromBinary(buf); if(message.isNull()) { printf("Warning: server responded with what doesn't seem to be a STUN packet, skipping.\n"); return; } if(!pool->writeIncomingMessage(message)) printf("Warning: received unexpected message, skipping.\n"); } }; class TurnClientTest : public QObject { Q_OBJECT public: int mode; bool debug; QHostAddress relayAddr; int relayPort; QString relayUser, relayPass, relayRealm; QHostAddress peerAddr; int peerPort; QUdpSocket *udp; StunTransactionPool *pool; QList writeItems; // true = turn-originated, false = external TurnClient *turn; TurnClientTest() : udp(0), pool(0), turn(0) { } ~TurnClientTest() { // make sure transactions are always deleted before the pool delete turn; } public slots: void start() { connect(ProcessQuit::instance(), SIGNAL(quit()), SLOT(do_quit())); turn = new TurnClient(this); if(debug) turn->setDebugLevel(TurnClient::DL_Packet); else turn->setDebugLevel(TurnClient::DL_Info); connect(turn, SIGNAL(connected()), SLOT(turn_connected())); connect(turn, SIGNAL(tlsHandshaken()), SLOT(turn_tlsHandshaken())); connect(turn, SIGNAL(closed()), SLOT(turn_closed())); connect(turn, SIGNAL(needAuthParams()), SLOT(turn_needAuthParams())); connect(turn, SIGNAL(retrying()), SLOT(turn_retrying())); connect(turn, SIGNAL(activated()), SLOT(turn_activated())); connect(turn, SIGNAL(readyRead()), SLOT(turn_readyRead())); connect(turn, SIGNAL(packetsWritten(int, const QHostAddress &, int)), SLOT(turn_packetsWritten(int, const QHostAddress &, int))); connect(turn, SIGNAL(error(XMPP::TurnClient::Error)), SLOT(turn_error(XMPP::TurnClient::Error))); connect(turn, SIGNAL(outgoingDatagram(const QByteArray &)), SLOT(turn_outgoingDatagram(const QByteArray &))); connect(turn, SIGNAL(debugLine(const QString &)), SLOT(turn_debugLine(const QString &))); turn->setClientSoftwareNameAndVersion("nettool (Iris)"); if(mode == 0) { udp = new QUdpSocket(this); connect(udp, SIGNAL(readyRead()), SLOT(udp_readyRead())); // QUdpSocket bytesWritten is not DOR-DS safe, so we queue connect(udp, SIGNAL(bytesWritten(qint64)), SLOT(udp_bytesWritten(qint64)), Qt::QueuedConnection); pool = new StunTransactionPool(StunTransaction::Udp, this); if(debug) pool->setDebugLevel(StunTransactionPool::DL_Packet); else pool->setDebugLevel(StunTransactionPool::DL_Info); connect(pool, SIGNAL(outgoingMessage(const QByteArray &, const QHostAddress &, int)), SLOT(pool_outgoingMessage(const QByteArray &, const QHostAddress &, int))); connect(pool, SIGNAL(needAuthParams()), SLOT(pool_needAuthParams())); connect(pool, SIGNAL(debugLine(const QString &)), SLOT(pool_debugLine(const QString &))); pool->setLongTermAuthEnabled(true); if(!relayUser.isEmpty()) { pool->setUsername(relayUser); pool->setPassword(relayPass.toUtf8()); if(!relayRealm.isEmpty()) pool->setRealm(relayRealm); } if(!udp->bind()) { printf("Error binding to local port.\n"); emit quit(); return; } turn->connectToHost(pool); } else { if(!relayUser.isEmpty()) { turn->setUsername(relayUser); turn->setPassword(relayPass.toUtf8()); if(!relayRealm.isEmpty()) turn->setRealm(relayRealm); } printf("TCP connecting...\n"); turn->connectToHost(relayAddr, relayPort, mode == 2 ? TurnClient::TlsMode : TurnClient::PlainMode); } } signals: void quit(); private: void processDatagram(const QByteArray &buf) { QByteArray data; QHostAddress fromAddr; int fromPort; bool notStun; if(!pool->writeIncomingMessage(buf, ¬Stun)) { data = turn->processIncomingDatagram(buf, notStun, &fromAddr, &fromPort); if(!data.isNull()) processDataPacket(data, fromAddr, fromPort); else printf("Warning: server responded with what doesn't seem to be a STUN or data packet, skipping.\n"); } } void processDataPacket(const QByteArray &buf, const QHostAddress &addr, int port) { printf("Received %d bytes from %s:%d: [%s]\n", buf.size(), qPrintable(addr.toString()), port, buf.data()); turn->close(); } private slots: void do_quit() { ProcessQuit::cleanup(); turn->close(); } void udp_readyRead() { while(udp->hasPendingDatagrams()) { QByteArray buf(udp->pendingDatagramSize(), 0); QHostAddress from; quint16 fromPort; udp->readDatagram(buf.data(), buf.size(), &from, &fromPort); if(from == relayAddr && fromPort == relayPort) { processDatagram(buf); } else { printf("Response from unknown sender %s:%d, dropping.\n", qPrintable(from.toString()), fromPort); } } } void udp_bytesWritten(qint64 bytes) { Q_UNUSED(bytes); bool wasTurnOriginated = writeItems.takeFirst(); if(wasTurnOriginated) turn->outgoingDatagramsWritten(1); } void pool_outgoingMessage(const QByteArray &packet, const QHostAddress &toAddress, int toPort) { // in this example, we aren't using IP-associated transactions Q_UNUSED(toAddress); Q_UNUSED(toPort); writeItems += false; udp->writeDatagram(packet, relayAddr, relayPort); } void pool_needAuthParams() { relayUser = prompt("Username:"); relayPass = prompt("Password:"); pool->setUsername(relayUser); pool->setPassword(relayPass.toUtf8()); QString str = prompt(QString("Realm: [%1]").arg(pool->realm())); if(!str.isEmpty()) { relayRealm = str; pool->setRealm(relayRealm); } else relayRealm = pool->realm(); pool->continueAfterParams(); } void pool_debugLine(const QString &line) { turn_debugLine(line); } void turn_connected() { printf("TCP connected\n"); } void turn_tlsHandshaken() { printf("TLS handshake completed\n"); } void turn_closed() { printf("Done\n"); emit quit(); } void turn_needAuthParams() { relayUser = prompt("Username:"); relayPass = prompt("Password:"); turn->setUsername(relayUser); turn->setPassword(relayPass.toUtf8()); QString str = prompt(QString("Realm: [%1]").arg(turn->realm())); if(!str.isEmpty()) { relayRealm = str; turn->setRealm(relayRealm); } else relayRealm = turn->realm(); turn->continueAfterParams(); } void turn_retrying() { printf("Mismatch error, retrying...\n"); } void turn_activated() { StunAllocate *allocate = turn->stunAllocate(); QHostAddress saddr = allocate->reflexiveAddress(); quint16 sport = allocate->reflexivePort(); printf("Server says we are %s;%d\n", qPrintable(saddr.toString()), sport); saddr = allocate->relayedAddress(); sport = allocate->relayedPort(); printf("Server relays via %s;%d\n", qPrintable(saddr.toString()), sport); // optional: flag this destination to use a channelbind turn->addChannelPeer(peerAddr, peerPort); QByteArray buf = "Hello, world!"; printf("Relaying test packet of %d bytes [%s] to %s;%d...\n", buf.size(), buf.data(), qPrintable(peerAddr.toString()), peerPort); turn->write(buf, peerAddr, peerPort); } void turn_readyRead() { QHostAddress addr; int port; QByteArray buf = turn->read(&addr, &port); processDataPacket(buf, addr, port); } void turn_packetsWritten(int count, const QHostAddress &addr, int port) { Q_UNUSED(addr); Q_UNUSED(port); printf("%d packet(s) written\n", count); } void turn_error(XMPP::TurnClient::Error e) { Q_UNUSED(e); printf("Error: %s\n", qPrintable(turn->errorString())); emit quit(); } void turn_outgoingDatagram(const QByteArray &buf) { writeItems += true; udp->writeDatagram(buf, relayAddr, relayPort); } void turn_debugLine(const QString &line) { printf("%s\n", qPrintable(line)); } }; void usage() { printf("nettool: simple testing utility\n"); printf("usage: nettool (options) [command]\n"); printf(" options: --debug, --user=x, --pass=x, --realm=x\n"); printf("\n"); printf(" netmon monitor network interfaces\n"); printf(" rname (-r) [domain] (record type) look up record (default = a)\n"); printf(" rnamel [domain] [record type] look up record (long-lived)\n"); printf(" raddr [domain] look up AAAA/A\n"); printf(" browse [service type] browse for local services\n"); printf(" rservi [instance] [service type] look up browsed instance\n"); printf(" rservd [domain] [service type] look up normal SRV\n"); printf(" rservp [domain] [port] look up non-SRV\n"); printf(" pserv [inst] [type] [port] (attr) (-a [rec]) publish service instance\n"); printf(" stun [addr](;port) (local port) STUN binding\n"); printf(" turn [mode] [relayaddr](;port) [peeraddr](;port) TURN UDP echo test\n"); printf("\n"); printf("record types: a aaaa ptr srv mx txt hinfo null\n"); printf("service types: _service._proto format (e.g. \"_xmpp-client._tcp\")\n"); printf("attributes: var0[=val0],...,varn[=valn]\n"); printf("rname -r: for null type, dump raw record data to stdout\n"); printf("pserv -a: add extra record. format: null:filename.dat\n"); printf("turn modes: udp tcp tcp-tls\n"); printf("\n"); } int main(int argc, char **argv) { QCA::Initializer qcaInit; QCoreApplication qapp(argc, argv); QStringList args = qapp.arguments(); args.removeFirst(); QString user, pass, realm; bool debug = false; for(int n = 0; n < args.count(); ++n) { QString s = args[n]; if(!s.startsWith("--")) continue; QString var; QString val; int x = s.indexOf('='); if(x != -1) { var = s.mid(2, x - 2); val = s.mid(x + 1); } else { var = s.mid(2); } bool known = true; if(var == "debug") debug = true; else if(var == "user") user = val; else if(var == "pass") pass = val; else if(var == "realm") realm = val; else known = false; if(!known) { fprintf(stderr, "Unknown option '%s'.\n", qPrintable(var)); return 1; } args.removeAt(n); --n; // adjust position } if(args.isEmpty()) { usage(); return 1; } if(args[0] == "netmon") { NetMonitor a; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "rname" || args[0] == "rnamel") { bool null_dump = false; for(int n = 1; n < args.count(); ++n) { if(args[n] == "-r") { null_dump = true; args.removeAt(n); --n; } } if(args.count() < 2) { usage(); return 1; } if(args[0] == "rnamel" && args.count() < 3) { usage(); return 1; } int x = NameRecord::A; if(args.count() >= 3) { x = str2rtype(args[2]); if(x == -1) { usage(); return 1; } } ResolveName a; a.name = args[1]; a.type = (NameRecord::Type)x; a.longlived = (args[0] == "rnamel") ? true : false; if(args[0] == "rname" && null_dump) a.null_dump = true; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "raddr") { if(args.count() < 2) { usage(); return 1; } ResolveAddr a; a.name = args[1]; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "browse") { if(args.count() < 2) { usage(); return 1; } BrowseServices a; a.type = args[1]; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "rservi" || args[0] == "rservd" || args[0] == "rservp") { // they all take 2 params if(args.count() < 3) { usage(); return 1; } ResolveService a; if(args[0] == "rservi") { a.mode = 0; a.instance = args[1]; a.type = args[2]; } else if(args[0] == "rservd") { a.mode = 1; a.domain = args[1]; a.type = args[2]; } else // rservp { a.mode = 2; a.domain = args[1]; a.port = args[2].toInt(); } QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "pserv") { QStringList addrecs; for(int n = 1; n < args.count(); ++n) { if(args[n] == "-a") { if(n + 1 < args.count()) { addrecs += args[n + 1]; args.removeAt(n); args.removeAt(n); --n; } else { usage(); return 1; } } } QByteArray extra_null; for(int n = 0; n < addrecs.count(); ++n) { const QString &str = addrecs[n]; int x = str.indexOf(':'); if(x == -1 || str.mid(0, x) != "null") { usage(); return 1; } QString null_file = str.mid(x + 1); if(!null_file.isEmpty()) { QFile f(null_file); if(!f.open(QFile::ReadOnly)) { printf("can't read file\n"); return 1; } extra_null = f.readAll(); } } if(args.count() < 4) { usage(); return 1; } QMap attribs; if(args.count() > 4) { QStringList parts = args[4].split(','); for(int n = 0; n < parts.count(); ++n) { const QString &str = parts[n]; int x = str.indexOf('='); if(x != -1) attribs.insert(str.mid(0, x), str.mid(x + 1).toUtf8()); else attribs.insert(str, QByteArray()); } } PublishService a; a.instance = args[1]; a.type = args[2]; a.port = args[3].toInt(); a.attribs = attribs; a.extra_null = extra_null; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "stun") { if(args.count() < 2) { usage(); return 1; } QString addrstr, portstr; int x = args[1].indexOf(';'); if(x != -1) { addrstr = args[1].mid(0, x); portstr = args[1].mid(x + 1); } else addrstr = args[1]; QHostAddress addr = QHostAddress(addrstr); if(addr.isNull()) { printf("Error: addr must be an IP address\n"); return 1; } int port = 3478; if(!portstr.isEmpty()) port = portstr.toInt(); int localPort = -1; if(args.count() >= 3) localPort = args[2].toInt(); if(!QCA::isSupported("hmac(sha1)")) { printf("Error: Need hmac(sha1) support to use STUN.\n"); return 1; } StunBind a; a.debug = debug; a.localPort = localPort; a.addr = addr; a.port = port; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else if(args[0] == "turn") { if(args.count() < 4) { usage(); return 1; } int mode; if(args[1] == "udp") mode = 0; else if(args[1] == "tcp") mode = 1; else if(args[1] == "tcp-tls") mode = 2; else { usage(); return 1; } QString addrstr, portstr; int x = args[2].indexOf(';'); if(x != -1) { addrstr = args[2].mid(0, x); portstr = args[2].mid(x + 1); } else addrstr = args[2]; QHostAddress raddr = QHostAddress(addrstr); if(raddr.isNull()) { printf("Error: relayaddr must be an IP address\n"); return 1; } int rport = 3478; if(!portstr.isEmpty()) rport = portstr.toInt(); portstr.clear(); x = args[3].indexOf(';'); if(x != -1) { addrstr = args[3].mid(0, x); portstr = args[3].mid(x + 1); } else addrstr = args[3]; QHostAddress paddr = QHostAddress(addrstr); if(raddr.isNull()) { printf("Error: peeraddr must be an IP address\n"); return 1; } int pport = 4588; if(!portstr.isEmpty()) pport = portstr.toInt(); if(!QCA::isSupported("hmac(sha1)")) { printf("Error: Need hmac(sha1) support to use TURN.\n"); return 1; } if(mode == 2 && !QCA::isSupported("tls")) { printf("Error: Need tls support to use tcp-tls mode.\n"); return 1; } TurnClientTest a; a.mode = mode; a.debug = debug; a.relayAddr = raddr; a.relayPort = rport; a.relayUser = user; a.relayPass = pass; a.relayRealm = realm; a.peerAddr = paddr; a.peerPort = pport; QObject::connect(&a, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &a, SLOT(start())); qapp.exec(); } else { usage(); return 1; } return 0; } #include "main.moc"