diff options
Diffstat (limited to 'src/os/macos.rs')
-rw-r--r-- | src/os/macos.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/os/macos.rs b/src/os/macos.rs new file mode 100644 index 0000000..0f05e37 --- /dev/null +++ b/src/os/macos.rs @@ -0,0 +1,155 @@ +use ::pnet::datalink::Channel::Ethernet; +use ::pnet::datalink::DataLinkReceiver; +use ::pnet::datalink::{self, Config, NetworkInterface}; +use ::std::io::{self, stdin, Write}; +use ::termion::event::Event; +use ::termion::input::TermRead; + +use ::std::collections::HashMap; +use ::std::net::IpAddr; + +use std::process::Command; +use regex::{Regex}; + +use signal_hook::iterator::Signals; + +use crate::network::{Connection, Protocol}; +use crate::OsInputOutput; + +use std::net::{SocketAddr}; + +struct KeyboardEvents; + +impl Iterator for KeyboardEvents { + type Item = Event; + fn next(&mut self) -> Option<Event> { + match stdin().events().next() { + Some(Ok(ev)) => Some(ev), + _ => None, + } + } +} + +fn get_datalink_channel( + interface: &NetworkInterface, +) -> Result<Box<dyn DataLinkReceiver>, failure::Error> { + match datalink::channel(interface, Config::default()) { + Ok(Ethernet(_tx, rx)) => Ok(rx), + Ok(_) => failure::bail!("Unknown interface type"), + Err(e) => failure::bail!("Failed to listen to network interface: {}", e), + } +} + +fn get_interface(interface_name: &str) -> Option<NetworkInterface> { + datalink::interfaces() + .into_iter() + .find(|iface| iface.name == interface_name) +} + +#[derive(Debug)] +struct RawConnection { + ip: String, + local_port: String, + remote_port: String, + protocol: String, + process_name: String, +} + +fn get_open_sockets() -> HashMap<Connection, String> { + let mut open_sockets = HashMap::new(); + + let output = Command::new("lsof") + .args(&["-n","-P", "-i4"])//"4tcp" + .output() + .expect("failed to execute process"); + + let regex = Regex::new(r"([^\s]+).*(TCP|UDP).*:(.*)->(.*):(\d*)(\s|$)").unwrap(); + + let output_string = String::from_utf8(output.stdout).unwrap(); + let lines = output_string.lines(); + + for line in lines { + let raw_connection_iter = regex.captures_iter(line).filter_map(|cap| { + let process_name = String::from(cap.get(1).unwrap().as_str()); + let protocol = String::from(cap.get(2).unwrap().as_str()); + let local_port = String::from(cap.get(3).unwrap().as_str()); + let ip = String::from(cap.get(4).unwrap().as_str()); + let remote_port = String::from(cap.get(5).unwrap().as_str()); + let connection = RawConnection{process_name, ip,local_port, remote_port, protocol}; + Some(connection) + }); + + let raw_connection_vec = raw_connection_iter.map(|m| m).collect::<Vec<_>>(); + + if let Some(raw_connection) = raw_connection_vec.first() { + let protocol = Protocol::from_string(&raw_connection.protocol).unwrap(); + let ip_address = IpAddr::V4(raw_connection.ip.parse().unwrap()); + let remote_port = raw_connection.remote_port.parse::<u16>().unwrap(); + let local_port = raw_connection.local_port.parse::<u16>().unwrap(); + + let socket_addr = SocketAddr::new(ip_address, remote_port); + let connection = Connection::new(socket_addr, local_port, protocol).unwrap(); + + open_sockets.insert(connection, raw_connection.process_name.clone()); + } + } + + return open_sockets; +} + +fn lookup_addr(ip: &IpAddr) -> Option<String> { + ::dns_lookup::lookup_addr(ip).ok() +} + +fn sigwinch() -> (Box<dyn Fn(Box<dyn Fn()>) + Send>, Box<dyn Fn() + Send>) { + let signals = Signals::new(&[signal_hook::SIGWINCH]).unwrap(); + let on_winch = { + let signals = signals.clone(); + move |cb: Box<dyn Fn()>| { + for signal in signals.forever() { + match signal { + signal_hook::SIGWINCH => cb(), + _ => unreachable!(), + } + } + } + }; + let cleanup = move || { + signals.close(); + }; + (Box::new(on_winch), Box::new(cleanup)) +} + +pub fn create_write_to_stdout() -> Box<dyn FnMut(String) + Send> { + Box::new({ + let mut stdout = io::stdout(); + move |output: String| { + writeln!(stdout, "{}", output).unwrap(); + } + }) +} + +pub fn get_input(interface_name: &str) -> Result<OsInputOutput, failure::Error> { + let keyboard_events = Box::new(KeyboardEvents); + let network_interface = match get_interface(interface_name) { + Some(interface) => interface, + None => { + failure::bail!("Cannot find interface {}", interface_name); + } + }; + let network_frames = get_datalink_channel(&network_interface)?; + let lookup_addr = Box::new(lookup_addr); + let write_to_stdout = create_write_to_stdout(); + let (on_winch, cleanup) = sigwinch(); + + Ok(OsInputOutput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + on_winch, + cleanup, + write_to_stdout, + }) +} |