aboutsummaryrefslogtreecommitdiff
path: root/source/QXmppSocks.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2010-02-25 12:21:53 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2010-02-25 12:21:53 +0000
commitb9123796e79b1f9c08b5b113900826008f5d6dc8 (patch)
tree5a18ffd99ee0af574f83f3e88aaf421abf5aa68c /source/QXmppSocks.cpp
parent9334406b1ac2716ba799c5bb71d25efefaca29bc (diff)
downloadqxmpp-b9123796e79b1f9c08b5b113900826008f5d6dc8.tar.gz
add code for handling SOCKS5 bytestreams
Diffstat (limited to 'source/QXmppSocks.cpp')
-rw-r--r--source/QXmppSocks.cpp302
1 files changed, 302 insertions, 0 deletions
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);
+}