Nothing to see here, move along meow
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}