Compare commits

...

22 Commits
v0.1.5 ... main

Author SHA1 Message Date
_ 2cc1ed4a92 ⬆️ use directories v5.0.0 instead of vendoring it 2023-03-31 05:45:40 +00:00
Reactor Scram bd73d832fa
Update rust.yml
Finally read the part that says "this is deprecated use the thing GitHub has built in"

frik
2021-12-16 16:14:07 -06:00
Reactor Scram fd43fbbb1e
Update rust.yml 2021-12-16 16:12:46 -06:00
Reactor Scram 97d7d000dc
Update rust.yml
oof
2021-12-16 16:11:11 -06:00
Reactor Scram bda991bc67
Update rust.yml
you are getting to watch a perma-noob learn about CI in real time, this is special
2021-12-16 15:56:34 -06:00
Reactor Scram ed816a0d6f
Update rust.yml 2021-12-16 15:50:45 -06:00
_ b05a87cc9c use `config_local_dir` 2021-12-16 21:47:12 +00:00
_ 354a74aeaa vendor my fork of `directories`.
I'll swap back to the crates.io version if `config_local_dir` is merged
upstream, or if it times out I'll fork it.
2021-12-16 21:46:25 +00:00
_ d9fd0fd29d 🚧 wip: working on avalanche idea to make MACs easier for humans to read 2021-12-16 20:51:44 +00:00
_ f121e3fd55 📝 add idea 2021-12-13 00:25:15 +00:00
_ ab42de5823 🚨 clippy pass 2021-12-09 18:22:23 +00:00
_ ed58df2e6b add ini files for both client and server
Long-lived servers can have their nickname configured in `server.ini`.
Clients can have a hosts-file-like nickname lookup in `client.ini`.
2021-12-09 18:15:03 +00:00
_ 73434756b6 this will eventually go into 0.1.6 2021-12-09 17:02:04 +00:00
_ 8aae200ebf add `directories` dep and `--version` subcommand 2021-12-09 17:01:29 +00:00
_ a0a64cd79c 📝 one more pass on README 2021-12-09 16:53:32 +00:00
_ 0815691af2 🐛 bug: fix readme 2021-12-09 16:50:54 +00:00
_ 4f66c0495e add `find-nick` command 2021-12-09 16:46:55 +00:00
_ b261d7ba4a ♻️ refactor: refactor the client a lot so I can reuse its code for new subcommands 2021-12-09 16:21:14 +00:00
_ 5665f484a2 🔇 remove debugging println 2021-12-09 15:49:23 +00:00
_ 814fee2bd5 🚧 2021-12-09 15:46:00 +00:00
_ e7496a7c0e 🚧 wip: add CLI docs 2021-12-09 15:45:22 +00:00
_ 18e38f0611 add `--timeout-ms` for client
Empirical testing shows that 200 ms is probably enough on my LAN, so
I set the default to 500 ms.
2021-12-09 15:34:42 +00:00
13 changed files with 567 additions and 91 deletions

View File

@ -15,7 +15,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
- name: Build
run: cargo build --verbose
- name: Run tests

0
.gitmodules vendored Normal file
View File

