summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sapd.pro6
-rw-r--r--sapsocket.cc3
-rw-r--r--webproxyconn.cc160
-rw-r--r--webproxyconn.h36
-rw-r--r--webproxytrans.cc74
-rw-r--r--webproxytrans.h42
6 files changed, 293 insertions, 28 deletions
diff --git a/sapd.pro b/sapd.pro
index f8a4f00..2ca4132 100644
--- a/sapd.pro
+++ b/sapd.pro
@@ -58,7 +58,8 @@ SOURCES += main.cc \
notificationagent.cc \
notificationconn.cc \
webproxyagent.cc \
- webproxyconn.cc
+ webproxyconn.cc \
+ webproxytrans.cc
HEADERS += \
sapbtlistener.h \
@@ -84,7 +85,8 @@ HEADERS += \
notificationconn.h \
endianhelpers.h \
webproxyagent.h \
- webproxyconn.h
+ webproxyconn.h \
+ webproxytrans.h
OTHER_FILES += \
rpm/sapd.yaml \
diff --git a/sapsocket.cc b/sapsocket.cc
index b41e8f5..e7376d9 100644
--- a/sapsocket.cc
+++ b/sapsocket.cc
@@ -88,8 +88,6 @@ void SAPSocket::acceptIncomingData(const QByteArray &data)
} else {
_inLastSeqNum = frame.seqNum;
- qDebug() << "Realiable received" << _inLastSeqNum;
-
if (!_timer.isActive()) {
_timer.start(DELAYED_ACK_TIME, Qt::CoarseTimer, this);
}
@@ -110,7 +108,6 @@ void SAPSocket::timerEvent(QTimerEvent *event)
{
if (event->timerId() == _timer.timerId()) {
if (_inLastSeqNum != _inLastAck) {
- qDebug() << "Acking" << _inLastAck << _inLastSeqNum;
peer()->writeAckToSession(_sessionId, _inLastSeqNum);
_inLastAck = _inLastSeqNum;
}
diff --git a/webproxyconn.cc b/webproxyconn.cc
index 1eb4909..d1590ca 100644
--- a/webproxyconn.cc
+++ b/webproxyconn.cc
@@ -1,7 +1,9 @@
#include <QtCore/QDebug>
+#include <QtCore/QUrl>
#include "sappeer.h"
#include "endianhelpers.h"
+#include "webproxytrans.h"
#include "webproxyconn.h"
WebProxyConn::WebProxyConn(SAPConnection *conn, QObject *parent)
@@ -13,13 +15,13 @@ WebProxyConn::WebProxyConn(SAPConnection *conn, QObject *parent)
connect(_in, SIGNAL(messageReceived()), SLOT(handleMessageReceived()));
}
-WebProxyConn::RequestMessage WebProxyConn::unpackRequestMessage(const QByteArray &data)
+WebProxyConn::Message WebProxyConn::unpackMessage(const QByteArray &data)
{
- RequestMessage msg;
+ Message msg;
int offset = 0;
msg.command = read<quint8>(data, offset);
msg.subCommand = read<quint8>(data, offset);
- msg.type = static_cast<RequestMessageType>(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);
@@ -28,35 +30,163 @@ WebProxyConn::RequestMessage WebProxyConn::unpackRequestMessage(const QByteArray
return msg;
}
-void WebProxyConn::handleStartTransaction(const RequestMessage &msg)
+QByteArray WebProxyConn::packMessage(const Message &msg)
{
- QString req = QString::fromUtf8(msg.payload);
+ 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;
+
qDebug() << req;
+ qDebug() << "--- end ---";
+
+ 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::handleCancelTransaction(const RequestMessage &msg)
+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 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 if it was a CONNECT request.
+ if (hdr.connect) {
+ payload = removeHeaders(payload);
+ }
+
+ _trans.insert(msg.transactionId, trans);
+ }
+
+ if (!payload.isEmpty()) {
+ qDebug() << "Sending" << msg.transactionId << QString::fromLatin1(payload.mid(0, 30)) << "-";
+ 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();
- RequestMessage req = unpackRequestMessage(data);
+ Message msg = unpackMessage(data);
- if (req.command != 1 || req.subCommand != 1) {
- qWarning() << "Invalid command/subcommand: " << req.command << "/" << req.subCommand;
+ if (msg.command != 1 || msg.subCommand != 1) {
+ qWarning() << "Invalid command/subcommand: " << msg.command << "/" << msg.subCommand;
return;
}
- switch (req.type) {
- case RequestStartTransaction:
- handleStartTransaction(req);
+ switch (msg.type) {
+ case MessageRequest:
+ handleRequest(msg);
break;
- case RequestCancelTransaction:
- handleCancelTransaction(req);
+ case MessageAbort:
+ handleAbort(msg);
break;
default:
- qWarning() << "Unknown request type" << req.type;
+ 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;
+
+ qDebug() << "Receiving" << msg.transactionId << QString::fromLatin1(data.mid(0, 30)) << "-";
+
+ 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();
+}
diff --git a/webproxyconn.h b/webproxyconn.h
index 705428d..c183462 100644
--- a/webproxyconn.h
+++ b/webproxyconn.h
@@ -5,6 +5,8 @@
#include "sapconnection.h"
#include "sapsocket.h"
+class WebProxyTrans;
+
class WebProxyConn : public QObject
{
Q_OBJECT
@@ -13,31 +15,49 @@ public:
WebProxyConn(SAPConnection *conn, QObject *parent = 0);
protected:
- enum RequestMessageType {
- RequestStartTransaction = 1,
- RequestCancelTransaction = 4
+ enum MessageType {
+ MessageRequest = 1,
+ MessageResponse = 2,
+ MessageError = 3,
+ MessageAbort = 4
};
- struct RequestMessage {
+ struct Message {
quint8 command; // Seems to be always 1
quint8 subCommand; // Seems to be always 1
- RequestMessageType type;
+ MessageType type;
quint8 transactionId; // Monotonically increasing
QByteArray payload;
};
- static RequestMessage unpackRequestMessage(const QByteArray &data);
+ static Message unpackMessage(const QByteArray &data);
+ static QByteArray packMessage(const Message &msg);
+
+ struct RequestHeader {
+ /** Whether this is a CONNECT request, i.e. tunnel. */
+ bool connect;
+ QString host;
+ int port;
+ };
+
+ static RequestHeader parseRequestHeader(const QByteArray &req);
+ static QByteArray removeHeaders(const QByteArray &req);
+
+ void sendMessage(const Message &msg);
- void handleStartTransaction(const RequestMessage &msg);
- void handleCancelTransaction(const RequestMessage &msg);
+ void handleRequest(const Message &msg);
+ void handleAbort(const Message &msg);
private slots:
void handleMessageReceived();
+ void handleTransDataReceived(const QByteArray &data);
+ void handleTransDisconnected();
private:
SAPConnection *_conn;
SAPSocket *_in;
SAPSocket *_out;
+ QMap<int, WebProxyTrans*> _trans;
};
#endif // WEBPROXYCONN_H
diff --git a/webproxytrans.cc b/webproxytrans.cc
new file mode 100644
index 0000000..23b41b7
--- /dev/null
+++ b/webproxytrans.cc
@@ -0,0 +1,74 @@
+#include <QtCore/QDebug>
+#include <QtCore/QUrl>
+
+#include "webproxyconn.h"
+#include "webproxytrans.h"
+
+static const QByteArray connectResponse("HTTP/1.1 200 Connection established\r\n\r\n");
+
+WebProxyTrans::WebProxyTrans(int transactionId, bool tunnel, const QString &host, int port, WebProxyConn *conn)
+ : QObject(conn), _transactionId(transactionId), _tunnel(tunnel), _socket(new QTcpSocket(this))
+{
+ connect(_socket, SIGNAL(connected()), this, SLOT(handleSocketConnected()));
+ connect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(handleSocketDataWritten(qint64)));
+ connect(_socket, SIGNAL(readyRead()), this, SLOT(handleSocketDataReady()));
+ connect(_socket, SIGNAL(disconnected()), this, SLOT(handleSocketDisconnected()));
+
+ _socket->connectToHost(host, port);
+}
+
+int WebProxyTrans::transactionId() const
+{
+ return _transactionId;
+}
+
+void WebProxyTrans::write(const QByteArray &data)
+{
+ if (!_outBuf.isEmpty() || !_socket->isValid()) {
+ _outBuf.append(data);
+ } else if (_socket->isValid()) {
+ // Write directly to socket
+ _socket->write(data);
+ }
+}
+
+void WebProxyTrans::handleSocketConnected()
+{
+ qDebug() << "Transaction" << _transactionId << "Connected";
+ if (_tunnel) {
+ emit dataReceived(connectResponse);
+ }
+ writeFromBuffer();
+}
+
+void WebProxyTrans::handleSocketDataWritten(qint64 written)
+{
+ Q_UNUSED(written);
+ writeFromBuffer();
+}
+
+void WebProxyTrans::handleSocketDataReady()
+{
+ emit dataReceived(_socket->readAll());
+}
+
+void WebProxyTrans::handleSocketDisconnected()
+{
+ qDebug() << "Transaction" << _transactionId << "Disconnected";
+ emit disconnected();
+}
+
+WebProxyConn* WebProxyTrans::conn()
+{
+ return static_cast<WebProxyConn*>(parent());
+}
+
+void WebProxyTrans::writeFromBuffer()
+{
+ if (!_outBuf.isEmpty()) {
+ qint64 written = _socket->write(_outBuf);
+ if (written > 0) {
+ _outBuf.remove(0, written);
+ }
+ }
+}
diff --git a/webproxytrans.h b/webproxytrans.h
new file mode 100644
index 0000000..950c7b9
--- /dev/null
+++ b/webproxytrans.h
@@ -0,0 +1,42 @@
+#ifndef WEBPROXYTRANS_H
+#define WEBPROXYTRANS_H
+
+#include <QtCore/QIODevice>
+#include <QtNetwork/QTcpSocket>
+
+class WebProxyConn;
+
+class WebProxyTrans : public QObject
+{
+ Q_OBJECT
+public:
+ explicit WebProxyTrans(int transactionId, bool tunnel, const QString &host, int port, WebProxyConn *conn);
+
+ int transactionId() const;
+
+ void write(const QByteArray &data);
+
+signals:
+ void connected();
+ void dataReceived(const QByteArray &data);
+ void error();
+ void disconnected();
+
+private slots:
+ void handleSocketConnected();
+ void handleSocketDataWritten(qint64 written);
+ void handleSocketDataReady();
+ void handleSocketDisconnected();
+
+private:
+ WebProxyConn* conn();
+ void writeFromBuffer();
+
+private:
+ int _transactionId;
+ bool _tunnel;
+ QTcpSocket *_socket;
+ QByteArray _outBuf;
+};
+
+#endif // WEBPROXYTRANS_H