diff --git a/Cargo.lock b/Cargo.lock index 22a372c..20f55ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -60,18 +66,15 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" dependencies = [ - "brotli", - "flate2", + "compression-codecs", + "compression-core", "futures-core", - "memchr", "pin-project-lite", "tokio", - "zstd", - "zstd-safe", ] [[package]] @@ -161,9 +164,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.1" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -203,9 +206,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.47" +version = "1.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" dependencies = [ "find-msvc-tools", "jobserver", @@ -248,6 +251,26 @@ dependencies = [ "inout", ] +[[package]] +name = "compression-codecs" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -265,9 +288,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -313,9 +336,9 @@ checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -327,6 +350,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -436,15 +465,21 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "serde", + "serde_core", +] [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -717,9 +752,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.4", "libc", @@ -727,9 +762,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -808,6 +843,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -832,6 +868,7 @@ dependencies = [ "cbc", "chrono", "dotenvy", + "hashbrown", "http-body-util", "hyper", "reqwest", @@ -1144,9 +1181,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ "web-time", "zeroize", @@ -1271,6 +1308,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.11" @@ -1459,9 +1502,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" dependencies = [ "async-compression", "bitflags", @@ -1493,9 +1536,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -1505,9 +1548,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -1516,9 +1559,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -1537,9 +1580,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -1633,9 +1676,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1646,9 +1689,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -1659,9 +1702,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1669,9 +1712,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -1682,18 +1725,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -1970,18 +2013,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", @@ -2068,9 +2111,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 8e7ab0a..30b2f67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ thiserror = "2" http-body-util = "0.1.1" chrono = { version = "0.4", features = ["clock"] } reqwest = { version = "0.12", features = ["json", "rustls-tls", "gzip"], default-features = false } +hashbrown = { version = "0.16", features = ["serde"] } \ No newline at end of file diff --git a/src/proxy/crypto.rs b/src/crypto.rs similarity index 63% rename from src/proxy/crypto.rs rename to src/crypto.rs index b421d58..4298b5a 100644 --- a/src/proxy/crypto.rs +++ b/src/crypto.rs @@ -1,12 +1,13 @@ use std::fmt; use aes::Aes128; -use base64::{Engine as _, engine::general_purpose::STANDARD}; +use base64::{Engine, engine::general_purpose::STANDARD}; use cbc::{ - Decryptor, - cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7}, + Decryptor, Encryptor, + cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7}, }; +type Aes128CbcEnc = Encryptor; type Aes128CbcDec = Decryptor; #[derive(Debug)] @@ -14,6 +15,7 @@ pub enum CryptoError { Base64(base64::DecodeError), Aes(cbc::cipher::InvalidLength), Unpad(cbc::cipher::block_padding::UnpadError), + Pad(aes::cipher::inout::PadError), } impl fmt::Display for CryptoError { @@ -22,6 +24,7 @@ impl fmt::Display for CryptoError { CryptoError::Base64(e) => write!(f, "Base64 decode failed: {}", e), CryptoError::Aes(e) => write!(f, "AES decryption failed: {}", e), CryptoError::Unpad(e) => write!(f, "PKCS7 unpadding failed: {}", e), + CryptoError::Pad(e) => write!(f, "PKCS7 padding failed: {}", e), } } } @@ -52,3 +55,19 @@ pub fn decrypt(ciphertext_b64: &str, key: &str, iv: &str) -> Result Result { + // 1. Initialize AES-128 in CBC mode + let key_bytes = key.as_bytes(); + let iv_bytes = iv.as_bytes(); + let encryptor = Aes128CbcEnc::new_from_slices(key_bytes, iv_bytes).map_err(CryptoError::Aes)?; + + // 2. Encrypt the plaintext with PKCS7 padding + let mut buffer = plaintext.as_bytes().to_vec(); + let ciphertext = encryptor + .encrypt_padded_mut::(&mut buffer, plaintext.len()) + .map_err(CryptoError::Pad)?; + + // 3. Base64 encode the ciphertext + Ok(STANDARD.encode(ciphertext)) +} diff --git a/src/jsonrpc.rs b/src/jsonrpc.rs index 8b13789..c3861f0 100644 --- a/src/jsonrpc.rs +++ b/src/jsonrpc.rs @@ -1 +1,19 @@ +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[derive(Serialize, Deserialize)] +pub struct Request { + pub method: String, + pub params: Option>, + pub id: Value, + pub jsonrpc: Option +} + +#[derive(Serialize, Deserialize)] +pub struct Response { + pub result: Option, + pub params: Option>, + pub id: Value, + pub jsonrpc: Option +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index efdc001..8650165 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ use std::{net::SocketAddr, sync::Arc}; use anyhow::Context; -use axum::{Router, routing::any}; +use axum::handler::Handler; use dotenvy::dotenv; +use serde_json::Value; +use tokio::sync::RwLock; use tower_http::compression::CompressionLayer; use tracing::{error, info, level_filters::LevelFilter}; use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +mod crypto; mod jsonrpc; +mod middleware; mod proxy; #[derive(Clone)] @@ -16,6 +20,8 @@ pub struct AppState { pub target_url: reqwest::Url, pub key: String, pub iv: String, + pub command_queue: Arc>>, + pub saved_tactics: Arc>>, } const DEFAULT_TARGET_URL: &str = "https://cloud.linspirer.com:883"; @@ -65,11 +71,16 @@ async fn main() -> anyhow::Result<()> { target_url, key, iv, + command_queue: Arc::new(RwLock::new(Vec::new())), + saved_tactics: Arc::new(RwLock::new(None)), }); - // Build our application with a single route - let app = Router::new() - .route("/{*path}", any(proxy::proxy_handler)) + // Build our application + let app = proxy::proxy_handler + .layer(axum::middleware::from_fn_with_state( + state.clone(), + middleware::log_middleware, + )) .layer(CompressionLayer::new().gzip(true)) .with_state(state); diff --git a/src/middleware.rs b/src/middleware.rs new file mode 100644 index 0000000..7a687b4 --- /dev/null +++ b/src/middleware.rs @@ -0,0 +1,178 @@ +use std::str; +use std::sync::Arc; + +use axum::{ + extract::{OriginalUri, State}, + http::{Request, StatusCode}, + middleware::Next, + response::{IntoResponse, Response}, +}; +use http_body_util::BodyExt; +use serde_json::Value; +use tracing::{info, warn}; + +use crate::{AppState, crypto}; + +enum ResponseBody { + Original(String), + Modified(Value), +} + +pub async fn log_middleware( + State(state): State>, + OriginalUri(uri): OriginalUri, + req: Request, + next: Next, +) -> impl IntoResponse { + let (parts, body) = req.into_parts(); + let body_bytes = match body.collect().await { + Ok(body) => body.to_bytes(), + Err(e) => { + warn!("Failed to read request body: {}", e); + return ( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to read request body".to_string(), + ) + .into_response(); + } + }; + + let path = uri.path(); + + let (decrypted_request_log, method) = match str::from_utf8(&body_bytes) + .map_err(anyhow::Error::from) + .and_then(|body| process_and_log_request(body, &state.key, &state.iv)) + { + Ok(request_data) => { + let method = request_data + .get("method") + .and_then(Value::as_str) + .map(str::to_string); + (request_data, method) + } + Err(e) => { + warn!("Failed to process request for logging: {}", e); + let val = Value::String("Could not decrypt request".to_string()); + (val, None) + } + }; + + let req = Request::from_parts(parts, axum::body::Body::from(body_bytes)); + let res = next.run(req).await; + + let (resp_parts, body_bytes) = { + let (parts, body) = res.into_parts(); + let bytes = match body.collect().await { + Ok(b) => b.to_bytes(), + Err(e) => { + warn!("Failed to read response body: {}", e); + return ( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to read response body".to_string(), + ) + .into_response(); + } + }; + (parts, bytes) + }; + let resp_body_text = String::from_utf8(body_bytes.clone().to_vec()).unwrap_or_default(); + + let response_body_to_log = if Some("com.linspirer.device.getcommand") == method.as_deref() { + match handle_getcommand_response(&resp_body_text, &state).await { + Ok(new_body) => ResponseBody::Modified(new_body), + Err(e) => { + warn!( + "Failed to handle getcommand response: {}. Responding with empty command list.", + e + ); + let mut empty_response = + serde_json::from_str::(&resp_body_text).unwrap_or(Value::Null); + if let Some(obj) = empty_response.as_object_mut() { + obj.insert("result".to_string(), Value::Array(vec![])); + } + ResponseBody::Modified(empty_response) + } + } + } else { + ResponseBody::Original(resp_body_text) + }; + + let (decrypted_response_for_log, final_response_body) = match response_body_to_log { + ResponseBody::Original(body_text) => { + let decrypted = decrypt_and_format(&body_text, &state.key, &state.iv) + .unwrap_or_else(|_| "Could not decrypt or format response".to_string()); + (decrypted, body_text) + } + ResponseBody::Modified(body_value) => { + let pretty_printed = serde_json::to_string_pretty(&body_value).unwrap_or_default(); + let encrypted = crypto::encrypt(&pretty_printed, &state.key, &state.iv) + .unwrap_or_else(|_| "Failed to encrypt modified response".to_string()); + (pretty_printed, encrypted) + } + }; + + info!( + "{}\nRequest:\n{}\nResponse:\n{}\n{}", + path, + serde_json::to_string_pretty(&decrypted_request_log).unwrap_or_default(), + decrypted_response_for_log, + "-".repeat(80), + ); + + let mut response_builder = Response::builder().status(resp_parts.status); + if !resp_parts.headers.is_empty() { + *response_builder.headers_mut().unwrap() = resp_parts.headers; + } + response_builder + .body(axum::body::Body::from(final_response_body)) + .unwrap() +} + +fn process_and_log_request(body: &str, key: &str, iv: &str) -> anyhow::Result { + let mut request_data: Value = serde_json::from_str(body)?; + + if let Some(params_value) = request_data.get_mut("params") + && let Some(params_str) = params_value.as_str() + { + let params_str_owned = params_str.to_string(); + match crypto::decrypt(¶ms_str_owned, key, iv) { + Ok(decrypted_str) => { + let decrypted_params: Value = + serde_json::from_str(&decrypted_str).unwrap_or(Value::String(decrypted_str)); + *params_value = decrypted_params; + } + Err(e) => { + *params_value = Value::String(format!("decrypt failed: {}", e)); + } + } + } + Ok(request_data) +} + +async fn handle_getcommand_response(body_text: &str, state: &Arc) -> anyhow::Result { + let decrypted = crypto::decrypt(body_text, &state.key, &state.iv)?; + let mut response_json: Value = serde_json::from_str(&decrypted)?; + + if let Some(result) = response_json.get("result") + && let Some(commands) = result.as_array() + && !commands.is_empty() + { + let mut queue = state.command_queue.write().await; + for cmd in commands { + queue.push(cmd.clone()); + info!("Added command to the queue: {:?}", cmd); + } + } + + if let Some(obj) = response_json.as_object_mut() { + obj.insert("result".to_string(), Value::Array(vec![])); + } + + Ok(response_json) +} + +fn decrypt_and_format(body_text: &str, key: &str, iv: &str) -> anyhow::Result { + let decrypted = crypto::decrypt(body_text, key, iv)?; + let formatted: Value = serde_json::from_str(&decrypted)?; + Ok(serde_json::to_string_pretty(&formatted)?) +} \ No newline at end of file diff --git a/src/proxy.rs b/src/proxy.rs index 8490d51..048b8e7 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,23 +1,21 @@ use std::sync::Arc; use axum::{ - extract::{Path, State}, + extract::{OriginalUri, State}, http::{Request, StatusCode}, response::{IntoResponse, Response}, }; use http_body_util::BodyExt; -use serde_json::Value; -use tracing::{error, info, warn}; +use tracing::error; use crate::AppState; -mod crypto; - pub async fn proxy_handler( State(state): State>, - Path(path): Path, + OriginalUri(uri): OriginalUri, req: Request, ) -> impl IntoResponse { + let path = uri.path(); let (parts, body) = req.into_parts(); let body = match body.collect().await { Ok(body) => body.to_bytes(), @@ -31,22 +29,8 @@ pub async fn proxy_handler( } }; - let decrypted_request_log = match str::from_utf8(&body) - .map(|body| process_and_log_request(body, &state.key, &state.iv)) - { - Ok(Ok(log)) => log, - Ok(Err(e)) => { - warn!("Failed to process request for logging: {}", e); - Value::String("Could not decrypt request".to_string()) - } - Err(e) => { - warn!("Failed to decode request for logging: {}", e); - Value::String("Could not decrypt request".to_string()) - } - }; - let mut target_url = state.target_url.clone(); - target_url.set_path(&path); + target_url.set_path(path); target_url.set_query(parts.uri.query()); let mut forwarded_req = state.client.request(parts.method, target_url); if !parts.headers.is_empty() { @@ -77,21 +61,6 @@ pub async fn proxy_handler( } }; - let decrypted_response_log = match decrypt_response(&resp_body, &state.key, &state.iv).await { - Ok(log) => log, - Err(e) => { - warn!("Failed to process response for logging: {}", e); - "Could not decrypt response".to_string() - } - }; - - info!( - "\nRequest:\n{}\nResponse:\n{}\n{}", - serde_json::to_string_pretty(&decrypted_request_log).unwrap_or_default(), - decrypted_response_log, - "-".repeat(80), - ); - let mut response_builder = Response::builder().status(resp_parts.status); if !resp_parts.headers.is_empty() { *response_builder.headers_mut().unwrap() = resp_parts.headers; @@ -101,33 +70,6 @@ pub async fn proxy_handler( .unwrap() } -fn process_and_log_request(body: &str, key: &str, iv: &str) -> anyhow::Result { - let mut request_data: Value = serde_json::from_str(body)?; - - if let Some(params_value) = request_data.get_mut("params") - && let Some(params_str) = params_value.as_str() - { - let params_str_owned = params_str.to_string(); - match crypto::decrypt(¶ms_str_owned, key, iv) { - Ok(decrypted_str) => { - let decrypted_params: Value = - serde_json::from_str(&decrypted_str).unwrap_or(Value::String(decrypted_str)); - *params_value = decrypted_params; - } - Err(e) => { - *params_value = Value::String(format!("decrypt failed: {}", e)); - } - } - } - Ok(request_data) -} - -async fn decrypt_response(body_text: &str, key: &str, iv: &str) -> anyhow::Result { - let decrypted = crypto::decrypt(body_text, key, iv)?; - let formatted: Value = serde_json::from_str(&decrypted)?; - Ok(serde_json::to_string_pretty(&formatted)?) -} - async fn clone_response( resp: reqwest::Response, ) -> anyhow::Result<(axum::http::response::Parts, String)> {