get local ip and netmask

This commit is contained in:
Joe Ardent 2025-07-05 10:12:09 -07:00
parent 4047b29a50
commit a9e1256227
7 changed files with 169 additions and 51 deletions

141
Cargo.lock generated
View file

@ -52,7 +52,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -139,7 +139,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -199,6 +199,12 @@ version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.10.1" version = "1.10.1"
@ -344,7 +350,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -355,7 +361,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -409,7 +415,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -927,7 +933,7 @@ dependencies = [
"indoc", "indoc",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -989,15 +995,17 @@ dependencies = [
"chrono", "chrono",
"figment", "figment",
"julid-rs", "julid-rs",
"local-ip-address",
"mime", "mime",
"mime_guess", "mime_guess",
"native-dialog", "native-dialog",
"network-interface",
"ratatui", "ratatui",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"sha256", "sha256",
"thiserror", "thiserror 2.0.12",
"tokio", "tokio",
"tower-http", "tower-http",
] ]
@ -1056,6 +1064,18 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "local-ip-address"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "656b3b27f8893f7bbf9485148ff9a65f019e3f33bd5cdc87c83cab16b3fd9ec8"
dependencies = [
"libc",
"neli",
"thiserror 2.0.12",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.13" version = "0.4.13"
@ -1147,7 +1167,7 @@ dependencies = [
"objc2-core-graphics", "objc2-core-graphics",
"objc2-foundation", "objc2-foundation",
"raw-window-handle", "raw-window-handle",
"thiserror", "thiserror 2.0.12",
"versions", "versions",
"wfd", "wfd",
"which", "which",
@ -1171,6 +1191,44 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "neli"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9"
dependencies = [
"byteorder",
"libc",
"log",
"neli-proc-macros",
]
[[package]]
name = "neli-proc-macros"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe"
dependencies = [
"either",
"proc-macro2",
"quote",
"serde",
"syn 1.0.109",
]
[[package]]
name = "network-interface"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3329f515506e4a2de3aa6e07027a6758e22e0f0e8eaf64fa47261cec2282602"
dependencies = [
"cc",
"libc",
"serde",
"thiserror 1.0.69",
"winapi",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "8.0.0" version = "8.0.0"
@ -1368,7 +1426,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -1444,7 +1502,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -1506,7 +1564,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
"version_check", "version_check",
"yansi", "yansi",
] ]
@ -1600,7 +1658,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [ dependencies = [
"getrandom 0.2.16", "getrandom 0.2.16",
"libredox", "libredox",
"thiserror", "thiserror 2.0.12",
] ]
[[package]] [[package]]
@ -1789,7 +1847,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -1954,7 +2012,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -1963,6 +2021,17 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.104" version = "2.0.104"
@ -1991,7 +2060,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2028,13 +2097,33 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.12" version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 2.0.12",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
] ]
[[package]] [[package]]
@ -2045,7 +2134,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2086,7 +2175,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2383,7 +2472,7 @@ dependencies = [
"log", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2418,7 +2507,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2507,7 +2596,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2518,7 +2607,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2767,7 +2856,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
"synstructure", "synstructure",
] ]
@ -2788,7 +2877,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2808,7 +2897,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
"synstructure", "synstructure",
] ]
@ -2848,5 +2937,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.104",
] ]

View file

@ -8,9 +8,11 @@ axum = { version = "0.8", features = ["macros"] }
chrono = "0.4" chrono = "0.4"
figment = { version = "0.10", features = ["toml", "test", "env"] } figment = { version = "0.10", features = ["toml", "test", "env"] }
julid-rs = { version = "1", default-features = false, features = ["serde"] } julid-rs = { version = "1", default-features = false, features = ["serde"] }
local-ip-address = "0.6"
mime = "0.3" mime = "0.3"
mime_guess = "2" mime_guess = "2"
native-dialog = "0.9" native-dialog = "0.9"
network-interface = { version = "2", features = ["serde"] }
ratatui = "0.29" ratatui = "0.29"
reqwest = { version = "0.12", features = ["json"] } reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }

View file

@ -22,7 +22,7 @@ impl Client {
src.set_port(device.port); // Update the port to the one the device sent src.set_port(device.port); // Update the port to the one the device sent
let mut peers = self.peers.lock().await; let mut peers = self.peers.lock().await;
peers.insert(device.fingerprint.clone(), (src.clone(), device.clone())); peers.insert(device.fingerprint.clone(), (src, device.clone()));
if device.announce != Some(true) { if device.announce != Some(true) {
return; return;
@ -31,13 +31,13 @@ impl Client {
// Announce in return upon receiving a valid device message and it wants // Announce in return upon receiving a valid device message and it wants
// announcements // announcements
if let Err(e) = self.announce_multicast().await { if let Err(e) = self.announce_multicast().await {
eprintln!("Error during multicast announcement: {}", e); eprintln!("Error during multicast announcement: {e}");
} }
if let Err(e) = self.announce_http(Some(src)).await { if let Err(e) = self.announce_http(Some(src)).await {
eprintln!("Error during HTTP announcement: {}", e); eprintln!("Error during HTTP announcement: {e}");
}; };
} else { } else {
eprintln!("Received invalid message: {}", message); eprintln!("Received invalid message: {message}");
} }
} }
} }

View file

@ -38,6 +38,9 @@ pub enum LocalSendError {
#[error("Cancel Failed")] #[error("Cancel Failed")]
CancelFailed, CancelFailed,
#[error("No broadcast IP available")]
NoBroadcastIP,
} }
pub type Result<T> = std::result::Result<T, LocalSendError>; pub type Result<T> = std::result::Result<T, LocalSendError>;

View file

