From d70e57ea90464f95e5935f6c62bf7f38d9af279f Mon Sep 17 00:00:00 2001 From: Imbus Date: Tue, 20 Jul 2021 18:21:21 +0200 Subject: [PATCH] Cleanup, polish and version bumping --- Cargo.lock | 197 +----------------------------------------------- Cargo.toml | 4 +- README.md | 83 +++++++------------- src/input.rs | 7 +- src/machines.rs | 4 +- src/main.rs | 56 ++++++++------ src/packet.rs | 92 ++++------------------ waker.json | 32 -------- 8 files changed, 86 insertions(+), 389 deletions(-) delete mode 100644 waker.json diff --git a/Cargo.lock b/Cargo.lock index 4ce6d51..06531ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,12 +22,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" - [[package]] name = "bitflags" version = "1.2.1" @@ -55,15 +49,6 @@ dependencies = [ "vec_map", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "dirs" version = "3.0.2" @@ -84,21 +69,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "eff-wordlist" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226cc2588a6b769f444d06dedd29a366ea92469edea1cef7873fbd76e5b34c24" -dependencies = [ - "rand 0.6.5", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "getrandom" version = "0.2.3" @@ -133,15 +103,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "proc-macro2" @@ -161,161 +125,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", - "rand_hc 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg", - "rand_core 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.9" @@ -422,9 +231,7 @@ version = "0.1.0" dependencies = [ "clap", "dirs", - "eff-wordlist", "hex", - "rand 0.8.4", "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 4b97ec5..2fef035 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,5 @@ serde = { version = "*", features = ["derive"] } serde_json = "*" dirs = "*" clap = "*" -eff-wordlist = "*" -rand = "*" +# eff-wordlist = "*" +# rand = "*" diff --git a/README.md b/README.md index 700413d..4565428 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,36 @@ -TODO: Review parsing methods -Add confirm for wake all +# Waker +**An intelligent wake on lan utility to keep track of your machines** -Hosts should be pinged on listing +The output from program help flag should provide a clear idea of its use: +``` +Waker 0.1.0 +Imbus64 +Utility for sending magic packets to configured machines. -Structs defined: - Host - - Implement methods to retrieve as macbytes/ipv4addr - - Field containing MagicPacket? - Machines - - WakeAll method - - PingAll method - MagicPacket - - Finalze parsers +USAGE: + waker [FLAGS] [OPTIONS] [MAC ADDRESSES]... -The format of a Wake-on-LAN (WOL) magic packet is defined -as a byte array with 6 bytes of value 255 (0xFF) and -16 repetitions of the target machine’s 48-bit (6-byte) MAC address. +FLAGS: + -a, --add Add a new host + --all Wake all configured hosts + -e, --edit Enter edit mode + -h, --help Prints help information + -l, --list List all configured entries + -p, --print-config Print contents of configuration file to stdout + -V, --version Prints version information -102 bytes +OPTIONS: + --backup Backup configuration file -several macs per name +ARGS: + ... -edit, delete, add +``` +This project is currently in beta. Many features are implemented, but some may not work as expected. -wakemode::all - -wake(bytes) method -async ping method(ip)? - -const MAGIC_BYTES_HEADER: [u8; 6] = [0xFF; 6]; -vec bytes = FFFFFFFFFFFF .reserve(enough) 102? - -match run_mode { - Some() => Do according to some - None => Figure out what to do(set run_mode) -} - -add 8 char string -> to bytes ask for name and add -add 11 char string -> if mac[2,5,8] is same char -> split by it and ask for name -> add - -store json in ~/.config/waker.conf -dump-json method -export/import? append? - -struct mechine { - vec of all macs - ip of machine? so we can ping it? - name -} - -struct mac_list { - Vec -} - - -ask what do: - 1. Wake - 2. Wake all - 3. Add - 4. Edit +## Future plans: +- Further testing and polish in general. +- [ ] Include pinging functionality, so the user gets feedback on what machines are already awake (Top priority) +- [ ] Enable users to import an existing config, appending selected hosts to current config. (Second priority) +- [ ] Perhaps wrap run_mode in an Option, with None being the default when the program is invoked without CLI parameters +- [ ] Rewrite all input/blocking related code into a struct of some sort diff --git a/src/input.rs b/src/input.rs index 17166c2..dd8eee2 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,7 +1,7 @@ use std::io; use std::io::prelude::*; -/// Python like input method with prompt message +/// Python like input function with prompt message pub fn input(prompt: &str) -> String { print!("{}", prompt); let _ = io::stdout().flush(); @@ -15,11 +15,14 @@ pub fn input(prompt: &str) -> String { input.trim().to_string() } -/// Simple confirm dialogue +/// Simple confirm dialogue. Appends " [y/N]: " to your message, and prints feedback on your +/// choice. pub fn confirm(message: &str) -> bool { let answer = input(format!("{} [y/N]: ", message).as_str()).to_uppercase(); if answer == "YES" || answer == "Y" { + println!("Yes"); return true; } + println!("No"); return false; } diff --git a/src/machines.rs b/src/machines.rs index cfbb969..482ecad 100644 --- a/src/machines.rs +++ b/src/machines.rs @@ -56,7 +56,9 @@ impl Machines { Ok(machines) } - fn create_skeleton_config(file: &PathBuf) -> Result<(), Box> { + /// Creates file and dumps a skeleton config into it + pub fn create_skeleton_config(file: &PathBuf) -> Result<(), Box> { + std::fs::File::create(&file)?; let skel_machines = Machines::new(); skel_machines.dump(file)?; return Ok(()); diff --git a/src/main.rs b/src/main.rs index d25577e..e246407 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ mod host; // The actual Host struct mod input; // Gives us a python-like input function, as well as a simple confirm function mod machines; // Struct that holds a vec of Hosts, as well as operations on those mod packet; // The actual magic packet struct, with wake methods e.t.c. -// mod random_machine; // Exposes a function that generates a random Host, with random mac, ip and name // The actual host struct. mod sanitizers; // Functions that sanitizes MAC and IP addresses // use crate::packet::*; @@ -76,20 +75,17 @@ fn prompt_file_creation(config_path: &PathBuf) -> Option { config_path.to_str().unwrap() ); if input::confirm(&msg) { + let newfile = std::fs::File::create(&config_path).expect(&format!( "Could not create file: {}", config_path.to_str().unwrap() )); // Deserialize an empty Machines struct into the new config file. - // The parser is not equipped to handle empty or malformatted files... - // This could potentially be moved into a static method in Machines, like - // Machines::init_config_file(), which does this for us. - // - // For now, this will do. - let skeleton_machines = Machines::new(); - skeleton_machines.dump(&config_path).unwrap(); - return Some(newfile); + match Machines::create_skeleton_config(config_path) { + Ok(()) => { return Some(newfile); } + Err(_) => return None, + } } else { return None; } @@ -169,7 +165,7 @@ fn edit_host(host: &mut Host, editmode: HostEditMode) { let newip = input("New IP: "); match sanitizers::sanitize(&newip, sanitizers::AddrType::IPv4) { Some(ip) => { - host.ips[index as usize] = newip; + host.ips[index as usize] = ip; } None => { println!("Could not parse IP"); @@ -226,7 +222,7 @@ fn edit_host(host: &mut Host, editmode: HostEditMode) { let newmac = input("New MAC: "); match sanitizers::sanitize(&newmac, sanitizers::AddrType::MAC) { Some(mac_addr) => { - host.macs[index as usize] = newmac; + host.macs[index as usize] = mac_addr; } None => { println!("Could not parse MAC"); @@ -345,8 +341,11 @@ fn main() -> Result<(), Box> { let file = prompt_file_creation(&config_path); if file.is_none() { println!("Exiting..."); - return Ok(()); } + else { + println!("Config file created."); + } + return Ok(()); } // TODO: More sophisticated error checking and logging @@ -362,22 +361,29 @@ fn main() -> Result<(), Box> { RunMode::Wake(wake_mode) => { match wake_mode { WakeMode::WakeAll => { - machines.wakeall(); - for host in &machines.list { - println!("Woke {}", host.name) + if confirm("You are about to wake all configured machines.\nContinue?") { + machines.wakeall(); + for host in &machines.list { + println!("Woke {}", host.name) + } } } WakeMode::WakeSome => { - println!("{}", machines); - let indexes = which_indexes( - "Select which hosts to wake up (Comma separated integers): ", - &machines, - ); - for index in indexes { - // TODO: Bounds checking - let host = &machines.list[index as usize]; - host.wake(); - println!("Woke {}", host.name) + if ! machines.list.is_empty() { + println!("{}", machines); + let indexes = which_indexes( + "Select which hosts to wake up (Comma separated integers): ", + &machines, + ); + for index in indexes { + // TODO: Bounds checking + let host = &machines.list[index as usize]; + host.wake(); + println!("Woke {}", host.name) + } + } + else { + println!("No machines configured yet... Try \"waker --help\" for information about usage"); } } _ => println!("undefined"), diff --git a/src/packet.rs b/src/packet.rs index 1a7c87c..f55a566 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,7 +1,5 @@ -use std::{ - error::Error, - net::{Ipv4Addr, ToSocketAddrs, UdpSocket}, -}; +use std::{convert::TryInto, error::Error, net::{Ipv4Addr, ToSocketAddrs, UdpSocket}}; +use crate::sanitizers::{self, sanitize}; // The format of a Wake-on-LAN (WOL) magic packet is defined // as a byte array with 6 bytes of value 255 (0xFF) followed by @@ -34,73 +32,22 @@ impl MagicPacket { return MagicPacket { bytes: magic_bytes }; } - // This is horrible code /// Parse a MAC-string into a packet. - /// Takes both separated and unseparated + /// The MAC-string should be 17 characters long, separated by colons (i.e. XX:XX:XX:XX:XX:XX) pub fn from_str(mac_str: &str) -> Result> { - use hex::FromHex; - let mut mac_string = mac_str.trim().to_string(); - - if mac_string.len() == 17 { - for i in 0..5 { - mac_string.remove(14 - (i * 3)); // Deal with it - } - } - - let mut mac_arr: [u8; 6]; - let hex_vec = Vec::from_hex(mac_string)?; - - unsafe { - mac_arr = std::mem::MaybeUninit::uninit().assume_init(); - let src: *const u8 = &hex_vec[0]; - let dst: *mut u8 = &mut mac_arr[0]; - dst.copy_from_nonoverlapping(src, 6); - } - return Ok(MagicPacket::new(&mac_arr)); + let mac_bytes = MagicPacket::parse_macstr(mac_str, ':')?; + return Ok(MagicPacket::new(&mac_bytes)); } - pub fn from_str2(mac_str: &str) -> Result> { - return Ok(MagicPacket::new(&MagicPacket::parse(mac_str).unwrap())); - } - - // Stolen - fn mac_to_byte(data: &str, sep: char) -> Vec { - data.split(sep) + // This method is a bit allocation heavy. + pub fn parse_macstr>(mac_str: S, sep: char) -> Result, Box> { + let sanitized_macstr = sanitize(mac_str.as_ref(), sanitizers::AddrType::MAC).unwrap(); + let bytes_split: Vec = sanitized_macstr.split(sep) .flat_map(|x| hex::decode(x).expect("Invalid mac!")) - .collect() - } - - // Parses string by position if string is 12+5 characters long (delimited by : for example) - pub fn parse>(mac_str: S) -> Result, Box> { - use hex::FromHex; - let mut mstr: &str = mac_str.as_ref(); - mstr = mstr.trim(); - let mut bytes: [u8; 6] = [0, 0, 0, 0, 0, 0]; - - for (index, byte) in bytes.iter_mut().enumerate() { - // 0,3,6,9... - let substr = &mstr[3 * index..3 * index + 2]; - *byte = <[u8; 1]>::from_hex(substr).unwrap()[0]; - } - - return Ok(Box::new(bytes)); - } - - // Loops the string and parses any valid hex - pub fn parse_harder>(mac_str: S) -> Result, Box> { - let mstr: String = mac_str.into(); - let mut hexdigits: String = mstr - .chars() - .filter(|c| char::is_ascii_hexdigit(c)) .collect(); - let mut bytes: [u8; 6] = [0; 6]; - if hexdigits.len() >= 12 { - hexdigits.truncate(12); // May not be needed, since bytes is only 6 bytes, and from_hex might be smart enough to realize... - //let bytes: [u8; 6] = hex::FromHex::from_hex(hexdigits)?; - bytes = hex::FromHex::from_hex(hexdigits)?; - } - return Ok(Box::new(bytes)); + let arr: [u8; 6] = bytes_split.try_into().unwrap(); + Ok(Box::new(arr)) } /// Send packet to/from specific address/interface @@ -118,11 +65,6 @@ impl MagicPacket { (Ipv4Addr::new(0x0, 0x0, 0x0, 0x0), 0), ) } - - /// Return raw bytes, ready to be broadcasted - pub fn get_bytes(&self) -> &[u8; 102] { - &self.bytes - } } #[cfg(test)] @@ -142,7 +84,7 @@ mod tests { #[test] fn magic_packet_from_new() { - let packet3 = MagicPacket::from_str2("10:10:10:10:10:10").unwrap(); + let packet3 = MagicPacket::from_str("10:10:10:10:10:10").unwrap(); //let vec: [u8; 102] = [0xFF; 102]; let slice = &packet3.bytes[packet3.bytes.len() - 6..]; for a in slice { @@ -155,15 +97,9 @@ mod tests { #[test] fn test_parse() { - let mp = MagicPacket::parse("ff:ff:ff:ff:ff:ff").unwrap(); + let mp = MagicPacket::parse_macstr("ff:ff:ff:ff:ff:ff", ':').unwrap(); assert_eq!([0xFF; 6], *mp); - let mp2 = MagicPacket::parse("10:10:10:10:10:10").unwrap(); + let mp2 = MagicPacket::parse_macstr("10:10:10:10:10:10", ':').unwrap(); assert_eq!([0x10; 6], *mp2); } - - #[test] - fn parse_harder_test() { - let mac = MagicPacket::parse_harder("10:10:10:10:10:10").unwrap(); - assert_eq!([0x10; 6], *mac); - } } diff --git a/waker.json b/waker.json deleted file mode 100644 index 4320a45..0000000 --- a/waker.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "list": [ - { - "name": "Name", - "macs": [ - "E9:A3:29:73:FE:D2" - ], - "ips": [] - }, - { - "name": "i", - "macs": [ - "7D:E1:AC:94:29:79" - ], - "ips": [] - }, - { - "name": "PartyMaskinen", - "macs": [ - "96:F8:FF:30:4C:EE" - ], - "ips": [] - }, - { - "name": "Kalle", - "macs": [ - "FF:FF:FF:FF:FF:FF" - ], - "ips": [] - } - ] -} \ No newline at end of file