Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 32 kB View raw
1#![no_std] 2#![no_main] 3 4mod rng; 5mod ssh; 6 7use lancer_core::ipc::{MAX_IPC_BYTES, unpack_bytes}; 8use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; 9use lancer_user::path; 10use lancer_user::syscall; 11use ssh::{SshProgress, SshSession}; 12 13const SHELL_RING_BASE_SLOT: u64 = 64; 14const SHELL_RING_FRAME_COUNT: u64 = 4; 15const OWN_NOTIF_SLOT: u64 = 3; 16const NETSTACK_NOTIF_SLOT: u64 = 4; 17 18const MAX_SESSIONS: usize = 4; 19 20const TAG_ENDPOINT: u64 = 1; 21const TAG_NOTIFICATION: u64 = 2; 22const TAG_SCHED_CONTEXT: u64 = 5; 23const TAG_FRAME: u64 = 11; 24const RIGHTS_ALL: u64 = 0b1111; 25 26const SLOT_UNTYPED: u64 = 28; 27 28const VFS_RING_BASE: u64 = 128; 29const VFS_RING_FRAMES: u64 = 16; 30const VFS_CLIENT_NOTIF_SLOT: u64 = 12; 31const VFS_NOTIF_SLOT: u64 = 13; 32const VFS_PROC_SLOT: u64 = 15; 33 34const BOOT_MODULE_COUNT: u64 = 7; 35 36const RING_BASE: u64 = 0x4000_0000; 37const RING_HALF: usize = 8192; 38 39const MSG_DATA: u8 = 0; 40const MSG_CONNECT: u8 = 1; 41const MSG_DISCONNECT: u8 = 2; 42const MSG_DNS_RESULT: u8 = 4; 43const MSG_PING_RESULT: u8 = 6; 44const MSG_PING_DONE: u8 = 7; 45const MSG_UDP_RECV: u8 = 10; 46const MSG_UDP_CLOSE: u8 = 11; 47const MSG_UDP_BOUND: u8 = 12; 48const MSG_DNS_RESULT6: u8 = 14; 49const MSG_IFCONFIG_RESULT: u8 = 17; 50const MSG_ARP_ENTRY: u8 = 19; 51const MSG_ARP_DONE: u8 = 20; 52const MSG_NETSTAT_ENTRY: u8 = 22; 53const MSG_NETSTAT_ENTRY_UDP: u8 = 23; 54const MSG_NETSTAT_DONE: u8 = 24; 55 56const IDLE_TIMEOUT_MS: i64 = 300_000; 57const NOTIFY_BIT_SHELL_DEATH_BASE: u64 = 0x10; 58const NOTIFY_BIT_SHELL_NETSOCK: u64 = 0x04; 59 60const INPUT_RING_PAGES: u64 = 1; 61const INPUT_RING_SIZE: usize = 4096; 62const INPUT_RING_SLOT_SIZE: u32 = 64; 63 64const NETSOCK_RING_PAGES: u64 = 2; 65const NETSOCK_RING_HALF: usize = 4096; 66const NETSOCK_RING_SLOT_SIZE: u32 = 128; 67 68static mut SSH_INBUFS: [[u8; 35000]; MAX_SESSIONS] = [[0u8; 35000]; MAX_SESSIONS]; 69static mut SSH_OUTBUFS: [[u8; 35000]; MAX_SESSIONS] = [[0u8; 35000]; MAX_SESSIONS]; 70 71fn slot_proc(i: usize) -> u64 { 72 30 + i as u64 * 7 73} 74fn slot_sched(i: usize) -> u64 { 75 31 + i as u64 * 7 76} 77fn slot_output_ep(i: usize) -> u64 { 78 32 + i as u64 * 7 79} 80fn slot_input_ring_mem(i: usize) -> u64 { 81 33 + i as u64 * 7 82} 83fn slot_netsock_mem(i: usize) -> u64 { 84 34 + i as u64 * 7 85} 86fn slot_shell_notif(i: usize) -> u64 { 87 36 + i as u64 * 7 88} 89 90fn input_ring_vaddr(i: usize) -> u64 { 91 0x7000_0000 + i as u64 * 0x1000 92} 93fn netsock_ring_vaddr(i: usize) -> u64 { 94 0x8000_0000 + i as u64 * 0x2000 95} 96 97struct Session<'a> { 98 ssh: Option<SshSession<'a>>, 99 shell_running: bool, 100 input_tx: Option<PacketRingWriter>, 101 netsock_tx: Option<PacketRingWriter>, 102 netsock_rx: Option<PacketRingReader>, 103 last_activity_ms: i64, 104} 105 106impl<'a> Session<'a> { 107 const fn new() -> Self { 108 Self { 109 ssh: None, 110 shell_running: false, 111 input_tx: None, 112 netsock_tx: None, 113 netsock_rx: None, 114 last_activity_ms: 0, 115 } 116 } 117} 118 119fn find_module_by_name(name: &[u8]) -> Option<u64> { 120 let mut dummy = [0u8; 0]; 121 let total = syscall::module_info(u64::MAX, &mut dummy); 122 if total < 0 { 123 return None; 124 } 125 126 let mut path_buf = [0u8; 128]; 127 (BOOT_MODULE_COUNT..total as u64).find(|&i| { 128 path_buf = [0u8; 128]; 129 let path_len = syscall::module_info(i, &mut path_buf); 130 match path_len < 0 { 131 true => false, 132 false => { 133 let used = (path_len as usize).min(path_buf.len()); 134 path::bytes_eq(path::extract_filename(&path_buf[..used]), name) 135 } 136 } 137 }) 138} 139 140fn send_ssh_bytes(ssh: &mut SshSession, tx: &PacketRingWriter, conn_id: u8, data: &[u8]) { 141 let mut offset = 0usize; 142 core::iter::from_fn(|| match offset < data.len() { 143 true => { 144 let written = ssh.write_plaintext(&data[offset..]); 145 offset += written; 146 flush_ssh_output(ssh, tx, conn_id); 147 Some(()) 148 } 149 false => None, 150 }) 151 .take(64) 152 .count(); 153} 154 155fn flush_ssh_output(ssh: &mut SshSession, tx: &PacketRingWriter, conn_id: u8) -> bool { 156 let mut flushed = false; 157 let mut tmp = [0u8; 126]; 158 core::iter::from_fn(|| { 159 let n = ssh.peek_output(&mut tmp[2..]); 160 match n > 0 { 161 true => { 162 tmp[0] = conn_id; 163 tmp[1] = MSG_DATA; 164 match tx.try_push(&tmp[..2 + n]) { 165 true => { 166 ssh.consume_output(n); 167 flushed = true; 168 Some(()) 169 } 170 false => None, 171 } 172 } 173 false => None, 174 } 175 }) 176 .take(32) 177 .count(); 178 if flushed { 179 syscall::notify_signal(NETSTACK_NOTIF_SLOT, 4); 180 } 181 flushed 182} 183 184fn drive_ssh(ssh: &mut SshSession, tx: &PacketRingWriter, conn_id: u8) -> bool { 185 let mut any_flushed = false; 186 let mut keep_going = true; 187 core::iter::from_fn(|| match keep_going { 188 true => { 189 any_flushed |= flush_ssh_output(ssh, tx, conn_id); 190 let r = ssh.progress(); 191 any_flushed |= flush_ssh_output(ssh, tx, conn_id); 192 match r { 193 SshProgress::Continue => Some(()), 194 SshProgress::ShellReady => { 195 any_flushed = true; 196 None 197 } 198 SshProgress::Idle => { 199 keep_going = false; 200 None 201 } 202 SshProgress::Disconnected => { 203 keep_going = false; 204 None 205 } 206 } 207 } 208 false => None, 209 }) 210 .take(128) 211 .count(); 212 any_flushed 213} 214 215fn spawn_shell(conn_id: usize, module_idx: u64, session: &mut Session) -> bool { 216 let proc_slot = slot_proc(conn_id); 217 let sched_slot = slot_sched(conn_id); 218 let output_ep = slot_output_ep(conn_id); 219 let input_mem = slot_input_ring_mem(conn_id); 220 let netsock_mem = slot_netsock_mem(conn_id); 221 let shell_notif = slot_shell_notif(conn_id); 222 223 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_ENDPOINT, 0, output_ep, 1); 224 if r < 0 { 225 return false; 226 } 227 228 let child_pid = syscall::proc_create(SLOT_UNTYPED, proc_slot); 229 if child_pid < 0 { 230 syscall::cap_revoke(output_ep); 231 return false; 232 } 233 234 let r = syscall::proc_load_module(proc_slot, module_idx); 235 if r < 0 { 236 syscall::proc_destroy(proc_slot); 237 syscall::cap_revoke(output_ep); 238 return false; 239 } 240 241 let r = syscall::cap_grant(output_ep, proc_slot, 2, RIGHTS_ALL); 242 if r < 0 { 243 syscall::proc_destroy(proc_slot); 244 syscall::cap_revoke(output_ep); 245 return false; 246 } 247 248 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_FRAME, 0, input_mem, INPUT_RING_PAGES); 249 if r < 0 { 250 syscall::proc_destroy(proc_slot); 251 syscall::cap_revoke(output_ep); 252 return false; 253 } 254 let ir_vaddr = input_ring_vaddr(conn_id); 255 let map_ok = (0..INPUT_RING_PAGES) 256 .all(|i| syscall::frame_map(input_mem + i, ir_vaddr + i * 4096, 1) >= 0); 257 if !map_ok { 258 (0..INPUT_RING_PAGES).for_each(|i| { 259 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 260 }); 261 (0..INPUT_RING_PAGES).for_each(|i| { 262 syscall::cap_revoke(input_mem + i); 263 }); 264 syscall::proc_destroy(proc_slot); 265 syscall::cap_revoke(output_ep); 266 return false; 267 } 268 let input_tx = unsafe { 269 PacketRingWriter::init(ir_vaddr as *mut u8, INPUT_RING_SIZE, INPUT_RING_SLOT_SIZE) 270 }; 271 let r = syscall::cap_grant(input_mem, proc_slot, 1, RIGHTS_ALL); 272 if r < 0 { 273 (0..INPUT_RING_PAGES).for_each(|i| { 274 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 275 }); 276 (0..INPUT_RING_PAGES).for_each(|i| { 277 syscall::cap_revoke(input_mem + i); 278 }); 279 syscall::proc_destroy(proc_slot); 280 syscall::cap_revoke(output_ep); 281 return false; 282 } 283 284 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_FRAME, 0, netsock_mem, NETSOCK_RING_PAGES); 285 if r < 0 { 286 (0..INPUT_RING_PAGES).for_each(|i| { 287 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 288 }); 289 (0..INPUT_RING_PAGES).for_each(|i| { 290 syscall::cap_revoke(input_mem + i); 291 }); 292 syscall::proc_destroy(proc_slot); 293 syscall::cap_revoke(output_ep); 294 return false; 295 } 296 let ns_vaddr = netsock_ring_vaddr(conn_id); 297 let ns_map_ok = (0..NETSOCK_RING_PAGES) 298 .all(|i| syscall::frame_map(netsock_mem + i, ns_vaddr + i * 4096, 1) >= 0); 299 if !ns_map_ok { 300 (0..NETSOCK_RING_PAGES).for_each(|i| { 301 syscall::frame_unmap(netsock_mem + i, ns_vaddr + i * 4096); 302 }); 303 (0..NETSOCK_RING_PAGES).for_each(|i| { 304 syscall::cap_revoke(netsock_mem + i); 305 }); 306 (0..INPUT_RING_PAGES).for_each(|i| { 307 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 308 }); 309 (0..INPUT_RING_PAGES).for_each(|i| { 310 syscall::cap_revoke(input_mem + i); 311 }); 312 syscall::proc_destroy(proc_slot); 313 syscall::cap_revoke(output_ep); 314 return false; 315 } 316 let netsock_tx = unsafe { 317 PacketRingWriter::init( 318 ns_vaddr as *mut u8, 319 NETSOCK_RING_HALF, 320 NETSOCK_RING_SLOT_SIZE, 321 ) 322 }; 323 let _ = unsafe { 324 PacketRingWriter::init( 325 (ns_vaddr + NETSOCK_RING_HALF as u64) as *mut u8, 326 NETSOCK_RING_HALF, 327 NETSOCK_RING_SLOT_SIZE, 328 ) 329 }; 330 let netsock_rx = unsafe { 331 PacketRingReader::attach( 332 (ns_vaddr + NETSOCK_RING_HALF as u64) as *mut u8, 333 NETSOCK_RING_HALF, 334 ) 335 }; 336 let netsock_grant_ok = (0..NETSOCK_RING_PAGES) 337 .all(|i| syscall::cap_grant(netsock_mem + i, proc_slot, 3 + i, RIGHTS_ALL) >= 0); 338 if !netsock_grant_ok { 339 (0..NETSOCK_RING_PAGES).for_each(|i| { 340 syscall::frame_unmap(netsock_mem + i, ns_vaddr + i * 4096); 341 }); 342 (0..NETSOCK_RING_PAGES).for_each(|i| { 343 syscall::cap_revoke(netsock_mem + i); 344 }); 345 (0..INPUT_RING_PAGES).for_each(|i| { 346 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 347 }); 348 (0..INPUT_RING_PAGES).for_each(|i| { 349 syscall::cap_revoke(input_mem + i); 350 }); 351 syscall::proc_destroy(proc_slot); 352 syscall::cap_revoke(output_ep); 353 return false; 354 } 355 356 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, shell_notif, 1); 357 if r < 0 { 358 (0..NETSOCK_RING_PAGES).for_each(|i| { 359 syscall::frame_unmap(netsock_mem + i, ns_vaddr + i * 4096); 360 }); 361 (0..NETSOCK_RING_PAGES).for_each(|i| { 362 syscall::cap_revoke(netsock_mem + i); 363 }); 364 (0..INPUT_RING_PAGES).for_each(|i| { 365 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 366 }); 367 (0..INPUT_RING_PAGES).for_each(|i| { 368 syscall::cap_revoke(input_mem + i); 369 }); 370 syscall::proc_destroy(proc_slot); 371 syscall::cap_revoke(output_ep); 372 return false; 373 } 374 let r = syscall::cap_grant(shell_notif, proc_slot, 9, RIGHTS_ALL); 375 if r < 0 { 376 syscall::cap_revoke(shell_notif); 377 (0..NETSOCK_RING_PAGES).for_each(|i| { 378 syscall::frame_unmap(netsock_mem + i, ns_vaddr + i * 4096); 379 }); 380 (0..NETSOCK_RING_PAGES).for_each(|i| { 381 syscall::cap_revoke(netsock_mem + i); 382 }); 383 (0..INPUT_RING_PAGES).for_each(|i| { 384 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 385 }); 386 (0..INPUT_RING_PAGES).for_each(|i| { 387 syscall::cap_revoke(input_mem + i); 388 }); 389 syscall::proc_destroy(proc_slot); 390 syscall::cap_revoke(output_ep); 391 return false; 392 } 393 394 let _ = syscall::cap_grant(OWN_NOTIF_SLOT, proc_slot, 10, 0b0010); 395 396 (0..VFS_RING_FRAMES).for_each(|i| { 397 let _ = syscall::cap_grant(VFS_RING_BASE + i, proc_slot, 11 + i, RIGHTS_ALL); 398 }); 399 [ 400 (VFS_CLIENT_NOTIF_SLOT, 28u64), 401 (VFS_NOTIF_SLOT, 29), 402 (VFS_PROC_SLOT, 15), 403 ] 404 .iter() 405 .for_each(|&(src, dst)| { 406 let _ = syscall::cap_grant(src, proc_slot, dst, RIGHTS_ALL); 407 }); 408 409 let death_badge = NOTIFY_BIT_SHELL_DEATH_BASE << conn_id as u64; 410 syscall::proc_bind_death_notif(proc_slot, OWN_NOTIF_SLOT, death_badge); 411 412 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_SCHED_CONTEXT, 0, sched_slot, 1); 413 if r < 0 { 414 cleanup_shell_caps(conn_id); 415 return false; 416 } 417 418 let r = syscall::sched_configure(sched_slot, 10_000_000, 10_000_000, 100); 419 if r < 0 { 420 cleanup_shell_caps(conn_id); 421 return false; 422 } 423 424 let r = syscall::sched_attach(sched_slot, child_pid as u64); 425 if r < 0 { 426 cleanup_shell_caps(conn_id); 427 return false; 428 } 429 430 let r = syscall::proc_start(proc_slot, 2); 431 if r < 0 { 432 cleanup_shell_caps(conn_id); 433 return false; 434 } 435 436 syscall::notify_signal(VFS_CLIENT_NOTIF_SLOT, 1); 437 438 session.shell_running = true; 439 session.input_tx = Some(input_tx); 440 session.netsock_tx = Some(netsock_tx); 441 session.netsock_rx = Some(netsock_rx); 442 443 true 444} 445 446fn cleanup_shell_caps(conn_id: usize) { 447 let ir_vaddr = input_ring_vaddr(conn_id); 448 let ns_vaddr = netsock_ring_vaddr(conn_id); 449 let input_mem = slot_input_ring_mem(conn_id); 450 let netsock_mem = slot_netsock_mem(conn_id); 451 452 (0..INPUT_RING_PAGES).for_each(|i| { 453 syscall::frame_unmap(input_mem + i, ir_vaddr + i * 4096); 454 }); 455 (0..INPUT_RING_PAGES).for_each(|i| { 456 syscall::cap_revoke(input_mem + i); 457 }); 458 459 (0..NETSOCK_RING_PAGES).for_each(|i| { 460 syscall::frame_unmap(netsock_mem + i, ns_vaddr + i * 4096); 461 }); 462 (0..NETSOCK_RING_PAGES).for_each(|i| { 463 syscall::cap_revoke(netsock_mem + i); 464 }); 465 466 syscall::cap_revoke(slot_shell_notif(conn_id)); 467 syscall::cap_revoke(slot_sched(conn_id)); 468 syscall::cap_revoke(slot_output_ep(conn_id)); 469} 470 471fn drain_shell_output(conn_id: usize, ssh: &mut SshSession, tx: &PacketRingWriter) -> bool { 472 let ep_slot = slot_output_ep(conn_id); 473 let cid = conn_id as u8; 474 let mut ipc_raw = [0u8; MAX_IPC_BYTES]; 475 let mut had_data = false; 476 477 core::iter::from_fn(|| { 478 let (status, msg) = syscall::nb_recv(ep_slot); 479 match status >= 0 { 480 true => { 481 let count = unpack_bytes(&msg, &mut ipc_raw); 482 const _: () = assert!(MAX_IPC_BYTES * 2 <= 80); 483 if count > 0 { 484 had_data = true; 485 let mut translated = [0u8; MAX_IPC_BYTES * 2]; 486 let tlen = (0..count).fold(0usize, |pos, i| match ipc_raw[i] { 487 b'\n' => { 488 translated[pos] = b'\r'; 489 translated[pos + 1] = b'\n'; 490 pos + 2 491 } 492 b => { 493 translated[pos] = b; 494 pos + 1 495 } 496 }); 497 send_ssh_bytes(ssh, tx, cid, &translated[..tlen]); 498 } 499 Some(()) 500 } 501 false => None, 502 } 503 }) 504 .take(64) 505 .count(); 506 had_data 507} 508 509fn drain_shell_netsock( 510 conn_id: usize, 511 netsock_rx: &PacketRingReader, 512 netstack_tx: &PacketRingWriter, 513) -> bool { 514 let cid = conn_id as u8; 515 let mut buf = [0u8; 128]; 516 let mut relayed = false; 517 518 core::iter::from_fn(|| match netsock_rx.try_pop(&mut buf) { 519 Some(n) if n > 0 => { 520 buf[0] = cid; 521 match netstack_tx.try_push(&buf[..n]) { 522 true => { 523 relayed = true; 524 Some(()) 525 } 526 false => None, 527 } 528 } 529 _ => None, 530 }) 531 .take(16) 532 .count(); 533 534 relayed 535} 536 537fn relay_netstack_to_shell(netsock_tx: &PacketRingWriter, data: &[u8], shell_notif: u64) { 538 let mut buf = [0u8; 128]; 539 let copy_len = data.len().min(buf.len() - 1); 540 buf[0] = 0; 541 buf[1..1 + copy_len].copy_from_slice(&data[..copy_len]); 542 if netsock_tx.try_push(&buf[..1 + copy_len]) { 543 syscall::notify_signal(shell_notif, 4); 544 } 545} 546 547fn teardown_session(conn_id: usize, session: &mut Session, tx: &PacketRingWriter) { 548 if session.shell_running { 549 syscall::proc_destroy(slot_proc(conn_id)); 550 cleanup_shell_caps(conn_id); 551 session.shell_running = false; 552 session.input_tx = None; 553 session.netsock_tx = None; 554 session.netsock_rx = None; 555 } 556 if let Some(ssh) = session.ssh.as_mut() { 557 ssh.close(); 558 } 559 let ctrl = [conn_id as u8, MSG_DISCONNECT]; 560 let _ = tx.try_push(&ctrl); 561 drop(session.ssh.take()); 562} 563 564#[unsafe(no_mangle)] 565pub extern "C" fn lancer_main() -> ! { 566 lancer_user::show!(rsh, "starting"); 567 568 if !(0..SHELL_RING_FRAME_COUNT) 569 .all(|i| syscall::frame_map(SHELL_RING_BASE_SLOT + i, RING_BASE + i * 4096, 1) >= 0) 570 { 571 lancer_user::show!(rsh, error, "ring frame_map failed"); 572 syscall::exit(); 573 } 574 575 let rx_base = RING_BASE as *mut u8; 576 let tx_base = (RING_BASE + RING_HALF as u64) as *mut u8; 577 578 let rx = unsafe { PacketRingReader::attach(rx_base, RING_HALF) }; 579 let tx = unsafe { PacketRingWriter::init(tx_base, RING_HALF, 128) }; 580 581 let host_key = match sunset::SignKey::generate(sunset::KeyType::Ed25519, None) { 582 Ok(k) => k, 583 Err(_) => { 584 lancer_user::show!(rsh, error, "keygen failed"); 585 syscall::exit(); 586 } 587 }; 588 589 let shell_module_idx = find_module_by_name(b"shell"); 590 match shell_module_idx { 591 Some(_) => lancer_user::show!(rsh, "shell module found"), 592 None => lancer_user::show!(rsh, warn, "shell module not found"), 593 } 594 595 lancer_user::show!(rsh, "ready"); 596 597 let mut sessions: [Session; MAX_SESSIONS] = [ 598 Session::new(), 599 Session::new(), 600 Session::new(), 601 Session::new(), 602 ]; 603 let mut pop_buf = [0u8; 128]; 604 let mut plaintext_buf = [0u8; 256]; 605 606 syscall::ntfn_bind(OWN_NOTIF_SLOT); 607 608 loop { 609 let active_shell = (0..MAX_SESSIONS).find(|&i| sessions[i].shell_running); 610 let mut any_output = false; 611 612 let bits = match active_shell { 613 Some(cid) => { 614 let (status, msg) = syscall::recv(slot_output_ep(cid)); 615 match status == syscall::BOUND_NOTIFICATION_BADGE { 616 true => msg[1], 617 false => { 618 if status >= 0 { 619 let Session { ref mut ssh, .. } = sessions[cid]; 620 if let Some(ssh) = ssh.as_mut() { 621 let mut ipc_raw = [0u8; MAX_IPC_BYTES]; 622 let count = unpack_bytes(&msg, &mut ipc_raw); 623 if count > 0 { 624 let mut translated = [0u8; MAX_IPC_BYTES * 2]; 625 let tlen = (0..count).fold(0usize, |pos, i| match ipc_raw[i] { 626 b'\n' => { 627 translated[pos] = b'\r'; 628 translated[pos + 1] = b'\n'; 629 pos + 2 630 } 631 b => { 632 translated[pos] = b; 633 pos + 1 634 } 635 }); 636 send_ssh_bytes(ssh, &tx, cid as u8, &translated[..tlen]); 637 any_output = true; 638 } 639 } 640 } 641 let (_, poll_bits) = syscall::notify_poll(OWN_NOTIF_SLOT); 642 poll_bits 643 } 644 } 645 } 646 None => { 647 let (_, wait_bits) = syscall::notify_wait(OWN_NOTIF_SLOT); 648 wait_bits 649 } 650 }; 651 652 let now_ms = syscall::clock_monotonic_ms().max(0); 653 654 (0..MAX_SESSIONS).for_each(|i| { 655 let death_bit = NOTIFY_BIT_SHELL_DEATH_BASE << i as u64; 656 if bits & death_bit == 0 || !sessions[i].shell_running { 657 return; 658 } 659 660 if let Some(ssh) = sessions[i].ssh.as_mut() { 661 drain_shell_output(i, ssh, &tx); 662 } 663 syscall::proc_destroy(slot_proc(i)); 664 cleanup_shell_caps(i); 665 sessions[i].shell_running = false; 666 sessions[i].input_tx = None; 667 sessions[i].netsock_tx = None; 668 sessions[i].netsock_rx = None; 669 any_output = true; 670 671 if let Some(ssh) = sessions[i].ssh.as_mut() { 672 ssh.close(); 673 } 674 let ctrl = [i as u8, MSG_DISCONNECT]; 675 let _ = tx.try_push(&ctrl); 676 drop(sessions[i].ssh.take()); 677 }); 678 679 if bits & NOTIFY_BIT_SHELL_NETSOCK != 0 { 680 (0..MAX_SESSIONS).for_each(|i| { 681 if let Some(ref netsock_rx) = sessions[i].netsock_rx { 682 let relayed = drain_shell_netsock(i, netsock_rx, &tx); 683 if relayed { 684 syscall::notify_signal(NETSTACK_NOTIF_SLOT, 4); 685 any_output = true; 686 } 687 } 688 }); 689 } 690 691 (0..MAX_SESSIONS).for_each(|i| { 692 if let Some(ssh) = sessions[i].ssh.as_mut() { 693 any_output |= drive_ssh(ssh, &tx, i as u8); 694 } 695 }); 696 697 let mut sessions_to_drop: u8 = 0; 698 let mut need_spawn: u8 = 0; 699 700 core::iter::from_fn(|| match rx.try_pop(&mut pop_buf) { 701 Some(n) if n >= 2 => { 702 let conn_id = pop_buf[0] as usize; 703 let msg_type = pop_buf[1]; 704 if conn_id >= MAX_SESSIONS { 705 return Some(()); 706 } 707 708 match msg_type { 709 MSG_CONNECT => { 710 if sessions[conn_id].shell_running { 711 syscall::proc_destroy(slot_proc(conn_id)); 712 cleanup_shell_caps(conn_id); 713 sessions[conn_id].shell_running = false; 714 sessions[conn_id].input_tx = None; 715 sessions[conn_id].netsock_tx = None; 716 sessions[conn_id].netsock_rx = None; 717 } 718 drop(sessions[conn_id].ssh.take()); 719 let inbuf: &'static mut [u8; 35000] = unsafe { 720 &mut *((&raw mut SSH_INBUFS) as *mut [u8; 35000]).add(conn_id) 721 }; 722 let outbuf: &'static mut [u8; 35000] = unsafe { 723 &mut *((&raw mut SSH_OUTBUFS) as *mut [u8; 35000]).add(conn_id) 724 }; 725 inbuf.fill(0); 726 outbuf.fill(0); 727 sessions[conn_id].ssh = 728 Some(SshSession::new(inbuf, outbuf, host_key.clone())); 729 sessions[conn_id].last_activity_ms = now_ms; 730 if let Some(ssh) = sessions[conn_id].ssh.as_mut() { 731 any_output |= drive_ssh(ssh, &tx, conn_id as u8); 732 syscall::notify_signal(NETSTACK_NOTIF_SLOT, 4); 733 } 734 } 735 MSG_DISCONNECT => { 736 teardown_session(conn_id, &mut sessions[conn_id], &tx); 737 } 738 MSG_DATA if n > 2 => { 739 let Session { 740 ref mut ssh, 741 ref input_tx, 742 ref shell_running, 743 ref mut last_activity_ms, 744 .. 745 } = sessions[conn_id]; 746 *last_activity_ms = now_ms; 747 if let Some(ssh) = ssh.as_mut() { 748 let data = &pop_buf[2..n]; 749 let mut off = 0usize; 750 core::iter::from_fn(|| match off < data.len() { 751 true => { 752 let accepted = ssh.feed_input(&data[off..]); 753 match accepted { 754 0 => { 755 any_output |= drive_ssh(ssh, &tx, conn_id as u8); 756 if ssh.shell_ready && *shell_running { 757 let pt_n = ssh.read_plaintext(&mut plaintext_buf); 758 if pt_n > 0 759 && let Some(it) = input_tx 760 && it.try_push(&plaintext_buf[..pt_n]) 761 { 762 syscall::notify_signal( 763 slot_shell_notif(conn_id), 764 2, 765 ); 766 } 767 } 768 any_output |= flush_ssh_output(ssh, &tx, conn_id as u8); 769 let retry = ssh.feed_input(&data[off..]); 770 match retry { 771 0 => None, 772 n2 => { 773 off += n2; 774 Some(()) 775 } 776 } 777 } 778 n2 => { 779 off += n2; 780 Some(()) 781 } 782 } 783 } 784 false => None, 785 }) 786 .take(32) 787 .count(); 788 789 any_output |= drive_ssh(ssh, &tx, conn_id as u8); 790 if ssh.shell_ready && *shell_running { 791 let pt_n = ssh.read_plaintext(&mut plaintext_buf); 792 if pt_n > 0 793 && let Some(it) = input_tx 794 && it.try_push(&plaintext_buf[..pt_n]) 795 { 796 syscall::notify_signal(slot_shell_notif(conn_id), 2); 797 } 798 } 799 } 800 } 801 MSG_DNS_RESULT 802 | MSG_DNS_RESULT6 803 | MSG_PING_RESULT 804 | MSG_PING_DONE 805 | MSG_UDP_RECV 806 | MSG_UDP_CLOSE 807 | MSG_UDP_BOUND 808 | MSG_IFCONFIG_RESULT 809 | MSG_ARP_ENTRY 810 | MSG_ARP_DONE 811 | MSG_NETSTAT_ENTRY 812 | MSG_NETSTAT_ENTRY_UDP 813 | MSG_NETSTAT_DONE 814 if sessions[conn_id].shell_running => 815 { 816 if let Some(ref netsock_tx) = sessions[conn_id].netsock_tx { 817 relay_netstack_to_shell( 818 netsock_tx, 819 &pop_buf[1..n], 820 slot_shell_notif(conn_id), 821 ); 822 } 823 any_output = true; 824 } 825 _ => {} 826 } 827 Some(()) 828 } 829 _ => None, 830 }) 831 .take(64) 832 .count(); 833 834 (0..MAX_SESSIONS).for_each(|i| { 835 let Session { 836 ref mut ssh, 837 ref shell_running, 838 ref input_tx, 839 .. 840 } = sessions[i]; 841 if let Some(ssh) = ssh.as_mut() { 842 any_output |= drive_ssh(ssh, &tx, i as u8); 843 if ssh.shell_ready && !*shell_running { 844 need_spawn |= 1 << i; 845 } 846 if ssh.shell_ready && *shell_running { 847 let pt_n = ssh.read_plaintext(&mut plaintext_buf); 848 if pt_n > 0 849 && let Some(it) = input_tx 850 && it.try_push(&plaintext_buf[..pt_n]) 851 { 852 syscall::notify_signal(slot_shell_notif(i), 2); 853 } 854 } 855 any_output |= flush_ssh_output(ssh, &tx, i as u8); 856 } 857 }); 858 859 (0..MAX_SESSIONS).for_each(|i| { 860 if need_spawn & (1 << i) == 0 { 861 return; 862 } 863 match shell_module_idx { 864 Some(idx) => { 865 let ok = spawn_shell(i, idx, &mut sessions[i]); 866 if !ok { 867 if let Some(ssh) = sessions[i].ssh.as_mut() { 868 send_ssh_bytes(ssh, &tx, i as u8, b"Failed to start shell\r\n"); 869 flush_ssh_output(ssh, &tx, i as u8); 870 ssh.close(); 871 } 872 sessions_to_drop |= 1 << i; 873 } 874 } 875 None => { 876 if let Some(ssh) = sessions[i].ssh.as_mut() { 877 send_ssh_bytes(ssh, &tx, i as u8, b"Shell module not found\r\n"); 878 flush_ssh_output(ssh, &tx, i as u8); 879 ssh.close(); 880 } 881 sessions_to_drop |= 1 << i; 882 } 883 } 884 }); 885 886 (0..MAX_SESSIONS).for_each(|i| { 887 if sessions[i].shell_running 888 && let Some(ssh) = sessions[i].ssh.as_mut() 889 { 890 any_output |= drain_shell_output(i, ssh, &tx); 891 } 892 }); 893 894 (0..MAX_SESSIONS).for_each(|i| { 895 if sessions_to_drop & (1 << i) != 0 { 896 teardown_session(i, &mut sessions[i], &tx); 897 } 898 }); 899 900 (0..MAX_SESSIONS).for_each(|i| { 901 if sessions[i].ssh.is_none() { 902 return; 903 } 904 if now_ms - sessions[i].last_activity_ms > IDLE_TIMEOUT_MS { 905 teardown_session(i, &mut sessions[i], &tx); 906 } 907 }); 908 909 if any_output { 910 syscall::notify_signal(NETSTACK_NOTIF_SLOT, 4); 911 } 912 } 913}