Set SSH socket variable to NULL if connect() fails. Failure to do
[u/mdw/putty] / sftp.c
CommitLineData
4c7f0d61 1/*
2 * sftp.c: SFTP generic client code.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
4a8fc3c4 7#include <string.h>
4c7f0d61 8#include <assert.h>
4c7f0d61 9
78139cd8 10#include "misc.h"
4c7f0d61 11#include "int64.h"
12#include "sftp.h"
13
4c7f0d61 14#define GET_32BIT(cp) \
15 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
16 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
17 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
18 ((unsigned long)(unsigned char)(cp)[3]))
19
20#define PUT_32BIT(cp, value) { \
21 (cp)[0] = (unsigned char)((value) >> 24); \
22 (cp)[1] = (unsigned char)((value) >> 16); \
23 (cp)[2] = (unsigned char)((value) >> 8); \
24 (cp)[3] = (unsigned char)(value); }
25
26struct sftp_packet {
27 char *data;
28 int length, maxlen;
29 int savedpos;
30 int type;
31};
32
9954aaa3 33static const char *fxp_error_message;
34static int fxp_errtype;
35
4c7f0d61 36/* ----------------------------------------------------------------------
37 * SFTP packet construction functions.
38 */
32874aea 39static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
40{
4c7f0d61 41 if (pkt->maxlen < length) {
32874aea 42 pkt->maxlen = length + 256;
4c7f0d61 43 pkt->data = srealloc(pkt->data, pkt->maxlen);
44 }
45}
32874aea 46static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
47{
4c7f0d61 48 pkt->length += len;
49 sftp_pkt_ensure(pkt, pkt->length);
32874aea 50 memcpy(pkt->data + pkt->length - len, data, len);
4c7f0d61 51}
32874aea 52static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
53{
4c7f0d61 54 sftp_pkt_adddata(pkt, &byte, 1);
55}
32874aea 56static struct sftp_packet *sftp_pkt_init(int pkt_type)
57{
4c7f0d61 58 struct sftp_packet *pkt;
59 pkt = smalloc(sizeof(struct sftp_packet));
60 pkt->data = NULL;
61 pkt->savedpos = -1;
62 pkt->length = 0;
63 pkt->maxlen = 0;
32874aea 64 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
4c7f0d61 65 return pkt;
66}
32874aea 67static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
68{
4c7f0d61 69 sftp_pkt_adddata(pkt, &value, 1);
70}
32874aea 71static void sftp_pkt_adduint32(struct sftp_packet *pkt,
72 unsigned long value)
73{
4c7f0d61 74 unsigned char x[4];
75 PUT_32BIT(x, value);
76 sftp_pkt_adddata(pkt, x, 4);
77}
32874aea 78static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
79{
4c7f0d61 80 unsigned char x[8];
81 PUT_32BIT(x, value.hi);
32874aea 82 PUT_32BIT(x + 4, value.lo);
4c7f0d61 83 sftp_pkt_adddata(pkt, x, 8);
84}
32874aea 85static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
86{
4c7f0d61 87 sftp_pkt_adduint32(pkt, 0);
88 pkt->savedpos = pkt->length;
89}
32874aea 90static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
91{
4c7f0d61 92 sftp_pkt_adddata(pkt, data, strlen(data));
32874aea 93 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
4c7f0d61 94}
95static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
32874aea 96 char *data, int len)
97{
4c7f0d61 98 sftp_pkt_adddata(pkt, data, len);
32874aea 99 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
4c7f0d61 100}
32874aea 101static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
102{
4c7f0d61 103 sftp_pkt_addstring_start(pkt);
104 sftp_pkt_addstring_str(pkt, data);
105}
d92624dc 106static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
107{
108 sftp_pkt_adduint32(pkt, attrs.flags);
109 if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
110 sftp_pkt_adduint32(pkt, attrs.size.hi);
111 sftp_pkt_adduint32(pkt, attrs.size.lo);
112 }
113 if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
114 sftp_pkt_adduint32(pkt, attrs.uid);
115 sftp_pkt_adduint32(pkt, attrs.gid);
116 }
117 if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
118 sftp_pkt_adduint32(pkt, attrs.permissions);
119 }
120 if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
121 sftp_pkt_adduint32(pkt, attrs.atime);
122 sftp_pkt_adduint32(pkt, attrs.mtime);
123 }
124 if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
125 /*
126 * We currently don't support sending any extended
127 * attributes.
128 */
129 }
130}
4c7f0d61 131
132/* ----------------------------------------------------------------------
133 * SFTP packet decode functions.
134 */
135
32874aea 136static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
137{
d8770b12 138 unsigned char value;
4c7f0d61 139 if (pkt->length - pkt->savedpos < 1)
32874aea 140 return 0; /* arrgh, no way to decline (FIXME?) */
4c7f0d61 141 value = (unsigned char) pkt->data[pkt->savedpos];
142 pkt->savedpos++;
143 return value;
144}
32874aea 145static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
146{
4c7f0d61 147 unsigned long value;
148 if (pkt->length - pkt->savedpos < 4)
32874aea 149 return 0; /* arrgh, no way to decline (FIXME?) */
150 value = GET_32BIT(pkt->data + pkt->savedpos);
4c7f0d61 151 pkt->savedpos += 4;
152 return value;
153}
154static void sftp_pkt_getstring(struct sftp_packet *pkt,
32874aea 155 char **p, int *length)
156{
4c7f0d61 157 *p = NULL;
158 if (pkt->length - pkt->savedpos < 4)
32874aea 159 return;
160 *length = GET_32BIT(pkt->data + pkt->savedpos);
4c7f0d61 161 pkt->savedpos += 4;
162 if (pkt->length - pkt->savedpos < *length)
32874aea 163 return;
164 *p = pkt->data + pkt->savedpos;
4c7f0d61 165 pkt->savedpos += *length;
166}
32874aea 167static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
168{
4c7f0d61 169 struct fxp_attrs ret;
170 ret.flags = sftp_pkt_getuint32(pkt);
171 if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
172 unsigned long hi, lo;
173 hi = sftp_pkt_getuint32(pkt);
174 lo = sftp_pkt_getuint32(pkt);
175 ret.size = uint64_make(hi, lo);
176 }
177 if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
178 ret.uid = sftp_pkt_getuint32(pkt);
179 ret.gid = sftp_pkt_getuint32(pkt);
180 }
181 if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
182 ret.permissions = sftp_pkt_getuint32(pkt);
183 }
184 if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
185 ret.atime = sftp_pkt_getuint32(pkt);
186 ret.mtime = sftp_pkt_getuint32(pkt);
187 }
188 if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
189 int count;
190 count = sftp_pkt_getuint32(pkt);
191 while (count--) {
192 char *str;
193 int len;
194 /*
195 * We should try to analyse these, if we ever find one
196 * we recognise.
197 */
198 sftp_pkt_getstring(pkt, &str, &len);
199 sftp_pkt_getstring(pkt, &str, &len);
200 }
201 }
202 return ret;
203}
32874aea 204static void sftp_pkt_free(struct sftp_packet *pkt)
205{
206 if (pkt->data)
207 sfree(pkt->data);
4c7f0d61 208 sfree(pkt);
209}
210
211/* ----------------------------------------------------------------------
4a8fc3c4 212 * Send and receive packet functions.
4c7f0d61 213 */
32874aea 214int sftp_send(struct sftp_packet *pkt)
215{
4a8fc3c4 216 int ret;
4c7f0d61 217 char x[4];
218 PUT_32BIT(x, pkt->length);
32874aea 219 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
4c7f0d61 220 sftp_pkt_free(pkt);
4a8fc3c4 221 return ret;
4c7f0d61 222}
32874aea 223struct sftp_packet *sftp_recv(void)
224{
4c7f0d61 225 struct sftp_packet *pkt;
226 char x[4];
4c7f0d61 227
4a8fc3c4 228 if (!sftp_recvdata(x, 4))
229 return NULL;
4c7f0d61 230
231 pkt = smalloc(sizeof(struct sftp_packet));
232 pkt->savedpos = 0;
233 pkt->length = pkt->maxlen = GET_32BIT(x);
234 pkt->data = smalloc(pkt->length);
235
4a8fc3c4 236 if (!sftp_recvdata(pkt->data, pkt->length)) {
237 sftp_pkt_free(pkt);
238 return NULL;
4c7f0d61 239 }
240
241 pkt->type = sftp_pkt_getbyte(pkt);
242
243 return pkt;
244}
245
246/* ----------------------------------------------------------------------
247 * String handling routines.
248 */
249
32874aea 250static char *mkstr(char *s, int len)
251{
252 char *p = smalloc(len + 1);
4c7f0d61 253 memcpy(p, s, len);
254 p[len] = '\0';
255 return p;
256}
257
258/* ----------------------------------------------------------------------
259 * SFTP primitives.
260 */
261
4c7f0d61 262/*
263 * Deal with (and free) an FXP_STATUS packet. Return 1 if
264 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
265 * Also place the status into fxp_errtype.
266 */
32874aea 267static int fxp_got_status(struct sftp_packet *pktin)
268{
4c7f0d61 269 static const char *const messages[] = {
270 /* SSH_FX_OK. The only time we will display a _message_ for this
271 * is if we were expecting something other than FXP_STATUS on
272 * success, so this is actually an error message! */
273 "unexpected OK response",
274 "end of file",
275 "no such file or directory",
276 "permission denied",
277 "failure",
278 "bad message",
279 "no connection",
280 "connection lost",
281 "operation unsupported",
282 };
283
284 if (pktin->type != SSH_FXP_STATUS) {
285 fxp_error_message = "expected FXP_STATUS packet";
286 fxp_errtype = -1;
287 } else {
288 fxp_errtype = sftp_pkt_getuint32(pktin);
289 if (fxp_errtype < 0 ||
32874aea 290 fxp_errtype >= sizeof(messages) / sizeof(*messages))
291 fxp_error_message = "unknown error code";
4c7f0d61 292 else
293 fxp_error_message = messages[fxp_errtype];
294 }
295
296 if (fxp_errtype == SSH_FX_OK)
297 return 1;
298 else if (fxp_errtype == SSH_FX_EOF)
299 return 0;
300 else
301 return -1;
302}
303
32874aea 304static void fxp_internal_error(char *msg)
305{
4c7f0d61 306 fxp_error_message = msg;
307 fxp_errtype = -1;
308}
309
32874aea 310const char *fxp_error(void)
311{
4c7f0d61 312 return fxp_error_message;
313}
314
32874aea 315int fxp_error_type(void)
316{
4c7f0d61 317 return fxp_errtype;
318}
319
320/*
321 * Perform exchange of init/version packets. Return 0 on failure.
322 */
32874aea 323int fxp_init(void)
324{
4c7f0d61 325 struct sftp_packet *pktout, *pktin;
326 int remotever;
327
328 pktout = sftp_pkt_init(SSH_FXP_INIT);
329 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
330 sftp_send(pktout);
331
332 pktin = sftp_recv();
0d694692 333 if (!pktin) {
334 fxp_internal_error("could not connect");
335 return 0;
336 }
4c7f0d61 337 if (pktin->type != SSH_FXP_VERSION) {
338 fxp_internal_error("did not receive FXP_VERSION");
339 return 0;
340 }
341 remotever = sftp_pkt_getuint32(pktin);
342 if (remotever > SFTP_PROTO_VERSION) {
32874aea 343 fxp_internal_error
344 ("remote protocol is more advanced than we support");
4c7f0d61 345 return 0;
346 }
347 /*
348 * In principle, this packet might also contain extension-
349 * string pairs. We should work through them and look for any
350 * we recognise. In practice we don't currently do so because
351 * we know we don't recognise _any_.
352 */
353 sftp_pkt_free(pktin);
354
355 return 1;
356}
357
358/*
f9e162aa 359 * Canonify a pathname.
4c7f0d61 360 */
32874aea 361char *fxp_realpath(char *path)
362{
4c7f0d61 363 struct sftp_packet *pktin, *pktout;
364 int id;
365
366 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
367 sftp_pkt_adduint32(pktout, 0x123); /* request id */
368 sftp_pkt_addstring_start(pktout);
369 sftp_pkt_addstring_str(pktout, path);
4c7f0d61 370 sftp_send(pktout);
371 pktin = sftp_recv();
f65c4643 372 if (!pktin) {
373 fxp_internal_error("did not receive a valid SFTP packet\n");
374 return NULL;
375 }
4c7f0d61 376 id = sftp_pkt_getuint32(pktin);
377 if (id != 0x123) {
378 fxp_internal_error("request ID mismatch\n");
379 return NULL;
380 }
381 if (pktin->type == SSH_FXP_NAME) {
382 int count;
383 char *path;
384 int len;
385
386 count = sftp_pkt_getuint32(pktin);
387 if (count != 1) {
388 fxp_internal_error("REALPATH returned name count != 1\n");
389 return NULL;
390 }
391 sftp_pkt_getstring(pktin, &path, &len);
392 if (!path) {
393 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
394 return NULL;
395 }
396 path = mkstr(path, len);
397 sftp_pkt_free(pktin);
398 return path;
399 } else {
400 fxp_got_status(pktin);
401 return NULL;
402 }
403}
404
405/*
406 * Open a file.
407 */
32874aea 408struct fxp_handle *fxp_open(char *path, int type)
409{
4c7f0d61 410 struct sftp_packet *pktin, *pktout;
411 int id;
412
413 pktout = sftp_pkt_init(SSH_FXP_OPEN);
414 sftp_pkt_adduint32(pktout, 0x567); /* request id */
415 sftp_pkt_addstring(pktout, path);
416 sftp_pkt_adduint32(pktout, type);
417 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
418 sftp_send(pktout);
419 pktin = sftp_recv();
f65c4643 420 if (!pktin) {
421 fxp_internal_error("did not receive a valid SFTP packet\n");
422 return NULL;
423 }
4c7f0d61 424 id = sftp_pkt_getuint32(pktin);
425 if (id != 0x567) {
426 fxp_internal_error("request ID mismatch\n");
427 return NULL;
428 }
429 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 430 char *hstring;
431 struct fxp_handle *handle;
432 int len;
433
434 sftp_pkt_getstring(pktin, &hstring, &len);
435 if (!hstring) {
436 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
437 return NULL;
438 }
439 handle = smalloc(sizeof(struct fxp_handle));
440 handle->hstring = mkstr(hstring, len);
f9e162aa 441 handle->hlen = len;
4c7f0d61 442 sftp_pkt_free(pktin);
443 return handle;
444 } else {
445 fxp_got_status(pktin);
446 return NULL;
447 }
448}
449
450/*
451 * Open a directory.
452 */
32874aea 453struct fxp_handle *fxp_opendir(char *path)
454{
4c7f0d61 455 struct sftp_packet *pktin, *pktout;
456 int id;
457
458 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
459 sftp_pkt_adduint32(pktout, 0x456); /* request id */
460 sftp_pkt_addstring(pktout, path);
461 sftp_send(pktout);
462 pktin = sftp_recv();
f65c4643 463 if (!pktin) {
464 fxp_internal_error("did not receive a valid SFTP packet\n");
465 return NULL;
466 }
4c7f0d61 467 id = sftp_pkt_getuint32(pktin);
468 if (id != 0x456) {
469 fxp_internal_error("request ID mismatch\n");
470 return NULL;
471 }
472 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 473 char *hstring;
474 struct fxp_handle *handle;
475 int len;
476
477 sftp_pkt_getstring(pktin, &hstring, &len);
478 if (!hstring) {
479 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
480 return NULL;
481 }
482 handle = smalloc(sizeof(struct fxp_handle));
483 handle->hstring = mkstr(hstring, len);
f9e162aa 484 handle->hlen = len;
4c7f0d61 485 sftp_pkt_free(pktin);
486 return handle;
487 } else {
488 fxp_got_status(pktin);
489 return NULL;
490 }
491}
492
493/*
494 * Close a file/dir.
495 */
32874aea 496void fxp_close(struct fxp_handle *handle)
497{
4c7f0d61 498 struct sftp_packet *pktin, *pktout;
499 int id;
500
501 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
502 sftp_pkt_adduint32(pktout, 0x789); /* request id */
f9e162aa 503 sftp_pkt_addstring_start(pktout);
504 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 505 sftp_send(pktout);
506 pktin = sftp_recv();
f65c4643 507 if (!pktin) {
508 fxp_internal_error("did not receive a valid SFTP packet\n");
509 return;
510 }
4c7f0d61 511 id = sftp_pkt_getuint32(pktin);
512 if (id != 0x789) {
513 fxp_internal_error("request ID mismatch\n");
514 return;
515 }
516 fxp_got_status(pktin);
517 sfree(handle->hstring);
518 sfree(handle);
519}
520
9954aaa3 521int fxp_mkdir(char *path)
522{
523 struct sftp_packet *pktin, *pktout;
524 int id;
525
526 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
527 sftp_pkt_adduint32(pktout, 0x234); /* request id */
d92624dc 528 sftp_pkt_addstring(pktout, path);
9954aaa3 529 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
530 sftp_send(pktout);
531 pktin = sftp_recv();
f65c4643 532 if (!pktin) {
533 fxp_internal_error("did not receive a valid SFTP packet\n");
534 return 0;
535 }
9954aaa3 536 id = sftp_pkt_getuint32(pktin);
537 if (id != 0x234) {
538 fxp_internal_error("request ID mismatch\n");
539 return 0;
540 }
541 id = fxp_got_status(pktin);
542 if (id != 1) {
543 return 0;
544 }
545 return 1;
546}
547
548int fxp_rmdir(char *path)
549{
550 struct sftp_packet *pktin, *pktout;
551 int id;
552
553 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
554 sftp_pkt_adduint32(pktout, 0x345); /* request id */
d92624dc 555 sftp_pkt_addstring(pktout, path);
9954aaa3 556 sftp_send(pktout);
557 pktin = sftp_recv();
f65c4643 558 if (!pktin) {
559 fxp_internal_error("did not receive a valid SFTP packet\n");
560 return 0;
561 }
9954aaa3 562 id = sftp_pkt_getuint32(pktin);
563 if (id != 0x345) {
564 fxp_internal_error("request ID mismatch\n");
565 return 0;
566 }
567 id = fxp_got_status(pktin);
568 if (id != 1) {
569 return 0;
570 }
571 return 1;
572}
573
d92624dc 574int fxp_remove(char *fname)
9954aaa3 575{
576 struct sftp_packet *pktin, *pktout;
577 int id;
578
579 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
580 sftp_pkt_adduint32(pktout, 0x678); /* request id */
d92624dc 581 sftp_pkt_addstring(pktout, fname);
582 sftp_send(pktout);
583 pktin = sftp_recv();
f65c4643 584 if (!pktin) {
585 fxp_internal_error("did not receive a valid SFTP packet\n");
586 return 0;
587 }
d92624dc 588 id = sftp_pkt_getuint32(pktin);
589 if (id != 0x678) {
590 fxp_internal_error("request ID mismatch\n");
591 return 0;
592 }
593 id = fxp_got_status(pktin);
594 if (id != 1) {
595 return 0;
596 }
597 return 1;
598}
599
600int fxp_rename(char *srcfname, char *dstfname)
601{
602 struct sftp_packet *pktin, *pktout;
603 int id;
604
605 pktout = sftp_pkt_init(SSH_FXP_RENAME);
606 sftp_pkt_adduint32(pktout, 0x678); /* request id */
607 sftp_pkt_addstring(pktout, srcfname);
608 sftp_pkt_addstring(pktout, dstfname);
609 sftp_send(pktout);
610 pktin = sftp_recv();
f65c4643 611 if (!pktin) {
612 fxp_internal_error("did not receive a valid SFTP packet\n");
613 return 0;
614 }
d92624dc 615 id = sftp_pkt_getuint32(pktin);
616 if (id != 0x678) {
617 fxp_internal_error("request ID mismatch\n");
618 return 0;
619 }
620 id = fxp_got_status(pktin);
621 if (id != 1) {
622 return 0;
623 }
624 return 1;
625}
626
627/*
628 * Retrieve the attributes of a file. We have fxp_stat which works
629 * on filenames, and fxp_fstat which works on open file handles.
630 */
631int fxp_stat(char *fname, struct fxp_attrs *attrs)
632{
633 struct sftp_packet *pktin, *pktout;
634 int id;
635
636 pktout = sftp_pkt_init(SSH_FXP_STAT);
637 sftp_pkt_adduint32(pktout, 0x678); /* request id */
638 sftp_pkt_addstring(pktout, fname);
639 sftp_send(pktout);
640 pktin = sftp_recv();
f65c4643 641 if (!pktin) {
642 fxp_internal_error("did not receive a valid SFTP packet\n");
643 return 0;
644 }
d92624dc 645 id = sftp_pkt_getuint32(pktin);
646 if (id != 0x678) {
647 fxp_internal_error("request ID mismatch\n");
648 return 0;
649 }
650
651 if (pktin->type == SSH_FXP_ATTRS) {
652 *attrs = sftp_pkt_getattrs(pktin);
653 return 1;
654 } else {
655 fxp_got_status(pktin);
656 return 0;
657 }
658}
659
660int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs)
661{
662 struct sftp_packet *pktin, *pktout;
663 int id;
664
665 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
666 sftp_pkt_adduint32(pktout, 0x678); /* request id */
9954aaa3 667 sftp_pkt_addstring_start(pktout);
d92624dc 668 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
669 sftp_send(pktout);
670 pktin = sftp_recv();
f65c4643 671 if (!pktin) {
672 fxp_internal_error("did not receive a valid SFTP packet\n");
673 return 0;
674 }
d92624dc 675 id = sftp_pkt_getuint32(pktin);
676 if (id != 0x678) {
677 fxp_internal_error("request ID mismatch\n");
678 return 0;
679 }
680
681 if (pktin->type == SSH_FXP_ATTRS) {
682 *attrs = sftp_pkt_getattrs(pktin);
683 return 1;
684 } else {
685 fxp_got_status(pktin);
686 return 0;
687 }
688}
689
690/*
691 * Set the attributes of a file.
692 */
693int fxp_setstat(char *fname, struct fxp_attrs attrs)
694{
695 struct sftp_packet *pktin, *pktout;
696 int id;
697
698 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
699 sftp_pkt_adduint32(pktout, 0x678); /* request id */
700 sftp_pkt_addstring(pktout, fname);
701 sftp_pkt_addattrs(pktout, attrs);
9954aaa3 702 sftp_send(pktout);
703 pktin = sftp_recv();
f65c4643 704 if (!pktin) {
705 fxp_internal_error("did not receive a valid SFTP packet\n");
706 return 0;
707 }
9954aaa3 708 id = sftp_pkt_getuint32(pktin);
709 if (id != 0x678) {
710 fxp_internal_error("request ID mismatch\n");
711 return 0;
712 }
713 id = fxp_got_status(pktin);
714 if (id != 1) {
715 return 0;
716 }
717 return 1;
718}
fd5e5847 719int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs)
720{
721 struct sftp_packet *pktin, *pktout;
722 int id;
723
724 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
725 sftp_pkt_adduint32(pktout, 0x678); /* request id */
726 sftp_pkt_addstring_start(pktout);
727 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
728 sftp_pkt_addattrs(pktout, attrs);
729 sftp_send(pktout);
730 pktin = sftp_recv();
f65c4643 731 if (!pktin) {
732 fxp_internal_error("did not receive a valid SFTP packet\n");
733 return 0;
734 }
fd5e5847 735 id = sftp_pkt_getuint32(pktin);
736 if (id != 0x678) {
737 fxp_internal_error("request ID mismatch\n");
738 return 0;
739 }
740 id = fxp_got_status(pktin);
741 if (id != 1) {
742 return 0;
743 }
744 return 1;
745}
9954aaa3 746
4c7f0d61 747/*
748 * Read from a file. Returns the number of bytes read, or -1 on an
749 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
750 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
751 * error indicator. It might even depend on the SFTP server.)
752 */
32874aea 753int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
754 int len)
755{
4c7f0d61 756 struct sftp_packet *pktin, *pktout;
757 int id;
758
759 pktout = sftp_pkt_init(SSH_FXP_READ);
760 sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
f9e162aa 761 sftp_pkt_addstring_start(pktout);
762 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 763 sftp_pkt_adduint64(pktout, offset);
764 sftp_pkt_adduint32(pktout, len);
765 sftp_send(pktout);
766 pktin = sftp_recv();
f65c4643 767 if (!pktin) {
768 fxp_internal_error("did not receive a valid SFTP packet\n");
769 return -1;
770 }
4c7f0d61 771 id = sftp_pkt_getuint32(pktin);
772 if (id != 0xBCD) {
773 fxp_internal_error("request ID mismatch");
4a8fc3c4 774 return -1;
4c7f0d61 775 }
776 if (pktin->type == SSH_FXP_DATA) {
777 char *str;
778 int rlen;
779
780 sftp_pkt_getstring(pktin, &str, &rlen);
781
782 if (rlen > len || rlen < 0) {
783 fxp_internal_error("READ returned more bytes than requested");
784 return -1;
785 }
786
787 memcpy(buffer, str, rlen);
788 sfree(pktin);
789 return rlen;
790 } else {
791 fxp_got_status(pktin);
792 return -1;
793 }
794}
795
796/*
797 * Read from a directory.
798 */
32874aea 799struct fxp_names *fxp_readdir(struct fxp_handle *handle)
800{
4c7f0d61 801 struct sftp_packet *pktin, *pktout;
802 int id;
803
804 pktout = sftp_pkt_init(SSH_FXP_READDIR);
805 sftp_pkt_adduint32(pktout, 0xABC); /* request id */
f9e162aa 806 sftp_pkt_addstring_start(pktout);
807 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 808 sftp_send(pktout);
809 pktin = sftp_recv();
f65c4643 810 if (!pktin) {
811 fxp_internal_error("did not receive a valid SFTP packet\n");
812 return NULL;
813 }
4c7f0d61 814 id = sftp_pkt_getuint32(pktin);
815 if (id != 0xABC) {
816 fxp_internal_error("request ID mismatch\n");
4a8fc3c4 817 return NULL;
4c7f0d61 818 }
819 if (pktin->type == SSH_FXP_NAME) {
820 struct fxp_names *ret;
821 int i;
822 ret = smalloc(sizeof(struct fxp_names));
823 ret->nnames = sftp_pkt_getuint32(pktin);
824 ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
825 for (i = 0; i < ret->nnames; i++) {
826 char *str;
827 int len;
828 sftp_pkt_getstring(pktin, &str, &len);
829 ret->names[i].filename = mkstr(str, len);
830 sftp_pkt_getstring(pktin, &str, &len);
831 ret->names[i].longname = mkstr(str, len);
832 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
833 }
834 return ret;
835 } else {
836 fxp_got_status(pktin);
837 return NULL;
838 }
839}
840
841/*
842 * Write to a file. Returns 0 on error, 1 on OK.
843 */
32874aea 844int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
845 int len)
846{
4c7f0d61 847 struct sftp_packet *pktin, *pktout;
848 int id;
849
850 pktout = sftp_pkt_init(SSH_FXP_WRITE);
851 sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
f9e162aa 852 sftp_pkt_addstring_start(pktout);
853 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 854 sftp_pkt_adduint64(pktout, offset);
855 sftp_pkt_addstring_start(pktout);
856 sftp_pkt_addstring_data(pktout, buffer, len);
857 sftp_send(pktout);
858 pktin = sftp_recv();
f65c4643 859 if (!pktin) {
860 fxp_internal_error("did not receive a valid SFTP packet\n");
861 return 0;
862 }
4c7f0d61 863 id = sftp_pkt_getuint32(pktin);
4a8fc3c4 864 if (id != 0xDCB) {
865 fxp_internal_error("request ID mismatch\n");
d8770b12 866 return 0;
4a8fc3c4 867 }
4c7f0d61 868 fxp_got_status(pktin);
869 return fxp_errtype == SSH_FX_OK;
870}
871
872/*
873 * Free up an fxp_names structure.
874 */
32874aea 875void fxp_free_names(struct fxp_names *names)
876{
4c7f0d61 877 int i;
878
879 for (i = 0; i < names->nnames; i++) {
880 sfree(names->names[i].filename);
881 sfree(names->names[i].longname);
882 }
883 sfree(names->names);
884 sfree(names);
885}