Run entire source base through GNU indent to tidy up the varying
[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 /* ----------------------------------------------------------------------
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
107 /* ----------------------------------------------------------------------
108 * SFTP packet decode functions.
109 */
110
111 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
112 {
113 unsigned char value;
114 if (pkt->length - pkt->savedpos < 1)
115 return 0; /* arrgh, no way to decline (FIXME?) */
116 value = (unsigned char) pkt->data[pkt->savedpos];
117 pkt->savedpos++;
118 return value;
119 }
120 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
121 {
122 unsigned long value;
123 if (pkt->length - pkt->savedpos < 4)
124 return 0; /* arrgh, no way to decline (FIXME?) */
125 value = GET_32BIT(pkt->data + pkt->savedpos);
126 pkt->savedpos += 4;
127 return value;
128 }
129 static void sftp_pkt_getstring(struct sftp_packet *pkt,
130 char **p, int *length)
131 {
132 *p = NULL;
133 if (pkt->length - pkt->savedpos < 4)
134 return;
135 *length = GET_32BIT(pkt->data + pkt->savedpos);
136 pkt->savedpos += 4;
137 if (pkt->length - pkt->savedpos < *length)
138 return;
139 *p = pkt->data + pkt->savedpos;
140 pkt->savedpos += *length;
141 }
142 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
143 {
144 struct fxp_attrs ret;
145 ret.flags = sftp_pkt_getuint32(pkt);
146 if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
147 unsigned long hi, lo;
148 hi = sftp_pkt_getuint32(pkt);
149 lo = sftp_pkt_getuint32(pkt);
150 ret.size = uint64_make(hi, lo);
151 }
152 if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
153 ret.uid = sftp_pkt_getuint32(pkt);
154 ret.gid = sftp_pkt_getuint32(pkt);
155 }
156 if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
157 ret.permissions = sftp_pkt_getuint32(pkt);
158 }
159 if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
160 ret.atime = sftp_pkt_getuint32(pkt);
161 ret.mtime = sftp_pkt_getuint32(pkt);
162 }
163 if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
164 int count;
165 count = sftp_pkt_getuint32(pkt);
166 while (count--) {
167 char *str;
168 int len;
169 /*
170 * We should try to analyse these, if we ever find one
171 * we recognise.
172 */
173 sftp_pkt_getstring(pkt, &str, &len);
174 sftp_pkt_getstring(pkt, &str, &len);
175 }
176 }
177 return ret;
178 }
179 static void sftp_pkt_free(struct sftp_packet *pkt)
180 {
181 if (pkt->data)
182 sfree(pkt->data);
183 sfree(pkt);
184 }
185
186 /* ----------------------------------------------------------------------
187 * Send and receive packet functions.
188 */
189 int sftp_send(struct sftp_packet *pkt)
190 {
191 int ret;
192 char x[4];
193 PUT_32BIT(x, pkt->length);
194 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
195 sftp_pkt_free(pkt);
196 return ret;
197 }
198 struct sftp_packet *sftp_recv(void)
199 {
200 struct sftp_packet *pkt;
201 char x[4];
202
203 if (!sftp_recvdata(x, 4))
204 return NULL;
205
206 pkt = smalloc(sizeof(struct sftp_packet));
207 pkt->savedpos = 0;
208 pkt->length = pkt->maxlen = GET_32BIT(x);
209 pkt->data = smalloc(pkt->length);
210
211 if (!sftp_recvdata(pkt->data, pkt->length)) {
212 sftp_pkt_free(pkt);
213 return NULL;
214 }
215
216 pkt->type = sftp_pkt_getbyte(pkt);
217
218 return pkt;
219 }
220
221 /* ----------------------------------------------------------------------
222 * String handling routines.
223 */
224
225 static char *mkstr(char *s, int len)
226 {
227 char *p = smalloc(len + 1);
228 memcpy(p, s, len);
229 p[len] = '\0';
230 return p;
231 }
232
233 /* ----------------------------------------------------------------------
234 * SFTP primitives.
235 */
236
237 static const char *fxp_error_message;
238 static int fxp_errtype;
239
240 /*
241 * Deal with (and free) an FXP_STATUS packet. Return 1 if
242 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
243 * Also place the status into fxp_errtype.
244 */
245 static int fxp_got_status(struct sftp_packet *pktin)
246 {
247 static const char *const messages[] = {
248 /* SSH_FX_OK. The only time we will display a _message_ for this
249 * is if we were expecting something other than FXP_STATUS on
250 * success, so this is actually an error message! */
251 "unexpected OK response",
252 "end of file",
253 "no such file or directory",
254 "permission denied",
255 "failure",
256 "bad message",
257 "no connection",
258 "connection lost",
259 "operation unsupported",
260 };
261
262 if (pktin->type != SSH_FXP_STATUS) {
263 fxp_error_message = "expected FXP_STATUS packet";
264 fxp_errtype = -1;
265 } else {
266 fxp_errtype = sftp_pkt_getuint32(pktin);
267 if (fxp_errtype < 0 ||
268 fxp_errtype >= sizeof(messages) / sizeof(*messages))
269 fxp_error_message = "unknown error code";
270 else
271 fxp_error_message = messages[fxp_errtype];
272 }
273
274 if (fxp_errtype == SSH_FX_OK)
275 return 1;
276 else if (fxp_errtype == SSH_FX_EOF)
277 return 0;
278 else
279 return -1;
280 }
281
282 static void fxp_internal_error(char *msg)
283 {
284 fxp_error_message = msg;
285 fxp_errtype = -1;
286 }
287
288 const char *fxp_error(void)
289 {
290 return fxp_error_message;
291 }
292
293 int fxp_error_type(void)
294 {
295 return fxp_errtype;
296 }
297
298 /*
299 * Perform exchange of init/version packets. Return 0 on failure.
300 */
301 int fxp_init(void)
302 {
303 struct sftp_packet *pktout, *pktin;
304 int remotever;
305
306 pktout = sftp_pkt_init(SSH_FXP_INIT);
307 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
308 sftp_send(pktout);
309
310 pktin = sftp_recv();
311 if (!pktin) {
312 fxp_internal_error("could not connect");
313 return 0;
314 }
315 if (pktin->type != SSH_FXP_VERSION) {
316 fxp_internal_error("did not receive FXP_VERSION");
317 return 0;
318 }
319 remotever = sftp_pkt_getuint32(pktin);
320 if (remotever > SFTP_PROTO_VERSION) {
321 fxp_internal_error
322 ("remote protocol is more advanced than we support");
323 return 0;
324 }
325 /*
326 * In principle, this packet might also contain extension-
327 * string pairs. We should work through them and look for any
328 * we recognise. In practice we don't currently do so because
329 * we know we don't recognise _any_.
330 */
331 sftp_pkt_free(pktin);
332
333 return 1;
334 }
335
336 /*
337 * Canonify a pathname.
338 */
339 char *fxp_realpath(char *path)
340 {
341 struct sftp_packet *pktin, *pktout;
342 int id;
343
344 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
345 sftp_pkt_adduint32(pktout, 0x123); /* request id */
346 sftp_pkt_addstring_start(pktout);
347 sftp_pkt_addstring_str(pktout, path);
348 sftp_send(pktout);
349 pktin = sftp_recv();
350 id = sftp_pkt_getuint32(pktin);
351 if (id != 0x123) {
352 fxp_internal_error("request ID mismatch\n");
353 return NULL;
354 }
355 if (pktin->type == SSH_FXP_NAME) {
356 int count;
357 char *path;
358 int len;
359
360 count = sftp_pkt_getuint32(pktin);
361 if (count != 1) {
362 fxp_internal_error("REALPATH returned name count != 1\n");
363 return NULL;
364 }
365 sftp_pkt_getstring(pktin, &path, &len);
366 if (!path) {
367 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
368 return NULL;
369 }
370 path = mkstr(path, len);
371 sftp_pkt_free(pktin);
372 return path;
373 } else {
374 fxp_got_status(pktin);
375 return NULL;
376 }
377 }
378
379 /*
380 * Open a file.
381 */
382 struct fxp_handle *fxp_open(char *path, int type)
383 {
384 struct sftp_packet *pktin, *pktout;
385 int id;
386
387 pktout = sftp_pkt_init(SSH_FXP_OPEN);
388 sftp_pkt_adduint32(pktout, 0x567); /* request id */
389 sftp_pkt_addstring(pktout, path);
390 sftp_pkt_adduint32(pktout, type);
391 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
392 sftp_send(pktout);
393 pktin = sftp_recv();
394 id = sftp_pkt_getuint32(pktin);
395 if (id != 0x567) {
396 fxp_internal_error("request ID mismatch\n");
397 return NULL;
398 }
399 if (pktin->type == SSH_FXP_HANDLE) {
400 char *hstring;
401 struct fxp_handle *handle;
402 int len;
403
404 sftp_pkt_getstring(pktin, &hstring, &len);
405 if (!hstring) {
406 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
407 return NULL;
408 }
409 handle = smalloc(sizeof(struct fxp_handle));
410 handle->hstring = mkstr(hstring, len);
411 handle->hlen = len;
412 sftp_pkt_free(pktin);
413 return handle;
414 } else {
415 fxp_got_status(pktin);
416 return NULL;
417 }
418 }
419
420 /*
421 * Open a directory.
422 */
423 struct fxp_handle *fxp_opendir(char *path)
424 {
425 struct sftp_packet *pktin, *pktout;
426 int id;
427
428 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
429 sftp_pkt_adduint32(pktout, 0x456); /* request id */
430 sftp_pkt_addstring(pktout, path);
431 sftp_send(pktout);
432 pktin = sftp_recv();
433 id = sftp_pkt_getuint32(pktin);
434 if (id != 0x456) {
435 fxp_internal_error("request ID mismatch\n");
436 return NULL;
437 }
438 if (pktin->type == SSH_FXP_HANDLE) {
439 char *hstring;
440 struct fxp_handle *handle;
441 int len;
442
443 sftp_pkt_getstring(pktin, &hstring, &len);
444 if (!hstring) {
445 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
446 return NULL;
447 }
448 handle = smalloc(sizeof(struct fxp_handle));
449 handle->hstring = mkstr(hstring, len);
450 handle->hlen = len;
451 sftp_pkt_free(pktin);
452 return handle;
453 } else {
454 fxp_got_status(pktin);
455 return NULL;
456 }
457 }
458
459 /*
460 * Close a file/dir.
461 */
462 void fxp_close(struct fxp_handle *handle)
463 {
464 struct sftp_packet *pktin, *pktout;
465 int id;
466
467 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
468 sftp_pkt_adduint32(pktout, 0x789); /* request id */
469 sftp_pkt_addstring_start(pktout);
470 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
471 sftp_send(pktout);
472 pktin = sftp_recv();
473 id = sftp_pkt_getuint32(pktin);
474 if (id != 0x789) {
475 fxp_internal_error("request ID mismatch\n");
476 return;
477 }
478 fxp_got_status(pktin);
479 sfree(handle->hstring);
480 sfree(handle);
481 }
482
483 /*
484 * Read from a file. Returns the number of bytes read, or -1 on an
485 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
486 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
487 * error indicator. It might even depend on the SFTP server.)
488 */
489 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
490 int len)
491 {
492 struct sftp_packet *pktin, *pktout;
493 int id;
494
495 pktout = sftp_pkt_init(SSH_FXP_READ);
496 sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
497 sftp_pkt_addstring_start(pktout);
498 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
499 sftp_pkt_adduint64(pktout, offset);
500 sftp_pkt_adduint32(pktout, len);
501 sftp_send(pktout);
502 pktin = sftp_recv();
503 id = sftp_pkt_getuint32(pktin);
504 if (id != 0xBCD) {
505 fxp_internal_error("request ID mismatch");
506 return -1;
507 }
508 if (pktin->type == SSH_FXP_DATA) {
509 char *str;
510 int rlen;
511
512 sftp_pkt_getstring(pktin, &str, &rlen);
513
514 if (rlen > len || rlen < 0) {
515 fxp_internal_error("READ returned more bytes than requested");
516 return -1;
517 }
518
519 memcpy(buffer, str, rlen);
520 sfree(pktin);
521 return rlen;
522 } else {
523 fxp_got_status(pktin);
524 return -1;
525 }
526 }
527
528 /*
529 * Read from a directory.
530 */
531 struct fxp_names *fxp_readdir(struct fxp_handle *handle)
532 {
533 struct sftp_packet *pktin, *pktout;
534 int id;
535
536 pktout = sftp_pkt_init(SSH_FXP_READDIR);
537 sftp_pkt_adduint32(pktout, 0xABC); /* request id */
538 sftp_pkt_addstring_start(pktout);
539 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
540 sftp_send(pktout);
541 pktin = sftp_recv();
542 id = sftp_pkt_getuint32(pktin);
543 if (id != 0xABC) {
544 fxp_internal_error("request ID mismatch\n");
545 return NULL;
546 }
547 if (pktin->type == SSH_FXP_NAME) {
548 struct fxp_names *ret;
549 int i;
550 ret = smalloc(sizeof(struct fxp_names));
551 ret->nnames = sftp_pkt_getuint32(pktin);
552 ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
553 for (i = 0; i < ret->nnames; i++) {
554 char *str;
555 int len;
556 sftp_pkt_getstring(pktin, &str, &len);
557 ret->names[i].filename = mkstr(str, len);
558 sftp_pkt_getstring(pktin, &str, &len);
559 ret->names[i].longname = mkstr(str, len);
560 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
561 }
562 return ret;
563 } else {
564 fxp_got_status(pktin);
565 return NULL;
566 }
567 }
568
569 /*
570 * Write to a file. Returns 0 on error, 1 on OK.
571 */
572 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
573 int len)
574 {
575 struct sftp_packet *pktin, *pktout;
576 int id;
577
578 pktout = sftp_pkt_init(SSH_FXP_WRITE);
579 sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
580 sftp_pkt_addstring_start(pktout);
581 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
582 sftp_pkt_adduint64(pktout, offset);
583 sftp_pkt_addstring_start(pktout);
584 sftp_pkt_addstring_data(pktout, buffer, len);
585 sftp_send(pktout);
586 pktin = sftp_recv();
587 id = sftp_pkt_getuint32(pktin);
588 if (id != 0xDCB) {
589 fxp_internal_error("request ID mismatch\n");
590 return 0;
591 }
592 fxp_got_status(pktin);
593 return fxp_errtype == SSH_FX_OK;
594 }
595
596 /*
597 * Free up an fxp_names structure.
598 */
599 void fxp_free_names(struct fxp_names *names)
600 {
601 int i;
602
603 for (i = 0; i < names->nnames; i++) {
604 sfree(names->names[i].filename);
605 sfree(names->names[i].longname);
606 }
607 sfree(names->names);
608 sfree(names);
609 }