diff --git a/src/main.rs b/src/main.rs index de3763d..a23c5a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,44 @@ use regex::Regex; use std::collections::HashSet; -use std::fs; -use std::fs::read_to_string; +use std::fmt::{Display, Formatter, Write}; +use std::{fmt, fs}; +use std::fs::{read_to_string, ReadDir}; use std::os::unix::fs::MetadataExt; -use crate::socket::socket::Id; +use std::path::PathBuf; +use crate::socket::socket::{Id, Tcp_Socket}; type inode = u64; type pid = u64; +type pids = HashSet; + mod socket; -fn get_socket_inodes_by_pid(pid : u64, socket_patt : &Regex) -> Vec { - let mut inodes : Vec = Vec::::new(); +fn get_socket_inodes_by_pid(pid : u64, socket_patt : &Regex) -> HashSet { + let mut inodes = HashSet::::new(); let mut path = String::from("/proc/"); path.push_str(&pid.to_string()); path.push_str("/fd/"); - let contents = fs::read_dir(path).expect("upsi 4 (proc kann nicht gelesen werden, Berechtigung?)"); + let mut contents: ReadDir; + match fs::read_dir(path) { + Ok(dir) => { + contents = dir; + }, + Err(_) => return HashSet::::new() + }; + for fd in contents { match fd { Ok(fd) => { - let name = fs::read_link(fd.path()).unwrap(); + let path_name = fs::read_link(fd.path()); + let mut name : PathBuf; + match path_name { + Ok(link) => name = link, + Err(_) => continue, + } let name2 = name.file_name().unwrap().to_str().unwrap(); if socket_patt.is_match(name2) { - inodes.push(socket_patt.captures(name2).unwrap().get(1).unwrap().as_str().parse::().unwrap()) + inodes.insert(socket_patt.captures(name2).unwrap().get(1).unwrap().as_str().parse::().unwrap()); } }, Err(_) => continue, @@ -31,6 +47,10 @@ fn get_socket_inodes_by_pid(pid : u64, socket_patt : &Regex) -> Vec { inodes } +fn get_socket_by_inode(sockets : &HashSet, inode : u64) -> Option { + sockets.clone().iter().map(|x| x.to_owned()).find(|x|x.inode == inode) +} + fn get_tcp_file_by_pid(pid_pattern : &Regex, pid: pid) -> String { let mut path = String::from("/proc/"); path.push_str(&pid.to_string()); @@ -38,8 +58,8 @@ fn get_tcp_file_by_pid(pid_pattern : &Regex, pid: pid) -> String { fs::read_to_string(path).unwrap().lines().skip(1).map(|x| x.to_owned()).collect::>().join("\n") } -fn parse_tcp_file_line(line : String) -> socket::socket::Socket { - socket::socket::Socket::new(socket::socket::Peer::build_from_hex(&get_index(line.clone(),1)), socket::socket::Peer::build_from_hex(&get_index(line.clone(),2)), Vec::::new(), socket::socket::SocketState::new(socket::socket::Peer::convert_port_hex_dec(&get_index(line.clone(), 3))), socket::socket::Id::create_def(), socket::socket::Id::create_def(), get_index(line, 9).parse().unwrap()) +fn parse_tcp_file_line(line : String) -> socket::socket::Tcp_Socket { + socket::socket::Tcp_Socket::new(socket::socket::Peer::build_from_hex(&get_index(&line.clone(),1)), socket::socket::Peer::build_from_hex(&get_index(&line.clone(),2)), HashSet::::new(), socket::socket::Tcp_SocketState::new(socket::socket::Peer::convert_port_hex_dec(&get_index(&line.clone(), 3))), socket::socket::Id::create_def(), socket::socket::Id::create_def(), get_index(&line, 9).parse().unwrap()) } fn get_readable_proc_pids(pid_pattern : &Regex) -> Vec { @@ -47,7 +67,6 @@ fn get_readable_proc_pids(pid_pattern : &Regex) -> Vec { let dirs = fs::read_dir("/proc/").expect("proc kann nicht gelesen werden, Berechtigung?"); for dir in dirs { match dir { - Ok(dir) => { let name = dir.file_name().into_string().unwrap(); if !pid_pattern.is_match(&name) { @@ -71,31 +90,124 @@ fn get_uid_guid_by_pid(pid : pid) -> (socket::socket::Id,socket::socket::Id) { let mut iter = status_file.lines(); let uid_str = iter.find(|x| x.starts_with("Uid:")).unwrap().to_owned(); let gid_str = iter.find(|x| x.starts_with("Gid:")).unwrap().to_owned(); - (Id::new(get_index(uid_str.clone(), 1).parse().unwrap(), get_index(uid_str.clone(), 2).parse().unwrap(), - get_index(uid_str.clone(), 3).parse().unwrap(), get_index(uid_str, 4).parse().unwrap()), - Id::new(get_index(gid_str.clone(), 1).parse().unwrap(), get_index(gid_str.clone(), 2).parse().unwrap(), - get_index(gid_str.clone(), 3).parse().unwrap(), get_index(gid_str, 4).parse().unwrap())) + (Id::new(get_index(&uid_str.clone(), 1).parse().unwrap(), get_index(&uid_str.clone(), 2).parse().unwrap(), + get_index(&uid_str.clone(), 3).parse().unwrap(), get_index(&uid_str, 4).parse().unwrap()), + Id::new(get_index(&gid_str.clone(), 1).parse().unwrap(), get_index(&gid_str.clone(), 2).parse().unwrap(), + get_index(&gid_str.clone(), 3).parse().unwrap(), get_index(&gid_str, 4).parse().unwrap())) } -fn get_all_tcp_sockets(pid_pattern : &Regex, socket_patt : &Regex) -> HashSet { - let mut sockets = HashSet::::new(); +fn add_pid_to_tcp_sockets(sockets : &mut Vec, inodes : HashSet, pid : pid) -> (){ + let s2 = sockets.clone(); + for (i, v) in s2.iter().enumerate(){ + if inodes.contains(&v.inode) { + sockets.get_mut(i).unwrap().pids.insert(pid); + } + } +} + +pub fn format_output(combination : socket::socket::Combination, sockets : &Vec) -> String{ + let header_loc_ip = "Local Address"; + let header_loc_port = "Port"; + let header_rem_ip = "Remote Address"; + let header_state = "State"; + let header_rem_port = header_loc_port; + let mut ret_str = String::from(""); + let mut max_loc_ip_len = header_loc_ip.len(); + let mut max_loc_port_len = header_loc_port.len(); + let mut max_rem_ip_len = header_rem_ip.len(); + let mut max_rem_port_len = max_loc_port_len; + let mut max_state_len = header_state.len(); + + if combination == socket::socket::Combination::tnp{ + let header_uid_real = "real-uid"; + let header_uid_eff = "effective-uid"; + let header_gid_real = "real-gid"; + let header_gid_eff = "effective-gid"; + let header_pids = "PIDs"; + let mut max_real_uid_len = header_uid_real.len(); + let mut max_eff_uid_len = header_uid_eff.len(); + let mut max_real_gid_len = header_gid_real.len(); + let mut max_eff_gid_len = header_gid_eff.len(); + let mut max_pids_len = header_pids.len(); + } + sockets.clone().iter().for_each(|x| { + + + let curr_loc_ip = x.get_loc_ip_string(); + let curr_loc_ip_len = x.get_loc_ip_string().len(); + let curr_loc_port = x.get_loc_port_string(); + let curr_loc_port_len = curr_loc_port.len(); + let curr_rem_ip = x.get_rem_ip_string(); + let curr_rem_ip_len = curr_rem_ip.len(); + let curr_rem_port = x.get_rem_port_string(); + let curr_rem_port_len = curr_rem_port.len(); + let curr_state = x.get_state().to_string(); + let curr_state_len = curr_state.len(); + max_loc_ip_len = if (curr_loc_ip_len > max_loc_ip_len) { curr_loc_ip_len } else { max_loc_ip_len }; + max_loc_port_len = if (curr_loc_port_len > max_loc_port_len) { curr_loc_port_len } else { max_loc_port_len }; + max_rem_ip_len = if (curr_rem_ip_len > max_rem_ip_len) { curr_rem_ip_len } else { max_rem_ip_len }; + max_rem_port_len = if (curr_rem_port_len > max_rem_port_len) { curr_rem_port_len } else { max_rem_port_len }; + max_state_len = if(curr_state_len > max_state_len) { curr_state_len } else { max_state_len }; + + + + if combination == socket::socket::Combination::tnp{ + + } + }); + max_loc_ip_len = max_loc_ip_len + 3; + max_loc_port_len = max_loc_port_len + 3; + max_rem_ip_len = max_rem_ip_len + 3; + match combination { + socket::socket::Combination::tn => { + ret_str.push_str(&format!("{:>max_state_len$} {:>max_loc_ip_len$}:{:max_rem_ip_len$}:{:max_state_len$} {:>max_loc_ip_len$}:{:max_rem_ip_len$}:{: { + } + } + + ret_str +} + +fn get_all_tcp_sockets(pid_pattern : &Regex, socket_patt : &Regex) -> Vec { + let mut sockets = Vec::::new(); let pids = get_readable_proc_pids(pid_pattern); + let mut inodes = HashSet::::new(); pids.iter().for_each(|x| { get_tcp_file_by_pid(&pid_pattern.clone(), *x).lines().for_each(|y| { - sockets.insert(parse_tcp_file_line(y.to_owned())); - }) + add_to_tcp_sockets(&mut sockets, parse_tcp_file_line(y.to_owned())); + }); + inodes = get_socket_inodes_by_pid(*x, socket_patt); + let (uid, gid) = get_uid_guid_by_pid(*x); + for (i, v) in sockets.clone().iter().enumerate() { + if inodes.contains(&v.inode){ + add_pid_to_tcp_sockets(&mut sockets, inodes.clone(), *x); + } + Tcp_Socket::add_uid_gid(sockets.get_mut(i).unwrap(),uid, gid); + } + }); sockets } -fn get_socket_info() -> Vec { - let mut sockets = Vec::::new(); +fn add_to_tcp_sockets(sockets : &mut Vec, socket: socket::socket::Tcp_Socket) -> (){ + if !sockets.contains(&socket) { + sockets.push(socket); + } +} + +fn get_socket_info() -> Vec { + let mut sockets = Vec::::new(); sockets } -fn get_index(line: String, index: usize) -> String { - line.split_whitespace().nth(index).expect("upsi").to_owned() +fn get_index(line: &String, index: usize) -> String { + line.clone().split_whitespace().nth(index).expect("upsi").to_owned() } fn get_remote_address() {} @@ -105,6 +217,5 @@ fn main() { let socket_patt : Regex = Regex::new(r"socket:\[([0-9]+)\]").expect("kannst kein regex?? xd"); let pid_pattern = Regex::new(r"^[1-4]?[0-9]{1,6}$").expect("kannst kein regex??"); let sockets = get_all_tcp_sockets(&pid_pattern, &socket_patt); - println!("{:?}", sockets); - println!("{}", sockets.len()); + println!("{}", format_output(socket::socket::Combination::tn, &sockets)); } diff --git a/src/socket.rs b/src/socket.rs index 2f7532e..c70381a 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -5,9 +5,9 @@ pub mod socket { use std::collections::HashSet; use std::fmt::Display; use std::fs; - use std::fs::read_to_string; use std::hash::{Hash, Hasher}; use std::os::unix::fs::MetadataExt; + use crate::pids; #[derive(PartialEq)] #[derive(Clone)] @@ -16,7 +16,7 @@ pub mod socket { pub ip_address: String, pub port: usize, } - #[derive(PartialEq)] + #[derive(PartialEq, Copy)] #[derive(Clone)] #[derive(Debug)] pub struct Id { @@ -85,12 +85,21 @@ pub mod socket { pub fn convert_port_hex_dec(port: &str) -> usize { usize::from_str_radix(port, 16).expect("upsi 3") } + + pub fn get_ip_string(&self) -> String { + self.clone().ip_address + } + + pub fn get_port_string(&self) -> String{ + if self.port == 0 { String::from("*")} else {self.port.to_string()} + } + } #[derive(PartialEq)] #[derive(Clone)] #[derive(Debug)] - pub enum SocketState{ + pub enum Tcp_SocketState { Established, SYN_Sent, SYN_Recv, @@ -108,57 +117,67 @@ pub mod socket { Undefined, } - impl SocketState { - pub fn new(index : usize) -> SocketState { + impl Tcp_SocketState { + pub fn new(index : usize) -> Tcp_SocketState { match index { - 1 => SocketState::Established, - 2 => SocketState::SYN_Sent, - 3 => SocketState::SYN_Recv, - 4 => SocketState::FIN_Wait1, - 5 => SocketState::FIN_Wait2, - 6 => SocketState::TIME_Wait, - 7 => SocketState::Close, - 8 => SocketState::Close_Wait, - 9 => SocketState::Last_ACK, - 10 => SocketState::Listening, - 11 => SocketState::Closing, - 12 => SocketState::New_SYN_Recv, - 13 => SocketState::Bound_Inactive, - 14 => SocketState::Max_States, - _ => SocketState::Undefined, + 1 => Tcp_SocketState::Established, + 2 => Tcp_SocketState::SYN_Sent, + 3 => Tcp_SocketState::SYN_Recv, + 4 => Tcp_SocketState::FIN_Wait1, + 5 => Tcp_SocketState::FIN_Wait2, + 6 => Tcp_SocketState::TIME_Wait, + 7 => Tcp_SocketState::Close, + 8 => Tcp_SocketState::Close_Wait, + 9 => Tcp_SocketState::Last_ACK, + 10 => Tcp_SocketState::Listening, + 11 => Tcp_SocketState::Closing, + 12 => Tcp_SocketState::New_SYN_Recv, + 13 => Tcp_SocketState::Bound_Inactive, + 14 => Tcp_SocketState::Max_States, + _ => Tcp_SocketState::Undefined, } } pub fn to_string(&self) -> String { match self { - SocketState::Established => String::from("Established"), - SocketState::SYN_Sent => String::from("SYN_Sent"), - SocketState::SYN_Recv => String::from("SYN_Recv"), - SocketState::FIN_Wait1 => String::from("FIN_Wait1"), - SocketState::FIN_Wait2 => String::from("FIN_Wait2"), - SocketState::TIME_Wait => String::from("TIME_Wait"), - SocketState::Close => String::from("Close"), - SocketState::Close_Wait => String::from("Close_Wait"), - SocketState::Last_ACK => String::from("Last_ACK"), - SocketState::Listening => String::from("Listening"), - SocketState::Closing => String::from("Closing"), - SocketState::New_SYN_Recv => String::from("New_SYN_Recv"), - SocketState::Bound_Inactive => String::from("Bound_Inactive"), - SocketState::Max_States => String::from("Max_States"), - SocketState::Undefined => String::from("Undefined"), + Tcp_SocketState::Established => String::from("Established"), + Tcp_SocketState::SYN_Sent => String::from("SYN_Sent"), + Tcp_SocketState::SYN_Recv => String::from("SYN_Recv"), + Tcp_SocketState::FIN_Wait1 => String::from("FIN_Wait1"), + Tcp_SocketState::FIN_Wait2 => String::from("FIN_Wait2"), + Tcp_SocketState::TIME_Wait => String::from("TIME_Wait"), + Tcp_SocketState::Close => String::from("Close"), + Tcp_SocketState::Close_Wait => String::from("Close_Wait"), + Tcp_SocketState::Last_ACK => String::from("Last_ACK"), + Tcp_SocketState::Listening => String::from("Listening"), + Tcp_SocketState::Closing => String::from("Closing"), + Tcp_SocketState::New_SYN_Recv => String::from("New_SYN_Recv"), + Tcp_SocketState::Bound_Inactive => String::from("Bound_Inactive"), + Tcp_SocketState::Max_States => String::from("Max_States"), + Tcp_SocketState::Undefined => String::from("Undefined"), } } } - impl PartialEq for Socket { + pub fn pids_to_string(pids : pids) -> String { + let mut ret_str = String::from(""); + pids.iter().for_each(|x| { + ret_str.push_str(&x.to_string()); + ret_str.push_str(", "); + }); + ret_str.drain(ret_str.len()-2..ret_str.len()); + ret_str + } + + impl PartialEq for Tcp_Socket { fn eq(&self, other: &Self) -> bool { return self.inode == other.inode; } } - impl Eq for Socket {} + impl Eq for Tcp_Socket {} - impl Hash for Socket { + impl Hash for Tcp_Socket { fn hash(&self, state: &mut H) -> (){ let mut state = self.inode * 31; state = state + 11; @@ -166,19 +185,25 @@ pub mod socket { } #[derive(Clone, Debug)] - pub struct Socket { + pub struct Tcp_Socket { peer_loc: Peer, peer_rem: Peer, - pids: Vec, - state : SocketState, + pub pids: HashSet, + state : Tcp_SocketState, user : Id, group : Id, - inode : u64 + pub inode : u64 } - impl Socket { - pub fn new(peer_loc : Peer, peer_rem : Peer, pids : Vec, state : SocketState, user : Id, group : Id, inode : u64) -> Socket { - Socket { + #[derive(Clone,Copy, PartialEq)] + pub enum Combination{ + tnp, + tn, + } + + impl Tcp_Socket { + pub fn new(peer_loc : Peer, peer_rem : Peer, pids : HashSet, state : Tcp_SocketState, user : Id, group : Id, inode : u64) -> Tcp_Socket { + Tcp_Socket { peer_loc, peer_rem, pids, @@ -200,29 +225,81 @@ pub mod socket { inode } + pub fn add_pid(socket : &mut Tcp_Socket, pid : u64) -> () { + socket.pids.insert(pid); + } + + pub fn add_uid_gid(socket : &mut Tcp_Socket, uid : Id, gid : Id) -> (){ + socket.user = uid; + socket.group = gid; + } + pub fn check_inode_seen(seen_inodes : &HashSet, pid : u64) -> bool { - let inode = Socket::get_tcp_file_inode(pid); + let inode = Tcp_Socket::get_tcp_file_inode(pid); seen_inodes.contains(&inode) } pub fn add_inode(seen_inodes : &mut HashSet, pid: u64){ - let inode = Socket::get_tcp_file_inode(pid); + let inode = Tcp_Socket::get_tcp_file_inode(pid); seen_inodes.insert(inode); } - pub fn to_string (self) -> String { + pub fn get_loc_ip_string(&self) -> String{ + self.peer_loc.get_ip_string() + } + + pub fn get_loc_port_string(&self) -> String{ + self.peer_loc.get_port_string() + } + + pub fn get_rem_ip_string(&self) -> String{ + self.peer_rem.get_ip_string() + } + + pub fn get_rem_port_string(&self) -> String{ + self.peer_rem.get_port_string() + } + + pub fn to_string (&self, combination: Combination) -> String { let mut ret_str = String::from(""); - ret_str.push_str(&self.peer_loc.ip_address); - ret_str.push(':'); - let loc_port_str = self.peer_loc.port.clone().to_string(); - ret_str.push_str(if self.peer_loc.port == 0 { "*" } else { &loc_port_str }); - ret_str.push(' '); - ret_str.push_str(&self.peer_rem.ip_address); - ret_str.push(':'); - let rem_port_str = self.peer_rem.port.clone().to_string(); - ret_str.push_str(if self.peer_rem.port == 0 { "*" } else { &rem_port_str }); - ret_str.push('\n'); - ret_str + match combination { + Combination::tnp => { + //state rem_ip:rem_port loc_ip:loc_port real|effective-uid real|effective-gid pids(.,.,.,.) + ret_str.push_str(&self.clone().to_string(Combination::tn)); + ret_str.push(' '); + ret_str.push_str(&self.user.real.to_string()); + ret_str.push('|'); + ret_str.push_str(&self.user.effective.to_string()); + ret_str.push(' '); + ret_str.push_str(&self.group.real.to_string()); + ret_str.push('|'); + ret_str.push_str(&self.group.effective.to_string()); + ret_str.push_str(&pids_to_string(self.clone().pids)); + ret_str + + } + Combination::tn => { + //state rem_ip:rem_port loc_ip:loc_port + ret_str.push_str(&self.state.to_string()); + ret_str.push(' '); + ret_str.push_str(&self.peer_loc.ip_address); + ret_str.push(':'); + let loc_port_str = self.peer_loc.port.clone().to_string(); + ret_str.push_str(if self.peer_loc.port == 0 { "*" } else { &loc_port_str }); + ret_str.push(' '); + ret_str.push_str(&self.peer_rem.ip_address); + ret_str.push(':'); + let rem_port_str = self.peer_rem.port.clone().to_string(); + ret_str.push_str(if self.peer_rem.port == 0 { "*" } else { &rem_port_str }); + ret_str.push('\n'); + ret_str + } + } + + } + + pub fn get_state(&self) -> Tcp_SocketState{ + self.state.clone() } fn get_all_unique_socket_infos() -> String { @@ -244,8 +321,8 @@ pub mod socket { let mut path = dir.path().into_os_string().into_string().expect("gehtnd"); path.push_str("/net/tcp"); let pid = &path.split("/").nth(2).expect("gesplittert"); - if !Socket::check_inode_seen(&seen_inodes, pid.parse::().unwrap()) { - Socket::add_inode(& mut seen_inodes, pid.parse::().unwrap()); + if !Tcp_Socket::check_inode_seen(&seen_inodes, pid.parse::().unwrap()) { + Tcp_Socket::add_inode(& mut seen_inodes, pid.parse::().unwrap()); let test = &fs::read_to_string(path) .expect("Could not read proc/pid/net/tcp") .lines() @@ -272,11 +349,11 @@ pub mod socket { #[cfg(test)] mod tests { - + use crate::socket::socket::Combination; use super::*; - fn init_sockets() -> Vec { - let mut sockets = Vec::::new(); + fn init_sockets() -> Vec { + let mut sockets = Vec::::new(); let root_uid = socket::Id::new(0,0,0,0); let root_gid = root_uid.clone(); let mux_uid = socket::Id::new(1000,1000,1000,1000); @@ -298,8 +375,8 @@ mod tests { #[test] fn test_to_string() -> () { let sockets = init_sockets(); - assert_eq!("93.83.160.12:21345 0.0.0.0:*\n", sockets[0].clone().to_string()); - assert_eq!("195.201.90.97:8843 0.0.0.0:443\n", sockets[1].clone().to_string()); - assert_eq!("255.255.255.255:193193 0.0.0.0:8080\n", sockets[2].clone().to_string()); + assert_eq!("93.83.160.12:21345 0.0.0.0:*\n", sockets[0].clone().to_string(Combination::tn)); + assert_eq!("195.201.90.97:8843 0.0.0.0:443\n", sockets[1].clone().to_string(Combination::tn)); + assert_eq!("255.255.255.255:193193 0.0.0.0:8080\n", sockets[2].clone().to_string(Combination::tn)); } }