use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::time::Duration;
use anyhow::Result;
use serde_json::json;
use tracing::debug;
use super::protocol::{BridgeRequest, BridgeResponse};
pub struct BridgeClient {
port: u16,
}
impl BridgeClient {
pub fn new(port: u16) -> Self {
Self { port }
}
#[allow(dead_code)]
pub fn port(&self) -> u16 {
self.port
}
pub fn send_command(
&self,
command: &str,
args: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let addr: std::net::SocketAddr = format!("127.0.0.1:{}", self.port)
.parse()
.map_err(|e| anyhow::anyhow!("Invalid address: {}", e))?;
let mut stream =
TcpStream::connect_timeout(&addr, Duration::from_secs(10)).map_err(|e| {
anyhow::anyhow!("Failed to connect to bridge on port {}: {}", self.port, e)
})?;
stream.set_read_timeout(Some(Duration::from_secs(300))).ok();
stream.set_write_timeout(Some(Duration::from_secs(30))).ok();
let request = BridgeRequest {
command: command.to_string(),
args,
};
let request_json = serde_json::to_string(&request)?;
debug!("Sending: {}", request_json);
writeln!(stream, "{}", request_json)?;
stream.flush()?;
let mut reader = BufReader::new(&stream);
let mut response_line = String::new();
reader.read_line(&mut response_line)?;
debug!("Received: {}", response_line.trim());
let response: BridgeResponse = serde_json::from_str(&response_line)?;
match response.status.as_str() {
"success" => Ok(response.data.unwrap_or(json!({}))),
"error" => {
let msg = response
.message
.unwrap_or_else(|| "Unknown error".to_string());
anyhow::bail!("{}", msg)
}
"shutdown" => Ok(json!({"status": "shutdown"})),
_ => Ok(response.data.unwrap_or(json!({}))),
}
}
pub fn ping(&self) -> Result<bool> {
match self.send_command("ping", None) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
pub fn shutdown(&self) -> Result<()> {
self.send_command("shutdown", None)?;
Ok(())
}
#[allow(dead_code)]
pub fn status(&self) -> Result<serde_json::Value> {
self.send_command("status", None)
}
pub fn bridge_info(&self) -> Result<serde_json::Value> {
self.send_command("bridge_info", None)
}
pub fn list_functions(
&self,
limit: Option<usize>,
filter: Option<String>,
) -> Result<serde_json::Value> {
self.send_command(
"list_functions",
Some(json!({"limit": limit, "filter": filter})),
)
}
pub fn decompile(
&self,
address: String,
with_vars: bool,
with_params: bool,
) -> Result<serde_json::Value> {
self.send_command(
"decompile",
Some(json!({
"address": address,
"with_vars": with_vars,
"with_params": with_params,
})),
)
}
pub fn list_strings(&self, limit: Option<usize>, filter: Option<String>) -> Result<serde_json::Value> {
self.send_command("list_strings", Some(json!({"limit": limit, "filter": filter})))
}
pub fn list_imports(&self) -> Result<serde_json::Value> {
self.send_command("list_imports", None)
}
pub fn list_exports(&self) -> Result<serde_json::Value> {
self.send_command("list_exports", None)
}
pub fn memory_map(&self) -> Result<serde_json::Value> {
self.send_command("memory_map", None)
}
pub fn program_info(&self) -> Result<serde_json::Value> {
self.send_command("program_info", None)
}
pub fn xrefs_to(&self, address: String) -> Result<serde_json::Value> {
self.send_command("xrefs_to", Some(json!({"address": address})))
}
pub fn xrefs_from(&self, address: String) -> Result<serde_json::Value> {
self.send_command("xrefs_from", Some(json!({"address": address})))
}
pub fn import_binary(
&self,
binary_path: &str,
program: Option<&str>,
) -> Result<serde_json::Value> {
self.send_command(
"import",
Some(json!({"binary_path": binary_path, "program": program})),
)
}
pub fn analyze(&self) -> Result<serde_json::Value> {
self.send_command("analyze", None)
}
pub fn list_programs(&self) -> Result<serde_json::Value> {
self.send_command("list_programs", None)
}
pub fn open_program(&self, program: &str) -> Result<serde_json::Value> {
self.send_command("open_program", Some(json!({"program": program})))
}
pub fn symbol_list(&self, limit: Option<usize>, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("symbol_list", Some(json!({"limit": limit, "filter": filter})))
}
pub fn symbol_get(&self, name: &str) -> Result<serde_json::Value> {
self.send_command("symbol_get", Some(json!({"name": name})))
}
pub fn symbol_create(&self, address: &str, name: &str) -> Result<serde_json::Value> {
self.send_command(
"symbol_create",
Some(json!({"address": address, "name": name})),
)
}
pub fn symbol_delete(&self, name: &str) -> Result<serde_json::Value> {
self.send_command("symbol_delete", Some(json!({"name": name})))
}
pub fn symbol_rename(&self, old_name: &str, new_name: &str) -> Result<serde_json::Value> {
self.send_command(
"symbol_rename",
Some(json!({"old_name": old_name, "new_name": new_name})),
)
}
pub fn type_list(&self, limit: Option<usize>, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("type_list", Some(json!({"limit": limit, "filter": filter})))
}
pub fn type_get(&self, name: &str) -> Result<serde_json::Value> {
self.send_command("type_get", Some(json!({"name": name})))
}
pub fn type_create(&self, definition: &str) -> Result<serde_json::Value> {
self.send_command("type_create", Some(json!({"definition": definition})))
}
pub fn type_apply(&self, address: &str, type_name: &str) -> Result<serde_json::Value> {
self.send_command(
"type_apply",
Some(json!({"address": address, "type_name": type_name})),
)
}
pub fn comment_list(&self, limit: Option<usize>, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("comment_list", Some(json!({"limit": limit, "filter": filter})))
}
pub fn comment_get(&self, address: &str) -> Result<serde_json::Value> {
self.send_command("comment_get", Some(json!({"address": address})))
}
pub fn comment_set(
&self,
address: &str,
text: &str,
comment_type: Option<&str>,
) -> Result<serde_json::Value> {
self.send_command(
"comment_set",
Some(json!({
"address": address,
"text": text,
"type": comment_type,
})),
)
}
pub fn comment_delete(&self, address: &str) -> Result<serde_json::Value> {
self.send_command("comment_delete", Some(json!({"address": address})))
}
pub fn graph_calls(&self, limit: Option<usize>) -> Result<serde_json::Value> {
self.send_command("graph_calls", Some(json!({"limit": limit})))
}
pub fn graph_callers(&self, function: &str, depth: Option<usize>) -> Result<serde_json::Value> {
self.send_command(
"graph_callers",
Some(json!({"function": function, "depth": depth})),
)
}
pub fn graph_callees(&self, function: &str, depth: Option<usize>) -> Result<serde_json::Value> {
self.send_command(
"graph_callees",
Some(json!({"function": function, "depth": depth})),
)
}
pub fn graph_export(&self, format: &str) -> Result<serde_json::Value> {
self.send_command("graph_export", Some(json!({"format": format})))
}
pub fn find_string(&self, pattern: &str) -> Result<serde_json::Value> {
self.send_command("find_string", Some(json!({"pattern": pattern})))
}
pub fn find_bytes(&self, hex: &str) -> Result<serde_json::Value> {
self.send_command("find_bytes", Some(json!({"hex": hex})))
}
pub fn find_function(&self, pattern: &str) -> Result<serde_json::Value> {
self.send_command("find_function", Some(json!({"pattern": pattern})))
}
pub fn find_calls(&self, function: &str) -> Result<serde_json::Value> {
self.send_command("find_calls", Some(json!({"function": function})))
}
pub fn find_crypto(&self) -> Result<serde_json::Value> {
self.send_command("find_crypto", None)
}
pub fn find_interesting(&self) -> Result<serde_json::Value> {
self.send_command("find_interesting", None)
}
pub fn diff_programs(&self, program1: &str, program2: &str) -> Result<serde_json::Value> {
self.send_command(
"diff_programs",
Some(json!({"program1": program1, "program2": program2})),
)
}
pub fn diff_functions(&self, func1: &str, func2: &str) -> Result<serde_json::Value> {
self.send_command(
"diff_functions",
Some(json!({"func1": func1, "func2": func2})),
)
}
pub fn patch_bytes(&self, address: &str, hex: &str) -> Result<serde_json::Value> {
self.send_command("patch_bytes", Some(json!({"address": address, "hex": hex})))
}
pub fn patch_nop(&self, address: &str) -> Result<serde_json::Value> {
self.send_command("patch_nop", Some(json!({"address": address})))
}
pub fn patch_export(&self, output: &str) -> Result<serde_json::Value> {
self.send_command("patch_export", Some(json!({"output": output})))
}
pub fn disasm(
&self,
address: &str,
num_instructions: Option<usize>,
) -> Result<serde_json::Value> {
self.send_command(
"disasm",
Some(json!({"address": address, "count": num_instructions})),
)
}
pub fn stats(&self) -> Result<serde_json::Value> {
self.send_command("stats", None)
}
pub fn script_run(&self, script_path: &str, args: &[String]) -> Result<serde_json::Value> {
self.send_command(
"script_run",
Some(json!({"path": script_path, "args": args})),
)
}
pub fn script_python(&self, code: &str) -> Result<serde_json::Value> {
self.send_command("script_python", Some(json!({"code": code})))
}
pub fn script_java(&self, code: &str) -> Result<serde_json::Value> {
self.send_command("script_java", Some(json!({"code": code})))
}
pub fn script_list(&self) -> Result<serde_json::Value> {
self.send_command("script_list", None)
}
pub fn program_close(&self) -> Result<serde_json::Value> {
self.send_command("close_program", None)
}
pub fn program_delete(&self, program: &str) -> Result<serde_json::Value> {
self.send_command("delete_program", Some(json!({"program": program})))
}
pub fn program_export(&self, format: &str, output: Option<&str>) -> Result<serde_json::Value> {
self.send_command(
"export_program",
Some(json!({"format": format, "output": output})),
)
}
}