aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src-tauri/src/main.rs')
-rw-r--r--src-tauri/src/main.rs358
1 files changed, 356 insertions, 2 deletions
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 77b3a8f..b555123 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1,11 +1,13 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-use std::net::{Ipv4Addr, Ipv6Addr};
+use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
use std::sync::Mutex;
+use std::time::{Duration, SystemTime};
use tauri::State;
+use chrono::{DateTime, Local};
use reqwest::{Client, Response};
use reqwest::{StatusCode, Url};
use serde::{Deserialize, Serialize};
@@ -104,6 +106,170 @@ struct Ipv6Connection {
raddr: Ipv6Addr,
}
+#[derive(Debug, Serialize)]
+struct Dhcpv6Status {
+ timestamp: String,
+ srvaddr: String,
+ srvid: String,
+ t1: String,
+ t2: String,
+ prefix: String,
+ wanaddr: String,
+ preflft: String,
+ validlft: String,
+ dns1: String,
+ dns2: String,
+ aftr: String,
+}
+
+impl Dhcpv6Status {
+ fn no_lease() -> Self {
+ Self::with_all(String::from(
+ "✖ Keine Lease vorhanden (erster Systemstart oder Stromausfall?)",
+ ))
+ }
+
+ fn with_all(message: String) -> Self {
+ Self {
+ timestamp: message.clone(),
+ srvaddr: message.clone(),
+ srvid: message.clone(),
+ t1: message.clone(),
+ t2: message.clone(),
+ prefix: message.clone(),
+ wanaddr: message.clone(),
+ preflft: message.clone(),
+ validlft: message.clone(),
+ dns1: message.clone(),
+ dns2: message.clone(),
+ aftr: message,
+ }
+ }
+}
+
+impl From<Dhcpv6Lease> for Dhcpv6Status {
+ fn from(lease: Dhcpv6Lease) -> Self {
+ let validity = if lease.is_valid() { "✅" } else { "❌" };
+
+ Self {
+ timestamp: format!(
+ "{} {}",
+ validity,
+ DateTime::<Local>::from(lease.timestamp).format("%d.%m.%Y %H:%M:%S UTC%Z")
+ ),
+ srvaddr: if lease.server
+ == SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0))
+ {
+ String::from("ff02::1:2 (Alle DHCPv6-Server, da der Server keine spezifische Adresse angegeben hat)")
+ } else {
+ format!("{}", lease.server)
+ },
+ srvid: hex::encode(lease.server_id),
+ t1: if lease.t1 == 0 {
+ String::from("Sofort")
+ } else if lease.t1 == u32::MAX {
+ String::from("Nie")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.t1.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "Alle {} Sekunden ({} Sekunden verbleibend)",
+ lease.t1, remaining_secs
+ )
+ },
+ t2: if lease.t2 == 0 {
+ String::from("Sofort")
+ } else if lease.t2 == u32::MAX {
+ String::from("Nie")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.t2.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "Alle {} Sekunden ({} Sekunden verbleibend)",
+ lease.t2, remaining_secs
+ )
+ },
+ prefix: format!("{}/{}", lease.prefix, lease.len),
+ wanaddr: format!("{}1/64", lease.prefix),
+ preflft: if lease.preflft == 0 {
+ String::from("⚠ Niemals für neue Verbindungen verwenden")
+ } else if lease.preflft == u32::MAX {
+ String::from("Unendlich")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.preflft.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "{} Sekunden ({} Sekunden verbleibend)",
+ lease.preflft, remaining_secs
+ )
+ },
+ validlft: if lease.validlft == 0 {
+ String::from("⚠ Internetanbieter verlangte manuell sofortigen Verfall")
+ } else if lease.validlft == u32::MAX {
+ String::from("Unendlich")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.validlft.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "{} Sekunden ({} Sekunden verbleibend)",
+ lease.validlft, remaining_secs
+ )
+ },
+ dns1: format!("{}", lease.dns1),
+ dns2: format!("{}", lease.dns2),
+ aftr: match lease.aftr {
+ Some(aftr) => format!("🟢 Aktiviert | Tunnel-Endpunkt (AFTR): {}", aftr),
+ None => String::from("⚪ Deaktiviert"),
+ },
+ }
+ }
+}
+
+#[derive(Debug, Deserialize)]
+struct Dhcpv6Lease {
+ timestamp: std::time::SystemTime,
+ server: SocketAddr,
+ server_id: Vec<u8>,
+ t1: u32,
+ t2: u32,
+ prefix: Ipv6Addr,
+ len: u8,
+ preflft: u32,
+ validlft: u32,
+ dns1: Ipv6Addr,
+ dns2: Ipv6Addr,
+ aftr: Option<String>,
+}
+
+#[derive(Debug, Serialize)]
+struct Duid {
+ duid: String,
+ status_text: String,
+}
+
+impl Dhcpv6Lease {
+ fn is_valid(&self) -> bool {
+ let expiry = self.timestamp + Duration::from_secs(self.validlft.into());
+ SystemTime::now() < expiry
+ }
+}
+
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
async fn connect(
@@ -421,6 +587,191 @@ async fn handle_connection_status_response(response: Response) -> ConnectionStat
}
}
+#[tauri::command]
+async fn dhcpv6_status(state: State<'_, Mutex<Session>>) -> Result<Dhcpv6Status, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(Dhcpv6Status::with_all(String::from(
+ "❗ Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ )))
+ }
+ };
+
+ let response = client
+ .get(instance.url.join("/data/read").unwrap())
+ .query(&[("path", "/data/dhcp6.lease")])
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_dhcpv6_status_response(response).await,
+ Err(e) => Dhcpv6Status::with_all(format!("❗ Abfrage fehlgeschlagen: {}", e)),
+ })
+}
+
+async fn handle_dhcpv6_status_response(response: Response) -> Dhcpv6Status {
+ let status = response.status();
+ if status.is_success() {
+ match response.json::<Dhcpv6Lease>().await {
+ Ok(lease) => Dhcpv6Status::from(lease),
+ Err(e) => Dhcpv6Status::with_all(format!("❗ Fehlerhafte Leasedatei. Fehler: {}", e)),
+ }
+ } else if status == StatusCode::UNAUTHORIZED {
+ Dhcpv6Status::with_all(String::from(
+ "❗ Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!",
+ ))
+ } else if status == StatusCode::NOT_FOUND {
+ Dhcpv6Status::no_lease()
+ } else if status.is_client_error() {
+ Dhcpv6Status::with_all(format!("❗ Clientseitiger Fehler: {}", status))
+ } else if status.is_server_error() {
+ Dhcpv6Status::with_all(format!("❗ Serverseitiger Fehler: {}", status))
+ } else {
+ Dhcpv6Status::with_all(format!("❗ Unerwarteter Statuscode: {}", status))
+ }
+}
+
+#[tauri::command]
+async fn load_duid(state: State<'_, Mutex<Session>>) -> Result<Duid, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(Duid {
+ duid: String::new(),
+ status_text: String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ),
+ })
+ }
+ };
+
+ let response = client
+ .get(instance.url.join("/data/read").unwrap())
+ .query(&[("path", "/data/dhcp6.duid")])
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_load_duid_response(response).await,
+ Err(e) => Duid {
+ duid: String::new(),
+ status_text: format!("Abruf des aktuellen Client-DUID fehlgeschlagen: {}", e),
+ },
+ })
+}
+
+async fn handle_load_duid_response(response: Response) -> Duid {
+ let status = response.status();
+ if status.is_success() {
+ let bytes = match response.bytes().await {
+ Ok(bytes) => bytes,
+ Err(e) => {
+ return Duid {
+ duid: String::new(),
+ status_text: format!(
+ "Keine Rohdaten vom Server erhalten, bitte Neustart durchführen. Fehler: {}",
+ e
+ ),
+ }
+ }
+ };
+
+ Duid {
+ duid: hex::encode(bytes),
+ status_text: String::new(),
+ }
+ } else if status == StatusCode::UNAUTHORIZED {
+ Duid {
+ duid: String::new(),
+ status_text: String::from(
+ "Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!",
+ ),
+ }
+ } else if status == StatusCode::NOT_FOUND {
+ Duid{
+ duid:String::new(),
+ status_text:String::from("Kein Client-DUID gespeichert (erster Systemstart oder Stromausfall?), wird bei Bedarf zufällig generiert und gespeichert"),
+ }
+ } else if status.is_client_error() {
+ Duid {
+ duid: String::new(),
+ status_text: format!("Clientseitiger Fehler: {}", status),
+ }
+ } else if status.is_server_error() {
+ Duid {
+ duid: String::new(),
+ status_text: format!("Serverseitiger Fehler: {}", status),
+ }
+ } else {
+ Duid {
+ duid: String::new(),
+ status_text: format!("Unerwarteter Statuscode: {}", status),
+ }
+ }
+}
+
+#[tauri::command]
+async fn change_duid(duid: String, state: State<'_, Mutex<Session>>) -> Result<String, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ))
+ }
+ };
+
+ let bytes = match hex::decode(&duid) {
+ Ok(bytes) => bytes,
+ Err(e) => {
+ return Ok(format!(
+ "Eingabe ist keine gültige Hexadezimalsequenz: {}",
+ e
+ ))
+ }
+ };
+
+ let response = client
+ .post(instance.url.join("/data/write").unwrap())
+ .query(&[("path", "/data/dhcp6.duid")])
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .body(bytes)
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_change_duid_response(response),
+ Err(e) => format!("Änderung fehlgeschlagen: {}", e),
+ })
+}
+
+fn handle_change_duid_response(response: Response) -> String {
+ let status = response.status();
+ if status.is_success() {
+ String::from("Änderung erfolgreich")
+ } else if status == StatusCode::UNAUTHORIZED {
+ String::from("Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!")
+ } else if status.is_client_error() {
+ format!("Clientseitiger Fehler: {}", status)
+ } else if status.is_server_error() {
+ format!("Serverseitiger Fehler: {}", status)
+ } else {
+ format!("Unerwarteter Statuscode: {}", status)
+ }
+}
+
fn main() {
tauri::Builder::default()
.manage(Mutex::new(Session {
@@ -436,7 +787,10 @@ fn main() {
load_wan_credentials,
change_wan_credentials,
kill,
- connection_status
+ connection_status,
+ dhcpv6_status,
+ load_duid,
+ change_duid
])
.run(tauri::generate_context!())
.expect("error while running tauri application");