#include #include #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(data, offset); msg.subCommand = read(data, offset); msg.type = static_cast(read(data, offset)); msg.transactionId = read(data, offset); const quint32 len = read(data, offset); msg.payload = data.mid(offset, len); return msg; } QByteArray WebProxyConn::packMessage(const Message &msg) { QByteArray data; append(data, msg.command); append(data, msg.subCommand); append(data, msg.type); append(data, msg.transactionId); append(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(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(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(); }