@ -102,7 +102,7 @@ impl Client {
let client = self.clone(); let client = self.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = client.listen_multicast().await { if let Err(e) = client.listen_multicast().await {
eprintln!("UDP listener error: {}", e); eprintln!("UDP listener error: {e}");
} }
}) })
}; };
@ -112,7 +112,7 @@ impl Client {
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
if let Err(e) = client.announce(None).await { if let Err(e) = client.announce(None).await {
eprintln!("Announcement error: {}", e); eprintln!("Announcement error: {e}");
} }
tokio::time::sleep(std::time::Duration::from_secs(5)).await; tokio::time::sleep(std::time::Duration::from_secs(5)).await;
} }

View file

@ -1,8 +1,13 @@
use joecalsend::{Client, models::device::DeviceInfo}; #![feature(ip_as_octets)]
use tokio::io; #![feature(slice_as_array)]
use std::net::Ipv4Addr;
use joecalsend::{Client, error, models::device::DeviceInfo};
use local_ip_address::local_ip;
use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig, V4IfAddr};
#[tokio::main] #[tokio::main]
async fn main() -> io::Result<()> { async fn main() -> error::Result<()> {
let device = DeviceInfo::default(); let device = DeviceInfo::default();
dbg!(&device); dbg!(&device);
@ -10,6 +15,27 @@ async fn main() -> io::Result<()> {
.await .await
.unwrap(); .unwrap();
let (h1, h2, h3) = client.start().await.unwrap(); let (h1, h2, h3) = client.start().await.unwrap();
let ip = local_ip().unwrap();
if !ip.is_ipv4() {
return Err(error::LocalSendError::NoBroadcastIP);
}
let local_device_ip = Ipv4Addr::from_bits(u32::from_be_bytes(
ip.as_octets().as_array().unwrap().to_owned(),
));
let nifs = NetworkInterface::show().unwrap();
let mut mask = Ipv4Addr::from_bits(u32::MAX);
'outer: for nif in nifs {
for addr in nif.addr {
if let Addr::V4(V4IfAddr { ip, netmask, .. }) = addr
&& ip == local_device_ip
{
mask = netmask.unwrap_or(mask);
break 'outer;
}
}
}
dbg!(mask);
let _ = tokio::join!(h1, h2, h3); let _ = tokio::join!(h1, h2, h3);
Ok(()) Ok(())

View file

@ -60,7 +60,7 @@ impl Client {
.send() .send()
.await?; .await?;
println!("Response: {:?}", response); println!("Response: {response:?}");
let response: PrepareUploadResponse = response.json().await?; let response: PrepareUploadResponse = response.json().await?;
@ -102,18 +102,16 @@ impl Client {
let request = self let request = self
.http_client .http_client
.post(&format!( .post(format!(
"{}://{}/api/localsend/v2/upload?sessionId={}&fileId={}&token={}", "{}://{}/api/localsend/v2/upload?sessionId={session_id}&fileId={file_id}&token={token}",
session.receiver.protocol, session.addr, session_id, file_id, token session.receiver.protocol, session.addr))
))
//.post(&format!("https://webhook.site/2f23a529-b687-4375-ad5f-54906ab26ac7?session_id={}&file_id={}&token={}", session_id, file_id, token))
.body(body); .body(body);
println!("Uploading file: {:?}", request); println!("Uploading file: {request:?}");
let response = request.send().await?; let response = request.send().await?;
if response.status() != 200 { if response.status() != 200 {
println!("Upload failed: {:?}", response); println!("Upload failed: {response:?}");
return Err(LocalSendError::UploadFailed); return Err(LocalSendError::UploadFailed);
} }
@ -159,7 +157,7 @@ impl Client {
let request = self let request = self
.http_client .http_client
.post(&format!( .post(format!(
"{}://{}/api/localsend/v2/cancel?sessionId={}", "{}://{}/api/localsend/v2/cancel?sessionId={}",
session.receiver.protocol, session.addr, session_id session.receiver.protocol, session.addr, session_id
)) ))
@ -193,8 +191,8 @@ pub async fn register_prepare_upload(
let file_tokens: HashMap<String, String> = req let file_tokens: HashMap<String, String> = req
.files .files
.iter() .keys()
.map(|(id, _)| (id.clone(), Julid::new().to_string())) // Replace with actual token logic .map(|id| (id.clone(), Julid::new().to_string())) // Replace with actual token logic
.collect(); .collect();
let session = Session { let session = Session {
@ -213,16 +211,16 @@ pub async fn register_prepare_upload(
.await .await
.insert(session_id.clone(), session); .insert(session_id.clone(), session);
return ( (
StatusCode::OK, StatusCode::OK,
Json(PrepareUploadResponse { Json(PrepareUploadResponse {
session_id, session_id,
files: file_tokens, files: file_tokens,
}), }),
) )
.into_response(); .into_response()
} else { } else {
return StatusCode::FORBIDDEN.into_response(); StatusCode::FORBIDDEN.into_response()
} }
} }
@ -269,7 +267,7 @@ pub async fn register_upload(
if let Err(e) = tokio::fs::create_dir_all(&*download_dir).await { if let Err(e) = tokio::fs::create_dir_all(&*download_dir).await {
return ( return (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to create directory: {}", e), format!("Failed to create directory: {e}"),
) )
.into_response(); .into_response();
} }
@ -281,7 +279,7 @@ pub async fn register_upload(
if let Err(e) = tokio::fs::write(&file_path, body).await { if let Err(e) = tokio::fs::write(&file_path, body).await {
return ( return (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to write file: {}", e), format!("Failed to write file: {e}"),
) )
.into_response(); .into_response();
} }