diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-02-25 12:21:53 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-02-25 12:21:53 +0000 |
| commit | b9123796e79b1f9c08b5b113900826008f5d6dc8 (patch) | |
| tree | 5a18ffd99ee0af574f83f3e88aaf421abf5aa68c /source | |
| parent | 9334406b1ac2716ba799c5bb71d25efefaca29bc (diff) | |
| download | qxmpp-b9123796e79b1f9c08b5b113900826008f5d6dc8.tar.gz | |
add code for handling SOCKS5 bytestreams
Diffstat (limited to 'source')
| -rw-r--r-- | source/QXmppByteStreamIq.cpp | 163 | ||||
| -rw-r--r-- | source/QXmppByteStreamIq.h | 84 | ||||
| -rw-r--r-- | source/QXmppSocks.cpp | 302 | ||||
| -rw-r--r-- | source/QXmppSocks.h | 83 | ||||
| -rw-r--r-- | source/source.pro | 4 |
5 files changed, 636 insertions, 0 deletions
diff --git a/source/QXmppByteStreamIq.cpp b/source/QXmppByteStreamIq.cpp new file mode 100644 index 00000000..3f9f3ecb --- /dev/null +++ b/source/QXmppByteStreamIq.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2010 Bolloré telecom + * + * 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. + * + */ + +#include <QDomElement> + +#include "QXmppByteStreamIq.h" +#include "QXmppConstants.h" +#include "QXmppUtils.h" + +QHostAddress QXmppByteStreamIq::StreamHost::host() const +{ + return m_host; +} + +void QXmppByteStreamIq::StreamHost::setHost(const QHostAddress &host) +{ + m_host = host; +} + +QString QXmppByteStreamIq::StreamHost::jid() const +{ + return m_jid; +} + +void QXmppByteStreamIq::StreamHost::setJid(const QString &jid) +{ + m_jid = jid; +} + +quint16 QXmppByteStreamIq::StreamHost::port() const +{ + return m_port; +} + +void QXmppByteStreamIq::StreamHost::setPort(quint16 port) +{ + m_port = port; +} + +QXmppByteStreamIq::Mode QXmppByteStreamIq::mode() const +{ + return m_mode; +} + +void QXmppByteStreamIq::setMode(QXmppByteStreamIq::Mode mode) +{ + m_mode = mode; +} + +QString QXmppByteStreamIq::sid() const +{ + return m_sid; +} + +void QXmppByteStreamIq::setSid(const QString &sid) +{ + m_sid = sid; +} + +QList<QXmppByteStreamIq::StreamHost> QXmppByteStreamIq::streamHosts() const +{ + return m_streamHosts; +} + +void QXmppByteStreamIq::setStreamHosts(const QList<QXmppByteStreamIq::StreamHost> &streamHosts) +{ + m_streamHosts = streamHosts; +} + +QString QXmppByteStreamIq::streamHostUsed() const +{ + return m_streamHostUsed; +} + +void QXmppByteStreamIq::setStreamHostUsed(const QString &jid) +{ + m_streamHostUsed = jid; +} + +bool QXmppByteStreamIq::isByteStreamIq(QDomElement &element) +{ + return element.firstChildElement("query").namespaceURI() == ns_bytestreams; +} + +void QXmppByteStreamIq::parse(QDomElement &element) +{ + setId(element.attribute("id")); + setFrom(element.attribute("from")); + setTo(element.attribute("to")); + setTypeFromStr(element.attribute("type")); + + QDomElement queryElement = element.firstChildElement("query"); + m_sid = queryElement.attribute("sid"); + const QString modeStr = queryElement.attribute("mode"); + if (modeStr == "tcp") + m_mode = Tcp; + else if (modeStr == "udp") + m_mode = Udp; + else + m_mode = None; + + QDomElement hostElement = queryElement.firstChildElement("streamhost"); + while (!hostElement.isNull()) + { + StreamHost streamHost; + streamHost.setHost(QHostAddress(hostElement.attribute("host"))); + streamHost.setJid(hostElement.attribute("jid")); + streamHost.setPort(hostElement.attribute("port").toInt()); + m_streamHosts.append(streamHost); + + hostElement = hostElement.nextSiblingElement("streamhost"); + } + m_streamHostUsed = queryElement.firstChildElement("streamhost-used").attribute("jid"); +} + +void QXmppByteStreamIq::toXmlElementFromChild(QXmlStreamWriter *writer) const +{ + writer->writeStartElement("query"); + helperToXmlAddAttribute(writer, "xmlns", ns_bytestreams); + helperToXmlAddAttribute(writer, "sid", m_sid); + QString modeStr; + if (m_mode == Tcp) + modeStr = "tcp"; + else if (m_mode == Udp) + modeStr = "udp"; + helperToXmlAddAttribute(writer, "mode", modeStr); + foreach (const StreamHost& streamHost, m_streamHosts) + { + writer->writeStartElement("streamhost"); + helperToXmlAddAttribute(writer, "host", streamHost.host().toString()); + helperToXmlAddAttribute(writer, "jid", streamHost.jid()); + helperToXmlAddAttribute(writer, "port", QString::number(streamHost.port())); + writer->writeEndElement(); + } + if (!m_streamHostUsed.isEmpty()) + { + writer->writeStartElement("streamhost-used"); + helperToXmlAddAttribute(writer, "jid", m_streamHostUsed); + writer->writeEndElement(); + } + + writer->writeEndElement(); +} diff --git a/source/QXmppByteStreamIq.h b/source/QXmppByteStreamIq.h new file mode 100644 index 00000000..294e2710 --- /dev/null +++ b/source/QXmppByteStreamIq.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 Bolloré telecom + * + * 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. + * + */ + +#ifndef QXMPPBYTESTREAMIQ_H +#define QXMPPBYTESTREAMIQ_H + +#include "QXmppIq.h" + +#include <QHostAddress> + +class QDomElement; +class QXmlStreamWriter; + +class QXmppByteStreamIq : public QXmppIq +{ +public: + enum Mode { + None = 0, + Tcp, + Udp, + }; + + class StreamHost + { + public: + QString jid() const; + void setJid(const QString &jid); + + QHostAddress host() const; + void setHost(const QHostAddress &host); + + quint16 port() const; + void setPort(quint16 port); + + private: + QHostAddress m_host; + QString m_jid; + quint16 m_port; + }; + + QXmppByteStreamIq::Mode mode() const; + void setMode(QXmppByteStreamIq::Mode mode); + + QString sid() const; + void setSid(const QString &sid); + + QList<QXmppByteStreamIq::StreamHost> streamHosts() const; + void setStreamHosts(const QList<QXmppByteStreamIq::StreamHost> &streamHosts); + + QString streamHostUsed() const; + void setStreamHostUsed(const QString &jid); + + void parse(QDomElement &element); + void toXmlElementFromChild(QXmlStreamWriter *writer) const; + static bool isByteStreamIq(QDomElement &element); + +private: + Mode m_mode; + QString m_sid; + QList<StreamHost> m_streamHosts; + QString m_streamHostUsed; +}; + +#endif diff --git a/source/QXmppSocks.cpp b/source/QXmppSocks.cpp new file mode 100644 index 00000000..575a1102 --- /dev/null +++ b/source/QXmppSocks.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2010 Bolloré telecom + * + * 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. + * + */ + +#include <QTcpServer> +#include <QTcpSocket> + +#include "QXmppSocks.h" + +const static char SocksVersion = 5; + +enum AuthenticationMethod { + NoAuthentication = 0, + GSSAPI = 1, + UsernamePassword = 2, +}; + +enum Command { + ConnectCommand = 1, + BindCommand = 2, + AssociateCommand = 3, +}; + +enum AddressType { + IPv4Address = 1, + DomainName = 3, + IPv6Address = 4, +}; + +enum ReplyType { + Succeeded = 0, + SocksFailure = 1, + ConnectionNotAllowed = 2, + NetworkUnreachable = 3, + HostUnreachable = 4, + ConnectionRefused = 5, + TtlExpired = 6, + CommandNotSupported = 7, + AddressTypeNotSupported = 8, +}; + +QByteArray encodeHostAndPort(quint8 type, const QByteArray &host, quint16 port) +{ + QByteArray buffer; + buffer.resize(2); + // set host name + buffer[0] = type; + buffer[1] = host.size(); + buffer.append(host); + // set port + int pos = buffer.size(); + buffer.resize(pos + 2); + quint16 p = htons(port); + memcpy(buffer.data() + pos, &p, 2); + return buffer; +} + +bool parseHostAndPort(const QByteArray buffer, quint8 &type, QByteArray &host, quint16 &port) +{ + if (buffer.size() < 4) + return false; + + // parse host type + int pos = 0; + type = buffer.at(pos); + pos++; + + // parse host name + quint8 hostLength = buffer.at(pos); + pos++; + if (buffer.size() < hostLength + 4) + { + qWarning("Invalid host length"); + return false; + } + host = buffer.mid(pos, hostLength); + pos += hostLength; + + // parse host port + quint16 p; + memcpy(&p, buffer.data() + pos, 2); + port = ntohs(p); + return true; +} + +QXmppSocksClient::QXmppSocksClient(const QHostAddress &proxyAddress, quint16 proxyPort, QObject *parent) + : QObject(parent), m_proxyAddress(proxyAddress), m_proxyPort(proxyPort) +{ + m_socket = new QTcpSocket(this); +} + +void QXmppSocksClient::connectToHost(const QString &hostName, quint16 hostPort) +{ + m_hostName = hostName; + m_hostPort = hostPort; + m_socket->connectToHost(m_proxyAddress, m_proxyPort); +} + +QString QXmppSocksClient::errorString() const +{ + return m_socket->errorString(); +} + +QByteArray QXmppSocksClient::readAll() +{ + return m_socket->readAll(); +} + +bool QXmppSocksClient::waitForConnected(int msecs) +{ + if (!m_socket->waitForConnected(msecs)) + return false; + + // send connect to server + QByteArray buffer; + buffer.resize(3); + buffer[0] = SocksVersion; + buffer[1] = 0x01; // number of methods + buffer[2] = NoAuthentication; + m_socket->write(buffer); + + // wait for connect to server response + if (!m_socket->waitForReadyRead(msecs)) + return false; + buffer = m_socket->readAll(); + if (buffer.size() != 2 || buffer.at(0) != SocksVersion || buffer.at(1) != NoAuthentication) + { + qWarning("QXmppSocksClient received an invalid response during handshake"); + return false; + } + + // send CONNECT command + buffer.resize(3); + buffer[0] = SocksVersion; + buffer[1] = ConnectCommand; + buffer[2] = 0x00; // reserved + buffer.append(encodeHostAndPort( + DomainName, + m_hostName.toAscii(), + m_hostPort)); + m_socket->write(buffer); + + // wait for CONNECT response + if (!m_socket->waitForReadyRead(msecs)) + return false; + buffer = m_socket->readAll(); + if (buffer.size() < 6 || + buffer.at(0) != SocksVersion || + buffer.at(1) != Succeeded || + buffer.at(2) != 0) + { + qWarning("QXmppSocksClient received an invalid response to CONNECT command"); + return false; + } + + // parse host + quint8 hostType; + QByteArray hostName; + quint16 hostPort; + if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort)) + { + qWarning("QXmppSocksClient could not parse type/host/port"); + return false; + } + // FIXME : what do we do with the resulting name / port? + + connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected())); + connect(m_socket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + return true; +} + +QXmppSocksServer::QXmppSocksServer(QObject *parent) + : QObject(parent), + m_hostPort(0), + m_socket(0) +{ + m_server = new QTcpServer(this); + connect(m_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection())); +} + +bool QXmppSocksServer::listen(const QHostAddress &address, quint16 port) +{ + return m_server->listen(address, port); +} + +QHostAddress QXmppSocksServer::serverAddress() const +{ + return m_server->serverAddress(); +} + +quint16 QXmppSocksServer::serverPort() const +{ + return m_server->serverPort(); +} + +void QXmppSocksServer::slotNewConnection() +{ + m_socket = m_server->nextPendingConnection(); + if (!m_socket) + return; + + // wait for connect to server + if (!m_socket->waitForReadyRead()) + return; + QByteArray buffer = m_socket->readAll(); + if (buffer.size() < 3 || + buffer.at(0) != SocksVersion || + buffer.at(1) != 0x01 || + buffer.at(2) != NoAuthentication) + { + qWarning("QXmppSocksServer received invalid handshake"); + m_socket->close(); + return; + } + + // send connect to server response + buffer.resize(2); + buffer[0] = SocksVersion; + buffer[1] = NoAuthentication; + m_socket->write(buffer); + + // wait for connect command + if (!m_socket->waitForReadyRead()) + return; + buffer = m_socket->readAll(); + if (buffer.size() < 4 || + buffer.at(0) != SocksVersion || + buffer.at(1) != ConnectCommand || + buffer.at(2) != 0x00) + { + qWarning("QXmppSocksServer received an invalid command"); + m_socket->close(); + return; + } + + // parse host + quint8 hostType; + QByteArray hostName; + quint16 hostPort; + if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort)) + { + qWarning("QXmppSocksServer could not parse type/host/port"); + m_socket->close(); + return; + } + if (hostName != m_hostName || hostPort != m_hostPort) + { + qWarning("QXmppSocksServer got wrong host or port"); + m_socket->close(); + return; + } + + //send connect response + buffer.resize(3); + buffer[0] = SocksVersion; + buffer[1] = Succeeded; + buffer[2] = 0x00; + buffer.append(encodeHostAndPort( + DomainName, + m_server->serverAddress().toString().toAscii(), + m_server->serverPort())); + m_socket->write(buffer); + + // connect signals + connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected())); + connect(m_socket, SIGNAL(bytesWritten(qint64)), this, SIGNAL(bytesWritten(qint64))); +} + +void QXmppSocksServer::setHostName(const QString &hostName) +{ + m_hostName = hostName; +} + +void QXmppSocksServer::setHostPort(quint16 hostPort) +{ + m_hostPort = hostPort; +} + +void QXmppSocksServer::write(const QByteArray &data) +{ + if (m_socket) + m_socket->write(data); +} diff --git a/source/QXmppSocks.h b/source/QXmppSocks.h new file mode 100644 index 00000000..f9a7e71c --- /dev/null +++ b/source/QXmppSocks.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 Bolloré telecom + * + * 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. + * + */ + +#ifndef QXMPPSOCKS_H +#define QXMPPSOCKS_H + +#include <QHostAddress> +#include <QObject> + +class QTcpServer; +class QTcpSocket; + +class QXmppSocksClient : public QObject +{ + Q_OBJECT + +public: + QXmppSocksClient(const QHostAddress &proxyAddress, quint16 proxyPort, QObject *parent=0); + void connectToHost(const QString &hostName, quint16 hostPort); + QString errorString() const; + QByteArray readAll(); + bool waitForConnected(int msecs = 30000); + +signals: + void disconnected(); + void readyRead(); + +private: + QHostAddress m_proxyAddress; + quint16 m_proxyPort; + QString m_hostName; + quint16 m_hostPort; + QTcpSocket *m_socket; +}; + +class QXmppSocksServer : public QObject +{ + Q_OBJECT + +public: + QXmppSocksServer(QObject *parent=0); + bool listen(const QHostAddress &address, quint16 port = 0); + QHostAddress serverAddress() const; + quint16 serverPort() const; + void setHostName(const QString &hostName); + void setHostPort(quint16 hostPort); + void write(const QByteArray &data); + +signals: + void bytesWritten(qint64); + void disconnected(); + +private slots: + void slotNewConnection(); + +private: + QString m_hostName; + quint16 m_hostPort; + QTcpServer *m_server; + QTcpSocket *m_socket; +}; + +#endif diff --git a/source/source.pro b/source/source.pro index 7d1368a8..52037813 100644 --- a/source/source.pro +++ b/source/source.pro @@ -17,6 +17,7 @@ HEADERS += QXmppUtils.h \ QXmppArchiveIq.h \ QXmppArchiveManager.h \ QXmppBind.h \ + QXmppByteStreamIq.h \ QXmppClient.h \ QXmppConfiguration.h \ QXmppConstants.h \ @@ -30,6 +31,7 @@ HEADERS += QXmppUtils.h \ QXmppRoster.h \ QXmppRosterIq.h \ QXmppSession.h \ + QXmppSocks.h \ QXmppStanza.h \ QXmppStream.h \ QXmppStreamInitiationIq.h \ @@ -51,6 +53,7 @@ SOURCES += QXmppUtils.cpp \ QXmppArchiveIq.cpp \ QXmppArchiveManager.cpp \ QXmppBind.cpp \ + QXmppByteStreamIq.cpp \ QXmppClient.cpp \ QXmppConfiguration.cpp \ QXmppConstants.cpp \ @@ -64,6 +67,7 @@ SOURCES += QXmppUtils.cpp \ QXmppRoster.cpp \ QXmppRosterIq.cpp \ QXmppSession.cpp \ + QXmppSocks.cpp \ QXmppStanza.cpp \ QXmppStream.cpp \ QXmppStreamInitiationIq.cpp \ |
