#include "session.h" Session::Session(QObject *parent) : QObject(parent) { } Session::~Session() { unload(); } void Session::open_db(std::filesystem::path path) { if(db) { unload(); } auto rc = sqlite3_open(path.c_str(), &db); if(rc != SQLITE_OK) { qDebug("cannot open database"); qDebug() << sqlite3_errmsg(db); unload(); throw; } } void Session::prepare(const char* query, sqlite3_stmt** stmt) { auto rc = sqlite3_prepare_v2(db, query, strlen(query), stmt, 0); if(rc != SQLITE_OK) { qDebug() << "cannot prepare statement"; qDebug() << query; qDebug() << sqlite3_errmsg(db); unload(); throw; } } bool Session::prepare_and_exec(const char* query) { sqlite3_stmt* res; prepare(query, &res); bool result = sqlite3_step(res)==SQLITE_OK; sqlite3_finalize(res); return result; } bool Session::table_exists(std::string name) { sqlite3_stmt* res; prepare(get_tbl, &res); bind_text(res, 1, name); auto rc = sqlite3_step(res); sqlite3_finalize(res); return rc == SQLITE_ROW; } bool Session::reset(sqlite3_stmt *stmt) { auto res = sqlite3_reset(stmt); if(res != SQLITE_ROW && res != SQLITE_DONE && res != SQLITE_OK) { qDebug() << "cannot reset statement"; qDebug() << sqlite3_errmsg(db); unload(); throw; return false; } return true; } std::map Session::getHeaders(int id, bool get) { auto stmt = stmt_get_request_headers; if(!get) { stmt = stmt_get_response_headers; } reset(stmt); std::map result; int j = 1; sqlite3_bind_int(stmt, j++, id); auto rc = sqlite3_step(stmt); while(rc == SQLITE_ROW) { int j = 0; std::string key = std::string((const char*)sqlite3_column_text(stmt, j++)); std::string value = std::string((const char*)sqlite3_column_text(stmt, j++)); result.insert({key, value}); rc = sqlite3_step(stmt); } if(rc != SQLITE_DONE) { qDebug() << "error getting history items" << rc; qDebug() << sqlite3_errmsg(db); return std::map(); } return result; } std::map Session::getRequestHeader(int id) { return getHeaders(id, true); } std::map Session::getResponseHeader(int id) { return getHeaders(id, false); } void Session::prepare_tables() { if(table_exists("setting")) { prepare(get_setting, &stmt_get_setting); auto v = getSetting("version"); if(v) { if(std::stoi(*v) != LITTLESNITCH_VERSION) { qDebug() << "version " << v.value().c_str() << " is not supported!"; unload(); throw; } qDebug() << "loaded session with version " << v.value().c_str(); } else { qDebug() << "setting table but no version found, aborting"; unload(); throw; } } else { prepare_and_exec(create_request_tbl); prepare_and_exec(create_request_header_tbl); prepare_and_exec(create_response_tbl); prepare_and_exec(create_response_header_tbl); prepare_and_exec(create_flow_tbl); prepare_and_exec(create_setting_tbl); prepare(get_setting, &stmt_get_setting); } prepare(get_all_history, &stmt_get_all_history); prepare(get_request_headers, &stmt_get_request_headers); prepare(get_response_headers, &stmt_get_response_headers); prepare(update_setting, &stmt_update_setting); prepare(insert_request, &stmt_insert_request); prepare(insert_request_header, &stmt_insert_request_header); prepare(insert_response, &stmt_insert_response); prepare(insert_response_header, &stmt_insert_response_header); prepare(insert_flow, &stmt_insert_flow); prepare(insert_setting, &stmt_insert_setting); saveSetting("version", std::to_string(LITTLESNITCH_VERSION)); } void Session::load(std::filesystem::path path) { open_db(path); prepare_tables(); loaded = true; } void Session::unload() { if(db) { sqlite3_close(db); } loaded = false; } bool Session::isLoaded() { return loaded; } std::optional Session::getSetting(std::string key) { reset(stmt_get_setting); int j = 1; bind_text(stmt_get_setting, j++, key); auto rc = sqlite3_step(stmt_get_setting); if(rc == SQLITE_ROW) { auto value = std::string((const char*)sqlite3_column_text(stmt_get_setting, 0)); return value; } else if(rc == SQLITE_DONE) { return std::nullopt; } else { qDebug() << "getting setting failed " << rc; qDebug() << sqlite3_errmsg(db); return std::nullopt; } return std::nullopt; } std::vector* Session::getHistoryItems() { reset(stmt_get_all_history); std::vector* result = new std::vector(); auto rc = sqlite3_step(stmt_get_all_history); while(rc == SQLITE_ROW) { int j = 0; HistoryItem i; i.id = sqlite3_column_int(stmt_get_all_history, j++); i.timestamp = sqlite3_column_double(stmt_get_all_history, j++); i.method = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.scheme = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.host = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.port = sqlite3_column_int(stmt_get_all_history, j++); i.path = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++)); i.status_code = sqlite3_column_int(stmt_get_all_history, j++); 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); result->push_back(i); rc = sqlite3_step(stmt_get_all_history); } if(rc != SQLITE_DONE) { qDebug() << "error getting history items" << rc; qDebug() << sqlite3_errmsg(db); delete result; return nullptr; } return result; } void Session::bind_text(sqlite3_stmt* stmt, int id, std::string text) { //qDebug() << id << " " << text.c_str() << " " << strlen(text.c_str()); sqlite3_bind_text(stmt, id, text.c_str(), -1, SQLITE_TRANSIENT); } int Session::saveRequest(http::Flow flow) { reset(stmt_insert_request); int j = 1; bind_text(stmt_insert_request, j++, flow.request.server_ip_address); sqlite3_bind_int(stmt_insert_request, j++, flow.request.tls); bind_text(stmt_insert_request, j++, flow.request.content); bind_text(stmt_insert_request, j++, flow.request.scheme); bind_text(stmt_insert_request, j++, flow.request.method); bind_text(stmt_insert_request, j++, flow.request.host); sqlite3_bind_int(stmt_insert_request, j++, flow.request.port); bind_text(stmt_insert_request, j++, flow.request.http_version); bind_text(stmt_insert_request, j++, flow.request.path); sqlite3_bind_double(stmt_insert_request, j++, flow.request.timestamp_start); sqlite3_bind_double(stmt_insert_request, j++, flow.request.timestamp_end); bind_text(stmt_insert_request, j++, flow.request.error); auto rc = sqlite3_step(stmt_insert_request); if(rc != SQLITE_DONE) { qDebug() << "inserting request failed" << rc; qDebug() << sqlite3_errmsg(db); return -1; } int id = sqlite3_last_insert_rowid(db); for(auto&[k,v] : flow.request.headers) { saveRequestHeader(k,v,id); } return id; } int Session::saveRequestHeader(std::string key, std::string value, int id) { reset(stmt_insert_request_header); int j = 1; bind_text(stmt_insert_request_header, j++, key); bind_text(stmt_insert_request_header, j++, value); sqlite3_bind_int(stmt_insert_request_header, j++, id); auto rc = sqlite3_step(stmt_insert_request_header); if(rc != SQLITE_DONE) { qDebug() << "inserting request header failed" << rc; qDebug() << sqlite3_errmsg(db); return -1; } return sqlite3_last_insert_rowid(db); } int Session::saveResponse(http::Flow flow) { reset(stmt_insert_response); int j = 1; sqlite3_bind_int(stmt_insert_response, j++, flow.response.status_code); bind_text(stmt_insert_response, j++, flow.response.http_version); bind_text(stmt_insert_response, j++, flow.response.reason); bind_text(stmt_insert_response, j++, flow.response.content); sqlite3_bind_double(stmt_insert_response, j++, flow.response.timestamp_start); sqlite3_bind_double(stmt_insert_response, j++, flow.response.timestamp_end); auto rc = sqlite3_step(stmt_insert_response); if(rc != SQLITE_DONE) { qDebug() << "inserting response failed" << rc; qDebug() << sqlite3_errmsg(db); return -1; } int id = sqlite3_last_insert_rowid(db); for(auto&[k,v] : flow.response.headers) { saveResponseHeader(k,v,id); } return id; } int Session::saveResponseHeader(std::string key, std::string value, int id) { reset(stmt_insert_response_header); int j = 1; bind_text(stmt_insert_response_header, j++, key); bind_text(stmt_insert_response_header, j++, value); sqlite3_bind_int(stmt_insert_response_header, j++, id); auto rc = sqlite3_step(stmt_insert_response_header); if(rc != SQLITE_DONE) { qDebug() << "inserting response header failed" << rc; qDebug() << sqlite3_errmsg(db); return -1; } return sqlite3_last_insert_rowid(db); } int Session::saveFlow(http::Flow flow) { reset(stmt_insert_flow); int request_id = saveRequest(flow); int response_id = saveResponse(flow); int j = 1; bind_text(stmt_insert_flow, j++, flow.uid); sqlite3_bind_int(stmt_insert_flow, j++, request_id); sqlite3_bind_int(stmt_insert_flow, j++, response_id); auto rc = sqlite3_step(stmt_insert_flow); if(rc != SQLITE_DONE) { qDebug() << "inserting flow failed" << rc; qDebug() << sqlite3_errmsg(db); return -1; } qDebug() << "saved flow"; return sqlite3_last_insert_rowid(db); } bool Session::saveSetting(std::string key, std::string value) { auto old_value = getSetting(key); if(!old_value) { reset(stmt_insert_setting); int j = 1; bind_text(stmt_insert_setting, j++, key); bind_text(stmt_insert_setting, j++, value); auto rc = sqlite3_step(stmt_insert_setting); if(rc != SQLITE_DONE) { qDebug() << "inserting setting failed" << rc; qDebug() << sqlite3_errmsg(db); return false; } return sqlite3_last_insert_rowid(db); } else { reset(stmt_update_setting); int j = 1; bind_text(stmt_update_setting, j++, value); bind_text(stmt_insert_setting, j++, key); auto rc = sqlite3_step(stmt_update_setting); if(rc != SQLITE_DONE) { qDebug() << "updating setting failed" << rc; qDebug() << sqlite3_errmsg(db); return false; } return true; } return false; }