diff options
Diffstat (limited to 'agents/webproxyconn.cc')
-rw-r--r-- | agents/webproxyconn.cc | 187 |
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(); +} |