22void pc_sftp_rd_init(SftpReader *r,
const uint8_t *payload,
size_t len)
30uint8_t pc_sftp_rd_u8(SftpReader *r)
32 if (!r->ok || r->off + 1 > r->len)
37 return r->p[r->off++];
40uint32_t pc_sftp_rd_u32(SftpReader *r)
42 if (!r->ok || r->off + 4 > r->len)
47 uint32_t v = ((uint32_t)r->p[r->off] << 24) | ((uint32_t)r->p[r->off + 1] << 16) |
48 ((uint32_t)r->p[r->off + 2] << 8) | (uint32_t)r->p[r->off + 3];
53uint64_t pc_sftp_rd_u64(SftpReader *r)
55 if (!r->ok || r->off + 8 > r->len)
61 for (
int i = 0; i < 8; i++)
63 v = (v << 8) | r->p[r->off + i];
69bool pc_sftp_rd_string(SftpReader *r,
const uint8_t **out, uint32_t *out_len)
71 uint32_t n = pc_sftp_rd_u32(r);
72 if (!r->ok || r->off + n > r->len)
89bool pc_sftp_rd_attrs(SftpReader *r, SftpAttrs *a)
91 a->flags = pc_sftp_rd_u32(r);
96 if (a->flags & PC_SSH_FILEXFER_ATTR_SIZE)
98 a->size = pc_sftp_rd_u64(r);
100 if (a->flags & PC_SSH_FILEXFER_ATTR_UIDGID)
105 if (a->flags & PC_SSH_FILEXFER_ATTR_PERMS)
107 a->permissions = pc_sftp_rd_u32(r);
109 if (a->flags & PC_SSH_FILEXFER_ATTR_ACMODTIME)
111 a->atime = pc_sftp_rd_u32(r);
112 a->mtime = pc_sftp_rd_u32(r);
114 if (a->flags & PC_SSH_FILEXFER_ATTR_EXTENDED)
116 uint32_t ec = pc_sftp_rd_u32(r);
117 for (uint32_t i = 0; i < ec && r->ok; i++)
119 pc_sftp_rd_string(r,
nullptr,
nullptr);
120 pc_sftp_rd_string(r,
nullptr,
nullptr);
128void pc_sftp_wr_init(SftpWriter *w, uint8_t *out,
size_t cap)
136void pc_sftp_wr_u8(SftpWriter *w, uint8_t v)
138 if (w->ovf || w->off + 1 > w->cap)
146void pc_sftp_wr_u32(SftpWriter *w, uint32_t v)
148 if (w->ovf || w->off + 4 > w->cap)
153 w->p[w->off++] = (uint8_t)(v >> 24);
154 w->p[w->off++] = (uint8_t)(v >> 16);
155 w->p[w->off++] = (uint8_t)(v >> 8);
156 w->p[w->off++] = (uint8_t)v;
159void pc_sftp_wr_u64(SftpWriter *w, uint64_t v)
161 if (w->ovf || w->off + 8 > w->cap)
166 for (
int i = 7; i >= 0; i--)
168 w->p[w->off++] = (uint8_t)(v >> (8 * i));
172void pc_sftp_wr_bytes(SftpWriter *w,
const void *b,
size_t n)
174 if (w->ovf || w->off + n > w->cap)
179 memcpy(w->p + w->off, b, n);
183void pc_sftp_wr_string(SftpWriter *w,
const void *s, uint32_t n)
185 pc_sftp_wr_u32(w, n);
186 pc_sftp_wr_bytes(w, s, n);
189void pc_sftp_wr_attrs(SftpWriter *w,
const SftpAttrs *a)
191 pc_sftp_wr_u32(w, a->flags);
192 if (a->flags & PC_SSH_FILEXFER_ATTR_SIZE)
194 pc_sftp_wr_u64(w, a->size);
196 if (a->flags & PC_SSH_FILEXFER_ATTR_UIDGID)
198 pc_sftp_wr_u32(w, 0);
199 pc_sftp_wr_u32(w, 0);
201 if (a->flags & PC_SSH_FILEXFER_ATTR_PERMS)
203 pc_sftp_wr_u32(w, a->permissions);
205 if (a->flags & PC_SSH_FILEXFER_ATTR_ACMODTIME)
207 pc_sftp_wr_u32(w, a->atime);
208 pc_sftp_wr_u32(w, a->mtime);
212size_t pc_sftp_wr_finish(SftpWriter *w)
218 uint32_t plen = (uint32_t)(w->off - 4);
219 w->p[0] = (uint8_t)(plen >> 24);
220 w->p[1] = (uint8_t)(plen >> 16);
221 w->p[2] = (uint8_t)(plen >> 8);
222 w->p[3] = (uint8_t)plen;
226size_t pc_sftp_wr_pos(
const SftpWriter *w)
231void pc_sftp_wr_patch_u32(SftpWriter *w,
size_t at, uint32_t v)
237 w->p[at] = (uint8_t)(v >> 24);
238 w->p[at + 1] = (uint8_t)(v >> 16);
239 w->p[at + 2] = (uint8_t)(v >> 8);
240 w->p[at + 3] = (uint8_t)v;
245size_t pc_sftp_frame_len(
const uint8_t *buf,
size_t have,
size_t max)
251 uint32_t plen = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
252 size_t total = (size_t)plen + 4;
253 if (plen == 0 || total > max)
262size_t pc_sftp_build_version(uint8_t *out,
size_t cap)
265 pc_sftp_wr_init(&w, out, cap);
266 pc_sftp_wr_u8(&w, PC_SSH_FXP_VERSION);
267 pc_sftp_wr_u32(&w, PC_SFTP_VERSION);
268 return pc_sftp_wr_finish(&w);
271size_t pc_sftp_build_status(uint32_t
id, uint32_t code,
const char *msg, uint8_t *out,
size_t cap)
274 pc_sftp_wr_init(&w, out, cap);
275 pc_sftp_wr_u8(&w, PC_SSH_FXP_STATUS);
276 pc_sftp_wr_u32(&w,
id);
277 pc_sftp_wr_u32(&w, code);
278 size_t ml = msg ? strnlen(msg, cap) : 0;
279 pc_sftp_wr_string(&w, msg ? msg :
"", (uint32_t)ml);
280 pc_sftp_wr_string(&w,
"", 0);
281 return pc_sftp_wr_finish(&w);
284size_t pc_sftp_build_handle(uint32_t
id,
const void *handle, uint32_t hlen, uint8_t *out,
size_t cap)
287 pc_sftp_wr_init(&w, out, cap);
288 pc_sftp_wr_u8(&w, PC_SSH_FXP_HANDLE);
289 pc_sftp_wr_u32(&w,
id);
290 pc_sftp_wr_string(&w, handle, hlen);
291 return pc_sftp_wr_finish(&w);
294size_t pc_sftp_build_attrs(uint32_t
id,
const SftpAttrs *a, uint8_t *out,
size_t cap)
297 pc_sftp_wr_init(&w, out, cap);
298 pc_sftp_wr_u8(&w, PC_SSH_FXP_ATTRS);
299 pc_sftp_wr_u32(&w,
id);
300 pc_sftp_wr_attrs(&w, a);
301 return pc_sftp_wr_finish(&w);
304size_t pc_sftp_build_data(uint32_t
id,
const void *data, uint32_t dlen, uint8_t *out,
size_t cap)
307 pc_sftp_wr_init(&w, out, cap);
308 pc_sftp_wr_u8(&w, PC_SSH_FXP_DATA);
309 pc_sftp_wr_u32(&w,
id);
310 pc_sftp_wr_string(&w, data, dlen);
311 return pc_sftp_wr_finish(&w);
314size_t pc_sftp_build_name1(uint32_t
id,
const char *name,
const char *longname,
const SftpAttrs *a, uint8_t *out,
318 pc_sftp_wr_init(&w, out, cap);
319 pc_sftp_wr_u8(&w, PC_SSH_FXP_NAME);
320 pc_sftp_wr_u32(&w,
id);
321 pc_sftp_wr_u32(&w, 1);
322 pc_sftp_wr_string(&w, name, (uint32_t)strnlen(name, cap));
323 pc_sftp_wr_string(&w, longname, (uint32_t)strnlen(longname, cap));
324 pc_sftp_wr_attrs(&w, a);
325 return pc_sftp_wr_finish(&w);
328size_t pc_sftp_format_longname(
bool is_dir, uint32_t perms, uint64_t size, uint32_t mtime,
const char *name,
char *out,
331 static const char *kMonths[12] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
332 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
334 mode[0] = is_dir ?
'd' :
'-';
335 static const char rwx[9] = {
'r',
'w',
'x',
'r',
'w',
'x',
'r',
'w',
'x'};
336 for (
int i = 0; i < 9; i++)
338 mode[1 + i] = (perms & (1u << (8 - i))) ? rwx[i] :
'-';
342 time_t t = (time_t)mtime;
344 memset(&tmv, 0,
sizeof(tmv));
349 int mon = (tmv.tm_mon >= 0 && tmv.tm_mon < 12) ? tmv.tm_mon : 0;
358 pc_sb sb_out = {out, cap, 0,
true};
SFTP protocol v3 wire codec (SSH_FXP_*, draft-ietf-secsh-filexfer-02) - the pure, host-testable half ...
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_u64_clip(pc_sb *b, uint64_t v, uint8_t columns)
Append v as decimal, right-aligned in at least columns with leading spaces, if the whole field fits -...
void pc_sb_put_clip(pc_sb *b, const char *s)
Append as much of s as fits and stop, WITHOUT latching ok.
Bump-append target; ok latches false once an append would overflow cap.
Reentrant UTC broken-down time, portable across the host and target toolchains.
struct tm * pc_gmtime_r(const time_t *epoch, struct tm *out)
Convert epoch to broken-down UTC in caller storage (reentrant).