summaryrefslogtreecommitdiff
path: root/agents/webproxyconn.cc
diff options
context:
space:
mode:
Diffstat (limited to 'agents/webproxyconn.cc')
-rw-r--r--agents/webproxyconn.cc187
1 files changed, 187 insertions, 0 deletions
diff --git a/agents/webproxyconn.cc b/agents/webproxyconn.cc
new file mode 100644
index 0000000..91a9510
--- /dev/null
+++ b/agents/webproxyconn.cc
@@ -0,0 +1,187 @@
+#include <QtCore/QDebug>
+#include <QtCore/QUrl>
+
+#include "sappeer.h"
+#include "endianhelpers.h"
+#include "webproxytrans.h"
+#include "webproxyconn.h"
+
+WebProxyConn::WebProxyConn(SAPConnection *conn, QObject *parent)
+ : QObject(parent), _conn(conn),
+ _in(conn->getSocket(501)), _out(conn->getSocket(502))
+{
+ connect(_conn, SIGNAL(disconnected()), SLOT(deleteLater()));
+ connect(_conn, SIGNAL(destroyed()), SLOT(deleteLater()));
+ Q_ASSERT(_in && _out);
+ connect(_in, SIGNAL(messageReceived()), SLOT(handleMessageReceived()));
+}
+
+WebProxyConn::Message WebProxyConn::unpackMessage(const QByteArray &data)
+{
+ Message msg;
+ int offset = 0;
+ msg.command = read<quint8>(data, offset);
+ msg.subCommand = read<quint8>(data, offset);
+ msg.type = static_cast<MessageType>(read<quint8>(data, offset));
+ msg.transactionId = read<quint8>(data, offset);
+
+ const quint32 len = read<quint32>(data, offset);
+ msg.payload = data.mid(offset, len);
+
+ return msg;
+}
+
+QByteArray WebProxyConn::packMessage(const Message &msg)
+{
+ QByteArray data;
+ append<quint8>(data, msg.command);
+ append<quint8>(data, msg.subCommand);
+ append<quint8>(data, msg.type);
+ append<quint8>(data, msg.transactionId);
+ append<quint32>(data, msg.payload.size());
+ data.append(msg.payload);
+ return data;
+}
+
+WebProxyConn::RequestHeader WebProxyConn::parseRequestHeader(const QByteArray &req)
+{
+ RequestHeader hdr;
+ int first = req.indexOf('\n');
+ if (first <= 0) {
+ qWarning() << "Invalid request header";
+ return hdr;
+ }
+
+ QStringList reqLine = QString::fromLatin1(req.mid(0, first).trimmed()).split(' ');
+ QString method = reqLine.at(0);
+
+ if (QString::compare(method, "connect", Qt::CaseInsensitive) == 0) {
+ hdr.connect = true;
+ QStringList host = reqLine.at(1).split(':');
+ hdr.host = host.at(0);
+ hdr.port = host.at(1).toUInt();
+ } else {
+ QUrl url(reqLine.at(1));
+ hdr.connect = false;
+ hdr.host = url.host(QUrl::EncodeUnicode);
+ if (QString::compare(url.scheme(), "https", Qt::CaseInsensitive) == 0) {
+ hdr.port = url.port(443);
+ } else {
+ hdr.port = url.port(80);
+ }
+ }
+
+ return hdr;
+}
+
+QByteArray WebProxyConn::removeHeaders(const QByteArray &req)
+{
+ int offset = 0;
+ int next;
+
+ while ((next = req.indexOf('\n', offset)) > 0) {
+ QByteArray line = req.mid(offset, next - offset).trimmed();
+ if (line.isEmpty()) {
+ return req.mid(next + 1);
+ }
+ offset = next + 1;
+ }
+
+ return QByteArray();
+}
+
+void WebProxyConn::sendMessage(const Message &msg)
+{
+ _out->send(packMessage(msg));
+}
+
+void WebProxyConn::handleRequest(const Message &msg)
+{
+ QByteArray payload = msg.payload;
+ WebProxyTrans *trans = _trans.value(msg.transactionId, 0);
+
+ if (!trans) {
+ RequestHeader hdr = parseRequestHeader(msg.payload);
+ qDebug() << "Starting transaction" << msg.transactionId << "to" << hdr.host << hdr.port << (hdr.connect ? "tunnel" : "http");
+
+ trans = new WebProxyTrans(msg.transactionId, hdr.connect, hdr.host, hdr.port, this);
+ connect(trans, SIGNAL(dataReceived(QByteArray)), this, SLOT(handleTransDataReceived(QByteArray)));
+ connect(trans, SIGNAL(disconnected()), this, SLOT(handleTransDisconnected()));
+
+ // Discard request body, but if it was a CONNECT request.
+ if (hdr.connect) {
+ payload = removeHeaders(payload);
+ }
+
+ _trans.insert(msg.transactionId, trans);
+ }
+
+ if (!payload.isEmpty()) {
+ trans->write(payload);
+ }
+}
+
+void WebProxyConn::handleAbort(const Message &msg)
+{
+ qDebug() << "Abort transaction" << msg.transactionId;
+ WebProxyTrans *trans = _trans.value(msg.transactionId, 0);
+ if (trans) {
+ delete trans;
+ _trans.remove(msg.transactionId);
+ } else {
+ qWarning() << "Transaction" << msg.transactionId << "does not exist";
+ }
+}
+
+void WebProxyConn::handleMessageReceived()
+{
+ QByteArray data = _in->receive();
+ Message msg = unpackMessage(data);
+
+ if (msg.command != 1 || msg.subCommand != 1) {
+ qWarning() << "Invalid command/subcommand: " << msg.command << "/" << msg.subCommand;
+ return;
+ }
+
+ switch (msg.type) {
+ case MessageRequest:
+ handleRequest(msg);
+ break;
+ case MessageAbort:
+ handleAbort(msg);
+ break;
+ default:
+ qWarning() << "Unknown request type" << msg.type;
+ }
+}
+
+void WebProxyConn::handleTransDataReceived(const QByteArray &data)
+{
+ WebProxyTrans *trans = static_cast<WebProxyTrans*>(sender());
+ Message msg;
+ msg.command = 1;
+ msg.subCommand = 1;
+ msg.type = MessageResponse;
+ msg.transactionId = trans->transactionId();
+ msg.payload = data;
+
+ sendMessage(msg);
+}
+
+void WebProxyConn::handleTransDisconnected()
+{
+ WebProxyTrans *trans = static_cast<WebProxyTrans*>(sender());
+ Message msg;
+ msg.command = 1;
+ msg.subCommand = 1;
+ msg.type = MessageAbort;
+ msg.transactionId = trans->transactionId();
+ msg.payload.clear(); // Empty payload signals disconnection
+
+ qDebug() << "Sending disconnected event";
+
+ sendMessage(msg);
+
+ _trans.remove(msg.transactionId);
+ trans->deleteLater();
+}