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