aboutsummaryrefslogtreecommitdiff
path: root/source/QXmppStun.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2010-08-11 07:31:23 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2010-08-11 07:31:23 +0000
commit40c39853816cfab113d79682c34bc76a2c79c357 (patch)
treee4d6a184cf565cb87477339ce738299ff9787bc3 /source/QXmppStun.cpp
parent551c284e35280b7b91a939fe7352e496ffea402a (diff)
downloadqxmpp-40c39853816cfab113d79682c34bc76a2c79c357.tar.gz
rename "source" directory to "src"
Diffstat (limited to 'source/QXmppStun.cpp')
-rw-r--r--source/QXmppStun.cpp979
1 files changed, 0 insertions, 979 deletions
diff --git a/source/QXmppStun.cpp b/source/QXmppStun.cpp
deleted file mode 100644
index 5bd3fd54..00000000
--- a/source/QXmppStun.cpp
+++ /dev/null
@@ -1,979 +0,0 @@
-/*
- * Copyright (C) 2008-2010 The QXmpp developers
- *
- * Author:
- * Jeremy Lainé
- *
- * Source:
- * http://code.google.com/p/qxmpp
- *
- * This file is a part of QXmpp library.
- *
- * 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.
- *
- */
-
-#define QXMPP_DEBUG_STUN
-
-#include <QCryptographicHash>
-#include <QDebug>
-#include <QNetworkInterface>
-#include <QUdpSocket>
-#include <QTimer>
-
-#include "QXmppStun.h"
-#include "QXmppUtils.h"
-
-#define ID_SIZE 12
-
-static const quint32 STUN_MAGIC = 0x2112A442;
-static const quint16 STUN_HEADER = 20;
-static const quint8 STUN_IPV4 = 0x01;
-static const quint8 STUN_IPV6 = 0x02;
-
-enum MessageType {
- BindingRequest = 0x0001,
- BindingIndication = 0x0011,
- BindingResponse = 0x0101,
- BindingError = 0x0111,
- SharedSecretRequest = 0x0002,
- SharedSecretResponse = 0x0102,
- SharedSecretError = 0x0112,
-};
-
-enum AttributeType {
- MappedAddress = 0x0001,
- Username = 0x0006,
- MessageIntegrity = 0x0008,
- ErrorCode = 0x0009,
- XorMappedAddress = 0x0020,
- Priority = 0x0024,
- UseCandidate = 0x0025,
- Software = 0x8022,
- Fingerprint = 0x8028,
- IceControlled = 0x8029,
- IceControlling = 0x802a,
- OtherAddress = 0x802c,
-};
-
-static bool decodeAddress(QDataStream &stream, quint16 a_length, QHostAddress &address, quint16 &port)
-{
- if (a_length < 4)
- return false;
- quint8 reserved, protocol;
- stream >> reserved;
- stream >> protocol;
- stream >> port;
- if (protocol == STUN_IPV4)
- {
- if (a_length != 8)
- return false;
- quint32 addr;
- stream >> addr;
- address = QHostAddress(addr);
- } else if (protocol == STUN_IPV6) {
- if (a_length != 20)
- return false;
- Q_IPV6ADDR addr;
- stream.readRawData((char*)&addr, sizeof(addr));
- address = QHostAddress(addr);
- } else {
- return false;
- }
- return true;
-}
-
-static void encodeAddress(QDataStream &stream, const QHostAddress &address, quint16 port)
-{
- stream << quint16(8);
- stream << quint8(0);
- if (address.protocol() == QAbstractSocket::IPv4Protocol)
- {
- stream << quint8(STUN_IPV4);
- stream << port;
- stream << address.toIPv4Address();
- } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
- stream << quint8(STUN_IPV6);
- stream << port;
- Q_IPV6ADDR addr = address.toIPv6Address();
- stream.writeRawData((char*)&addr, sizeof(addr));
- }
-}
-
-static void encodeString(QDataStream &stream, const QString &string)
-{
- const QByteArray utf8string = string.toUtf8();
- stream << quint16(utf8string.size());
- stream.writeRawData(utf8string.data(), utf8string.size());
- if (utf8string.size() % 4)
- {
- const QByteArray padding(4 - (utf8string.size() % 4), 0);
- stream.writeRawData(padding.data(), padding.size());
- }
-}
-
-static QByteArray randomByteArray(int length)
-{
- QByteArray result(length, 0);
- for (int i = 0; i < length; ++i)
- result[i] = quint8(qrand() % 255);
- return result;
-}
-
-/// Constructs a new QXmppStunMessage.
-
-QXmppStunMessage::QXmppStunMessage()
- : errorCode(0),
- priority(0),
- mappedPort(0),
- otherPort(0),
- xorMappedPort(0),
- useCandidate(false)
-{
- m_id = QByteArray(ID_SIZE, 0);
-}
-
-QByteArray QXmppStunMessage::id() const
-{
- return m_id;
-}
-
-void QXmppStunMessage::setId(const QByteArray &id)
-{
- Q_ASSERT(id.size() == ID_SIZE);
- m_id = id;
-}
-
-quint16 QXmppStunMessage::type() const
-{
- return m_type;
-}
-
-void QXmppStunMessage::setType(quint16 type)
-{
- m_type = type;
-}
-
-/// Decodes a QXmppStunMessage and checks its integrity using the given
-/// password.
-///
-/// \param buffer
-/// \param password
-
-bool QXmppStunMessage::decode(const QByteArray &buffer, const QString &password, QStringList *errors)
-{
- QStringList silent;
- if (!errors)
- errors = &silent;
-
- if (buffer.size() < STUN_HEADER)
- {
- *errors << QLatin1String("Received a truncated STUN packet");
- return false;
- }
-
- // parse STUN header
- QDataStream stream(buffer);
- quint16 length;
- quint32 cookie;
- stream >> m_type;
- stream >> length;
- stream >> cookie;
- stream.readRawData(m_id.data(), m_id.size());
-
- if (cookie != STUN_MAGIC || length != buffer.size() - STUN_HEADER)
- {
- *errors << QLatin1String("Received an invalid STUN packet");
- return false;
- }
-
- // parse STUN attributes
- int done = 0;
- bool after_integrity = false;
- while (done < length)
- {
- quint16 a_type, a_length;
- stream >> a_type;
- stream >> a_length;
- const int pad_length = 4 * ((a_length + 3) / 4) - a_length;
-
- // only FINGERPRINT is allowed after MESSAGE-INTEGRITY
- if (after_integrity && a_type != Fingerprint)
- {
- *errors << QString("Skipping attribute %1 after MESSAGE-INTEGRITY").arg(QString::number(a_type));
- stream.skipRawData(a_length + pad_length);
- done += 4 + a_length + pad_length;
- continue;
- }
-
- if (a_type == Priority)
- {
- // PRIORITY
- if (a_length != sizeof(priority))
- return false;
- stream >> priority;
-
- } else if (a_type == ErrorCode) {
-
- // ERROR-CODE
- if (a_length < 4)
- return false;
- quint16 reserved;
- quint8 errorCodeHigh, errorCodeLow;
- stream >> reserved;
- stream >> errorCodeHigh;
- stream >> errorCodeLow;
- errorCode = errorCodeHigh * 100 + errorCodeLow;
- QByteArray phrase(a_length - 4, 0);
- stream.readRawData(phrase.data(), phrase.size());
- errorPhrase = QString::fromUtf8(phrase);
-
- } else if (a_type == UseCandidate) {
-
- // USE-CANDIDATE
- if (a_length != 0)
- return false;
- useCandidate = true;
-
- } else if (a_type == Software) {
-
- // SOFTWARE
- QByteArray utf8Software(a_length, 0);
- stream.readRawData(utf8Software.data(), utf8Software.size());
- software = QString::fromUtf8(utf8Software);
-
- } else if (a_type == MappedAddress) {
-
- // MAPPED-ADDRESS
- if (!decodeAddress(stream, a_length, mappedHost, mappedPort))
- {
- *errors << QLatin1String("Bad MAPPED-ADDRESS");
- return false;
- }
-
- } else if (a_type == OtherAddress) {
-
- // OTHER-ADDRESS
- if (!decodeAddress(stream, a_length, otherHost, otherPort))
- {
- *errors << QLatin1String("Bad OTHER-ADDRESS");
- return false;
- }
-
- } else if (a_type == XorMappedAddress) {
-
- // XOR-MAPPED-ADDRESS
- if (a_length < 4)
- return false;
- quint8 reserved, protocol;
- quint16 xport;
- stream >> reserved;
- stream >> protocol;
- stream >> xport;
- xorMappedPort = xport ^ (STUN_MAGIC >> 16);
- if (protocol == STUN_IPV4)
- {
- if (a_length != 8)
- return false;
- quint32 xaddr;
- stream >> xaddr;
- xorMappedHost = QHostAddress(xaddr ^ STUN_MAGIC);
- } else if (protocol == STUN_IPV6) {
- if (a_length != 20)
- return false;
- QByteArray xaddr(16, 0);
- stream.readRawData(xaddr.data(), xaddr.size());
- QByteArray xpad;
- QDataStream(&xpad, QIODevice::WriteOnly) << STUN_MAGIC;
- xpad += m_id;
- Q_IPV6ADDR addr;
- for (int i = 0; i < 16; i++)
- addr[i] = xaddr[i] ^ xpad[i];
- xorMappedHost = QHostAddress(addr);
- } else {
- *errors << QString("Bad protocol %1").arg(QString::number(protocol));
- return false;
- }
-
- } else if (a_type == MessageIntegrity) {
-
- // MESSAGE-INTEGRITY
- if (a_length != 20)
- return false;
- QByteArray integrity(20, 0);
- stream.readRawData(integrity.data(), integrity.size());
-
- // check HMAC-SHA1
- if (!password.isEmpty())
- {
- const QByteArray key = password.toUtf8();
- QByteArray copy = buffer.left(STUN_HEADER + done);
- setBodyLength(copy, done + 24);
- if (integrity != generateHmacSha1(key, copy))
- {
- *errors << QLatin1String("Bad message integrity");
- return false;
- }
- }
-
- // from here onwards, only FINGERPRINT is allowed
- after_integrity = true;
-
- } else if (a_type == Fingerprint) {
-
- // FINGERPRINT
- if (a_length != 4)
- return false;
- quint32 fingerprint;
- stream >> fingerprint;
-
- // check CRC32
- QByteArray copy = buffer.left(STUN_HEADER + done);
- setBodyLength(copy, done + 8);
- const quint32 expected = generateCrc32(copy) ^ 0x5354554eL;
- if (fingerprint != expected)
- {
- *errors << QLatin1String("Bad fingerprint");
- return false;
- }
-
- // stop parsing, no more attributes are allowed
- return true;
-
- } else if (a_type == IceControlling) {
-
- /// ICE-CONTROLLING
- if (a_length != 8)
- return false;
- iceControlling.resize(8);
- stream.readRawData(iceControlling.data(), iceControlling.size());
-
- } else if (a_type == IceControlled) {
-
- /// ICE-CONTROLLED
- if (a_length != 8)
- return false;
- iceControlled.resize(8);
- stream.readRawData(iceControlled.data(), iceControlled.size());
-
- } else if (a_type == Username) {
-
- // USERNAME
- QByteArray utf8Username(a_length, 0);
- stream.readRawData(utf8Username.data(), utf8Username.size());
- username = QString::fromUtf8(utf8Username);
-
- } else {
-
- // Unknown attribute
- stream.skipRawData(a_length);
- *errors << QString("Skipping unknown attribute %1").arg(QString::number(a_type));
-
- }
- stream.skipRawData(pad_length);
- done += 4 + a_length + pad_length;
- }
- return true;
-}
-
-/// Encodes the current QXmppStunMessage, optionally calculating the
-/// message integrity attribute using the given password.
-///
-/// \param password
-
-QByteArray QXmppStunMessage::encode(const QString &password) const
-{
- QByteArray buffer;
- QDataStream stream(&buffer, QIODevice::WriteOnly);
-
- // encode STUN header
- quint16 length = 0;
- stream << m_type;
- stream << length;
- stream << STUN_MAGIC;
- stream.writeRawData(m_id.data(), m_id.size());
-
- // MAPPED-ADDRESS
- if (mappedPort && !mappedHost.isNull() &&
- (mappedHost.protocol() == QAbstractSocket::IPv4Protocol ||
- mappedHost.protocol() == QAbstractSocket::IPv6Protocol))
- {
- stream << quint16(MappedAddress);
- encodeAddress(stream, mappedHost, mappedPort);
- }
-
- // OTHER-ADDRESS
- if (otherPort && !otherHost.isNull() &&
- (otherHost.protocol() == QAbstractSocket::IPv4Protocol ||
- otherHost.protocol() == QAbstractSocket::IPv6Protocol))
- {
- stream << quint16(OtherAddress);
- encodeAddress(stream, otherHost, otherPort);
- }
-
- // XOR-MAPPED-ADDRESS
- if (xorMappedPort && !xorMappedHost.isNull() &&
- (xorMappedHost.protocol() == QAbstractSocket::IPv4Protocol ||
- xorMappedHost.protocol() == QAbstractSocket::IPv6Protocol))
- {
- stream << quint16(XorMappedAddress);
- stream << quint16(8);
- stream << quint8(0);
- if (xorMappedHost.protocol() == QAbstractSocket::IPv4Protocol)
- {
- stream << quint8(STUN_IPV4);
- stream << quint16(xorMappedPort ^ (STUN_MAGIC >> 16));
- stream << quint32(xorMappedHost.toIPv4Address() ^ STUN_MAGIC);
- } else {
- stream << quint8(STUN_IPV6);
- stream << quint16(xorMappedPort ^ (STUN_MAGIC >> 16));
- Q_IPV6ADDR addr = xorMappedHost.toIPv6Address();
- QByteArray xaddr;
- QDataStream(&xaddr, QIODevice::WriteOnly) << STUN_MAGIC;
- xaddr += m_id;
- for (int i = 0; i < 16; i++)
- xaddr[i] = xaddr[i] ^ addr[i];
- stream.writeRawData(xaddr.data(), xaddr.size());
- }
- }
-
- // ERROR-CODE
- if (errorCode)
- {
- const quint16 reserved = 0;
- const quint8 errorCodeHigh = errorCode / 100;
- const quint8 errorCodeLow = errorCode % 100;
- const QByteArray phrase = errorPhrase.toUtf8();
- stream << quint16(ErrorCode);
- stream << quint16(phrase.size() + 4);
- stream << reserved;
- stream << errorCodeHigh;
- stream << errorCodeLow;
- stream.writeRawData(phrase.data(), phrase.size());
- if (phrase.size() % 4)
- {
- const QByteArray padding(4 - (phrase.size() % 4), 0);
- stream.writeRawData(padding.data(), padding.size());
- }
- }
-
- // PRIORITY
- if (priority)
- {
- stream << quint16(Priority);
- stream << quint16(sizeof(priority));
- stream << priority;
- }
-
- // USE-CANDIDATE
- if (useCandidate)
- {
- stream << quint16(UseCandidate);
- stream << quint16(0);
- }
-
- // SOFTWARE
- if (!software.isEmpty())
- {
- stream << quint16(Software);
- encodeString(stream, software);
- }
-
- // ICE-CONTROLLING or ICE-CONTROLLED
- if (!iceControlling.isEmpty())
- {
- stream << quint16(IceControlling);
- stream << quint16(iceControlling.size());
- stream.writeRawData(iceControlling.data(), iceControlling.size());
- } else if (!iceControlled.isEmpty()) {
- stream << quint16(IceControlled);
- stream << quint16(iceControlled.size());
- stream.writeRawData(iceControlled.data(), iceControlled.size());
- }
-
- // USERNAME
- if (!username.isEmpty())
- {
- stream << quint16(Username);
- encodeString(stream, username);
- }
-
- // set body length
- setBodyLength(buffer, buffer.size() - STUN_HEADER);
-
- // MESSAGE-INTEGRITY
- if (!password.isEmpty())
- {
- const QByteArray key = password.toUtf8();
- setBodyLength(buffer, buffer.size() - STUN_HEADER + 24);
- QByteArray integrity = generateHmacSha1(key, buffer);
- stream << quint16(MessageIntegrity);
- stream << quint16(integrity.size());
- stream.writeRawData(integrity.data(), integrity.size());
- }
-
- // FINGERPRINT
- setBodyLength(buffer, buffer.size() - STUN_HEADER + 8);
- quint32 fingerprint = generateCrc32(buffer) ^ 0x5354554eL;
- stream << quint16(Fingerprint);
- stream << quint16(sizeof(fingerprint));
- stream << fingerprint;
-
- return buffer;
-}
-
-/// If the given packet looks like a STUN message, returns the message
-/// type, otherwise returns 0.
-///
-/// \param buffer
-
-quint16 QXmppStunMessage::peekType(const QByteArray &buffer)
-{
- if (buffer.size() < STUN_HEADER)
- return 0;
-
- // parse STUN header
- QDataStream stream(buffer);
- quint16 type;
- quint16 length;
- quint32 cookie;
- stream >> type;
- stream >> length;
- stream >> cookie;
-
- if (cookie != STUN_MAGIC || length != buffer.size() - STUN_HEADER)
- return 0;
-
- return type;
-}
-
-void QXmppStunMessage::setBodyLength(QByteArray &buffer, qint16 length) const
-{
- QDataStream stream(&buffer, QIODevice::WriteOnly);
- stream.device()->seek(2);
- stream << length;
-}
-
-QString QXmppStunMessage::toString() const
-{
- QStringList dumpLines;
- QString typeName;
- switch (m_type & 0x000f)
- {
- case 1: typeName = "Binding"; break;
- case 2: typeName = "Shared Secret"; break;
- default: typeName = "Unknown"; break;
- }
- switch (m_type & 0x0ff0)
- {
- case 0x000: typeName += " Request"; break;
- case 0x010: typeName += " Indication"; break;
- case 0x100: typeName += " Response"; break;
- case 0x110: typeName += " Error"; break;
- default: break;
- }
- dumpLines << QString(" type %1 (%2)")
- .arg(typeName)
- .arg(QString::number(m_type));
- dumpLines << QString(" id %1").arg(QString::fromAscii(m_id.toHex()));
-
- // attributes
- if (!username.isEmpty())
- dumpLines << QString(" * USERNAME %1").arg(username);
- if (errorCode)
- dumpLines << QString(" * ERROR-CODE %1 %2")
- .arg(QString::number(errorCode))
- .arg(errorPhrase);
- if (!software.isEmpty())
- dumpLines << QString(" * SOFTWARE %1").arg(software);
- if (mappedPort)
- dumpLines << QString(" * MAPPED-ADDRESS %1 %2")
- .arg(mappedHost.toString())
- .arg(QString::number(mappedPort));
- if (otherPort)
- dumpLines << QString(" * OTHER-ADDRESS %1 %2")
- .arg(otherHost.toString())
- .arg(QString::number(otherPort));
- if (xorMappedPort)
- dumpLines << QString(" * XOR-MAPPED-ADDRESS %1 %2")
- .arg(xorMappedHost.toString())
- .arg(QString::number(xorMappedPort));
- if (!iceControlling.isEmpty())
- dumpLines << QString(" * ICE-CONTROLLING %1")
- .arg(QString::fromAscii(iceControlling.toHex()));
- if (!iceControlled.isEmpty())
- dumpLines << QString(" * ICE-CONTROLLED %1")
- .arg(QString::fromAscii(iceControlled.toHex()));
-
- return dumpLines.join("\n");
-}
-
-QXmppStunSocket::Pair::Pair()
- : checked(QIODevice::NotOpen)
-{
- // FIXME : calculate priority
- priority = 1862270975;
- transaction = randomByteArray(ID_SIZE);
-}
-
-QString QXmppStunSocket::Pair::toString() const
-{
- QString str = QString("%1 %2").arg(remote.host().toString(), QString::number(remote.port()));
- if (!reflexive.host().isNull() && reflexive.port())
- str += QString(" (reflexive %1 %2)").arg(reflexive.host().toString(), QString::number(reflexive.port()));
- return str;
-}
-
-/// Constructs a new QXmppStunSocket.
-///
-
-QXmppStunSocket::QXmppStunSocket(bool iceControlling, QObject *parent)
- : QObject(parent),
- m_activePair(0),
- m_iceControlling(iceControlling)
-{
- m_localUser = generateStanzaHash(4);
- m_localPassword = generateStanzaHash(22);
-
- m_socket = new QUdpSocket(this);
- connect(m_socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
- if (!m_socket->bind())
- qWarning("QXmppStunSocket could not start listening");
-
- m_timer = new QTimer(this);
- m_timer->setInterval(500);
- connect(m_timer, SIGNAL(timeout()), this, SLOT(checkCandidates()));
-}
-
-QXmppStunSocket::~QXmppStunSocket()
-{
- foreach (Pair *pair, m_pairs)
- delete pair;
-}
-
-/// Returns the component id for the current socket, e.g. 1 for RTP
-/// and 2 for RTCP.
-
-int QXmppStunSocket::component() const
-{
- return m_component;
-}
-
-/// Sets the component id for the current socket, e.g. 1 for RTP
-/// and 2 for RTCP.
-///
-/// \param component
-
-void QXmppStunSocket::setComponent(int component)
-{
- m_component = component;
-}
-
-void QXmppStunSocket::checkCandidates()
-{
- debug("Checking remote candidates");
- foreach (Pair *pair, m_pairs)
- {
- // send a binding request
- QXmppStunMessage message;
- message.setId(pair->transaction);
- message.setType(BindingRequest);
- message.priority = pair->priority;
- message.username = QString("%1:%2").arg(m_remoteUser, m_localUser);
- if (m_iceControlling)
- {
- message.iceControlling = QByteArray(8, 0);
- message.useCandidate = true;
- } else {
- message.iceControlled = QByteArray(8, 0);
- }
- writeStun(message, pair);
- }
-}
-
-/// Closes the socket.
-
-void QXmppStunSocket::close()
-{
- m_socket->close();
- m_timer->stop();
-}
-
-/// Start ICE connectivity checks.
-
-void QXmppStunSocket::connectToHost()
-{
- checkCandidates();
- m_timer->start();
-}
-
-/// Returns true if ICE negotiation completed, false otherwise.
-
-bool QXmppStunSocket::isConnected() const
-{
- return m_activePair != 0;
-}
-
-void QXmppStunSocket::debug(const QString &message, QXmppLogger::MessageType type)
-{
- emit logMessage(type, QString("STUN(%1) %2").arg(QString::number(m_component)).arg(message));
-}
-
-/// Returns the list of local HOST CANDIDATES candidates by iterating
-/// over the available network interfaces.
-
-QList<QXmppJingleCandidate> QXmppStunSocket::localCandidates() const
-{
- QList<QXmppJingleCandidate> candidates;
- foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces())
- {
- if (!(interface.flags() & QNetworkInterface::IsRunning) ||
- interface.flags() & QNetworkInterface::IsLoopBack)
- continue;
-
- foreach (const QNetworkAddressEntry &entry, interface.addressEntries())
- {
- if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol ||
- entry.netmask().isNull() ||
- entry.netmask() == QHostAddress::Broadcast)
- continue;
-
- QXmppJingleCandidate candidate;
- candidate.setComponent(m_component);
- candidate.setHost(entry.ip());
- candidate.setId(generateStanzaHash(10));
- candidate.setNetwork(interface.index());
- candidate.setPort(m_socket->localPort());
- candidate.setPriority(2130706432 - m_component);
- candidate.setProtocol("udp");
- candidate.setType("host");
- candidates << candidate;
- }
- }
- return candidates;
-}
-
-QString QXmppStunSocket::localUser() const
-{
- return m_localUser;
-}
-
-void QXmppStunSocket::setLocalUser(const QString &user)
-{
- m_localUser = user;
-}
-
-QString QXmppStunSocket::localPassword() const
-{
- return m_localPassword;
-}
-
-void QXmppStunSocket::setLocalPassword(const QString &password)
-{
- m_localPassword = password;
-}
-
-/// Adds a remote STUN candidate.
-
-bool QXmppStunSocket::addRemoteCandidate(const QXmppJingleCandidate &candidate)
-{
- if (candidate.component() != m_component ||
- candidate.type() != "host" ||
- candidate.protocol() != "udp")
- return false;
-
- foreach (Pair *pair, m_pairs)
- if (pair->remote.host() == candidate.host() &&
- pair->remote.port() == candidate.port())
- return false;
-
- Pair *pair = new Pair;
- pair->remote = candidate;
- m_pairs << pair;
- return true;
-}
-
-/// Adds a discovered STUN candidate.
-
-QXmppStunSocket::Pair *QXmppStunSocket::addRemoteCandidate(const QHostAddress &host, quint16 port)
-{
- foreach (Pair *pair, m_pairs)
- if (pair->remote.host() == host &&
- pair->remote.port() == port)
- return pair;
-
- QXmppJingleCandidate candidate;
- candidate.setHost(host);
- candidate.setPort(port);
- candidate.setProtocol("udp");
- candidate.setComponent(m_component);
-
- Pair *pair = new Pair;
- pair->remote = candidate;
- m_pairs << pair;
-
- debug(QString("Added candidate %1").arg(pair->toString()));
- return pair;
-}
-
-void QXmppStunSocket::setRemoteUser(const QString &user)
-{
- m_remoteUser = user;
-}
-
-void QXmppStunSocket::setRemotePassword(const QString &password)
-{
- m_remotePassword = password;
-}
-
-void QXmppStunSocket::readyRead()
-{
- const qint64 size = m_socket->pendingDatagramSize();
- QHostAddress remoteHost;
- quint16 remotePort;
- QByteArray buffer(size, 0);
- m_socket->readDatagram(buffer.data(), buffer.size(), &remoteHost, &remotePort);
-
- // if this is not a STUN message, emit it
- quint16 messageType = QXmppStunMessage::peekType(buffer);
- if (!messageType)
- {
- emit datagramReceived(buffer);
- return;
- }
-
- // parse STUN message
- const QString messagePassword = (messageType & 0xFF00) ? m_remotePassword : m_localPassword;
- if (messagePassword.isEmpty())
- return;
- QXmppStunMessage message;
- QStringList errors;
- if (!message.decode(buffer, messagePassword, &errors))
- {
- foreach (const QString &error, errors)
- debug(error, QXmppLogger::WarningMessage);
- return;
- }
-#ifdef QXMPP_DEBUG_STUN
- debug(QString("Received from %1 port %2\n%3")
- .arg(remoteHost.toString())
- .arg(QString::number(remotePort))
- .arg(message.toString()),
- QXmppLogger::ReceivedMessage);
-#endif
-
- if (m_activePair)
- return;
-
- // process message
- Pair *pair = 0;
- if (message.type() == BindingRequest)
- {
- // add remote candidate
- pair = addRemoteCandidate(remoteHost, remotePort);
-
- // send a binding response
- QXmppStunMessage response;
- response.setId(message.id());
- response.setType(BindingResponse);
- response.username = message.username;
- response.xorMappedHost = pair->remote.host();
- response.xorMappedPort = pair->remote.port();
- writeStun(response, pair);
-
- // update state
- if (m_iceControlling || message.useCandidate)
- {
- debug(QString("ICE reverse check %1").arg(pair->toString()));
- pair->checked |= QIODevice::ReadOnly;
- }
-
- if (!m_iceControlling)
- {
- // send a triggered connectivity test
- QXmppStunMessage message;
- message.setId(pair->transaction);
- message.setType(BindingRequest);
- message.priority = pair->priority;
- message.username = QString("%1:%2").arg(m_remoteUser, m_localUser);
- message.iceControlled = QByteArray(8, 0);
- writeStun(message, pair);
- }
-
- } else if (message.type() == BindingResponse) {
-
- // find the pair for this transaction
- foreach (Pair *ptr, m_pairs)
- {
- if (ptr->transaction == message.id())
- {
- pair = ptr;
- break;
- }
- }
- if (!pair)
- {
- debug(QString("Unknown transaction %1").arg(QString::fromAscii(message.id().toHex())));
- return;
- }
- // store reflexive address
- pair->reflexive.setHost(message.xorMappedHost);
- pair->reflexive.setPort(message.xorMappedPort);
-
- // add the new remote candidate
- addRemoteCandidate(remoteHost, remotePort);
-
-#if 0
- // send a binding indication
- QXmppStunMessage indication;
- indication.setId(randomByteArray(ID_SIZE));
- indication.setType(BindingIndication);
- m_socket->writeStun(indication, pair);
-#endif
- // outgoing media can flow
- debug(QString("ICE forward check %1").arg(pair->toString()));
- pair->checked |= QIODevice::WriteOnly;
- }
-
- // signal completion
- if (pair && pair->checked == QIODevice::ReadWrite)
- {
- debug(QString("ICE completed %1").arg(pair->toString()));
- m_activePair = pair;
- m_timer->stop();
- emit connected();
- }
-}
-
-/// Sends a data packet to the remote party.
-///
-/// \param datagram
-
-qint64 QXmppStunSocket::writeDatagram(const QByteArray &datagram)
-{
- if (!m_activePair)
- return -1;
- return m_socket->writeDatagram(datagram, m_activePair->remote.host(), m_activePair->remote.port());
-}
-
-/// Sends a STUN packet to the remote party.
-
-qint64 QXmppStunSocket::writeStun(const QXmppStunMessage &message, QXmppStunSocket::Pair *pair)
-{
- const QString messagePassword = (message.type() & 0xFF00) ? m_localPassword : m_remotePassword;
-#ifdef QXMPP_DEBUG_STUN
- debug(
- QString("Sent to %1\n%2").arg(pair->toString(), message.toString()),
- QXmppLogger::SentMessage);
-#endif
- return m_socket->writeDatagram(message.encode(messagePassword), pair->remote.host(), pair->remote.port());
-}
-