146
Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
@ -26,6 +26,32 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "configparser"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06821ea598337a8412cf47c5b71c3bc694a7f0aed188ac28b836fab164a2c202"
[[package]]
name = "directories"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74be3be809c18e089de43bdc504652bb2bc473fca8756131f8689db8cf079ba9"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b"
dependencies = [
"libc",
"redox_users",
"windows-sys",
]
[[package]]
name = "getrandom"
version = "0.2.3"
@ -39,9 +65,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.109"
version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]]
name = "log"
@ -54,9 +80,12 @@ dependencies = [
[[package]]
name = "lookaround"
version = "0.1.5"
version = "0.1.6"
dependencies = [
"configparser",
"directories",
"mac_address",
"nix 0.25.0",
"rand",
"thiserror",
"tokio",
@ -68,7 +97,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89544d9544366f6cda81244514a80809b137b5a179947b73bfa9f2797480de69"
dependencies = [
"nix",
"nix 0.22.2",
"winapi",
]
@ -116,6 +145,20 @@ dependencies = [
"memoffset",
]
[[package]]
name = "nix"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
dependencies = [
"autocfg",
"bitflags",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
]
[[package]]
name = "ntapi"
version = "0.3.6"
@ -131,6 +174,12 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.15"
@ -195,6 +244,25 @@ dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
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 = "syn"
version = "1.0.82"
@ -272,3 +340,69 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

View File

@ -9,10 +9,13 @@ license = "AGPL-3.0"
name = "lookaround"
readme = "README.md"
repository = "https://six-five-six-four.com/git/reactor/lookaround"
version = "0.1.5"
version = "0.1.6"
[dependencies]
configparser = "3.0.0"
directories = "5.0.0"
mac_address = "1.1.2"
nix = "0.25.0"
rand = "0.8.4"
thiserror = "1.0.30"
tokio = { version = "1.14.0", features = ["fs", "net", "rt", "time"] }

View File

@ -4,6 +4,10 @@
_Has this ever happened to you?_
```text
$ ping $(lookaround find-nick laptop)
PING 192.168.1.101 (192.168.1.101) 56(84) bytes of data.
...
$ lookaround client
Found 3 peers:
@ -15,29 +19,48 @@ Found 3 peers:
LookAround is a Rust program for looking up your computers' MAC and IP addresses
within a LAN. There's no central server, so it's not a look-up, it's a look-around.
The client uses IP multicast to find servers within the
same multicast domain, similar to Avahi and Bonjour.
## Installing
Systems self-identify by MAC address and nicknames. Public keys with
TOFU semantics are intended before v1.0.0.
## Installation
Use the Cargo package manager from [Rust](https://rustup.rs/) to install LookAround.
Make sure Cargo is installed from [RustUp.](https://rustup.rs/)
```bash
# Install LookAround with Cargo
cargo install lookaround
# Find your config directory
# Prints something like `Using config dir "/home/user/.config/lookaround"`
lookaround config
```
To run the server as a normal user all the time,
put this systemd unit in `~/.config/systemd/user/lookaround.service`:
Create the files `client.ini` and/or `server.ini` in that directory
(e.g. /home/user/.config/lookaround/server.ini)
```ini
# Clients can store MAC-nickname pairs in client.ini, like a hosts file.
# This is useful if your servers are short-lived and you want the clients
# to be the source of truth for nicknames.
[nicknames]
11-11-11-11-11-11 = laptop
22-22-22-22-22-22 = desktop
```
```ini
# Long-lived servers can have their nickname configured in server.ini
[server]
nickname = my-computer
```
## Auto-Start (Linux)
Put this systemd unit in `~/.config/systemd/user/lookaround.service`:
```ini
[Unit]
Description=LookAround
[Service]
ExecStart=/home/user/.cargo/bin/lookaround server --nickname my-desktop
ExecStart=/home/user/.cargo/bin/lookaround server
Restart=always
[Install]
WantedBy=default.target
@ -52,23 +75,43 @@ systemctl --user status lookaround
systemctl --user enable lookaround
```
## Auto-Start (Windows)
(untested)
- Create a shortcut to the LookAround exe
- Change the shortcut's target to end in `lookaround.exe server` so it will run the server
- Cut-paste the shortcut into the Startup folder in `C:\ProgramData\somewhere`
## Usage
Run the server manually: (If you haven't installed it with systemd yet)
Run the server manually: (To test before installing)
```bash
lookaround server --nickname my-desktop
lookaround server --nickname my-computer
```
Run a client to ping all servers in the same multi-cast domain:
On a client computer:
```bash
# Use the `find-nick` subcommnad to find an IP...
lookaround find-nick laptop
# Prints `192.168.1.101`
# Or ping it...
ping $(lookaround find-nick laptop)
# Or SSH to it...
ssh user@$(lookaround find-nick laptop)
# Or pull a file from it
# (after starting `nc -l -p 9000 < some-file` on the laptop)
nc $(lookaround find-nick laptop) 9000
# Use the `client` subcommand to find all servers in the same multicast domain
lookaround client
```
Check which IP addresses LookAround will auto-detect:
```bash
lookaround my-ips
# Use a longer timeout if servers need more than 500 ms to respond
lookaround client --timeout-ms 1000
```
## Contributing

View File

@ -1,6 +1,6 @@
Cool ideas that can be done but probably won't be.
- Command for shell substituting IPs into commands
- Arbitrary TCP forwarding of (stdin? stdout? TCP?)
- Advertise TCP services in server response
- Arbitrary TCP forwarding of (stdin? stdout? TCP?) with interface cutover
- Netcat replacement "Just send a file" _including filename_
- Public-key crypto for trusting peers on first use (Hard cause it requires mutable disk state)

View File

@ -1,11 +1,19 @@
use crate::prelude::*;
pub const LOOKAROUND_VERSION: &str = env! ("CARGO_PKG_VERSION");
pub fn find_project_dirs () -> Option <ProjectDirs> {
ProjectDirs::from ("", "ReactorScram", "LookAround")
}
#[derive (Debug, thiserror::Error)]
pub enum AppError {
#[error (transparent)]
AddrParse (#[from] std::net::AddrParseError),
#[error (transparent)]
CliArgs (#[from] CliArgError),
#[error ("Operation timed out")]
Elapsed (#[from] tokio::time::error::Elapsed),
#[error (transparent)]
Io (#[from] std::io::Error),
#[error (transparent)]
@ -17,6 +25,8 @@ pub enum AppError {
#[error (transparent)]
Message (#[from] crate::message::MessageError),
#[error (transparent)]
ParseInt (#[from] std::num::ParseIntError),
#[error (transparent)]
Tlv (#[from] crate::tlv::TlvError),
}
@ -24,6 +34,8 @@ pub enum AppError {
pub enum CliArgError {
#[error ("Missing value for argument `{0}`")]
MissingArgumentValue (String),
#[error ("Missing required argument <{0}>")]
MissingRequiredArg (String),
#[error ("First argument should be a subcommand")]
MissingSubcommand,
#[error ("Unknown subcommand `{0}`")]

40
src/avalanche.rs Normal file
View File

@ -0,0 +1,40 @@
type Mac = [u8; 6];
pub fn debug () {
for input in [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 1],
] {
assert_eq! (unmix (mix (input)), input);
}
println! ("Passed");
}
// NOT intended for any cryptography or security. This is TRIVIALLY reversible.
// It's just to make it easier for humans to tell apart MACs where only a couple
// numbers differ.
fn mix (i: Mac) -> Mac {
[
i [0] ^ i [5],
i [1] ^ i [4],
i [2] ^ i [3],
i [3],
i [4],
i [5],
]
}
fn unmix (i: Mac) -> Mac {
[
i [0] ^ i [5],
i [1] ^ i [4],
i [2] ^ i [3],
i [3],
i [4],
i [5],
]
}

View File

@ -5,52 +5,34 @@ struct ServerResponse {
nickname: Option <String>,
}
pub async fn client <I : Iterator <Item=String>> (mut args: I) -> Result <(), AppError> {
use rand::RngCore;
let common_params = app_common::Params::default ();
let mut bind_addrs = vec! [];
while let Some (arg) = args.next () {
match arg.as_str () {
"--bind-addr" => {
bind_addrs.push (match args.next () {
None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => Ipv4Addr::from_str (&x)?
});
},
_ => return Err (CliArgError::UnrecognizedArgument (arg).into ()),
struct ConfigFile {
nicknames: HashMap <String, String>,
}
struct ClientParams {
common: app_common::Params,
bind_addrs: Vec <Ipv4Addr>,
nicknames: HashMap <String, String>,
timeout_ms: u64,
}
pub async fn client <I: Iterator <Item=String>> (args: I) -> Result <(), AppError> {
match get_mac_address() {
Ok(Some(ma)) => {
println!("Our MAC addr = {}", ma);
}
Ok(None) => println!("No MAC address found."),
Err(e) => println!("{:?}", e),
}
if bind_addrs.is_empty () {
bind_addrs = get_ips ()?;
}
let socket = UdpSocket::bind (SocketAddrV4::new (Ipv4Addr::UNSPECIFIED, 0)).await?;
for bind_addr in bind_addrs {
if let Err (e) = socket.join_multicast_v4 (common_params.multicast_addr, bind_addr) {
println! ("Error joining multicast group with iface {}: {:?}", bind_addr, e);
}
}
let mut idem_id = [0u8; 8];
rand::thread_rng ().fill_bytes (&mut idem_id);
let msg = Message::Request1 {
idem_id,
mac: None,
}.to_vec ()?;
for _ in 0..10 {
socket.send_to (&msg, (common_params.multicast_addr, common_params.server_port)).await?;
sleep (Duration::from_millis (100)).await;
}
let params = configure_client (args)?;
let socket = make_socket (&params.common, params.bind_addrs).await?;
let msg = Message::new_request1 ().to_vec ()?;
tokio::spawn (send_requests (Arc::clone (&socket), params.common, msg));
let mut peers = HashMap::with_capacity (10);
timeout (Duration::from_secs (2), listen_for_responses (&socket, &mut peers)).await.ok ();
timeout (Duration::from_millis (params.timeout_ms), listen_for_responses (&*socket, params.nicknames, &mut peers)).await.ok ();
let mut peers: Vec <_> = peers.into_iter ().collect ();
peers.sort_by_key (|(_, v)| v.mac);
@ -79,8 +61,162 @@ pub async fn client <I : Iterator <Item=String>> (mut args: I) -> Result <(), Ap
Ok (())
}
pub async fn find_nick <I: Iterator <Item=String>> (mut args: I) -> Result <(), AppError>
{
let mut nick = None;
let mut timeout_ms = 500;
let ConfigFile {
nicknames,
} = load_config_file ();
while let Some (arg) = args.next () {
match arg.as_str () {
"--timeout-ms" => {
timeout_ms = match args.next () {
None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => u64::from_str (&x)?,
};
},
_ => nick = Some (arg),
}
}
let needle_nick = nick.ok_or_else (|| CliArgError::MissingRequiredArg ("nickname".to_string ()))?;
let needle_nick = Some (needle_nick);
let common_params = Default::default ();
let socket = make_socket (&common_params, get_ips ()?).await?;
let msg = Message::new_request1 ().to_vec ()?;
tokio::spawn (send_requests (Arc::clone (&socket), common_params, msg));
timeout (Duration::from_millis (timeout_ms), async move { loop {
let (msgs, remote_addr) = match recv_msg_from (&socket).await {
Err (_) => continue,
Ok (x) => x,
};
let mut resp = ServerResponse {
mac: None,
nickname: None,
};
for msg in msgs.into_iter () {
match msg {
Message::Response1 (x) => resp.mac = x,
Message::Response2 (x) => resp.nickname = Some (x.nickname),
_ => (),
}
}
resp.nickname = get_peer_nickname (&nicknames, resp.mac, resp.nickname);
if resp.nickname == needle_nick {
println! ("{}", remote_addr.ip ());
return;
}
}}).await?;
Ok (())
}
fn configure_client <I: Iterator <Item=String>> (mut args: I)
-> Result <ClientParams, AppError>
{
let mut bind_addrs = vec! [];
let mut timeout_ms = 500;
let ConfigFile {
nicknames,
} = load_config_file ();
while let Some (arg) = args.next () {
match arg.as_str () {
"--bind-addr" => {
bind_addrs.push (match args.next () {
None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => Ipv4Addr::from_str (&x)?,
});
},
"--timeout-ms" => {
timeout_ms = match args.next () {
None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => u64::from_str (&x)?,
};
},
_ => return Err (CliArgError::UnrecognizedArgument (arg).into ()),
}
}
if bind_addrs.is_empty () {
bind_addrs = get_ips ()?;
}
Ok (ClientParams {
common: Default::default (),
bind_addrs,
nicknames,
timeout_ms,
})
}
fn load_config_file () -> ConfigFile {
let mut nicknames: HashMap <String, String> = Default::default ();
if let Some (proj_dirs) = find_project_dirs () {
let mut ini = Ini::new_cs ();
let path = proj_dirs.config_local_dir ().join ("client.ini");
if ini.load (&path).is_ok () {
let map_ref = ini.get_map_ref ();
if let Some (x) = map_ref.get ("nicknames") {
for (k, v) in x {
if let Some (v) = v {
let k = k.replace ('-', ":");
nicknames.insert (k, v.to_string ());
}
}
}
}
}
ConfigFile {
nicknames,
}
}
async fn make_socket (
common_params: &app_common::Params,
bind_addrs: Vec <Ipv4Addr>,
) -> Result <Arc <UdpSocket>, AppError> {
let socket = UdpSocket::bind (SocketAddrV4::new (Ipv4Addr::UNSPECIFIED, 0)).await?;
for bind_addr in &bind_addrs {
if let Err (e) = socket.join_multicast_v4 (common_params.multicast_addr, *bind_addr) {
println! ("Error joining multicast group with iface {}: {:?}", bind_addr, e);
}
}
Ok (Arc::new (socket))
}
async fn send_requests (
socket: Arc <UdpSocket>,
params: app_common::Params,
msg: Vec <u8>,
)
-> Result <(), AppError>
{
for _ in 0..10 {
socket.send_to (&msg, (params.multicast_addr, params.server_port)).await?;
sleep (Duration::from_millis (100)).await;
}
Ok::<_, AppError> (())
}
async fn listen_for_responses (
socket: &UdpSocket,
socket: &UdpSocket,
nicknames: HashMap <String, String>,
peers: &mut HashMap <SocketAddr, ServerResponse>
) {
loop {
@ -102,6 +238,63 @@ async fn listen_for_responses (
}
}
resp.nickname = get_peer_nickname (&nicknames, resp.mac, resp.nickname);
peers.insert (remote_addr, resp);
}
}
fn get_peer_nickname (
nicknames: &HashMap <String, String>,
mac: Option <[u8; 6]>,
peer_nickname: Option <String>
) -> Option <String>
{
match peer_nickname.as_deref () {
None => (),
Some ("") => (),
_ => return peer_nickname,
}
if let Some (mac) = &mac {
return nicknames.get (&format! ("{}", MacAddress::new (*mac))).cloned ()
}
None
}
#[cfg (test)]
mod test {
use super::*;
#[test]
fn test_nicknames () {
let mut nicks = HashMap::new ();
for (k, v) in [
("01:01:01:01:01:01", "phoenix")
] {
nicks.insert (k.to_string (), v.to_string ());
}
for (num, (mac, peer_nickname), expected) in [
// Somehow the server returns no MAC nor nick. In this case we are helpless
( 1, (None, None), None),
// If the server tells us its MAC, we can look up our nickname for it
( 2, (Some ([1, 1, 1, 1, 1, 1]), None), Some ("phoenix")),
// Unless it's not in our nick list.
( 3, (Some ([1, 1, 1, 1, 1, 2]), None), None),
// If the server tells us its nickname, that always takes priority
( 4, (None, Some ("snowflake")), Some ("snowflake")),
( 5, (Some ([1, 1, 1, 1, 1, 1]), Some ("snowflake")), Some ("snowflake")),
( 6, (Some ([1, 1, 1, 1, 1, 2]), Some ("snowflake")), Some ("snowflake")),
// But blank nicknames are treated like None
( 7, (None, Some ("")), None),
( 8, (Some ([1, 1, 1, 1, 1, 1]), Some ("")), Some ("phoenix")),
( 9, (Some ([1, 1, 1, 1, 1, 2]), Some ("")), None),
] {
let actual = get_peer_nickname (&nicks, mac, peer_nickname.map (str::to_string));
assert_eq! (actual.as_ref ().map (String::as_str), expected, "{}", num);
}
}
}

View File

@ -1,12 +1,13 @@
use prelude::*;
pub mod app_common;
mod avalanche;
mod client;
mod ip;
pub mod message;
mod prelude;
mod server;
mod tlv;
pub mod tlv;
fn main () -> Result <(), AppError> {
let rt = tokio::runtime::Builder::new_current_thread ()
@ -24,19 +25,15 @@ async fn async_main () -> Result <(), AppError> {
let _exe_name = args.next ();
match get_mac_address() {
Ok(Some(ma)) => {
println!("Our MAC addr = {}", ma);
}
Ok(None) => println!("No MAC address found."),
Err(e) => println!("{:?}", e),
}
let subcommand: Option <String> = args.next ();
match subcommand.as_ref ().map (|x| &x[..]) {
None => return Err (CliArgError::MissingSubcommand.into ()),
Some ("--version") => println! ("lookaround v{}", LOOKAROUND_VERSION),
Some ("client") => client::client (args).await?,
Some ("config") => config (),
Some ("debug-avalanche") => avalanche::debug (),
Some ("find-nick") => client::find_nick (args).await?,
Some ("my-ips") => my_ips ()?,
Some ("server") => server::server (args).await?,
Some (x) => return Err (CliArgError::UnknownSubcommand (x.to_string ()).into ()),
@ -45,6 +42,15 @@ async fn async_main () -> Result <(), AppError> {
Ok (())
}
fn config () {
if let Some (proj_dirs) = ProjectDirs::from ("", "ReactorScram", "LookAround") {
println! ("Using config dir {:?}", proj_dirs.config_local_dir ());
}
else {
println! ("Can't detect config dir.");
}
}
fn my_ips () -> Result <(), AppError> {
for addr in ip::get_ips ()?
{

View File

@ -1,13 +1,4 @@
use std::{
io::{
Cursor,
Write,
},
};
use crate::tlv;
use thiserror::Error;
use crate::prelude::*;
const MAGIC_NUMBER: [u8; 4] = [0x9a, 0x4a, 0x43, 0x81];
pub const PACKET_SIZE: usize = 1024;
@ -27,13 +18,25 @@ pub enum Message {
Response2 (Response2),
}
impl Message {
pub fn new_request1 () -> Message {
let mut idem_id = [0u8; 8];
rand::thread_rng ().fill_bytes (&mut idem_id);
Message::Request1 {
idem_id,
mac: None,
}
}
}
#[derive (Debug, PartialEq)]
pub struct Response2 {
pub idem_id: [u8; 8],
pub nickname: String,
}
#[derive (Debug, Error)]
#[derive (Debug, thiserror::Error)]
pub enum MessageError {
#[error (transparent)]
Io (#[from] std::io::Error),

View File

@ -1,19 +1,30 @@
pub use std::{
collections::HashMap,
env,
io::{
Cursor,
Write,
},
net::{
Ipv4Addr,
SocketAddr,
SocketAddrV4,
},
str::FromStr,
time::Duration,
sync::Arc,
time::{
Duration,
Instant,
},
};
pub use configparser::ini::Ini;
pub use directories::ProjectDirs;
pub use mac_address::{
MacAddress,
get_mac_address,
};
pub use rand::RngCore;
pub use tokio::{
net::UdpSocket,
time::{
@ -25,8 +36,10 @@ pub use tokio::{
pub use crate::{
app_common::{
self,
LOOKAROUND_VERSION,
AppError,
CliArgError,
find_project_dirs,
recv_msg_from,
},
ip::get_ips,
@ -35,4 +48,5 @@ pub use crate::{
PACKET_SIZE,
Message,
},
tlv,
};

View File

@ -10,6 +10,14 @@ struct Params {
pub async fn server <I: Iterator <Item=String>> (args: I) -> Result <(), AppError>
{
match get_mac_address() {
Ok(Some(ma)) => {
println!("Our MAC addr = {}", ma);
}
Ok(None) => println!("No MAC address found."),
Err(e) => println!("{:?}", e),
}
let params = configure (args)?;
let socket = UdpSocket::bind (SocketAddrV4::new (Ipv4Addr::UNSPECIFIED, params.common.server_port)).await?;
@ -31,6 +39,23 @@ fn configure <I: Iterator <Item=String>> (mut args: I) -> Result <Params, AppErr
let mut bind_addrs = vec![];
let mut nickname = String::new ();
if let Some (proj_dirs) = find_project_dirs () {
let mut ini = Ini::new_cs ();
let path = proj_dirs.config_local_dir ().join ("server.ini");
if ini.load (&path).is_ok () {
if let Some (x) = ini.get ("server", "nickname") {
nickname = x;
eprintln! ("Loaded nickname {:?}", nickname);
}
}
else {
eprintln! ("Can't load ini from {:?}, didn't load default configs", path);
}
}
else {
eprintln! ("Can't find config dir, didn't load default configs");
}
while let Some (arg) = args.next () {
match arg.as_str () {
"--bind-addr" => {