27enum class ScpSt : uint8_t
53 const char *root =
"/";
54 bool registered =
false;
59void ack(ScpConn *c, uint8_t
byte)
63void err_ack(ScpConn *c,
const char *msg)
66 buf[0] = PC_SCP_ACK_ERROR;
67 size_t ml = strnlen(msg,
sizeof(buf) - 3);
68 memcpy(buf + 1, msg, ml);
72void pc_scp_end(ScpConn *c)
83void pc_scp_on_open(uint8_t slot, uint32_t channel,
const char *cmd,
size_t cmd_len)
89 ScpConn *c = &s_scp.conns[slot];
102 ScpMode mode = pc_scp_parse_cmd(cmd, cmd_len, path,
sizeof(path));
103 if (mode == ScpMode::SINK)
105 size_t pl = strnlen(path,
sizeof(path));
106 c->dest_is_dir = (pl > 0 && path[pl - 1] ==
'/');
107 strncpy(c->dest, path,
sizeof(c->dest) - 1);
108 c->dest[
sizeof(c->dest) - 1] =
'\0';
109 c->st = ScpSt::WAIT_CLINE;
110 ack(c, PC_SCP_ACK_OK);
112 else if (mode == ScpMode::SOURCE)
114 err_ack(c,
"scp download not supported; use sftp get");
119 err_ack(c,
"unsupported scp command");
125bool pc_scp_resolve_dest(ScpConn *c,
const char *name,
char *out,
size_t cap)
130 pc_sb sb_sub = {sub,
sizeof(sub), 0,
true};
140 pc_sb sb_sub2 = {sub,
sizeof(sub), 0,
true};
150void pc_scp_on_data(uint8_t slot, uint32_t channel,
const uint8_t *data,
size_t len)
156 ScpConn *c = &s_scp.conns[slot];
157 if (!c->active || c->channel != channel)
164 if (c->st == ScpSt::WAIT_CLINE)
166 bool complete =
false;
169 char ch = (char)data[0];
177 if (c->cl_len <
sizeof(c->cl) - 1)
179 c->cl[c->cl_len++] = ch;
186 c->cl[c->cl_len] =
'\0';
191 if (!pc_scp_parse_cline(c->cl, c->cl_len, &mode, &size, name,
sizeof(name)))
193 err_ack(c,
"unsupported scp record");
198 if (!pc_scp_resolve_dest(c, name, disk,
sizeof(disk)))
200 err_ack(c,
"bad path");
204 c->file = s_scp.fs->open(disk,
"w");
207 err_ack(c,
"cannot create file");
212 c->st = (size == 0) ? ScpSt::WAIT_END : ScpSt::RECV;
214 ack(c, PC_SCP_ACK_OK);
217 if (c->st == ScpSt::RECV)
219 size_t take = (len < c->remaining) ? len : (size_t)c->remaining;
220 if (c->file.write(data, take) != take)
226 c->remaining -= take;
227 if (c->remaining == 0)
229 c->st = ScpSt::WAIT_END;
233 if (c->st == ScpSt::WAIT_END)
241 c->file = fs::File();
244 err_ack(c,
"write error");
248 ack(c, PC_SCP_ACK_OK);
258void pc_ssh_scp_begin(fs::FS &fs,
const char *root)
261 s_scp.root = (root && root[0]) ? root :
"/";
264 s_scp.conns[i].active =
false;
265 if (s_scp.conns[i].file)
267 s_scp.conns[i].file.close();
269 s_scp.conns[i].file = fs::File();
271 if (!s_scp.registered)
273 pc_ssh_channel_set_scp_open_cb(pc_scp_on_open);
274 pc_ssh_channel_set_scp_data_cb(pc_scp_on_data);
275 s_scp.registered =
true;
Shared filesystem path helpers for the file-transfer servers (SFTP / SCP, and the pattern the static ...
int fs_path_resolve(const char *root, const char *sub, char *out, size_t cap)
Resolve a mount root + a request sub path to an on-disk path in out: reject any .....
#define PC_SFTP_PATH_MAX
Largest absolute path the SFTP/SCP server resolves (mount root + request path).
SCP (RCP) protocol wire codec - the pure, host-testable half of the SCP-over-SSH server (PC_ENABLE_SS...
SSH connection protocol - multiplexed "session" channels (RFC 4254).
int pc_ssh_conn_send(uint8_t ssh_slot, uint32_t channel, const uint8_t *data, size_t len)
Send application data to the client over an SSH channel.
int pc_ssh_conn_close_channel(uint8_t ssh_slot, uint32_t channel)
Close an SSH channel from the server side: frame CHANNEL_EOF and CHANNEL_CLOSE as two binary packets ...
Glue between the TCP transport (conn_pool) and the SSH protocol stack.
SCP server - the fs::FS binding (PC_ENABLE_SSH_SCP).
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Bump-append target; ok latches false once an append would overflow cap.