free up allocated ports if they fail to authenticate

This commit is contained in:
soruh 2024-05-31 18:27:22 +02:00
parent 0770c00737
commit 19c334a974
4 changed files with 240 additions and 223 deletions

View File

@ -61,6 +61,8 @@ async fn authenticate(
return Ok(AuthResult::OutOfPorts); return Ok(AuthResult::OutOfPorts);
}; };
handler_metadata.port = Some(port);
// make sure the client is authenticated before opening any ports // make sure the client is authenticated before opening any ports
if !authenticated { if !authenticated {
let _ip = dyn_ip_update(&config.dyn_ip_server, number, pin, port).await?; let _ip = dyn_ip_update(&config.dyn_ip_server, number, pin, port).await?;
@ -98,8 +100,6 @@ async fn authenticate(
.or_default() .or_default()
.new_state(PortStatus::Idle); .new_state(PortStatus::Idle);
handler_metadata.port = Some(port);
break Ok(AuthResult::Success { port }); break Ok(AuthResult::Success { port });
} }

View File

@ -284,10 +284,18 @@ async fn connection_handler(
let mut port_handler = port_handler.lock().await; let mut port_handler = port_handler.lock().await;
if let Some(port_state) = port_handler.port_state.get_mut(&port) { if let Some(port_state) = port_handler.port_state.get_mut(&port) {
// the client is known. Mark is as disconnected
port_state.new_state(PortStatus::Disconnected); port_state.new_state(PortStatus::Disconnected);
port_handler.register_update(); } else {
// the client is not known. Free its port for realloction
assert!(
port_handler.free_ports.insert(port),
"tried to free up a port that was not allocted"
);
} }
port_handler.register_update();
if let Some(listener) = handler_metadata.listener.take() { if let Some(listener) = handler_metadata.listener.take() {
port_handler.start_rejector( port_handler.start_rejector(
port, port,

View File

@ -1,6 +1,6 @@
use std::{ use std::{
collections::{BTreeSet, HashMap, HashSet}, collections::{BTreeSet, HashMap, HashSet},
fmt::{Debug, Display}, fmt::Debug,
fs::File, fs::File,
io::{BufReader, BufWriter}, io::{BufReader, BufWriter},
ops::RangeInclusive, ops::RangeInclusive,
@ -41,7 +41,7 @@ pub struct PortHandler {
allowed_ports: AllowedList, allowed_ports: AllowedList,
#[serde(skip)] #[serde(skip)]
free_ports: HashSet<Port>, pub free_ports: HashSet<Port>,
errored_ports: BTreeSet<(UnixTimestamp, Port)>, errored_ports: BTreeSet<(UnixTimestamp, Port)>,
allocated_ports: HashMap<Number, Port>, allocated_ports: HashMap<Number, Port>,
@ -103,14 +103,6 @@ pub async fn cache_daemon(
} }
} }
#[derive(Hash, PartialEq, Eq)]
struct DisplayAsDebug<T: Display>(T);
impl<T: Display> Debug for DisplayAsDebug<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct PortState { pub struct PortState {
#[serde(deserialize_with = "deserialize_last_change")] #[serde(deserialize_with = "deserialize_last_change")]

View File

@ -5,8 +5,8 @@ window.onload = () => {
const connected = document.getElementById("connected"); const connected = document.getElementById("connected");
const free_ports = document.getElementById("free_ports"); const free_ports = document.getElementById("free_ports");
const timeout_duration = 10*1000; const timeout_duration = 10 * 1000;
const retry_timeout = 5*1000; const retry_timeout = 5 * 1000;
let reconnect_timeout; let reconnect_timeout;
let ping_timeout; let ping_timeout;
@ -20,21 +20,19 @@ window.onload = () => {
table_elem.firstChild.firstChild.lastChild.className = "sort sort-down"; table_elem.firstChild.firstChild.lastChild.className = "sort sort-down";
let time_ago = (ms) => {
let time_ago = ms => {
let value = ms / 1000; let value = ms / 1000;
// let prev = 0; // let prev = 0;
let unit = 0; let unit = 0;
let factors = [ let factors = [
[1, 'Sekunde', 'n'], [1, "Sekunde", "n"],
[60, 'Minute', 'n'], [60, "Minute", "n"],
[60, 'Stunde', 'n'], [60, "Stunde", "n"],
[24, 'Tag', 'en'], [24, "Tag", "en"],
[7, 'Woche', 'n'], [7, "Woche", "n"],
[4.348214, 'Monat', 'en'], [4.348214, "Monat", "en"],
[12, 'Jahr', 'en'], [12, "Jahr", "en"],
]; ];
for (let i in factors) { for (let i in factors) {
@ -50,7 +48,7 @@ window.onload = () => {
let factor = factors[unit]; let factor = factors[unit];
return [value, factors[unit][1] + (value == 1 ? "" : factor[2])]; return [value, factors[unit][1] + (value == 1 ? "" : factor[2])];
} };
sort = (element, key) => { sort = (element, key) => {
if (key == oldkey) { if (key == oldkey) {
@ -61,8 +59,8 @@ window.onload = () => {
} }
for (let child of table_elem.firstChild.firstChild.children) { for (let child of table_elem.firstChild.firstChild.children) {
child.className = "" child.className = "";
}; }
element.className = `sort ${direction > 0 ? "sort-up" : "sort-down"}`; element.className = `sort ${direction > 0 ? "sort-up" : "sort-down"}`;
@ -70,24 +68,26 @@ window.onload = () => {
}; };
let do_sort = (key, direction) => { let do_sort = (key, direction) => {
let is_number = !!~(["port", "number", "last_change"].indexOf(key)); let is_number = !!~(["port", "number", "last_change"].indexOf(key));
table = table.sort((a, b) => (direction * ( table = table.sort((a, b) => (direction * (
is_number ? a[key] - b[key] : ('' +a[key]).localeCompare(b[key], "de-DE") is_number ? a[key] - b[key] : ("" + a[key]).localeCompare(b[key], "de-DE")
))); )));
populate_table(); populate_table();
}; };
let fmt = Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'medium' }); let fmt = Intl.DateTimeFormat("de-DE", {
dateStyle: "medium",
timeStyle: "medium",
});
let format_date = date => fmt.format(date).replace(', ', ' '); let format_date = (date) => fmt.format(date).replace(", ", " ");
let format_time = date => fmt.format(date).split(', ', 2)[1]; let format_time = (date) => fmt.format(date).split(", ", 2)[1];
let populate_table = () => { let populate_table = () => {
// clear everything except for the header row // clear everything except for the header row
while(table_elem.rows.length > 1) { while (table_elem.rows.length > 1) {
table_elem.deleteRow(-1); table_elem.deleteRow(-1);
} }
@ -99,7 +99,7 @@ window.onload = () => {
row.number, row.number,
row.port, row.port,
row.status, row.status,
time_ago(last_update - row.last_change) time_ago(last_update - row.last_change),
]; ];
let names = [ let names = [
@ -107,10 +107,10 @@ window.onload = () => {
"number", "number",
"port", "port",
"status", "status",
"last_change" "last_change",
]; ];
for(let i in values) { for (let i in values) {
let value = values[i]; let value = values[i];
let name = names[i]; let name = names[i];
@ -136,30 +136,41 @@ window.onload = () => {
} }
}; };
let update_table = data => { let update_table = (data) => {
const allowed_ports = data.allowed_ports.map(x => x.end - x.start + 1).reduce((a,b) => a + b, 0); const allowed_ports = data.allowed_ports.map((x) => x.end - x.start + 1)
free_ports.innerText = `Freie Ports: ${allowed_ports - Object.keys(data.allocated_ports).length - data.errored_ports.length}`; .reduce((a, b) => a + b, 0);
free_ports.innerText = `Freie Ports: ${
allowed_ports - Object.keys(data.allocated_ports).length -
data.errored_ports.length
}`;
// last_change.innerHTML = `Letzte Änderung: ${format_date(new Date(+data.last_update * 1000))}`; // last_change.innerHTML = `Letzte Änderung: ${format_date(new Date(+data.last_update * 1000))}`;
table = []; table = [];
for(let number in data.allocated_ports) { for (let number in data.allocated_ports) {
let port = data.allocated_ports[number]; let port = data.allocated_ports[number];
number = +number; number = +number;
let {status, last_change} = data.port_state[port];
// allocated port has no state. This means that it is unknown and should not be displayed
if (data.port_state[port] == undefined) {
continue;
}
let { status, last_change } = data.port_state[port];
let rejector = data.rejectors[port] || null; let rejector = data.rejectors[port] || null;
if (rejector && rejector instanceof Array) { if (rejector && rejector instanceof Array) {
rejector = rejector.map(x => "0x"+x.toString(16).padStart(2, 0)).join(" ") rejector = rejector.map((x) => "0x" + x.toString(16).padStart(2, 0))
.join(" ");
} }
last_change = new Date(last_change * 1000); last_change = new Date(last_change * 1000);
let name = data.names[number] || null; let name = data.names[number] || null;
switch(status) { switch (status) {
case "disconnected": case "disconnected":
status = rejector ? `getrennt: ${rejector}` : "getrennt"; status = rejector ? `getrennt: ${rejector}` : "getrennt";
break; break;
@ -171,11 +182,18 @@ window.onload = () => {
break; break;
} }
table.push({port, number, status, last_change, rejector, name}) table.push({ port, number, status, last_change, rejector, name });
} }
for (let [timestamp, port] of data.errored_ports) { for (let [timestamp, port] of data.errored_ports) {
table.push({port, number: null, status: "Fehler", last_change: new Date(timestamp * 1000), rejector: null, name: ""}) table.push({
port,
number: null,
status: "Fehler",
last_change: new Date(timestamp * 1000),
rejector: null,
name: "",
});
} }
do_sort(oldkey, direction); do_sort(oldkey, direction);
@ -183,13 +201,12 @@ window.onload = () => {
populate_table(); populate_table();
}; };
let format_event = (event, method) =>
let format_event = (event, method) => (method || format_date)(new Date(+event.data * 1000)); (method || format_date)(new Date(+event.data * 1000));
let display_disconnected; let display_disconnected;
let connect_event_source = () => { let connect_event_source = () => {
clearTimeout(reconnect_timeout); clearTimeout(reconnect_timeout);
clearTimeout(ping_timeout); clearTimeout(ping_timeout);
ping_timeout = setTimeout(connect_event_source, timeout_duration); ping_timeout = setTimeout(connect_event_source, timeout_duration);
@ -197,17 +214,17 @@ window.onload = () => {
evtSource && evtSource.close && evtSource.close(); evtSource && evtSource.close && evtSource.close();
evtSource = new EventSource("/events"); evtSource = new EventSource("/events");
evtSource.addEventListener("change", event => { evtSource.addEventListener("change", (event) => {
last_ping.innerText = `Stand: ${format_event(event, format_time)}`; last_ping.innerText = `Stand: ${format_event(event, format_time)}`;
last_update = new Date(+event.data * 1000); last_update = new Date(+event.data * 1000);
fetch("/data") fetch("/data")
.then(res => res.json()) .then((res) => res.json())
.then(update_table); .then(update_table);
}); });
evtSource.addEventListener("ping", event => { evtSource.addEventListener("ping", (event) => {
clearTimeout(ping_timeout); clearTimeout(ping_timeout);
ping_timeout = setTimeout(connect_event_source, timeout_duration); ping_timeout = setTimeout(connect_event_source, timeout_duration);
@ -219,17 +236,17 @@ window.onload = () => {
populate_table(); populate_table();
clearTimeout(display_disconnected); clearTimeout(display_disconnected);
display_disconnected = setTimeout(() => connected.className = "hidden", 5000); display_disconnected = setTimeout(
() => connected.className = "hidden",
5000,
);
}); });
evtSource.onerror = () => { evtSource.onerror = () => {
clearTimeout(reconnect_timeout); clearTimeout(reconnect_timeout);
reconnect_timeout = setTimeout(connect_event_source, retry_timeout); reconnect_timeout = setTimeout(connect_event_source, retry_timeout);
}; };
} };
connect_event_source(); connect_event_source();
}; };