|
ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
|
This sketch serves files from a filesystem over the one authenticated SSH port: a client's sftp session reads/writes/lists files, and scp drops a file onto the device. It is the standards-track "southbound" path for pushing files - e.g. NC / G-code programs - onto the device securely, alongside a shell on the same port.
It is the SSH server example (SSH) plus two calls: mount a filesystem and pc_ssh_sftp_begin(fs, root) (+ pc_ssh_scp_begin(fs, root)). The SFTP subsystem and the SCP exec attach to the existing SSH channel layer.
sftp client's default): put / get / ls / mkdir / rmdir / rm / rename / stat / realpath, with streamed reads/writes (no heap beyond the fs layer's file handle) and a fixed handle table (PC_SFTP_MAX_HANDLES). Every path is checked for .. traversal before touching the filesystem.scp localfile admin@<ip>:/path): the rcp SINK direction, one file per transfer. (Download - scp <ip>:/path localfile - is a follow-up; use sftp get for now.)SD.begin(...) and pass SD instead of LittleFS.docs/SSH.md, "Host key
provisioning") - the sketch loads it with pc_ssh_rsa_load_pubkey().Edit SSID / PASSWORD (and the demo password in ssh_password_auth), flash, and open Serial @ 115200. Then, from a machine with the OpenSSH client:
Permission denied.** The demo accepts admin / s3cret (password auth). Force password auth if your client prefers keys: sftp -o PreferredAuthentications=password -o PubkeyAuthentication=no ....No SSH host key in NVS.** Provision the host key first (docs/SSH.md).put/scp prints a broken-pipe / non-zero exit at the end but the file is correct. That is the SSH server's non-graceful connection teardown (a plain ssh <host> exit shows it too, with exit 0). The transfer itself is byte-exact; it is a documented follow-up (a graceful SSH_MSG_DISCONNECT).scp <ip>:/path local hangs / "not supported".** Download over SCP is not implemented yet - use sftp get.READ returns a short DATA bounded by one SSH packet (SSH_PKT_BUF_SIZE); raise SSH_PKT_BUF_SIZE and PC_SFTP_MAX_READ for higher throughput (SSH_CHAN_MAX_PACKET derives from SSH_PKT_BUF_SIZE).root (e.g. pc_ssh_sftp_begin(LittleFS, "/gcode")) so a client cannot see the whole volume; the .. guard keeps requests inside it.services/dnc to drip a pushed .nc program to an attached controller over RS-232 / a socket.The SFTP/SCP server lives inside the library, so the flags must reach the whole build:
(The Arduino IDE reads the flags from build_opt.h beside the sketch automatically.)
An SSH CHANNEL_REQUEST of type subsystem/sftp (or exec "scp …") is recognized in the channel layer, which tags the channel and routes its data to the SFTP/SCP binding instead of the shell echo. The binding accumulates the channel byte stream into SSH_FXP_* (or rcp) records, executes each against the fs::FS mount - reusing the same file operations WebDAV and the static file server use - and frames responses back with pc_ssh_conn_send. A large SFTP WRITE (or an SCP upload) is streamed straight to the file as the fragments arrive, so a transfer is never bounded by a buffer. The wire codecs (services/sftp, services/scp) are pure and host-tested (native_ssh_sftp, native_scp); this glue binds their seams to the SSH channel + fs::FS. HW-verified against the OpenSSH sftp/scp clients on an ESP32-S3 with an SD card (byte-exact round trips).