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