diff --git a/adb_cli/src/main.rs b/adb_cli/src/main.rs index e3cd784..f30827d 100644 --- a/adb_cli/src/main.rs +++ b/adb_cli/src/main.rs @@ -134,6 +134,14 @@ fn main() -> Result<()> { device.install(&path)?; } DeviceCommands::Framebuffer { path } => device.framebuffer(&path)?, + DeviceCommands::Reverse { remote, local } => { + log::info!("Reverse port {} to local {}", remote, local); + device.reverse(remote, local)?; + } + DeviceCommands::ReverseRemoveAll => { + log::info!("Remove all previous forward rules"); + device.reverse_remove_all()?; + } } Ok(()) diff --git a/adb_cli/src/models/device.rs b/adb_cli/src/models/device.rs index b450486..1dcdfe8 100644 --- a/adb_cli/src/models/device.rs +++ b/adb_cli/src/models/device.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use adb_client::ADBProtoPort; use clap::Parser; use super::RebootTypeCommand; @@ -38,4 +39,13 @@ pub enum DeviceCommands { /// Framebuffer image destination path path: String, }, + /// Reverse socket connection from remote port to local port + Reverse { + /// Remote port + remote: ADBProtoPort, + /// Local port + local: ADBProtoPort, + }, + /// Remove all previously applied reverse rules + ReverseRemoveAll, } diff --git a/adb_cli/src/models/opts.rs b/adb_cli/src/models/opts.rs index 41f1b0c..bbdd3ff 100644 --- a/adb_cli/src/models/opts.rs +++ b/adb_cli/src/models/opts.rs @@ -17,7 +17,7 @@ pub struct Opts { pub enum MainCommand { /// Server related commands Host(ServerCommand), - /// Device related commands using server + /// Device related commands (using adb-server as a bridge) Local(ServerCommand), /// Emulator related commands Emu(EmulatorCommand), diff --git a/adb_client/src/adb_device_ext.rs b/adb_client/src/adb_device_ext.rs index 220d36f..038652e 100644 --- a/adb_client/src/adb_device_ext.rs +++ b/adb_client/src/adb_device_ext.rs @@ -4,7 +4,7 @@ use std::path::Path; use image::{ImageBuffer, ImageFormat, Rgba}; use crate::models::AdbStatResponse; -use crate::{RebootType, Result}; +use crate::{ADBProtoPort, RebootType, Result}; /// Trait representing all features available on both [`crate::ADBServerDevice`] and [`crate::ADBUSBDevice`] pub trait ADBDeviceExt { @@ -62,6 +62,18 @@ pub trait ADBDeviceExt { Ok(vec.into_inner()) } + /// Forward socket connection + fn forward(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()>; + + /// Remove all previously applied forward rules + fn forward_remove_all(&mut self) -> Result<()>; + + /// Reverse socket connection + fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()>; + + /// Remove all reverse rules + fn reverse_remove_all(&mut self) -> Result<()>; + /// Return a boxed instance representing this trait fn boxed(self) -> Box where diff --git a/adb_client/src/device/adb_message_device.rs b/adb_client/src/device/adb_message_device.rs index ef7b3ce..626ca57 100644 --- a/adb_client/src/device/adb_message_device.rs +++ b/adb_client/src/device/adb_message_device.rs @@ -8,7 +8,7 @@ use super::{models::MessageSubcommand, ADBTransportMessage, MessageCommand}; /// Generic structure representing an ADB device reachable over an [`ADBMessageTransport`]. /// Structure is totally agnostic over which transport is truly used. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ADBMessageDevice { transport: T, local_id: Option, @@ -235,6 +235,15 @@ impl ADBMessageDevice { Ok(response) } + pub(crate) fn set_random_local_id(&mut self) { + let mut rng = rand::thread_rng(); + self.local_id = Some(rng.gen()); + } + + pub(crate) fn set_remote_id(&mut self, remote_id: u32) { + self.remote_id = Some(remote_id); + } + pub(crate) fn get_local_id(&self) -> Result { self.local_id.ok_or(RustADBError::ADBRequestFailed( "connection not opened, no local_id".into(), diff --git a/adb_client/src/device/adb_message_device_commands.rs b/adb_client/src/device/adb_message_device_commands.rs index 4dc7bda..c62ccbb 100644 --- a/adb_client/src/device/adb_message_device_commands.rs +++ b/adb_client/src/device/adb_message_device_commands.rs @@ -1,4 +1,6 @@ -use crate::{models::AdbStatResponse, ADBDeviceExt, ADBMessageTransport, RebootType, Result}; +use crate::{ + models::AdbStatResponse, ADBDeviceExt, ADBMessageTransport, ADBProtoPort, RebootType, Result, +}; use std::{ io::{Read, Write}, path::Path, @@ -38,4 +40,20 @@ impl ADBDeviceExt for ADBMessageDevice { fn framebuffer_inner(&mut self) -> Result, Vec>> { self.framebuffer_inner() } + + fn forward(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + todo!() + } + + fn forward_remove_all(&mut self) -> Result<()> { + todo!() + } + + fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.reverse(remote, local) + } + + fn reverse_remove_all(&mut self) -> Result<()> { + self.reverse_remove_all() + } } diff --git a/adb_client/src/device/adb_tcp_device.rs b/adb_client/src/device/adb_tcp_device.rs index b1c622b..71a6fe5 100644 --- a/adb_client/src/device/adb_tcp_device.rs +++ b/adb_client/src/device/adb_tcp_device.rs @@ -5,7 +5,10 @@ use std::{io::Read, net::SocketAddr}; use super::adb_message_device::ADBMessageDevice; use super::models::MessageCommand; use super::ADBTransportMessage; -use crate::{ADBDeviceExt, ADBMessageTransport, ADBTransport, Result, RustADBError, TcpTransport}; +use crate::{ + ADBDeviceExt, ADBMessageTransport, ADBProtoPort, ADBTransport, Result, RustADBError, + TcpTransport, +}; /// Represent a device reached and available over USB. #[derive(Debug)] @@ -106,6 +109,26 @@ impl ADBDeviceExt for ADBTcpDevice { fn framebuffer_inner(&mut self) -> Result, Vec>> { self.inner.framebuffer_inner() } + + #[inline] + fn forward(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.inner.forward(remote, local) + } + + #[inline] + fn forward_remove_all(&mut self) -> Result<()> { + self.inner.forward_remove_all() + } + + #[inline] + fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.inner.reverse(remote, local) + } + + #[inline] + fn reverse_remove_all(&mut self) -> Result<()> { + self.inner.reverse_remove_all() + } } impl Drop for ADBTcpDevice { diff --git a/adb_client/src/device/adb_usb_device.rs b/adb_client/src/device/adb_usb_device.rs index b2d72d3..9803a58 100644 --- a/adb_client/src/device/adb_usb_device.rs +++ b/adb_client/src/device/adb_usb_device.rs @@ -14,6 +14,7 @@ use super::{ADBRsaKey, ADBTransportMessage}; use crate::device::adb_transport_message::{AUTH_RSAPUBLICKEY, AUTH_SIGNATURE, AUTH_TOKEN}; use crate::ADBDeviceExt; use crate::ADBMessageTransport; +use crate::ADBProtoPort; use crate::ADBTransport; use crate::{Result, RustADBError, USBTransport}; @@ -289,6 +290,26 @@ impl ADBDeviceExt for ADBUSBDevice { fn framebuffer_inner(&mut self) -> Result, Vec>> { self.inner.framebuffer_inner() } + + #[inline] + fn forward(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.inner.forward(remote, local) + } + + #[inline] + fn forward_remove_all(&mut self) -> Result<()> { + self.inner.forward_remove_all() + } + + #[inline] + fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.inner.reverse(remote, local) + } + + #[inline] + fn reverse_remove_all(&mut self) -> Result<()> { + self.inner.reverse_remove_all() + } } impl Drop for ADBUSBDevice { diff --git a/adb_client/src/device/commands/mod.rs b/adb_client/src/device/commands/mod.rs index 875f710..38be502 100644 --- a/adb_client/src/device/commands/mod.rs +++ b/adb_client/src/device/commands/mod.rs @@ -3,5 +3,6 @@ mod install; mod pull; mod push; mod reboot; +mod reverse; mod shell; mod stat; diff --git a/adb_client/src/device/commands/reverse.rs b/adb_client/src/device/commands/reverse.rs new file mode 100644 index 0000000..987ff7c --- /dev/null +++ b/adb_client/src/device/commands/reverse.rs @@ -0,0 +1,196 @@ +use std::{ + collections::HashMap, + io::{Read, Write}, + net::{SocketAddr, TcpStream}, + sync::mpsc::{self, SyncSender}, + thread::JoinHandle, +}; + +use crate::{ + constants::BUFFER_SIZE, + device::{adb_message_device::ADBMessageDevice, ADBTransportMessage, MessageCommand}, + ADBMessageTransport, ADBProtoPort, Result, RustADBError, +}; + +impl ADBMessageDevice { + /// Reverse socket connection + pub(crate) fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.open_session(format!("reverse:forward:{remote};{local}\0").as_bytes())?; + + let message = self.get_transport_mut().read_message()?; + let received_command = message.header().command(); + if received_command != MessageCommand::Write { + return Err(RustADBError::ADBRequestFailed(format!( + "expected command WRTE after message, got {}", + received_command + ))); + } + + let message = self.get_transport_mut().read_message()?; + let received_command = message.header().command(); + if received_command != MessageCommand::Clse { + return Err(RustADBError::ADBRequestFailed(format!( + "expected command CLSE after message, got {}", + received_command + ))); + } + + let mut s = Box::new(self.clone()); + + log::debug!("reverse connection setup... waiting for incoming messages"); + + let mut active_connections: HashMap< + u32, + (SyncSender, JoinHandle>), + > = HashMap::new(); + let mut transport = s.get_transport_mut().clone(); + + // Loop dispatching all received messages to corresponding "handlers" threads + // These threads are stored, and messages are dispatched via channels according to the `local_id` of incoming messages. + // Finished threads are collected before receiving every new message. + loop { + let finished_keys: Vec = active_connections + .iter() + .filter_map(|(&key, (_, handle))| { + if handle.is_finished() { + Some(key) + } else { + None + } + }) + .collect(); + + for key in finished_keys { + if let Some((_, handle)) = active_connections.remove(&key) { + log::trace!("removing finished thread {key}"); + if let Err(e) = handle + .join() + .map_err(|_| RustADBError::ADBRequestFailed("cannot join thread".into()))? + { + log::error!("error with thread {key}: {e}"); + } + } + } + + let message = transport.read_message()?; + let remote_id = message.header().arg0(); + log::trace!("received message for {remote_id}"); + + match active_connections.get(&remote_id) { + Some((sender, _)) => { + sender.send(message).map_err(|_| RustADBError::SendError)?; + } + None => { + let (tx, rx) = mpsc::sync_channel(5); + let s = s.clone(); + let handle = std::thread::spawn(move || { + let mut ss = s.clone(); + let received_command = message.header().command(); + if received_command != MessageCommand::Open { + return Err(RustADBError::ADBRequestFailed(format!( + "expected command OPEN after message, got {}", + received_command + ))); + } + + ss.set_random_local_id(); + ss.set_remote_id(message.header().arg0()); + + let local_id = ss.get_local_id()?; + let remote_id = ss.get_remote_id()?; + ss.get_transport_mut() + .write_message(ADBTransportMessage::new( + MessageCommand::Okay, + local_id, + remote_id, + &[], + ))?; + + let reverse_dst_proto = ADBProtoPort::try_from(&*message.into_payload())?; + + log::debug!("Received reverse connection request to {reverse_dst_proto}"); + + let message: ADBTransportMessage = rx.recv()?; + + let request_data = message.into_payload(); + + ss.get_transport_mut() + .write_message(ADBTransportMessage::new( + MessageCommand::Okay, + local_id, + remote_id, + &[], + ))?; + + match reverse_dst_proto { + ADBProtoPort::TCP(port) => { + let addr: SocketAddr = ([127, 0, 0, 1], port).into(); + let mut tcp_stream = TcpStream::connect(addr)?; + tcp_stream.write_all(&request_data)?; + + loop { + let mut buffer = [0; BUFFER_SIZE]; + let amount_read = tcp_stream.read(&mut buffer); + + match amount_read { + Ok(0) => break, + Ok(v) => { + ss.get_transport_mut().write_message( + ADBTransportMessage::new( + MessageCommand::Write, + local_id, + remote_id, + &buffer[..v], + ), + )?; + rx.recv()?; + } + Err(e) => return Err(RustADBError::IOError(e)), + } + } + } + }; + + ss.get_transport_mut() + .write_message(ADBTransportMessage::new( + MessageCommand::Clse, + local_id, + remote_id, + &[], + ))?; + + rx.recv()?; + + Ok(()) + }); + + active_connections.insert(remote_id, (tx, handle)); + } + } + } + } + + /// Remove all previously applied reverse rules + pub(crate) fn reverse_remove_all(&mut self) -> Result<()> { + self.open_session(b"reverse:killforward-all\0")?; + let message = self.get_transport_mut().read_message()?; + let received_command = message.header().command(); + if received_command != MessageCommand::Write { + return Err(RustADBError::ADBRequestFailed(format!( + "expected command WRTE after message, got {}", + received_command + ))); + } + + let message = self.get_transport_mut().read_message()?; + let received_command = message.header().command(); + if received_command != MessageCommand::Clse { + return Err(RustADBError::ADBRequestFailed(format!( + "expected command CLSE after message, got {}", + received_command + ))); + } + + Ok(()) + } +} diff --git a/adb_client/src/error.rs b/adb_client/src/error.rs index c53cb34..1b4bbc1 100644 --- a/adb_client/src/error.rs +++ b/adb_client/src/error.rs @@ -1,3 +1,5 @@ +use std::sync::mpsc; + use thiserror::Error; /// Custom Result type thrown by this crate. @@ -9,6 +11,9 @@ pub enum RustADBError { /// Indicates that an error occurred with I/O. #[error(transparent)] IOError(#[from] std::io::Error), + /// Unknown adb protocol received + #[error("Unknown protocol received: {0}")] + UnknownProtocol(String), /// Indicates that an error occurred when sending ADB request. #[error("ADB request failed - {0}")] ADBRequestFailed(String), @@ -111,9 +116,12 @@ pub enum RustADBError { /// An error occurred while getting mdns devices #[error(transparent)] MDNSError(#[from] mdns_sd::Error), - /// An error occurred while sending data to channel + /// An error occurred while sending data to a channel + #[error("error while sending data to channel")] + SendError, + /// An error occurred while receiving data from channel #[error(transparent)] - SendError(#[from] std::sync::mpsc::SendError), + RecvError(#[from] mpsc::RecvError), } impl From> for RustADBError { diff --git a/adb_client/src/lib.rs b/adb_client/src/lib.rs index bb0997f..f35e224 100644 --- a/adb_client/src/lib.rs +++ b/adb_client/src/lib.rs @@ -22,7 +22,8 @@ pub use emulator_device::ADBEmulatorDevice; pub use error::{Result, RustADBError}; pub use mdns::*; pub use models::{ - AdbStatResponse, AdbVersion, DeviceLong, DeviceShort, DeviceState, MDNSBackend, RebootType, + ADBProtoPort, AdbStatResponse, AdbVersion, DeviceLong, DeviceShort, DeviceState, MDNSBackend, + RebootType, }; pub use server::*; pub use server_device::ADBServerDevice; diff --git a/adb_client/src/mdns/mdns_discovery.rs b/adb_client/src/mdns/mdns_discovery.rs index 376d12f..d494594 100644 --- a/adb_client/src/mdns/mdns_discovery.rs +++ b/adb_client/src/mdns/mdns_discovery.rs @@ -44,9 +44,9 @@ impl MDNSDiscoveryService { continue; } ServiceEvent::ServiceResolved(service_info) => { - if let Err(e) = sender.send(MDNSDevice::from(service_info)) { - return Err(e.into()); - } + sender + .send(MDNSDevice::from(service_info)) + .map_err(|_| RustADBError::SendError)?; } } } diff --git a/adb_client/src/models/adb_proto_port.rs b/adb_client/src/models/adb_proto_port.rs new file mode 100644 index 0000000..4da1105 --- /dev/null +++ b/adb_client/src/models/adb_proto_port.rs @@ -0,0 +1,67 @@ +use core::str; +use std::{fmt::Display, str::FromStr}; + +use crate::RustADBError; + +/// Represents supported ADB reverse / forward protocols +#[derive(Clone, Debug)] +pub enum ADBProtoPort { + /// TCP + TCP(u16), +} + +impl FromStr for ADBProtoPort { + type Err = RustADBError; + + fn from_str(s: &str) -> Result { + Self::try_from(s.as_bytes()) + } +} + +impl TryFrom<&[u8]> for ADBProtoPort { + type Error = RustADBError; + + fn try_from(value: &[u8]) -> Result { + let v = str::from_utf8(value)?; + + if let Some(port) = v.strip_prefix("tcp:") { + // Remove trailing \0 + let port = port.trim_matches('\0'); + return Ok(Self::TCP(port.parse::()?)); + } + + Err(RustADBError::UnknownProtocol(v.to_string())) + } +} + +impl Display for ADBProtoPort { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ADBProtoPort::TCP(port) => write!(f, "tcp:{port}"), + } + } +} + +#[cfg(test)] +mod tests { + use super::ADBProtoPort; + use std::str::FromStr; + + #[test] + fn test_tcp_port_parsing() { + let case = "tcp:1247"; + let proto = ADBProtoPort::from_str(&*case).expect("cannot parse input"); + + match proto { + ADBProtoPort::TCP(port) => { + assert!(port == 1247); + } + } + } + + #[test] + fn test_wrong_tcp_port_parsing() { + let case = "tcp:12A47"; + assert!(ADBProtoPort::from_str(&*case).is_err()) + } +} diff --git a/adb_client/src/models/adb_server_command.rs b/adb_client/src/models/adb_server_command.rs index 1782726..e5ceb5d 100644 --- a/adb_client/src/models/adb_server_command.rs +++ b/adb_client/src/models/adb_server_command.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use super::RebootType; +use super::{ADBProtoPort, RebootType}; use std::net::SocketAddrV4; pub(crate) enum AdbServerCommand { @@ -27,9 +27,9 @@ pub(crate) enum AdbServerCommand { FrameBuffer, Sync, Reboot(RebootType), - Forward(String, String), + Forward(ADBProtoPort, ADBProtoPort), ForwardRemoveAll, - Reverse(String, String), + Reverse(ADBProtoPort, ADBProtoPort), ReverseRemoveAll, Reconnect, TcpIp(u16), @@ -45,7 +45,7 @@ impl Display for AdbServerCommand { AdbServerCommand::DevicesLong => write!(f, "host:devices-l"), AdbServerCommand::Sync => write!(f, "sync:"), AdbServerCommand::TrackDevices => write!(f, "host:track-devices"), - AdbServerCommand::TransportAny => write!(f, "host:transport-any"), + AdbServerCommand::TransportAny => write!(f, "host:transport:any"), AdbServerCommand::TransportSerial(serial) => write!(f, "host:transport:{serial}"), AdbServerCommand::ShellCommand(command) => match std::env::var("TERM") { Ok(term) => write!(f, "shell,TERM={term},raw:{command}"), diff --git a/adb_client/src/models/mod.rs b/adb_client/src/models/mod.rs index be0ca74..fec1adc 100644 --- a/adb_client/src/models/mod.rs +++ b/adb_client/src/models/mod.rs @@ -1,4 +1,5 @@ mod adb_emulator_command; +mod adb_proto_port; mod adb_request_status; mod adb_server_command; mod adb_stat_response; @@ -14,6 +15,7 @@ mod server_status; mod sync_command; pub(crate) use adb_emulator_command::ADBEmulatorCommand; +pub use adb_proto_port::ADBProtoPort; pub use adb_request_status::AdbRequestStatus; pub(crate) use adb_server_command::AdbServerCommand; pub use adb_stat_response::AdbStatResponse; diff --git a/adb_client/src/server_device/adb_server_device_commands.rs b/adb_client/src/server_device/adb_server_device_commands.rs index 9571b40..ecaa6d8 100644 --- a/adb_client/src/server_device/adb_server_device_commands.rs +++ b/adb_client/src/server_device/adb_server_device_commands.rs @@ -3,106 +3,21 @@ use std::{ path::Path, }; -use crate::{ - constants::BUFFER_SIZE, - models::{AdbServerCommand, AdbStatResponse, HostFeatures}, - ADBDeviceExt, Result, RustADBError, -}; +use crate::{models::AdbStatResponse, ADBDeviceExt, ADBProtoPort, Result}; use super::ADBServerDevice; impl ADBDeviceExt for ADBServerDevice { fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { - let supported_features = self.host_features()?; - if !supported_features.contains(&HostFeatures::ShellV2) - && !supported_features.contains(&HostFeatures::Cmd) - { - return Err(RustADBError::ADBShellNotSupported); - } - - let serial = self.identifier.clone(); - self.connect()? - .send_adb_request(AdbServerCommand::TransportSerial(serial))?; - self.get_transport_mut() - .send_adb_request(AdbServerCommand::ShellCommand(command.join(" ")))?; - - const BUFFER_SIZE: usize = 4096; - loop { - let mut buffer = [0; BUFFER_SIZE]; - match self - .get_transport_mut() - .get_raw_connection()? - .read(&mut buffer) - { - Ok(size) => { - if size == 0 { - return Ok(()); - } else { - output.write_all(&buffer[..size])?; - } - } - Err(e) => { - return Err(RustADBError::IOError(e)); - } - } - } + self.shell_command(command, output) } fn stat(&mut self, remote_path: &str) -> Result { self.stat(remote_path) } - fn shell( - &mut self, - mut reader: &mut dyn Read, - mut writer: Box<(dyn Write + Send)>, - ) -> Result<()> { - let supported_features = self.host_features()?; - if !supported_features.contains(&HostFeatures::ShellV2) - && !supported_features.contains(&HostFeatures::Cmd) - { - return Err(RustADBError::ADBShellNotSupported); - } - - let serial = self.identifier.clone(); - self.connect()? - .send_adb_request(AdbServerCommand::TransportSerial(serial))?; - self.get_transport_mut() - .send_adb_request(AdbServerCommand::Shell)?; - - let mut read_stream = self.get_transport_mut().get_raw_connection()?.try_clone()?; - - let mut write_stream = read_stream.try_clone()?; - - // Reading thread, reads response from adb-server - std::thread::spawn(move || -> Result<()> { - loop { - let mut buffer = [0; BUFFER_SIZE]; - match read_stream.read(&mut buffer) { - Ok(0) => { - read_stream.shutdown(std::net::Shutdown::Both)?; - return Ok(()); - } - Ok(size) => { - writer.write_all(&buffer[..size])?; - writer.flush()?; - } - Err(e) => { - return Err(RustADBError::IOError(e)); - } - } - } - }); - - // Read from given reader (that could be stdin e.g), and write content to server socket - if let Err(e) = std::io::copy(&mut reader, &mut write_stream) { - match e.kind() { - std::io::ErrorKind::BrokenPipe => return Ok(()), - _ => return Err(RustADBError::IOError(e)), - } - } - - Ok(()) + fn shell(&mut self, reader: &mut dyn Read, writer: Box<(dyn Write + Send)>) -> Result<()> { + self.shell(reader, writer) } fn pull(&mut self, source: &dyn AsRef, mut output: &mut dyn Write) -> Result<()> { @@ -124,4 +39,20 @@ impl ADBDeviceExt for ADBServerDevice { fn framebuffer_inner(&mut self) -> Result, Vec>> { self.framebuffer_inner() } + + fn forward(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.forward(remote, local) + } + + fn forward_remove_all(&mut self) -> Result<()> { + self.forward_remove_all() + } + + fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { + self.reverse(remote, local) + } + + fn reverse_remove_all(&mut self) -> Result<()> { + self.reverse_remove_all() + } } diff --git a/adb_client/src/server_device/commands/forward.rs b/adb_client/src/server_device/commands/forward.rs index 80b538a..9916367 100644 --- a/adb_client/src/server_device/commands/forward.rs +++ b/adb_client/src/server_device/commands/forward.rs @@ -1,8 +1,8 @@ -use crate::{models::AdbServerCommand, ADBServerDevice, Result}; +use crate::{models::AdbServerCommand, ADBProtoPort, ADBServerDevice, Result}; impl ADBServerDevice { /// Forward socket connection - pub fn forward(&mut self, remote: String, local: String) -> Result<()> { + pub fn forward(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { let serial = self.identifier.clone(); self.connect()? .send_adb_request(AdbServerCommand::TransportSerial(serial.clone()))?; diff --git a/adb_client/src/server_device/commands/logcat.rs b/adb_client/src/server_device/commands/logcat.rs index b2dc0d4..93050f7 100644 --- a/adb_client/src/server_device/commands/logcat.rs +++ b/adb_client/src/server_device/commands/logcat.rs @@ -1,6 +1,6 @@ use std::io::{self, Write}; -use crate::{ADBDeviceExt, ADBServerDevice, Result}; +use crate::{ADBServerDevice, Result}; struct LogFilter { writer: W, diff --git a/adb_client/src/server_device/commands/mod.rs b/adb_client/src/server_device/commands/mod.rs index 40010b8..0a4e9a2 100644 --- a/adb_client/src/server_device/commands/mod.rs +++ b/adb_client/src/server_device/commands/mod.rs @@ -9,6 +9,7 @@ mod reconnect; mod recv; mod reverse; mod send; +mod shell; mod stat; mod tcpip; mod transport; diff --git a/adb_client/src/server_device/commands/reverse.rs b/adb_client/src/server_device/commands/reverse.rs index a7f3a56..3b94da2 100644 --- a/adb_client/src/server_device/commands/reverse.rs +++ b/adb_client/src/server_device/commands/reverse.rs @@ -1,8 +1,8 @@ -use crate::{models::AdbServerCommand, ADBServerDevice, Result}; +use crate::{models::AdbServerCommand, ADBProtoPort, ADBServerDevice, Result}; impl ADBServerDevice { /// Reverse socket connection - pub fn reverse(&mut self, remote: String, local: String) -> Result<()> { + pub fn reverse(&mut self, remote: ADBProtoPort, local: ADBProtoPort) -> Result<()> { let serial = self.identifier.clone(); self.connect()? .send_adb_request(AdbServerCommand::TransportSerial(serial))?; diff --git a/adb_client/src/server_device/commands/shell.rs b/adb_client/src/server_device/commands/shell.rs new file mode 100644 index 0000000..7015d6e --- /dev/null +++ b/adb_client/src/server_device/commands/shell.rs @@ -0,0 +1,98 @@ +use std::io::{Read, Write}; + +use crate::{ + constants::BUFFER_SIZE, + models::{AdbServerCommand, HostFeatures}, + ADBServerDevice, Result, RustADBError, +}; + +impl ADBServerDevice { + pub(crate) fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { + let supported_features = self.host_features()?; + if !supported_features.contains(&HostFeatures::ShellV2) + && !supported_features.contains(&HostFeatures::Cmd) + { + return Err(RustADBError::ADBShellNotSupported); + } + + let serial = self.identifier.clone(); + self.connect()? + .send_adb_request(AdbServerCommand::TransportSerial(serial))?; + self.get_transport_mut() + .send_adb_request(AdbServerCommand::ShellCommand(command.join(" ")))?; + + const BUFFER_SIZE: usize = 4096; + loop { + let mut buffer = [0; BUFFER_SIZE]; + match self + .get_transport_mut() + .get_raw_connection()? + .read(&mut buffer) + { + Ok(size) => { + if size == 0 { + return Ok(()); + } else { + output.write_all(&buffer[..size])?; + } + } + Err(e) => { + return Err(RustADBError::IOError(e)); + } + } + } + } + + pub(crate) fn shell( + &mut self, + mut reader: &mut dyn Read, + mut writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + let supported_features = self.host_features()?; + if !supported_features.contains(&HostFeatures::ShellV2) + && !supported_features.contains(&HostFeatures::Cmd) + { + return Err(RustADBError::ADBShellNotSupported); + } + + let serial = self.identifier.clone(); + self.connect()? + .send_adb_request(AdbServerCommand::TransportSerial(serial))?; + self.get_transport_mut() + .send_adb_request(AdbServerCommand::Shell)?; + + let mut read_stream = self.get_transport_mut().get_raw_connection()?.try_clone()?; + + let mut write_stream = read_stream.try_clone()?; + + // Reading thread, reads response from adb-server + std::thread::spawn(move || -> Result<()> { + loop { + let mut buffer = [0; BUFFER_SIZE]; + match read_stream.read(&mut buffer) { + Ok(0) => { + read_stream.shutdown(std::net::Shutdown::Both)?; + return Ok(()); + } + Ok(size) => { + writer.write_all(&buffer[..size])?; + writer.flush()?; + } + Err(e) => { + return Err(RustADBError::IOError(e)); + } + } + } + }); + + // Read from given reader (that could be stdin e.g), and write content to server socket + if let Err(e) = std::io::copy(&mut reader, &mut write_stream) { + match e.kind() { + std::io::ErrorKind::BrokenPipe => return Ok(()), + _ => return Err(RustADBError::IOError(e)), + } + } + + Ok(()) + } +}