1use crate::dlfcn;
6use crate::{Error, Result};
7
8use std::env;
9use std::fs::File;
10use std::io::prelude::*;
11use std::io::BufReader;
12use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
13use std::path::Path;
14use std::process;
15use std::process::Command;
16use std::process::Stdio;
17use std::str;
18use std::sync::mpsc;
19use std::sync::Arc;
20use std::thread;
21
22use serde_json::Value;
23
24pub struct R2PipeLang {
26 read: BufReader<File>,
27 write: File,
28}
29
30pub struct R2PipeSpawn {
32 read: BufReader<process::ChildStdout>,
33 write: process::ChildStdin,
34 child: Option<process::Child>,
35}
36
37pub struct R2PipeTcp {
39 socket_addr: SocketAddr,
40}
41
42pub struct R2PipeHttp {
43 host: String,
44}
45
46pub struct R2PipeThread {
50 r2recv: mpsc::Receiver<String>,
51 r2send: mpsc::Sender<String>,
52 pub id: u16,
53 pub handle: thread::JoinHandle<Result<()>>,
54}
55
56#[derive(Default, Clone)]
57pub struct R2PipeSpawnOptions {
58 pub exepath: String,
59 pub args: Vec<&'static str>,
60}
61
62pub struct R2Pipe(Box<dyn Pipe>);
64pub trait Pipe {
65 fn cmd(&mut self, cmd: &str) -> Result<String>;
66 fn cmdj(&mut self, cmd: &str) -> Result<Value> {
67 let result = self.cmd(cmd)?;
68 if result.is_empty() {
69 return Err(Error::EmptyResponse);
70 }
71 Ok(serde_json::from_str(&result)?)
72 }
73 fn call(&mut self, cmd: &str) -> Result<String> {
75 self.cmd(&format!("\"\"{}", cmd))
76 }
77 fn callj(&mut self, cmd: &str) -> Result<Value> {
80 self.cmdj(&format!("\"\"{}", cmd))
81 }
82 fn close(&mut self) {}
83}
84fn getenv(k: &str) -> Option<i32> {
85 match env::var(k) {
86 Ok(val) => val.parse::<i32>().ok(),
87 Err(_) => None,
88 }
89}
90
91fn process_result(res: Vec<u8>) -> Result<String> {
92 let len = res.len();
93 if len == 0 {
94 Err(Error::EmptyResponse)
95 } else {
96 Ok(str::from_utf8(&res[..len - 1])?.to_string())
97 }
98}
99
100#[macro_export]
101macro_rules! open_pipe {
102 () => {
103 R2Pipe::open(),
104 };
105 ($x: expr) => {
106 match $x {
107 Some(path) => R2Pipe::load_native(&path.clone()).or_else(|_| R2Pipe::spawn(path, None)),
108 None => R2Pipe::open(),
109 }
110 };
111 ($x: expr, $y: expr) => {
112 match $x $y {
113 Some(path, opts) => R2Pipe::spawn(path, opts),
114 (None, None) => R2Pipe::open(),
115 }
116 }
117}
118
119impl R2Pipe {
120 pub fn load_native<T: AsRef<str>>(path: T) -> Result<R2Pipe> {
121 Ok(R2Pipe(Box::new(R2PipeNative::open(path.as_ref())?)))
122 }
123 #[cfg(not(windows))]
124 pub fn open() -> Result<R2Pipe> {
125 use std::os::unix::io::FromRawFd;
126
127 let (f_in, f_out) = R2Pipe::in_session().ok_or(Error::NoSession)?;
128
129 let res = unsafe {
130 let (d_in, d_out) = (libc::dup(f_in), libc::dup(f_out));
132 R2PipeLang {
133 read: BufReader::new(File::from_raw_fd(d_in)),
134 write: File::from_raw_fd(d_out),
135 }
136 };
137 Ok(R2Pipe(Box::new(res)))
138 }
139
140 #[cfg(windows)]
141 pub fn open() -> Result<R2Pipe> {
142 unimplemented!()
143 }
144 pub fn cmd(&mut self, cmd: &str) -> Result<String> {
145 self.0.cmd(cmd.trim())
146 }
147
148 pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
149 self.0.cmdj(cmd.trim())
150 }
151
152 pub fn close(&mut self) {
153 self.0.close();
154 }
155 pub fn call(&mut self, cmd: &str) -> Result<String> {
157 self.0.call(cmd)
158 }
159 pub fn callj(&mut self, cmd: &str) -> Result<Value> {
162 self.0.callj(cmd)
163 }
164
165 pub fn in_session() -> Option<(i32, i32)> {
166 let f_in = getenv("R2PIPE_IN")?;
167 let f_out = getenv("R2PIPE_OUT")?;
168 Some((f_in, f_out))
169 }
170
171 #[cfg(windows)]
172 pub fn in_windows_session() -> Option<String> {
173 match env::var("R2PIPE_PATH") {
174 Ok(val) => Some(format!("\\\\.\\pipe\\{}", val)),
175 Err(_) => None,
176 }
177 }
178
179 pub fn spawn<T: AsRef<str>>(name: T, opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe> {
181 if name.as_ref() == "" && R2Pipe::in_session().is_some() {
182 return R2Pipe::open();
183 }
184
185 let exepath = match opts {
186 Some(ref opt) => opt.exepath.clone(),
187 _ => {
188 if cfg!(windows) {
189 "radare2.exe"
190 } else {
191 "r2"
192 }
193 }
194 .to_owned(),
195 };
196 let args = match opts {
197 Some(ref opt) => opt.args.clone(),
198 _ => vec![],
199 };
200 let path = Path::new(name.as_ref());
201 let mut child = Command::new(exepath)
202 .arg("-q0")
203 .args(&args)
204 .arg(path)
205 .stdin(Stdio::piped())
206 .stdout(Stdio::piped())
207 .spawn()?;
208
209 let sin = child.stdin.take().unwrap();
211 let mut sout = child.stdout.take().unwrap();
212
213 let mut w = [0; 1];
215 sout.read_exact(&mut w)?;
216
217 let res = R2PipeSpawn {
218 read: BufReader::new(sout),
219 write: sin,
220 child: Some(child),
221 };
222
223 Ok(R2Pipe(Box::new(res)))
224 }
225
226 pub fn tcp<A: ToSocketAddrs>(addr: A) -> Result<R2Pipe> {
228 let stream = TcpStream::connect(addr)?;
230 let addr = stream.peer_addr()?;
231 Ok(R2Pipe(Box::new(R2PipeTcp { socket_addr: addr })))
232 }
233
234 pub fn http(host: &str) -> R2Pipe {
236 R2Pipe(Box::new(R2PipeHttp {
237 host: host.to_string(),
238 }))
239 }
240
241 pub fn threads(
246 names: Vec<&'static str>,
247 opts: Vec<Option<R2PipeSpawnOptions>>,
248 callback: Option<Arc<dyn Fn(u16, String) + Sync + Send>>,
249 ) -> Result<Vec<R2PipeThread>> {
250 if names.len() != opts.len() {
251 return Err(Error::ArgumentMismatch);
252 }
253
254 let mut pipes = Vec::new();
255
256 for n in 0..names.len() {
257 let (htx, rx) = mpsc::channel();
258 let (tx, hrx) = mpsc::channel();
259 let name = names[n];
260 let opt = opts[n].clone();
261 let cb = callback.clone();
262 let t = thread::spawn(move || -> Result<()> {
263 let mut r2 = R2Pipe::spawn(name, opt)?;
264 loop {
265 let cmd: String = hrx.recv()?;
266 if cmd == "q" {
267 break;
268 }
269 let res = r2.cmdj(&cmd)?.to_string();
270 htx.send(res.clone())?;
271 if let Some(cbs) = cb.clone() {
272 thread::spawn(move || {
273 cbs(n as u16, res);
274 });
275 };
276 }
277 Ok(())
278 });
279 pipes.push(R2PipeThread {
280 r2recv: rx,
281 r2send: tx,
282 id: n as u16,
283 handle: t,
284 });
285 }
286 Ok(pipes)
287 }
288}
289
290impl R2PipeThread {
291 pub fn send(&self, cmd: String) -> Result<()> {
292 Ok(self.r2send.send(cmd)?)
293 }
294
295 pub fn recv(&self, block: bool) -> Result<String> {
296 if block {
297 Ok(self.r2recv.recv()?)
298 } else {
299 Ok(self.r2recv.try_recv()?)
300 }
301 }
302}
303
304impl Pipe for R2PipeSpawn {
305 fn cmd(&mut self, cmd: &str) -> Result<String> {
306 let cmd = cmd.to_owned() + "\n";
307 self.write.write_all(cmd.as_bytes())?;
308
309 let mut res: Vec<u8> = Vec::new();
310 self.read.read_until(0u8, &mut res)?;
311 process_result(res)
312 }
313
314 fn close(&mut self) {
315 let _ = self.cmd("q!");
316 if let Some(child) = &mut self.child {
317 let _ = child.wait();
318 }
319 }
320}
321
322impl R2PipeSpawn {
323 pub fn take_child(&mut self) -> Option<process::Child> {
328 self.child.take()
329 }
330}
331
332impl Pipe for R2PipeLang {
333 fn cmd(&mut self, cmd: &str) -> Result<String> {
334 self.write.write_all(cmd.as_bytes())?;
335 let mut res: Vec<u8> = Vec::new();
336 self.read.read_until(0u8, &mut res)?;
337 process_result(res)
338 }
339}
340
341impl Pipe for R2PipeHttp {
342 fn cmd(&mut self, cmd: &str) -> Result<String> {
343 let host = if self.host.starts_with("http://") {
344 &self.host[7..]
345 } else {
346 &self.host
347 };
348 let mut stream = TcpStream::connect(host)?;
349 let req = format!("GET /cmd/{} HTTP/1.1\r\n", cmd);
350 let mut resp = Vec::with_capacity(1024);
351 stream.write_all(req.as_bytes())?;
352 stream.read_to_end(&mut resp)?;
353
354 let index = resp
356 .windows(4)
357 .position(|w| w == "\r\n\r\n".as_bytes())
358 .map(|i| i + 4)
359 .unwrap_or(0);
360
361 Ok(str::from_utf8(&resp[index..]).map(|s| s.to_string())?)
362 }
363}
364
365impl Pipe for R2PipeTcp {
366 fn cmd(&mut self, cmd: &str) -> Result<String> {
367 let mut stream = TcpStream::connect(self.socket_addr)?;
368 stream.write_all(cmd.as_bytes())?;
369 let mut res: Vec<u8> = Vec::new();
370 stream.read_to_end(&mut res)?;
371 res.push(0);
372 process_result(res)
373 }
374}
375
376pub struct R2PipeNative {
377 lib: dlfcn::LibHandle,
378 r_core: std::sync::Mutex<*mut libc::c_void>,
379 r_core_cmd_str_handle: fn(*mut libc::c_void, *const libc::c_char) -> *mut libc::c_char,
380}
381
382impl R2PipeNative {
383 pub fn open(file: &str) -> Result<R2PipeNative> {
384 let mut lib = dlfcn::LibHandle::new("libr_core", None)?;
385 let r_core_new: fn() -> *mut libc::c_void = unsafe { lib.load_sym("r_core_new")? };
386 let r_core_cmd_str_handle = unsafe { lib.load_sym("r_core_cmd_str")? };
387 let r_core = r_core_new();
388 if r_core.is_null() {
389 Err(Error::SharedLibraryLoadError)
390 } else {
391 let mut ret = R2PipeNative {
392 lib,
393 r_core: std::sync::Mutex::new(r_core),
394 r_core_cmd_str_handle,
395 };
396 ret.cmd(&format!("o {}", file))?;
397 Ok(ret)
398 }
399 }
400}
401
402impl Pipe for R2PipeNative {
403 fn cmd(&mut self, cmd: &str) -> Result<String> {
404 let r_core = *self.r_core.lock().unwrap();
405 let cmd = dlfcn::to_cstr(cmd)?;
406 let res = (self.r_core_cmd_str_handle)(r_core, cmd);
407 if res.is_null() {
408 Err(Error::EmptyResponse)
409 } else {
410 Ok(unsafe { std::ffi::CStr::from_ptr(res).to_str()?.to_string() })
411 }
412 }
413}
414
415impl Drop for R2PipeNative {
416 fn drop(&mut self) {
417 let r_core = *self.r_core.lock().unwrap();
418 if let Ok(r_core_free) =
419 unsafe { self.lib.load_sym::<fn(*mut libc::c_void)>("r_core_free") }
420 {
421 r_core_free(r_core);
422 }
423 }
424}
425
426#[cfg(test)]
427mod test {
428 use super::Pipe;
429 use super::R2PipeNative;
430 use crate::R2Pipe;
431
432 #[test]
433 fn spawn_test() {
434 let mut pipe = R2Pipe::spawn("/bin/ls", None).unwrap();
435 assert_eq!(pipe.cmd("echo test").unwrap(), "test\n");
436 }
437
438 #[test]
439 fn native_test() {
440 let mut r2p = R2PipeNative::open("/bin/ls").unwrap();
441 assert_eq!("a\n", r2p.cmd("echo a").unwrap());
442 }
443}