Skip to content

built-in read-only rsh server improvements - #271

Merged
techomancer merged 5 commits into
techomancer:mainfrom
pbartfai:rsh-shell-mode
Oct 21, 2026
Merged

techomancer merged 5 commits into
techomancer:mainfrom
pbartfai:rsh-shell-mode

Conversation

@pbartfai

Copy link
Copy Markdown
Contributor

Add a built-in read-only rsh server to the NAT gateway (network install source for IRIX inst)

Summary

Adds a built-in rsh server at the NAT gateway (TCP 514). With [network] rsh_dir = "DIR" (or --rsh-dir DIR), the guest can run rsh 192.168.0.1 <command> against a host directory without a host rshd. That makes DIR usable as a network installation source for IRIX inst, including the miniroot, which sends exec /bin/sh and drives it over stdin.

It is read-only and runs nothing on the host. A small set of commands is emulated, and every path is sandboxed to DIR.

What's included

Protocol (src/net/rsh.rs, new)

  • BSD rcmd/rshd: header port\0luser\0ruser\0command\0, \0 accept or \1 msg\n reject.
  • The stderr connection is opened by the gateway from a reserved port (512–1023) back to the client's stderr port.
  • The port field is acted on as soon as it arrives. rcmd sends it alone and waits for the connect-back before sending the rest.
  • Commands: cat, dd (if= bs= ibs= count= iseek=/skip=, records summary on stderr), ls [-1alinLd], echo, pwd. Unknown commands print X: Command not found.
  • Shell mode for exec /bin/sh, sh, /bin/sh and similar. Stdin is run line by line against the same commands:
    • ;, 2>/dev/null and cd/export/trap/exit (no-ops or exit) are handled.
    • The marker tail that inst waits for is implemented: ( status=$? ; trap '' 2 ; echo 'MARK\c' ; echo 'MARK'$status'\c' 1>&2 ). MARK goes to stdout, and MARK<status> goes to the stderr connection with a real exit status (0, 1, or 127).
    • echo X | dd (the iseek probe) and dd … | fgrep STR (IRIX 6.5 .idb scans) are supported.
    • ls -inld[L] prints the IRIX format: inode, mode, links, uid, gid, size, Mon D HH:MM, name. The time is UTC.
  • Other pipelines, $, redirects and if/test are refused with an error on stderr.
  • Sandbox: paths are canonicalized and must stay under DIR. .. and symlink escapes are rejected. The TOCTOU window between check and open is documented in the module docs.

Engine (src/net/)

  • TcpService gains pull(max) (streamed output instead of buffering whole files) and side_connect() (a service asks for a connection to the guest). The gateway's own outbound connection to the guest uses alloc_sport(guest_port, reserved), split out of alloc_fwd_sport.
  • Guest connections to port 514 are answered by the built-in server when rsh_dir is set. Otherwise they are relayed to the host as before.

Diagnostics

  • IRIS_RSH_TRACE=1 logs, in any build, each connection (built-in or relayed), the stderr connect-back, each request, each shell stdin line, and how every session ended (bytes, ms, complete or abandoned).

Config and docs

  • [network] rsh_dir, --rsh-dir, and the GUI machine path handling (iris-gui).
  • HELP.md (rsh section and both trace variables), FEATURES.md, CHANGELOG.md, docs/net-stack.md.

Testing

  • IRIX 6.5.22 installation over the built-in rsh server: completed successfully (tested by the author; booted via BOOTP/TFTP, packages loaded by inst through rsh).
  • Unit tests in src/net/rsh.rs (27, covering parsing, sandbox, cat/dd/ls, the split-request handshake, shell mode, the marker protocol, ls -inld, echo | dd and dd | fgrep) pass. I ran them in a standalone harness.
  • src/net/testnet.rs has end-to-end engine tests (stderr connect-back, no-stderr-port, split request with a small window, malformed request). These were written but have not been run: the full crate could not be built in the environment where this was developed. Please run cargo test --workspace in CI before merging.

Notes for reviewers

  • The server never executes anything on the host and cannot write. dd of= other than stdout is rejected.
  • The rsh protocol has no exit status, so only the inst marker carries one.
  • The shell is deliberately not a real shell. If inst or another client sends something unsupported, it fails with a clear stderr message, and IRIS_RSH_TRACE shows the exact line.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01NJtD8EfBuugQ7a2pv4vhsU

Writes each rsh request and how it ended (bytes, ms, complete / cut short /
never sent a whole request) to stderr in any build, for diagnosing slow or
hung network installs. Off unless the variable is set.

Standalone rsh.rs tests pass (20); the full crate is still unbuilt here.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJtD8EfBuugQ7a2pv4vhsU
With IRIS_RSH_TRACE set, report each guest connection to port 514 and whether
the built-in server answered it or it was relayed to the host (rsh_dir unset,
or not the gateway address), and the stderr connect-back. Print a startup
line when rsh_dir is on. "No trace" now distinguishes "never reached the
server" from "no requests".

Not built in full here (no crates.io access); rsh.rs tests pass standalone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJtD8EfBuugQ7a2pv4vhsU
BSD rcmd sends only the stderr port, then blocks accepting the connection back,
and sends the user names and command only after that. The server waited for the
whole request before connecting back, so both sides waited until the client's
timeout (inst: 120 s) and the guest then closed with nothing sent.

Request the connection back when the first field is complete. Also stop the
trace calling such a session "complete".

Standalone rsh.rs tests pass (23); the full crate is not built here, so the
updated testnet.rs test is unrun.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJtD8EfBuugQ7a2pv4vhsU
…mands

inst runs 'exec /bin/sh' and drives it over stdin. Shell mode runs each
stdin line (with ';', 2>/dev/null) against the read-only built-ins and
logs every line under IRIS_RSH_TRACE.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJtD8EfBuugQ7a2pv4vhsU
inst sends each command as one line ending in
( status=$? ; trap '' 2 ; echo 'MARK\c' ; echo 'MARK'$status'\c' 1>&2 ).
Run the commands, then write the marker to stdout and, with the exit status,
to the stderr connection. Add trap (no-op), echo X | dd, dd | fgrep and the
IRIX ls -inld[L] / /bin/ls -a formats, as the reference server does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJtD8EfBuugQ7a2pv4vhsU
@techomancer
techomancer merged commit f7e21ef into techomancer:main Oct 21, 2026
7 checks passed
@pbartfai
pbartfai deleted the rsh-shell-mode branch October 21, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants