diff --git a/src/bin/dosh-client.rs b/src/bin/dosh-client.rs index 4f2469b..6f70560 100644 --- a/src/bin/dosh-client.rs +++ b/src/bin/dosh-client.rs @@ -9,8 +9,8 @@ use dosh::auth::{ use dosh::config::{expand_tilde, load_client_config, load_server_config}; use dosh::crypto; use dosh::protocol::{ - self, AttachOk, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input, PacketKind, Resize, - ResumeRequest, SERVER_TO_CLIENT, TicketAttachBody, TicketAttachEnvelope, + self, AttachOk, AttachReject, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input, + PacketKind, Resize, ResumeRequest, SERVER_TO_CLIENT, TicketAttachBody, TicketAttachEnvelope, TicketAttachOkEnvelope, }; use serde::{Deserialize, Serialize}; @@ -633,6 +633,10 @@ async fn run_terminal( } } PacketKind::Pong => {} + PacketKind::AttachReject => { + let reject: AttachReject = protocol::from_body(&packet.body)?; + return Err(anyhow!("server rejected session: {}", reject.reason)); + } _ => {} } } @@ -773,7 +777,7 @@ impl RawMode { fn enter() -> Result { enable_raw_mode()?; let mut stdout = std::io::stdout(); - stdout.write_all(TERMINAL_CLEANUP)?; + stdout.write_all(TERMINAL_OPEN)?; stdout.flush()?; Ok(Self) } @@ -788,6 +792,23 @@ impl Drop for RawMode { } } +const TERMINAL_OPEN: &[u8] = concat!( + "\x1b[?1l", + "\x1b[0m", + "\x1b[?25h", + "\x1b[?1003l", + "\x1b[?1002l", + "\x1b[?1001l", + "\x1b[?1000l", + "\x1b[?1015l", + "\x1b[?1006l", + "\x1b[?1005l", + "\x1b[?2004l", + "\x1b[?1049l", + "\x1b[?1h" +) +.as_bytes(); + const TERMINAL_CLEANUP: &[u8] = concat!( "\x1b[?1l", "\x1b[0m", diff --git a/src/bin/dosh-server.rs b/src/bin/dosh-server.rs index 9ecf651..9ba365f 100644 --- a/src/bin/dosh-server.rs +++ b/src/bin/dosh-server.rs @@ -226,6 +226,11 @@ async fn handle_packet( handle_ticket_attach(state, socket, peer, packet.body).await } PacketKind::ResumeRequest => handle_resume(state, socket, peer, &packet).await, + PacketKind::Input | PacketKind::Resize | PacketKind::Ping | PacketKind::Ack + if find_client_key(state, &packet.header.conn_id).is_err() => + { + send_reject_to_client(socket, peer, packet.header.conn_id, "unknown client").await + } PacketKind::Input => handle_input(state, peer, &packet).await, PacketKind::Resize => handle_resize(state, peer, &packet).await, PacketKind::Ping => handle_ping(state, socket, peer, &packet).await, @@ -425,10 +430,19 @@ async fn handle_ticket_attach( } async fn send_reject(socket: &UdpSocket, peer: SocketAddr, reason: &str) -> Result<()> { + send_reject_to_client(socket, peer, [0u8; 16], reason).await +} + +async fn send_reject_to_client( + socket: &UdpSocket, + peer: SocketAddr, + client_id: [u8; 16], + reason: &str, +) -> Result<()> { let body = protocol::to_body(&AttachReject { reason: reason.to_string(), })?; - let out = protocol::encode_plain(PacketKind::AttachReject, [0u8; 16], 0, 0, &body)?; + let out = protocol::encode_plain(PacketKind::AttachReject, client_id, 0, 0, &body)?; socket.send_to(&out, peer).await?; Ok(()) } diff --git a/tests/integration_smoke.rs b/tests/integration_smoke.rs index 250cca3..3f1ada6 100644 --- a/tests/integration_smoke.rs +++ b/tests/integration_smoke.rs @@ -8,8 +8,8 @@ use dosh::auth::{build_bootstrap, load_or_create_server_secret}; use dosh::config::load_server_config; use dosh::crypto; use dosh::protocol::{ - self, AttachOk, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input, PacketKind, Resize, - ResumeRequest, SERVER_TO_CLIENT, + self, AttachOk, AttachReject, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input, + PacketKind, Resize, ResumeRequest, SERVER_TO_CLIENT, }; fn free_udp_port() -> u16 { @@ -176,6 +176,45 @@ fn collect_frame_text(socket: &UdpSocket, key: &[u8; 32], millis: u64) -> String text } +#[test] +fn unknown_live_client_gets_reject() { + let dir = tempfile::tempdir().unwrap(); + let port = free_udp_port(); + let config = write_server_config(&dir, port); + let mut server = start_server(&dir, &config); + let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); + socket + .set_read_timeout(Some(Duration::from_secs(2))) + .unwrap(); + let client_id = crypto::random_16(); + let key = crypto::random_32(); + let packet = protocol::encode_encrypted( + PacketKind::Ping, + client_id, + 1, + 0, + &key, + CLIENT_TO_SERVER, + b"", + ) + .unwrap(); + + socket + .send_to(&packet, format!("127.0.0.1:{port}")) + .unwrap(); + let mut buf = [0u8; 65535]; + let (n, _) = socket.recv_from(&mut buf).unwrap(); + let packet = protocol::decode(&buf[..n]).unwrap(); + let reject: AttachReject = protocol::from_body(&packet.body).unwrap(); + + let _ = server.kill(); + let _ = server.wait(); + + assert_eq!(packet.header.kind, PacketKind::AttachReject); + assert_eq!(packet.header.conn_id, client_id); + assert_eq!(reject.reason, "unknown client"); +} + #[test] fn local_attach_only_smoke() { let dir = tempfile::tempdir().unwrap();