xref: /titanic_44/usr/src/cmd/ssh/sftp/sftp-client.c (revision 18c2aff776a775d34a4c9893a4c72e0434d68e36)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 /*
6  * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /* XXX: memleaks */
30 /* XXX: signed vs unsigned */
31 /* XXX: remove all logging, only return status codes */
32 /* XXX: copy between two remote sites */
33 
34 #include "includes.h"
35 RCSID("$OpenBSD: sftp-client.c,v 1.35 2002/09/11 22:41:49 djm Exp $");
36 
37 #pragma ident	"%Z%%M%	%I%	%E% SMI"
38 
39 #include "sys-queue.h"
40 
41 #include "buffer.h"
42 #include "bufaux.h"
43 #include "getput.h"
44 #include "xmalloc.h"
45 #include "log.h"
46 #include "atomicio.h"
47 
48 #include "sftp.h"
49 #include "sftp-common.h"
50 #include "sftp-client.h"
51 
52 /* Minimum amount of data to read at at time */
53 #define MIN_READ_SIZE	512
54 
55 struct sftp_conn {
56 	int fd_in;
57 	int fd_out;
58 	u_int transfer_buflen;
59 	u_int num_requests;
60 	u_int version;
61 	u_int msg_id;
62 };
63 
64 static void
65 send_msg(int fd, Buffer *m)
66 {
67 	int mlen = buffer_len(m);
68 	int len;
69 	Buffer oqueue;
70 
71 	buffer_init(&oqueue);
72 	buffer_put_int(&oqueue, mlen);
73 	buffer_append(&oqueue, buffer_ptr(m), mlen);
74 	buffer_consume(m, mlen);
75 
76 	len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
77 	if (len <= 0)
78 		fatal("Couldn't send packet: %s", strerror(errno));
79 
80 	buffer_free(&oqueue);
81 }
82 
83 static void
84 get_msg(int fd, Buffer *m)
85 {
86 	u_int len, msg_len;
87 	unsigned char buf[4096];
88 
89 	len = atomicio(read, fd, buf, 4);
90 	if (len == 0)
91 		fatal("Connection closed");
92 	else if (len == (u_int) -1)
93 		fatal("Couldn't read packet: %s", strerror(errno));
94 
95 	msg_len = GET_32BIT(buf);
96 	if (msg_len > 256 * 1024)
97 		fatal("Received message too long %u", msg_len);
98 
99 	while (msg_len) {
100 		len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
101 		if (len == 0)
102 			fatal("Connection closed");
103 		else if (len == (u_int) -1)
104 			fatal("Couldn't read packet: %s", strerror(errno));
105 
106 		msg_len -= len;
107 		buffer_append(m, buf, len);
108 	}
109 }
110 
111 static void
112 send_string_request(int fd, u_int id, u_int code, char *s,
113     u_int len)
114 {
115 	Buffer msg;
116 
117 	buffer_init(&msg);
118 	buffer_put_char(&msg, code);
119 	buffer_put_int(&msg, id);
120 	buffer_put_string(&msg, s, len);
121 	send_msg(fd, &msg);
122 	debug3("Sent message fd %d T:%u I:%u", fd, code, id);
123 	buffer_free(&msg);
124 }
125 
126 static void
127 send_string_attrs_request(int fd, u_int id, u_int code, char *s,
128     u_int len, Attrib *a)
129 {
130 	Buffer msg;
131 
132 	buffer_init(&msg);
133 	buffer_put_char(&msg, code);
134 	buffer_put_int(&msg, id);
135 	buffer_put_string(&msg, s, len);
136 	encode_attrib(&msg, a);
137 	send_msg(fd, &msg);
138 	debug3("Sent message fd %d T:%u I:%u", fd, code, id);
139 	buffer_free(&msg);
140 }
141 
142 static u_int
143 get_status(int fd, u_int expected_id)
144 {
145 	Buffer msg;
146 	u_int type, id, status;
147 
148 	buffer_init(&msg);
149 	get_msg(fd, &msg);
150 	type = buffer_get_char(&msg);
151 	id = buffer_get_int(&msg);
152 
153 	if (id != expected_id)
154 		fatal("ID mismatch (%u != %u)", id, expected_id);
155 	if (type != SSH2_FXP_STATUS)
156 		fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
157 		    SSH2_FXP_STATUS, type);
158 
159 	status = buffer_get_int(&msg);
160 	buffer_free(&msg);
161 
162 	debug3("SSH2_FXP_STATUS %u", status);
163 
164 	return(status);
165 }
166 
167 static char *
168 get_handle(int fd, u_int expected_id, u_int *len)
169 {
170 	Buffer msg;
171 	u_int type, id;
172 	char *handle;
173 
174 	buffer_init(&msg);
175 	get_msg(fd, &msg);
176 	type = buffer_get_char(&msg);
177 	id = buffer_get_int(&msg);
178 
179 	if (id != expected_id)
180 		fatal("ID mismatch (%u != %u)", id, expected_id);
181 	if (type == SSH2_FXP_STATUS) {
182 		int status = buffer_get_int(&msg);
183 
184 		error("Couldn't get handle: %s", fx2txt(status));
185 		return(NULL);
186 	} else if (type != SSH2_FXP_HANDLE)
187 		fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
188 		    SSH2_FXP_HANDLE, type);
189 
190 	handle = buffer_get_string(&msg, len);
191 	buffer_free(&msg);
192 
193 	return(handle);
194 }
195 
196 static Attrib *
197 get_decode_stat(int fd, u_int expected_id, int quiet)
198 {
199 	Buffer msg;
200 	u_int type, id;
201 	Attrib *a;
202 
203 	buffer_init(&msg);
204 	get_msg(fd, &msg);
205 
206 	type = buffer_get_char(&msg);
207 	id = buffer_get_int(&msg);
208 
209 	debug3("Received stat reply T:%u I:%u", type, id);
210 	if (id != expected_id)
211 		fatal("ID mismatch (%u != %u)", id, expected_id);
212 	if (type == SSH2_FXP_STATUS) {
213 		int status = buffer_get_int(&msg);
214 
215 		if (quiet)
216 			debug("Couldn't stat remote file: %s", fx2txt(status));
217 		else
218 			error("Couldn't stat remote file: %s", fx2txt(status));
219 		return(NULL);
220 	} else if (type != SSH2_FXP_ATTRS) {
221 		fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
222 		    SSH2_FXP_ATTRS, type);
223 	}
224 	a = decode_attrib(&msg);
225 	buffer_free(&msg);
226 
227 	return(a);
228 }
229 
230 struct sftp_conn *
231 do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
232 {
233 	u_int type;
234 	int version;
235 	Buffer msg;
236 	struct sftp_conn *ret;
237 
238 	buffer_init(&msg);
239 	buffer_put_char(&msg, SSH2_FXP_INIT);
240 	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
241 	send_msg(fd_out, &msg);
242 
243 	buffer_clear(&msg);
244 
245 	get_msg(fd_in, &msg);
246 
247 	/* Expecting a VERSION reply */
248 	if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
249 		error("Invalid packet back from SSH2_FXP_INIT (type %u)",
250 		    type);
251 		buffer_free(&msg);
252 		return(NULL);
253 	}
254 	version = buffer_get_int(&msg);
255 
256 	debug2("Remote version: %d", version);
257 
258 	/* Check for extensions */
259 	while (buffer_len(&msg) > 0) {
260 		char *name = buffer_get_string(&msg, NULL);
261 		char *value = buffer_get_string(&msg, NULL);
262 
263 		debug2("Init extension: \"%s\"", name);
264 		xfree(name);
265 		xfree(value);
266 	}
267 
268 	buffer_free(&msg);
269 
270 	ret = xmalloc(sizeof(*ret));
271 	ret->fd_in = fd_in;
272 	ret->fd_out = fd_out;
273 	ret->transfer_buflen = transfer_buflen;
274 	ret->num_requests = num_requests;
275 	ret->version = version;
276 	ret->msg_id = 1;
277 
278 	/* Some filexfer v.0 servers don't support large packets */
279 	if (version == 0)
280 		ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
281 
282 	return(ret);
283 }
284 
285 u_int
286 sftp_proto_version(struct sftp_conn *conn)
287 {
288 	return(conn->version);
289 }
290 
291 int
292 do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
293 {
294 	u_int id, status;
295 	Buffer msg;
296 
297 	buffer_init(&msg);
298 
299 	id = conn->msg_id++;
300 	buffer_put_char(&msg, SSH2_FXP_CLOSE);
301 	buffer_put_int(&msg, id);
302 	buffer_put_string(&msg, handle, handle_len);
303 	send_msg(conn->fd_out, &msg);
304 	debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
305 
306 	status = get_status(conn->fd_in, id);
307 	if (status != SSH2_FX_OK)
308 		error("Couldn't close file: %s", fx2txt(status));
309 
310 	buffer_free(&msg);
311 
312 	return(status);
313 }
314 
315 
316 static int
317 do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
318     SFTP_DIRENT ***dir)
319 {
320 	Buffer msg;
321 	u_int type, id, handle_len, i, expected_id, ents = 0;
322 	char *handle;
323 
324 	id = conn->msg_id++;
325 
326 	buffer_init(&msg);
327 	buffer_put_char(&msg, SSH2_FXP_OPENDIR);
328 	buffer_put_int(&msg, id);
329 	buffer_put_cstring(&msg, path);
330 	send_msg(conn->fd_out, &msg);
331 
332 	buffer_clear(&msg);
333 
334 	handle = get_handle(conn->fd_in, id, &handle_len);
335 	if (handle == NULL)
336 		return(-1);
337 
338 	if (dir) {
339 		ents = 0;
340 		*dir = xmalloc(sizeof(**dir));
341 		(*dir)[0] = NULL;
342 	}
343 
344 	for (;;) {
345 		int count;
346 
347 		id = expected_id = conn->msg_id++;
348 
349 		debug3("Sending SSH2_FXP_READDIR I:%u", id);
350 
351 		buffer_clear(&msg);
352 		buffer_put_char(&msg, SSH2_FXP_READDIR);
353 		buffer_put_int(&msg, id);
354 		buffer_put_string(&msg, handle, handle_len);
355 		send_msg(conn->fd_out, &msg);
356 
357 		buffer_clear(&msg);
358 
359 		get_msg(conn->fd_in, &msg);
360 
361 		type = buffer_get_char(&msg);
362 		id = buffer_get_int(&msg);
363 
364 		debug3("Received reply T:%u I:%u", type, id);
365 
366 		if (id != expected_id)
367 			fatal("ID mismatch (%u != %u)", id, expected_id);
368 
369 		if (type == SSH2_FXP_STATUS) {
370 			int status = buffer_get_int(&msg);
371 
372 			debug3("Received SSH2_FXP_STATUS %d", status);
373 
374 			if (status == SSH2_FX_EOF) {
375 				break;
376 			} else {
377 				error("Couldn't read directory: %s",
378 				    fx2txt(status));
379 				do_close(conn, handle, handle_len);
380 				return(status);
381 			}
382 		} else if (type != SSH2_FXP_NAME)
383 			fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
384 			    SSH2_FXP_NAME, type);
385 
386 		count = buffer_get_int(&msg);
387 		if (count == 0)
388 			break;
389 		debug3("Received %d SSH2_FXP_NAME responses", count);
390 		for (i = 0; i < count; i++) {
391 			char *filename, *longname;
392 			Attrib *a;
393 
394 			filename = buffer_get_string(&msg, NULL);
395 			longname = buffer_get_string(&msg, NULL);
396 			a = decode_attrib(&msg);
397 
398 			if (printflag)
399 				printf("%s\n", longname);
400 
401 			if (dir) {
402 				*dir = xrealloc(*dir, sizeof(**dir) *
403 				    (ents + 2));
404 				(*dir)[ents] = xmalloc(sizeof(***dir));
405 				(*dir)[ents]->filename = xstrdup(filename);
406 				(*dir)[ents]->longname = xstrdup(longname);
407 				memcpy(&(*dir)[ents]->a, a, sizeof(*a));
408 				(*dir)[++ents] = NULL;
409 			}
410 
411 			xfree(filename);
412 			xfree(longname);
413 		}
414 	}
415 
416 	buffer_free(&msg);
417 	do_close(conn, handle, handle_len);
418 	xfree(handle);
419 
420 	return(0);
421 }
422 
423 int
424 do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
425 {
426 	return(do_lsreaddir(conn, path, 0, dir));
427 }
428 
429 void free_sftp_dirents(SFTP_DIRENT **s)
430 {
431 	int i;
432 
433 	for (i = 0; s[i]; i++) {
434 		xfree(s[i]->filename);
435 		xfree(s[i]->longname);
436 		xfree(s[i]);
437 	}
438 	xfree(s);
439 }
440 
441 int
442 do_rm(struct sftp_conn *conn, char *path)
443 {
444 	u_int status, id;
445 
446 	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
447 
448 	id = conn->msg_id++;
449 	send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
450 	    strlen(path));
451 	status = get_status(conn->fd_in, id);
452 	if (status != SSH2_FX_OK)
453 		error("Couldn't delete file: %s", fx2txt(status));
454 	return(status);
455 }
456 
457 int
458 do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
459 {
460 	u_int status, id;
461 
462 	id = conn->msg_id++;
463 	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
464 	    strlen(path), a);
465 
466 	status = get_status(conn->fd_in, id);
467 	if (status != SSH2_FX_OK)
468 		error("Couldn't create directory: %s", fx2txt(status));
469 
470 	return(status);
471 }
472 
473 int
474 do_rmdir(struct sftp_conn *conn, char *path)
475 {
476 	u_int status, id;
477 
478 	id = conn->msg_id++;
479 	send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
480 	    strlen(path));
481 
482 	status = get_status(conn->fd_in, id);
483 	if (status != SSH2_FX_OK)
484 		error("Couldn't remove directory: %s", fx2txt(status));
485 
486 	return(status);
487 }
488 
489 Attrib *
490 do_stat(struct sftp_conn *conn, char *path, int quiet)
491 {
492 	u_int id;
493 
494 	id = conn->msg_id++;
495 
496 	send_string_request(conn->fd_out, id,
497 	    conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
498 	    path, strlen(path));
499 
500 	return(get_decode_stat(conn->fd_in, id, quiet));
501 }
502 
503 Attrib *
504 do_lstat(struct sftp_conn *conn, char *path, int quiet)
505 {
506 	u_int id;
507 
508 	if (conn->version == 0) {
509 		if (quiet)
510 			debug("Server version does not support lstat operation");
511 		else
512 			log("Server version does not support lstat operation");
513 		return(do_stat(conn, path, quiet));
514 	}
515 
516 	id = conn->msg_id++;
517 	send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
518 	    strlen(path));
519 
520 	return(get_decode_stat(conn->fd_in, id, quiet));
521 }
522 
523 #if 0
524 Attrib *
525 do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
526 {
527 	u_int id;
528 
529 	id = conn->msg_id++;
530 	send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
531 	    handle_len);
532 
533 	return(get_decode_stat(conn->fd_in, id, quiet));
534 }
535 #endif
536 
537 int
538 do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
539 {
540 	u_int status, id;
541 
542 	id = conn->msg_id++;
543 	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
544 	    strlen(path), a);
545 
546 	status = get_status(conn->fd_in, id);
547 	if (status != SSH2_FX_OK)
548 		error("Couldn't setstat on \"%s\": %s", path,
549 		    fx2txt(status));
550 
551 	return(status);
552 }
553 
554 int
555 do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
556     Attrib *a)
557 {
558 	u_int status, id;
559 
560 	id = conn->msg_id++;
561 	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
562 	    handle_len, a);
563 
564 	status = get_status(conn->fd_in, id);
565 	if (status != SSH2_FX_OK)
566 		error("Couldn't fsetstat: %s", fx2txt(status));
567 
568 	return(status);
569 }
570 
571 char *
572 do_realpath(struct sftp_conn *conn, char *path)
573 {
574 	Buffer msg;
575 	u_int type, expected_id, count, id;
576 	char *filename, *longname;
577 
578 	expected_id = id = conn->msg_id++;
579 	send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
580 	    strlen(path));
581 
582 	buffer_init(&msg);
583 
584 	get_msg(conn->fd_in, &msg);
585 	type = buffer_get_char(&msg);
586 	id = buffer_get_int(&msg);
587 
588 	if (id != expected_id)
589 		fatal("ID mismatch (%u != %u)", id, expected_id);
590 
591 	if (type == SSH2_FXP_STATUS) {
592 		u_int status = buffer_get_int(&msg);
593 
594 		error("Couldn't canonicalise: %s", fx2txt(status));
595 		return(NULL);
596 	} else if (type != SSH2_FXP_NAME)
597 		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
598 		    SSH2_FXP_NAME, type);
599 
600 	count = buffer_get_int(&msg);
601 	if (count != 1)
602 		fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
603 
604 	filename = buffer_get_string(&msg, NULL);
605 	longname = buffer_get_string(&msg, NULL);
606 	(void) decode_attrib(&msg);
607 
608 	debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
609 
610 	xfree(longname);
611 
612 	buffer_free(&msg);
613 
614 	return(filename);
615 }
616 
617 int
618 do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
619 {
620 	Buffer msg;
621 	u_int status, id;
622 
623 	buffer_init(&msg);
624 
625 	/* Send rename request */
626 	id = conn->msg_id++;
627 	buffer_put_char(&msg, SSH2_FXP_RENAME);
628 	buffer_put_int(&msg, id);
629 	buffer_put_cstring(&msg, oldpath);
630 	buffer_put_cstring(&msg, newpath);
631 	send_msg(conn->fd_out, &msg);
632 	debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
633 	    newpath);
634 	buffer_free(&msg);
635 
636 	status = get_status(conn->fd_in, id);
637 	if (status != SSH2_FX_OK)
638 		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
639 		    newpath, fx2txt(status));
640 
641 	return(status);
642 }
643 
644 int
645 do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
646 {
647 	Buffer msg;
648 	u_int status, id;
649 
650 	if (conn->version < 3) {
651 		error("This server does not support the symlink operation");
652 		return(SSH2_FX_OP_UNSUPPORTED);
653 	}
654 
655 	buffer_init(&msg);
656 
657 	/* Send rename request */
658 	id = conn->msg_id++;
659 	buffer_put_char(&msg, SSH2_FXP_SYMLINK);
660 	buffer_put_int(&msg, id);
661 	buffer_put_cstring(&msg, oldpath);
662 	buffer_put_cstring(&msg, newpath);
663 	send_msg(conn->fd_out, &msg);
664 	debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
665 	    newpath);
666 	buffer_free(&msg);
667 
668 	status = get_status(conn->fd_in, id);
669 	if (status != SSH2_FX_OK)
670 		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
671 		    newpath, fx2txt(status));
672 
673 	return(status);
674 }
675 
676 #if 0
677 char *
678 do_readlink(struct sftp_conn *conn, char *path)
679 {
680 	Buffer msg;
681 	u_int type, expected_id, count, id;
682 	char *filename, *longname;
683 
684 	expected_id = id = conn->msg_id++;
685 	send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
686 	    strlen(path));
687 
688 	buffer_init(&msg);
689 
690 	get_msg(conn->fd_in, &msg);
691 	type = buffer_get_char(&msg);
692 	id = buffer_get_int(&msg);
693 
694 	if (id != expected_id)
695 		fatal("ID mismatch (%u != %u)", id, expected_id);
696 
697 	if (type == SSH2_FXP_STATUS) {
698 		u_int status = buffer_get_int(&msg);
699 
700 		error("Couldn't readlink: %s", fx2txt(status));
701 		return(NULL);
702 	} else if (type != SSH2_FXP_NAME)
703 		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
704 		    SSH2_FXP_NAME, type);
705 
706 	count = buffer_get_int(&msg);
707 	if (count != 1)
708 		fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
709 
710 	filename = buffer_get_string(&msg, NULL);
711 	longname = buffer_get_string(&msg, NULL);
712 	(void) decode_attrib(&msg);
713 
714 	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
715 
716 	xfree(longname);
717 
718 	buffer_free(&msg);
719 
720 	return(filename);
721 }
722 #endif
723 
724 static void
725 send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
726     char *handle, u_int handle_len)
727 {
728 	Buffer msg;
729 
730 	buffer_init(&msg);
731 	buffer_clear(&msg);
732 	buffer_put_char(&msg, SSH2_FXP_READ);
733 	buffer_put_int(&msg, id);
734 	buffer_put_string(&msg, handle, handle_len);
735 	buffer_put_int64(&msg, offset);
736 	buffer_put_int(&msg, len);
737 	send_msg(fd_out, &msg);
738 	buffer_free(&msg);
739 }
740 
741 int
742 do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
743     int pflag)
744 {
745 	Attrib junk, *a;
746 	Buffer msg;
747 	char *handle;
748 	int local_fd, status, num_req, max_req, write_error;
749 	int read_error, write_errno;
750 	u_int64_t offset, size;
751 	u_int handle_len, mode, type, id, buflen;
752 	struct request {
753 		u_int id;
754 		u_int len;
755 		u_int64_t offset;
756 		TAILQ_ENTRY(request) tq;
757 	};
758 	TAILQ_HEAD(reqhead, request) requests;
759 	struct request *req;
760 
761 	TAILQ_INIT(&requests);
762 
763 	a = do_stat(conn, remote_path, 0);
764 	if (a == NULL)
765 		return(-1);
766 
767 	/* XXX: should we preserve set[ug]id? */
768 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
769 		mode = S_IWRITE | (a->perm & 0777);
770 	else
771 		mode = 0666;
772 
773 	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
774 	    S_ISDIR(a->perm)) {
775 		error("Cannot download a directory: %s", remote_path);
776 		return(-1);
777 	}
778 
779 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
780 		size = a->size;
781 	else
782 		size = 0;
783 
784 	buflen = conn->transfer_buflen;
785 	buffer_init(&msg);
786 
787 	/* Send open request */
788 	id = conn->msg_id++;
789 	buffer_put_char(&msg, SSH2_FXP_OPEN);
790 	buffer_put_int(&msg, id);
791 	buffer_put_cstring(&msg, remote_path);
792 	buffer_put_int(&msg, SSH2_FXF_READ);
793 	attrib_clear(&junk); /* Send empty attributes */
794 	encode_attrib(&msg, &junk);
795 	send_msg(conn->fd_out, &msg);
796 	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
797 
798 	handle = get_handle(conn->fd_in, id, &handle_len);
799 	if (handle == NULL) {
800 		buffer_free(&msg);
801 		return(-1);
802 	}
803 
804 	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
805 	if (local_fd == -1) {
806 		error("Couldn't open local file \"%s\" for writing: %s",
807 		    local_path, strerror(errno));
808 		buffer_free(&msg);
809 		xfree(handle);
810 		return(-1);
811 	}
812 
813 	/* Read from remote and write to local */
814 	write_error = read_error = write_errno = num_req = offset = 0;
815 	max_req = 1;
816 	while (num_req > 0 || max_req > 0) {
817 		char *data;
818 		u_int len;
819 
820 		/* Send some more requests */
821 		while (num_req < max_req) {
822 			debug3("Request range %llu -> %llu (%d/%d)",
823 			    (unsigned long long)offset,
824 			    (unsigned long long)offset + buflen - 1,
825 			    num_req, max_req);
826 			req = xmalloc(sizeof(*req));
827 			req->id = conn->msg_id++;
828 			req->len = buflen;
829 			req->offset = offset;
830 			offset += buflen;
831 			num_req++;
832 			TAILQ_INSERT_TAIL(&requests, req, tq);
833 			send_read_request(conn->fd_out, req->id, req->offset,
834 			    req->len, handle, handle_len);
835 		}
836 
837 		buffer_clear(&msg);
838 		get_msg(conn->fd_in, &msg);
839 		type = buffer_get_char(&msg);
840 		id = buffer_get_int(&msg);
841 		debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
842 
843 		/* Find the request in our queue */
844 		for(req = TAILQ_FIRST(&requests);
845 		    req != NULL && req->id != id;
846 		    req = TAILQ_NEXT(req, tq))
847 			;
848 		if (req == NULL)
849 			fatal("Unexpected reply %u", id);
850 
851 		switch (type) {
852 		case SSH2_FXP_STATUS:
853 			status = buffer_get_int(&msg);
854 			if (status != SSH2_FX_EOF)
855 				read_error = 1;
856 			max_req = 0;
857 			TAILQ_REMOVE(&requests, req, tq);
858 			xfree(req);
859 			num_req--;
860 			break;
861 		case SSH2_FXP_DATA:
862 			data = buffer_get_string(&msg, &len);
863 			debug3("Received data %llu -> %llu",
864 			    (unsigned long long)req->offset,
865 			    (unsigned long long)req->offset + len - 1);
866 			if (len > req->len)
867 				fatal("Received more data than asked for "
868 				      "%u > %u", len, req->len);
869 			if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
870 			     atomicio(write, local_fd, data, len) != len) &&
871 			    !write_error) {
872 				write_errno = errno;
873 				write_error = 1;
874 				max_req = 0;
875 			}
876 			xfree(data);
877 
878 			if (len == req->len) {
879 				TAILQ_REMOVE(&requests, req, tq);
880 				xfree(req);
881 				num_req--;
882 			} else {
883 				/* Resend the request for the missing data */
884 				debug3("Short data block, re-requesting "
885 				    "%llu -> %llu (%2d)",
886 				    (unsigned long long)req->offset + len,
887 				    (unsigned long long)req->offset +
888 				    req->len - 1, num_req);
889 				req->id = conn->msg_id++;
890 				req->len -= len;
891 				req->offset += len;
892 				send_read_request(conn->fd_out, req->id,
893 				    req->offset, req->len, handle, handle_len);
894 				/* Reduce the request size */
895 				if (len < buflen)
896 					buflen = MAX(MIN_READ_SIZE, len);
897 			}
898 			if (max_req > 0) { /* max_req = 0 iff EOF received */
899 				if (size > 0 && offset > size) {
900 					/* Only one request at a time
901 					 * after the expected EOF */
902 					debug3("Finish at %llu (%2d)",
903 					    (unsigned long long)offset,
904 					    num_req);
905 					max_req = 1;
906 				}
907 				else if (max_req < conn->num_requests + 1) {
908 					++max_req;
909 				}
910 			}
911 			break;
912 		default:
913 			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
914 			    SSH2_FXP_DATA, type);
915 		}
916 	}
917 
918 	/* Sanity check */
919 	if (TAILQ_FIRST(&requests) != NULL)
920 		fatal("Transfer complete, but requests still in queue");
921 
922 	if (read_error) {
923 		error("Couldn't read from remote file \"%s\" : %s",
924 		    remote_path, fx2txt(status));
925 		do_close(conn, handle, handle_len);
926 	} else if (write_error) {
927 		error("Couldn't write to \"%s\": %s", local_path,
928 		    strerror(write_errno));
929 		status = -1;
930 		do_close(conn, handle, handle_len);
931 	} else {
932 		status = do_close(conn, handle, handle_len);
933 
934 		/* Override umask and utimes if asked */
935 #ifdef HAVE_FCHMOD
936 		if (pflag && fchmod(local_fd, mode) == -1)
937 #else
938 		if (pflag && chmod(local_path, mode) == -1)
939 #endif /* HAVE_FCHMOD */
940 			error("Couldn't set mode on \"%s\": %s", local_path,
941 			      strerror(errno));
942 		if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
943 			struct timeval tv[2];
944 			tv[0].tv_sec = a->atime;
945 			tv[1].tv_sec = a->mtime;
946 			tv[0].tv_usec = tv[1].tv_usec = 0;
947 			if (utimes(local_path, tv) == -1)
948 				error("Can't set times on \"%s\": %s",
949 				      local_path, strerror(errno));
950 		}
951 	}
952 	close(local_fd);
953 	buffer_free(&msg);
954 	xfree(handle);
955 
956 	return(status);
957 }
958 
959 int
960 do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
961     int pflag)
962 {
963 	int local_fd, status;
964 	u_int handle_len, id, type;
965 	u_int64_t offset;
966 	char *handle, *data;
967 	Buffer msg;
968 	struct stat sb;
969 	Attrib a;
970 	u_int32_t startid;
971 	u_int32_t ackid;
972 	struct outstanding_ack {
973 		u_int id;
974 		u_int len;
975 		u_int64_t offset;
976 		TAILQ_ENTRY(outstanding_ack) tq;
977 	};
978 	TAILQ_HEAD(ackhead, outstanding_ack) acks;
979 	struct outstanding_ack *ack;
980 
981 	TAILQ_INIT(&acks);
982 
983 	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
984 		error("Couldn't open local file \"%s\" for reading: %s",
985 		    local_path, strerror(errno));
986 		return(-1);
987 	}
988 	if (fstat(local_fd, &sb) == -1) {
989 		error("Couldn't fstat local file \"%s\": %s",
990 		    local_path, strerror(errno));
991 		close(local_fd);
992 		return(-1);
993 	}
994 	stat_to_attrib(&sb, &a);
995 
996 	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
997 	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
998 	a.perm &= 0777;
999 	if (!pflag)
1000 		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1001 
1002 	buffer_init(&msg);
1003 
1004 	/* Send open request */
1005 	id = conn->msg_id++;
1006 	buffer_put_char(&msg, SSH2_FXP_OPEN);
1007 	buffer_put_int(&msg, id);
1008 	buffer_put_cstring(&msg, remote_path);
1009 	buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1010 	encode_attrib(&msg, &a);
1011 	send_msg(conn->fd_out, &msg);
1012 	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1013 
1014 	buffer_clear(&msg);
1015 
1016 	handle = get_handle(conn->fd_in, id, &handle_len);
1017 	if (handle == NULL) {
1018 		close(local_fd);
1019 		buffer_free(&msg);
1020 		return(-1);
1021 	}
1022 
1023 	startid = ackid = id + 1;
1024 	data = xmalloc(conn->transfer_buflen);
1025 
1026 	/* Read from local and write to remote */
1027 	offset = 0;
1028 	for (;;) {
1029 		int len;
1030 
1031 		/*
1032 		 * Can't use atomicio here because it returns 0 on EOF, thus losing
1033 		 * the last block of the file
1034 		 */
1035 		do
1036 			len = read(local_fd, data, conn->transfer_buflen);
1037 		while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1038 
1039 		if (len == -1)
1040 			fatal("Couldn't read from \"%s\": %s", local_path,
1041 			    strerror(errno));
1042 
1043 		if (len != 0) {
1044 			ack = xmalloc(sizeof(*ack));
1045 			ack->id = ++id;
1046 			ack->offset = offset;
1047 			ack->len = len;
1048 			TAILQ_INSERT_TAIL(&acks, ack, tq);
1049 
1050 			buffer_clear(&msg);
1051 			buffer_put_char(&msg, SSH2_FXP_WRITE);
1052 			buffer_put_int(&msg, ack->id);
1053 			buffer_put_string(&msg, handle, handle_len);
1054 			buffer_put_int64(&msg, offset);
1055 			buffer_put_string(&msg, data, len);
1056 			send_msg(conn->fd_out, &msg);
1057 			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1058 			       id, (unsigned long long)offset, len);
1059 		} else if (TAILQ_FIRST(&acks) == NULL)
1060 			break;
1061 
1062 		if (ack == NULL)
1063 			fatal("Unexpected ACK %u", id);
1064 
1065 		if (id == startid || len == 0 ||
1066 		    id - ackid >= conn->num_requests) {
1067 			u_int r_id;
1068 
1069 			buffer_clear(&msg);
1070 			get_msg(conn->fd_in, &msg);
1071 			type = buffer_get_char(&msg);
1072 			r_id = buffer_get_int(&msg);
1073 
1074 			if (type != SSH2_FXP_STATUS)
1075 				fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1076 				    "got %d", SSH2_FXP_STATUS, type);
1077 
1078 			status = buffer_get_int(&msg);
1079 			debug3("SSH2_FXP_STATUS %d", status);
1080 
1081 			/* Find the request in our queue */
1082 			for(ack = TAILQ_FIRST(&acks);
1083 			    ack != NULL && ack->id != r_id;
1084 			    ack = TAILQ_NEXT(ack, tq))
1085 				;
1086 			if (ack == NULL)
1087 				fatal("Can't find request for ID %u", r_id);
1088 			TAILQ_REMOVE(&acks, ack, tq);
1089 
1090 			if (status != SSH2_FX_OK) {
1091 				error("Couldn't write to remote file \"%s\": %s",
1092 				      remote_path, fx2txt(status));
1093 				do_close(conn, handle, handle_len);
1094 				close(local_fd);
1095 				goto done;
1096 			}
1097 			debug3("In write loop, ack for %u %u bytes at %llu",
1098 			   ack->id, ack->len, (unsigned long long)ack->offset);
1099 			++ackid;
1100 			xfree(ack);
1101 		}
1102 		offset += len;
1103 	}
1104 	xfree(data);
1105 
1106 	if (close(local_fd) == -1) {
1107 		error("Couldn't close local file \"%s\": %s", local_path,
1108 		    strerror(errno));
1109 		do_close(conn, handle, handle_len);
1110 		status = -1;
1111 		goto done;
1112 	}
1113 
1114 	/* Override umask and utimes if asked */
1115 	if (pflag)
1116 		do_fsetstat(conn, handle, handle_len, &a);
1117 
1118 	status = do_close(conn, handle, handle_len);
1119 
1120 done:
1121 	xfree(handle);
1122 	buffer_free(&msg);
1123 	return(status);
1124 }
1125