mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-12 21:34:31 +00:00
address review: bind the control port before writing the token file
This commit is contained in:
1 parent
c59b50da54
commit
d67874e90b
2 files changed
+34
-13
No files matched your search
@@ -117,6 +117,24 @@ fn authenticate(stream: &mut TcpStream, token: &str) -> std::io::Result<()> {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "control token refused"))
|
||||
}
|
||||
|
||||
/// Bind first: a second launch on a taken port must not overwrite the running instance's token file.
|
||||
fn bind_then_write_token(port: u16, path: &Path, token: &str) -> Option<TcpListener> {
|
||||
let listener = match TcpListener::bind(("127.0.0.1", port)) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
log::error!("control server: can't bind 127.0.0.1:{port}: {e} (another app may own it; pick another port)");
|
||||
eprintln!("FigCraft control server failed to bind port {port}: {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if let Err(e) = write_token(path, token) {
|
||||
log::error!("control server disabled: can't write the token to {}: {e}", path.display());
|
||||
eprintln!("FigCraft control server disabled: can't write the token to {}: {e}", path.display());
|
||||
return None;
|
||||
}
|
||||
Some(listener)
|
||||
}
|
||||
|
||||
pub fn start(port: u16, queue: Queue, ctx: CtxSlot) {
|
||||
// Fail closed: without a token file the channel stays off rather than open to any local client.
|
||||
let token = match random_token() {
|
||||
@@ -132,19 +150,7 @@ pub fn start(port: u16, queue: Queue, ctx: CtxSlot) {
|
||||
eprintln!("FigCraft control server disabled: no config folder for the token");
|
||||
return;
|
||||
};
|
||||
if let Err(e) = write_token(&path, &token) {
|
||||
log::error!("control server disabled: can't write the token to {}: {e}", path.display());
|
||||
eprintln!("FigCraft control server disabled: can't write the token to {}: {e}", path.display());
|
||||
return;
|
||||
}
|
||||
let listener = match TcpListener::bind(("127.0.0.1", port)) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
log::error!("control server: can't bind 127.0.0.1:{port}: {e} (another app may own it; pick another port)");
|
||||
eprintln!("FigCraft control server failed to bind port {port}: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let Some(listener) = bind_then_write_token(port, &path, &token) else { return };
|
||||
eprintln!("FigCraft control server listening on 127.0.0.1:{port}; token in {}", path.display());
|
||||
let token: Arc<str> = Arc::from(token);
|
||||
let spawned = std::thread::Builder::new().name("figcraft-control".into()).spawn(move || {
|
||||
|
||||
@@ -381,3 +381,18 @@ fn tokens_are_fresh_hex_and_the_file_is_user_only() {
|
||||
}
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_failed_bind_leaves_the_existing_token_file_alone() {
|
||||
let taken = TcpListener::bind(("127.0.0.1", 0)).unwrap();
|
||||
let port = taken.local_addr().unwrap().port();
|
||||
let path = std::env::temp_dir().join(format!("figcraft-bind-first-{port}.token"));
|
||||
std::fs::write(&path, "old").unwrap();
|
||||
assert!(bind_then_write_token(port, &path, "new").is_none());
|
||||
assert_eq!(std::fs::read_to_string(&path).unwrap(), "old");
|
||||
drop(taken);
|
||||
let listener = bind_then_write_token(port, &path, "new").unwrap();
|
||||
assert_eq!(std::fs::read_to_string(&path).unwrap(), "new");
|
||||
drop(listener);
|
||||
std::fs::remove_file(&path).unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user