Logo Search packages:      
Sourcecode: kadu version File versions

main.cpp

/*
 * 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 <stdio.h>
#include <iris/processquit.h>
#include <iris/netinterface.h>
#include <iris/netavailability.h>
#include <iris/netnames.h>
#include <iris/addressresolver.h>
#include <iris/stunmessage.h>
#include <iris/stuntransaction.h>
#include <iris/stunbinding.h>
#include <iris/stunallocate.h>
#include <iris/turnclient.h>
#include <QtCrypto>

using namespace XMPP;

static QString prompt(const QString &s)
{
      printf("* %s ", qPrintable(s));
      fflush(stdout);
      char line[256];
      char *ret = fgets(line, 255, stdin);
      QString result;
      if(ret)
            result = QString::fromLocal8Bit(line);
      if(result[result.length() - 1] == '\n')
            result.truncate(result.length() - 1);
      return result;
}

00050 class NetMonitor : public QObject
{
      Q_OBJECT
public:
      NetInterfaceManager *man;
      QList<NetInterface*> 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<QHostAddress> 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<QByteArray> 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;
}

00193 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<XMPP::NameRecord> &)),
                  SLOT(dns_resultsReady(const QList<XMPP::NameRecord> &)));
            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<XMPP::NameRecord> &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();
      }
};

00268 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<QHostAddress> &)),
                  SLOT(dns_resultsReady(const QList<QHostAddress> &)));
            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<QHostAddress> &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();
      }
};

00313 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<QString,QByteArray> attribs = i.attributes();
            QMapIterator<QString,QByteArray> 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()
      {
      }
};

00360 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();
      }
};

00412 class PublishService : public QObject
{
      Q_OBJECT
public:
      QString instance;
      QString type;
      int port;
      QMap<QString,QByteArray> 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();
      }
};

00460 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");
      }
};

00575 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<bool> 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, &notStun))
            {
                  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<QString,QByteArray> 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"

Generated by  Doxygen 1.6.0   Back to index