diff --git a/historymodel.cpp b/historymodel.cpp index d672cb0..89bcda1 100644 --- a/historymodel.cpp +++ b/historymodel.cpp @@ -2,6 +2,26 @@ #include #include +std::string HistoryModel::getRequestHeader(const HistoryItem &item) const +{ + std::stringstream result; + result << item.method << " " << item.scheme << "://" << item.host << item.path << " " << item.request_http_version << "\n"; + for(auto& [k,v] : item.request_headers) { + result << k << ": " << v << "\n"; + } + return result.str(); +} + +std::string HistoryModel::getResponseHeader(const HistoryItem &item) const +{ + std::stringstream result; + result << item.response_http_version << " " << item.status_code << " " << item.reason << "\n"; + for(auto& [k,v] : item.response_headers) { + result << k << ": " << v << "\n"; + } + return result.str(); +} + HistoryModel::HistoryModel(QObject *parent) { Q_UNUSED(parent); @@ -21,7 +41,7 @@ int HistoryModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 7; + return 11; } QVariant HistoryModel::data(const QModelIndex &index, int role) const @@ -64,6 +84,18 @@ QVariant HistoryModel::data(const QModelIndex &index, int role) const case 6:{ return QString::number(item.size); } + case 7:{ + return QString::fromStdString(getRequestHeader(item)); + } + case 8:{ + return QString::fromStdString(item.request_content); + } + case 9:{ + return QString::fromStdString(getResponseHeader(item)); + } + case 10:{ + return QString::fromStdString(item.response_content); + } } throw std::out_of_range("history model col"); } catch (std::out_of_range const& exc) { @@ -101,6 +133,18 @@ QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int case 6: { return "size"; } + case 7: { + return "request headers"; + } + case 8: { + return "request content"; + } + case 9: { + return "response headers"; + } + case 10: { + return "response content"; + } } } return QVariant(); diff --git a/historymodel.h b/historymodel.h index bebe447..bed6295 100644 --- a/historymodel.h +++ b/historymodel.h @@ -16,10 +16,12 @@ struct HistoryItem { std::string reason; double rtt = 0.0; size_t size = 0; - std::string request_content; - std::string response_content; + std::string request_http_version; std::map request_headers; + std::string request_content; + std::string response_http_version; std::map response_headers; + std::string response_content; }; class HistoryModel : public QAbstractTableModel @@ -28,6 +30,8 @@ class HistoryModel : public QAbstractTableModel private: mutable std::mutex history_mutex; std::vector* current_items = nullptr; + std::string getRequestHeader(const HistoryItem& item) const; + std::string getResponseHeader(const HistoryItem& item) const; public: HistoryModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; diff --git a/mainwindow.cpp b/mainwindow.cpp index 3663bff..63fa875 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -45,9 +45,14 @@ void MainWindow::updateHistory() { void MainWindow::on_selectionChange(const QItemSelection &selection) { if(!selection.indexes().isEmpty()) { - auto index = selection.indexes().value(0); - qDebug() << history_proxy.itemData(index)[0].toInt(); - //ui->textEdit->setText(); + auto index = selection.indexes().at(7); + ui->textEdit->setText(history_proxy.data(index).toString()); + index = selection.indexes().at(8); + ui->textEdit_2->setText(history_proxy.data(index).toString()); + index = selection.indexes().at(9); + ui->textEdit_3->setText(history_proxy.data(index).toString()); + index = selection.indexes().at(10); + ui->textEdit_4->setText(history_proxy.data(index).toString()); } } diff --git a/mitmaddon/littlesnitch.py b/mitmaddon/littlesnitch.py index e3f1661..8ff7bfb 100644 --- a/mitmaddon/littlesnitch.py +++ b/mitmaddon/littlesnitch.py @@ -85,13 +85,37 @@ class LittleSnitchBridge: self.q = Queue() self.thread = NetworkThread("network", self.q) self.thread.start() - + def request(self, flow): self.q.put({'msg': 'request', 'flow': flow.get_state()}) + def requestheaders(self, flow): + self.q.put({'msg': 'requestheaders', 'flow': flow.get_state()}) + def response(self, flow): self.q.put({'msg': 'response', 'flow': flow.get_state()}) + def responseheaders(self, flow): + self.q.put({'msg': 'responseheaders', 'flow': flow.get_state()}) + + def error(self, flow): + self.q.put({'msg': 'error', 'flow': flow.get_state()}) + + def websocket_handshake(self): + self.q.put({'msg': 'websocket_handshake', 'flow': flow.get_state()}) + + def websocket_start(self, flow): + self.q.put({'msg': 'websocket_start', 'flow': flow.get_state()}) + + def websocket_message(self, flow): + self.q.put({'msg': 'websocket_message', 'flow': flow.get_state()}) + + def websocket_error(self, flow): + self.q.put({'msg': 'websocket_error', 'flow': flow.get_state()}) + + def websocket_end(self, flow): + self.q.put({'msg': 'websocket_end', 'flow': flow.get_state()}) + addons = [ LittleSnitchBridge() ] diff --git a/networkthread.cpp b/networkthread.cpp index c4a870e..0471a66 100644 --- a/networkthread.cpp +++ b/networkthread.cpp @@ -51,13 +51,31 @@ void NetworkThread::process() { std::string msg_type; if(!json_get(j, msg_type, "msg")) { qDebug() << "broken message received " << response.to_string().c_str(); + } else if(msg_type == "responseheaders") { + qDebug() << "received " << msg_type.c_str(); } else if(msg_type == "response") { qDebug() << "received " << msg_type.c_str(); emit httpMessage(j); + } else if(msg_type == "requestheaders") { + qDebug() << "received " << msg_type.c_str(); } else if(msg_type == "request") { qDebug() << "received " << msg_type.c_str(); } else if(msg_type == "ping") { qDebug() << "received " << msg_type.c_str(); + } else if(msg_type == "error") { + qDebug() << "received " << msg_type.c_str(); + + // websocket events + } else if(msg_type == "websocket_handshake") { + qDebug() << "received " << msg_type.c_str(); + } else if(msg_type == "websocket_start") { + qDebug() << "received " << msg_type.c_str(); + std::cout << std::setw(4) << j << "\n\n"; + } else if(msg_type == "websocket_message") { + qDebug() << "received " << msg_type.c_str(); + } else if(msg_type == "websocket_end") { + qDebug() << "received " << msg_type.c_str(); + } else if(msg_type == "websocket_error") { } else { qDebug() << "unknown or broken message type received: " << msg_type.c_str(); } diff --git a/session.cpp b/session.cpp index 3649e1c..348047e 100644 --- a/session.cpp +++ b/session.cpp @@ -212,7 +212,9 @@ std::vector* Session::getHistoryItems() { i.reason = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.rtt = sqlite3_column_double(stmt_get_all_history, j++); i.size = sqlite3_column_int(stmt_get_all_history, j++); + i.request_http_version = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.request_content = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); + i.response_http_version = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.response_content = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.request_headers = getRequestHeader(i.id); i.response_headers = getResponseHeader(i.id); diff --git a/session.h b/session.h index 4d47c20..b9e4262 100644 --- a/session.h +++ b/session.h @@ -66,7 +66,7 @@ private: const char* get_setting = R"rstr( SELECT value FROM setting WHERE key=?; )rstr"; const char* get_request_headers = R"rstr( SELECT * FROM response_header WHERE response_id=?; )rstr"; const char* get_response_headers = R"rstr( SELECT * FROM request_header WHERE request_id=?; )rstr"; - const char* get_all_history = R"rstr( SELECT f.id,req.timestamp_start,req.method,req.scheme,req.host,req.port,req.path,res.status_code,res.reason,res.timestamp_end-res.timestamp_start,length(res.content),req.content,res.content FROM flow f JOIN request req ON f.request_id=req.id JOIN response res ON f.response_id=res.id; )rstr"; + const char* get_all_history = R"rstr( SELECT f.id,req.timestamp_start,req.method,req.scheme,req.host,req.port,req.path,res.status_code,res.reason,res.timestamp_end-res.timestamp_start,length(res.content),req.http_version,req.content,res.http_version,res.content FROM flow f JOIN request req ON f.request_id=req.id JOIN response res ON f.response_id=res.id; )rstr"; const char* insert_request = R"rstr( INSERT INTO request (server_ip_address, tls, content, scheme, method, host, port, http_version, path, timestamp_start, timestamp_end, error) VALUES (?,?,?,?,?,?,?,?,?,?,?,?); )rstr"; const char* insert_request_header = R"rstr( INSERT INTO request_header (key, value, request_id) VALUES (?,?,?); )rstr";