Remove ghastly hack involving fxp_error_message.
[u/mdw/putty] / sftp.c
1 /*
2 * sftp.c: SFTP generic client code.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9
10 #include "misc.h"
11 #include "int64.h"
12 #include "sftp.h"
13
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
26 struct sftp_packet {
27 char *data;
28 int length, maxlen;
29 int savedpos;
30 int type;
31 };
32
33 static const char *fxp_error_message;
34 static int fxp_errtype;
35
36 /* ----------------------------------------------------------------------
37 * SFTP packet construction functions.
38 */
39 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
40 {
41 if (pkt->maxlen < length) {
42 pkt->maxlen = length + 256;
43 pkt->data = srealloc(pkt->data, pkt->maxlen);
44 }
45 }
46 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
47 {
48 pkt->length += len;
49 sftp_pkt_ensure(pkt, pkt->length);
50 memcpy(pkt->data + pkt->length - len, data, len);
51 }
52 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
53 {
54 sftp_pkt_adddata(pkt, &byte, 1);
55 }
56 static struct sftp_packet *sftp_pkt_init(int pkt_type)
57 {
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;
64 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
65 return pkt;
66 }
67 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
68 {
69 sftp_pkt_adddata(pkt, &value, 1);
70 }
71 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
72 unsigned long value)
73 {
74 unsigned char x[4];
75 PUT_32BIT(x, value);
76 sftp_pkt_adddata(pkt, x, 4);
77 }
78 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
79 {
80 unsigned char x[8];
81 PUT_32BIT(x, value.hi);
82 PUT_32BIT(x + 4, value.lo);
83 sftp_pkt_adddata(pkt, x, 8);
84 }
85 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
86 {
87 sftp_pkt_adduint32(pkt, 0);
88 pkt->savedpos = pkt->length;
89 }
90 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
91 {
92 sftp_pkt_adddata(pkt, data, strlen(data));
93 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
94 }
95 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
96 char *data, int len)
97 {
98 sftp_pkt_adddata(pkt, data, len);
99 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
100 }
101 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
102 {
103 sftp_pkt_addstring_start(pkt);
104 sftp_pkt_addstring_str(pkt, data);
105 }
106 static 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 }
131
132 /* ----------------------------------------------------------------------
133 * SFTP packet decode functions.
134 */
135
136 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
137 {
138 unsigned char value;
139 if (pkt->length - pkt->savedpos < 1)
140 return 0; /* arrgh, no way to decline (FIXME?) */
141 value = (unsigned char) pkt->data[pkt->savedpos];
142 pkt->savedpos++;
143 return value;
144 }
145 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
146 {
147 unsigned long value;
148 if (pkt->length - pkt->savedpos < 4)
149 return 0; /* arrgh, no way to decline (FIXME?) */
150 value = GET_32BIT(pkt->data + pkt->savedpos);
151 pkt->savedpos += 4;
152 return value;
153 }
154 static void sftp_pkt_getstring(struct sftp_packet *pkt,
155 char **p, int *length)
156 {
157 *p = NULL;
158 if (pkt->length - pkt->savedpos < 4)
159 return;
160 *length = GET_32BIT(pkt->data + pkt->savedpos);
161 pkt->savedpos += 4;
162 if (pkt->length - pkt->savedpos < *length)
163 return;
164 *p = pkt->data + pkt->savedpos;
165 pkt->savedpos += *length;
166 }
167 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
168 {
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 }
204 static void sftp_pkt_free(struct sftp_packet *pkt)
205 {
206 if (pkt->data)
207 sfree(pkt->data);
208 sfree(pkt);
209 }
210
211 /* ----------------------------------------------------------------------
212 * Send and receive packet functions.
213 */
214 int sftp_send(struct sftp_packet *pkt)
215 {
216 int ret;
217 char x[4];
218 PUT_32BIT(x, pkt->length);
219 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
220 sftp_pkt_free(pkt);
221 return ret;
222 }
223 struct sftp_packet *sftp_recv(void)
224 {
225 struct sftp_packet *pkt;
226 char x[4];
227
228 if (!sftp_recvdata(x, 4))
229 return NULL;
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
236 if (!sftp_recvdata(pkt->data, pkt->length)) {
237 sftp_pkt_free(pkt);
238 return NULL;
239 }
240
241 pkt->type = sftp_pkt_getbyte(pkt);
242
243 return pkt;
244 }
245
246 /* ----------------------------------------------------------------------
247 * String handling routines.
248 */
249
250 static char *mkstr(char *s, int len)
251 {
252 char *p = smalloc(len + 1);
253 memcpy(p, s, len);
254 p[len] = '\0';
255 return p;
256 }
257
258 /* ----------------------------------------------------------------------
259 * SFTP primitives.
260 */
261
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 */
267 static int fxp_got_status(struct sftp_packet *pktin)
268 {
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 ||
290 fxp_errtype >= sizeof(messages) / sizeof(*messages))
291 fxp_error_message = "unknown error code";
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
304 static void fxp_internal_error(char *msg)
305 {
306 fxp_error_message = msg;
307 fxp_errtype = -1;
308 }
309
310 const char *fxp_error(void)
311 {
312 return fxp_error_message;
313 }
314
315 int fxp_error_type(void)
316 {
317 return fxp_errtype;
318 }
319
320 /*
321 * Perform exchange of init/version packets. Return 0 on failure.
322 */
323 int fxp_init(void)
324 {
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();
333 if (!pktin) {
334 fxp_internal_error("could not connect");
335 return 0;
336 }
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) {
343 fxp_internal_error
344 ("remote protocol is more advanced than we support");
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 /*
359 * Canonify a pathname.
360 */
361 char *fxp_realpath(char *path)
362 {
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);
370 sftp_send(pktout);
371 pktin = sftp_recv();
372 if (!pktin) {
373 fxp_internal_error("did not receive a valid SFTP packet\n");
374 return NULL;
375 }
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 */
408 struct fxp_handle *fxp_open(char *path, int type)
409 {
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();
420 if (!pktin) {
421 fxp_internal_error("did not receive a valid SFTP packet\n");
422 return NULL;
423 }
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) {
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);
441 handle->hlen = len;
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 */
453 struct fxp_handle *fxp_opendir(char *path)
454 {
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();
463 if (!pktin) {
464 fxp_internal_error("did not receive a valid SFTP packet\n");
465 return NULL;
466 }
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) {
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);
484 handle->hlen = len;
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 */
496 void fxp_close(struct fxp_handle *handle)
497 {
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 */
503 sftp_pkt_addstring_start(pktout);
504 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
505 sftp_send(pktout);
506 pktin = sftp_recv();
507 if (!pktin) {
508 fxp_internal_error("did not receive a valid SFTP packet\n");
509 return;
510 }
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
521 int 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 */
528 sftp_pkt_addstring(pktout, path);
529 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
530 sftp_send(pktout);
531 pktin = sftp_recv();
532 if (!pktin) {
533 fxp_internal_error("did not receive a valid SFTP packet\n");
534 return 0;
535 }
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
548 int 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 */
555 sftp_pkt_addstring(pktout, path);
556 sftp_send(pktout);
557 pktin = sftp_recv();
558 if (!pktin) {
559 fxp_internal_error("did not receive a valid SFTP packet\n");
560 return 0;
561 }
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
574 int fxp_remove(char *fname)
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 */
581 sftp_pkt_addstring(pktout, fname);
582 sftp_send(pktout);
583 pktin = sftp_recv();
584 if (!pktin) {
585 fxp_internal_error("did not receive a valid SFTP packet\n");
586 return 0;
587 }
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
600 int 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();
611 if (!pktin) {
612 fxp_internal_error("did not receive a valid SFTP packet\n");
613 return 0;
614 }
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 */
631 int 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();
641 if (!pktin) {
642 fxp_internal_error("did not receive a valid SFTP packet\n");
643 return 0;
644 }
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
660 int 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 */
667 sftp_pkt_addstring_start(pktout);
668 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
669 sftp_send(pktout);
670 pktin = sftp_recv();
671 if (!pktin) {
672 fxp_internal_error("did not receive a valid SFTP packet\n");
673 return 0;
674 }
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 */
693 int 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);
702 sftp_send(pktout);
703 pktin = sftp_recv();
704 if (!pktin) {
705 fxp_internal_error("did not receive a valid SFTP packet\n");
706 return 0;
707 }
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 }
719 int 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();
731 if (!pktin) {
732 fxp_internal_error("did not receive a valid SFTP packet\n");
733 return 0;
734 }
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 }
746
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 */
753 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
754 int len)
755 {
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 */
761 sftp_pkt_addstring_start(pktout);
762 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
763 sftp_pkt_adduint64(pktout, offset);
764 sftp_pkt_adduint32(pktout, len);
765 sftp_send(pktout);
766 pktin = sftp_recv();
767 if (!pktin) {
768 fxp_internal_error("did not receive a valid SFTP packet\n");
769 return -1;
770 }
771 id = sftp_pkt_getuint32(pktin);
772 if (id != 0xBCD) {
773 fxp_internal_error("request ID mismatch");
774 return -1;
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 */
799 struct fxp_names *fxp_readdir(struct fxp_handle *handle)
800 {
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 */
806 sftp_pkt_addstring_start(pktout);
807 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
808 sftp_send(pktout);
809 pktin = sftp_recv();
810 if (!pktin) {
811 fxp_internal_error("did not receive a valid SFTP packet\n");
812 return NULL;
813 }
814 id = sftp_pkt_getuint32(pktin);
815 if (id != 0xABC) {
816 fxp_internal_error("request ID mismatch\n");
817 return NULL;
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 */
844 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
845 int len)
846 {
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 */
852 sftp_pkt_addstring_start(pktout);
853 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
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();
859 if (!pktin) {
860 fxp_internal_error("did not receive a valid SFTP packet\n");
861 return 0;
862 }
863 id = sftp_pkt_getuint32(pktin);
864 if (id != 0xDCB) {
865 fxp_internal_error("request ID mismatch\n");
866 return 0;
867 }
868 fxp_got_status(pktin);
869 return fxp_errtype == SSH_FX_OK;
870 }
871
872 /*
873 * Free up an fxp_names structure.
874 */
875 void fxp_free_names(struct fxp_names *names)
876 {
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 }