Working proof of concept
This commit is contained in:
commit
4bd8c3984d
13 changed files with 1091 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
458
Cargo.lock
generated
Normal file
458
Cargo.lock
generated
Normal file
|
@ -0,0 +1,458 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.97"
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "waker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
"eff-wordlist",
|
||||
"hex",
|
||||
"rand 0.8.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "waker"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
hex = "*"
|
||||
serde = { version = "*", features = ["derive"] }
|
||||
serde_json = "*"
|
||||
dirs = "*"
|
||||
clap = "*"
|
||||
eff-wordlist = "*"
|
||||
rand = "*"
|
50
README.md
Normal file
50
README.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
TODO: Review parsing methods
|
||||
|
||||
Hosts should be pinged on listing
|
||||
|
||||
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.
|
||||
|
||||
102 bytes
|
||||
|
||||
several macs per name
|
||||
|
||||
edit, delete, add
|
||||
|
||||
wakemode::all
|
||||
|
||||
wake(bytes) method
|
||||
async ping method(ip)?
|
||||
|
||||
const MAGIC_BYTES_HEADER: [u8; 6] = [0xFF; 6];
|
||||
vec<u8> 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
|
2
rustfmt.toml
Normal file
2
rustfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
enum_discrim_align_threshold = 20
|
||||
struct_field_align_threshold = 20
|
49
src/cli_args.rs
Normal file
49
src/cli_args.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use clap::{App, Arg, ArgMatches};
|
||||
|
||||
// use crate::main::RunMode;
|
||||
|
||||
// let a = RunMode;
|
||||
// waker FF:FF:FF:FF:FF:FF // Direct mode, wakes the given mac address
|
||||
// waker -a, --all // Wake all configured machines
|
||||
// waker -n, --name name1, name2 // Specified which configured name to wake
|
||||
// waker -m, --macs mac1, mac2 // Specifies which macs to send magic packet to in direct mode
|
||||
// waker -l, --list // Lists all configured machines
|
||||
// waker -e, --edit // Enters interactive editing mode
|
||||
// waker --backup-config [file] // Prints to stdout unless file is specified
|
||||
|
||||
pub fn get_cli_matches() -> ArgMatches<'static> {
|
||||
/* Move this out to a function that returns a config struct with all the
|
||||
* options */
|
||||
/* or just return the ArgMatches object for clarity */
|
||||
return App::new("Waker")
|
||||
//.version("0.01")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.author("Imbus64")
|
||||
.about("Utility for sending magic packets to configured machines.")
|
||||
.arg(
|
||||
Arg::with_name("all")
|
||||
.short("a")
|
||||
.long("all")
|
||||
.help("Wake all configured hosts"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("list")
|
||||
.short("l")
|
||||
.long("list")
|
||||
.conflicts_with("weight")
|
||||
.help("Print all entries"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("raw")
|
||||
.long("raw")
|
||||
.conflicts_with_all(&["list", "plain"])
|
||||
.help("Print raw log file to stdout"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("plain")
|
||||
.long("plain")
|
||||
.conflicts_with_all(&["list", "raw"])
|
||||
.help("Print all entries without pretty table formatting"),
|
||||
)
|
||||
.get_matches();
|
||||
}
|
18
src/host.rs
Normal file
18
src/host.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Host {
|
||||
pub name: String,
|
||||
pub macs: Vec<String>,
|
||||
// pub ips: Vec<String>,
|
||||
}
|
||||
|
||||
impl Host {
|
||||
pub fn new<S: Into<String>>(name: S, mac: S, ipv4: S) -> Host {
|
||||
Host {
|
||||
name: name.into(),
|
||||
macs: vec![mac.into()],
|
||||
// ips: vec![ipv4],
|
||||
}
|
||||
}
|
||||
}
|
25
src/input.rs
Normal file
25
src/input.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
/// Python like input method with prompt message
|
||||
pub fn input(prompt: &str) -> String {
|
||||
print!("{}", prompt);
|
||||
let _ = io::stdout().flush();
|
||||
|
||||
let mut input = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut input)
|
||||
.expect("Could not read line.");
|
||||
|
||||
// Remove leading and trailing whitespaces and return
|
||||
input.trim().to_string()
|
||||
}
|
||||
|
||||
/// Simple confirm dialogue
|
||||
pub fn confirm(message: &str) -> bool {
|
||||
let answer = input(format!("{} [y/N]: ", message).as_str()).to_uppercase();
|
||||
if answer == "YES" || answer == "Y" {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
104
src/machines.rs
Normal file
104
src/machines.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
// use std::{fs::{File, OpenOptions, metadata}, io::{Read, Write}, path::{Path, PathBuf}};
|
||||
use std::error::Error;
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::host::Host;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Possibly rename to HostList
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Machines {
|
||||
pub list: Vec<Host>,
|
||||
}
|
||||
|
||||
impl Machines {
|
||||
pub fn new() -> Machines {
|
||||
Machines {
|
||||
list: Vec::<Host>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: &str, mac_addr: &str) {
|
||||
self.list.push(Host {
|
||||
name: name.to_string(),
|
||||
macs: vec![mac_addr.to_string()],
|
||||
});
|
||||
}
|
||||
|
||||
pub fn from_json_file(json_path: &PathBuf) -> Result<Machines, Box<dyn Error>> {
|
||||
let machines: Machines;
|
||||
if json_path.exists() || json_path.is_file() {
|
||||
let json: String = std::fs::read_to_string(&json_path)?.parse()?;
|
||||
machines = serde_json::from_str(&json)?;
|
||||
// println!("Machines loaded from json");
|
||||
} else {
|
||||
machines = Machines::new();
|
||||
let serialized = serde_json::to_string_pretty(&machines)?;
|
||||
let mut file = File::create(&json_path)?;
|
||||
file.write_all(&serialized.as_bytes())?;
|
||||
std::fs::write(&json_path, &serialized)?;
|
||||
// println!("Machines created");
|
||||
}
|
||||
Ok(machines)
|
||||
}
|
||||
|
||||
/// Dump this struct in json format. Will NOT create file.
|
||||
pub fn dump(&self, json_path: &PathBuf) -> Result<bool, Box<dyn Error>> {
|
||||
let serialized = serde_json::to_string_pretty(&self)?;
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&json_path)?;
|
||||
file.write_all(&serialized.as_bytes())?;
|
||||
// println!("Object written to existing file (truncated)");
|
||||
// println!("{}", json_path.to_str().unwrap());
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
// The Machine struct holds no information about where its json configuration file resides, if it
|
||||
// did we could deserialize it into it in the destructor...
|
||||
// impl Drop for Machines {
|
||||
// fn drop(&mut self) {
|
||||
// println!("Destructor called...");
|
||||
// }
|
||||
// }
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn init_machines() {
|
||||
let m = Machines::new();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_machines_and_add() {
|
||||
let mut m = Machines::new();
|
||||
m.add("Demo_Machine", "FF:FF:FF:FF:FF:FF");
|
||||
assert_eq!(1, m.list.len());
|
||||
m.add("Demo_Machine2", "FF:FF:FF:FF:FF:FF");
|
||||
assert_eq!(2, m.list.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_and_load_from_file() {
|
||||
let mut m = Machines::new();
|
||||
m.add("File_Demo_Machine", "FF:FF:FF:FF:FF:FF");
|
||||
assert_eq!(1, m.list.len());
|
||||
let path = PathBuf::from("./DEMO_MACHINES.json");
|
||||
std::fs::File::create(&path).unwrap();
|
||||
m.dump(&path).unwrap();
|
||||
// println!("{}", path. to_str().unwrap());
|
||||
let m2 = Machines::from_json_file(&path).unwrap();
|
||||
|
||||
assert_eq!(1, m2.list.len());
|
||||
|
||||
// This cleanup will never happen if the previous assert fails...
|
||||
// TODO: Branch eq check to a panic instead
|
||||
std::fs::remove_file(&path).unwrap();
|
||||
}
|
||||
}
|
110
src/main.rs
Normal file
110
src/main.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
// use std::{fs::{File, OpenOptions, metadata}, io::{Read, Write}, path::{Path, PathBuf}};
|
||||
use std::path::PathBuf;
|
||||
use std::{error::Error, iter::Enumerate, string};
|
||||
|
||||
// use serde::{Deserialize, Serialize};
|
||||
//use serde_json::to;
|
||||
|
||||
mod cli_args; // Provides a custom function that specifies our command line options
|
||||
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.
|
||||
|
||||
// use crate::packet::*;
|
||||
use crate::machines::*;
|
||||
use crate::packet::MagicPacket;
|
||||
use random_machine::random_host;
|
||||
|
||||
// waker -a, --all // Wake all configured machines
|
||||
// waker -n, --name name1, name2 // Specified which configured name to wake
|
||||
// waker -m, --macs mac1, mac2 // Specifies which macs to send magic packet to in direct mode
|
||||
// waker -l, --list // Lists all configured machines
|
||||
// waker -e, --edit // Enters interactive editing mode
|
||||
// waker --backup-config [file] // Prints to stdout unless file is specified
|
||||
|
||||
enum RunMode {
|
||||
Wake { mode: WakeMode },
|
||||
Edit { mode: EditMode },
|
||||
List,
|
||||
ConfigBak,
|
||||
}
|
||||
|
||||
/// Specifies how and which machines should be woken
|
||||
enum WakeMode {
|
||||
WakeAll, // Wake every configured machine
|
||||
WakeSome { indexes: Vec<i32> }, // Wake machines with these indexes/ids
|
||||
Direct { mac_strings: Vec<String> }, // Wake these mac adresses, non-blocking
|
||||
}
|
||||
|
||||
/// Specifies how to perform edits
|
||||
enum EditMode {
|
||||
Pick, // Prompt the user for which machine to edit
|
||||
Direct { name: String }, // Edit machine with specified name
|
||||
DirectID { id: i32 }, // Edit machine with specified id
|
||||
}
|
||||
|
||||
/// Specifies how the program should backup its config file
|
||||
enum BackupMode {
|
||||
ToFile { path: PathBuf }, // Write to file
|
||||
ToStdout, // Write to stdout
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let config_path = match cfg!(debug_assertions) {
|
||||
// If this is a debug build, the the path becomes ./waker.json, relative to project root
|
||||
true => PathBuf::new().join("waker.json"),
|
||||
|
||||
// If this is a release build, this is essentially ~/.config/waker.json stored in a pathbuf object
|
||||
false => dirs::config_dir()
|
||||
.expect("Could not find config directory...")
|
||||
.join("waker.json"),
|
||||
};
|
||||
|
||||
// If file does not exist -> Ask to create it -> dump skeleton json into it
|
||||
if !config_path.is_file() {
|
||||
let msg = format!(
|
||||
"File \"{}\" does not seem to exist...\nCreate it?",
|
||||
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);
|
||||
} else {
|
||||
println!("Exiting...");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: More sophisticated error checking and logging
|
||||
let mut machines = Machines::from_json_file(&config_path)?;
|
||||
// let rhost = random_host();
|
||||
// machines.add("test", "FF:FF:FF:FF:FF:FF");
|
||||
// machines.add(&rhost.name, &rhost.macs[0]); // Hack, needs to have a method for this..
|
||||
|
||||
for (index, machine) in machines.list.iter().enumerate() {
|
||||
let macs_str = format!("{:?}", machine.macs);
|
||||
println!("{:<3}{:25}{}", index, macs_str, machine.name); // TODO: CHANGE THIS FORMAT TO INDEX, NAME, MACS
|
||||
for mac in &machine.macs {
|
||||
let mp = MagicPacket::from_str(&mac).expect("Could not parse mac address...");
|
||||
mp.send();
|
||||
}
|
||||
}
|
||||
|
||||
machines.dump(&config_path)?;
|
||||
return Ok(());
|
||||
}
|
162
src/packet.rs
Normal file
162
src/packet.rs
Normal file
|
@ -0,0 +1,162 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
net::{Ipv4Addr, ToSocketAddrs, UdpSocket},
|
||||
};
|
||||
|
||||
// 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
|
||||
// 16 repetitions of the target machine’s 48-bit (6-byte) MAC address.
|
||||
// This becomes a total of 102 bytes:
|
||||
|
||||
const MAGIC_HEADER: [u8; 6] = [0xFF; 6];
|
||||
|
||||
/// Contains raw bytes for magic packet
|
||||
pub struct MagicPacket {
|
||||
pub bytes: [u8; 102],
|
||||
}
|
||||
|
||||
impl MagicPacket {
|
||||
/// Create new MagicPacket from a raw 6-byte MAC address
|
||||
pub fn new(mac_bytes: &[u8; 6]) -> MagicPacket {
|
||||
let mut magic_bytes: [u8; 102];
|
||||
unsafe {
|
||||
magic_bytes = std::mem::MaybeUninit::uninit().assume_init();
|
||||
let mut src: *const u8 = &MAGIC_HEADER[0];
|
||||
let mut dst: *mut u8 = &mut magic_bytes[0];
|
||||
dst.copy_from_nonoverlapping(src, 6);
|
||||
|
||||
src = &mac_bytes[0];
|
||||
for _ in 0..16 {
|
||||
dst = dst.offset(6);
|
||||
dst.copy_from_nonoverlapping(src, 6);
|
||||
}
|
||||
}
|
||||
return MagicPacket { bytes: magic_bytes };
|
||||
}
|
||||
|
||||
// This is horrible code
|
||||
/// Parse a MAC-string into a packet.
|
||||
/// Takes both separated and unseparated
|
||||
pub fn from_str(mac_str: &str) -> Result<MagicPacket, Box<dyn Error>> {
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn from_str2(mac_str: &str) -> Result<MagicPacket, Box<dyn Error>> {
|
||||
return Ok(MagicPacket::new(&MagicPacket::parse(mac_str).unwrap()));
|
||||
}
|
||||
|
||||
// Parses string by position if string is 12+5 characters long (delimited by : for example)
|
||||
pub fn parse<S: AsRef<str>>(mac_str: S) -> Result<Box<[u8; 6]>, Box<dyn Error>> {
|
||||
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<S: Into<String>>(mac_str: S) -> Result<Box<[u8; 6]>, Box<dyn Error>> {
|
||||
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));
|
||||
}
|
||||
|
||||
/// Send packet to/from specific address/interface
|
||||
pub fn send_to<A: ToSocketAddrs>(&self, to_addr: A, from_addr: A) -> std::io::Result<()> {
|
||||
let socket = UdpSocket::bind(from_addr)?;
|
||||
socket.set_broadcast(true)?;
|
||||
socket.send_to(&self.bytes, to_addr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send package from whatever interface the os picks
|
||||
pub fn send(&self) -> std::io::Result<()> {
|
||||
self.send_to(
|
||||
(Ipv4Addr::new(0xFF, 0xFF, 0xFF, 0xFF), 9),
|
||||
(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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn init_packet() {
|
||||
let packet1 = MagicPacket::new(&[0xFF; 6]);
|
||||
let packet2 = MagicPacket::new(&[0xAA; 6]);
|
||||
|
||||
let vec: [u8; 102] = [0xFF; 102];
|
||||
|
||||
assert_eq!(packet1.bytes, vec);
|
||||
assert_ne!(packet2.bytes, vec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn magic_packet_from_new() {
|
||||
let packet3 = MagicPacket::from_str2("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 {
|
||||
println!("{}", a);
|
||||
}
|
||||
|
||||
let bytes: [u8; 6] = [0x10; 6];
|
||||
assert_eq!(slice, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let mp = MagicPacket::parse("ff:ff:ff:ff:ff:ff").unwrap();
|
||||
assert_eq!([0xFF; 6], *mp);
|
||||
let mp2 = MagicPacket::parse("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);
|
||||
}
|
||||
}
|
94
src/random_machine.rs
Normal file
94
src/random_machine.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use eff_wordlist::large::random_word;
|
||||
use crate::host::Host;
|
||||
use rand::thread_rng;
|
||||
use rand::prelude::*;
|
||||
|
||||
// This file exists purely for debugging/testing purposes
|
||||
|
||||
fn random_mac() -> String {
|
||||
let mut rng = thread_rng();
|
||||
let bytes: [u8; 6] = rng.gen(); // rand can handle array initialization
|
||||
let mac_str = format!(
|
||||
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
|
||||
bytes[0],
|
||||
bytes[1],
|
||||
bytes[2],
|
||||
bytes[3],
|
||||
bytes[4],
|
||||
bytes[5],
|
||||
);
|
||||
return mac_str;
|
||||
}
|
||||
|
||||
fn random_ip() -> String {
|
||||
let mut rng = thread_rng();
|
||||
let bytes: [u8; 4] = rng.gen(); // rand can handle array initialization
|
||||
|
||||
let mut ip_str = format!(
|
||||
"{}.{}.{}.{}",
|
||||
bytes[0],
|
||||
bytes[1],
|
||||
bytes[2],
|
||||
bytes[3],
|
||||
);
|
||||
return ip_str;
|
||||
}
|
||||
|
||||
fn random_name() -> String {
|
||||
let mut name = String::new();
|
||||
name.push_str(random_word());
|
||||
name.push_str(random_word());
|
||||
return name;
|
||||
}
|
||||
|
||||
pub fn random_host() -> Host {
|
||||
return Host::new(random_name(), random_mac(), random_ip());
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_random_name() {
|
||||
let mut testvec: Vec<String> = Vec::new();
|
||||
for _ in 0..10 {
|
||||
// println!("{}", random_name());
|
||||
let test_str = random_name();
|
||||
println!("{}", test_str);
|
||||
for other in &testvec {
|
||||
assert!(!other.eq(&test_str)); // Make sure this particular string is not already present in testvec
|
||||
}
|
||||
testvec.push(test_str);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_ip() {
|
||||
let mut testvec: Vec<String> = Vec::new();
|
||||
for _ in 0..10 {
|
||||
// println!("{}", random_name());
|
||||
let test_str = random_ip();
|
||||
println!("{}", test_str);
|
||||
assert!(test_str.len() >= 7 && test_str.len() <= 15);
|
||||
for other in &testvec {
|
||||
assert!(!other.eq(&test_str)); // Make sure this particular string is not already present in testvec
|
||||
}
|
||||
testvec.push(test_str);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_mac() {
|
||||
let mut testvec: Vec<String> = Vec::new();
|
||||
for _ in 0..10 {
|
||||
// println!("{}", random_name());
|
||||
let test_str = random_mac();
|
||||
println!("{}", test_str);
|
||||
// assert!(test_str.len() >= 7 && test_str.len() <= 15);
|
||||
assert!(test_str.len() == 17);
|
||||
for other in &testvec {
|
||||
assert!(!other.eq(&test_str)); // Make sure this particular string is not already present in testvec
|
||||
}
|
||||
testvec.push(test_str);
|
||||
}
|
||||
}
|
||||
}
|
3
waker.json
Normal file
3
waker.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"list": []
|
||||
}
|
Loading…
Reference in a new issue