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