sftp-client.c revision 137015
162642Sn_hibma/*
262642Sn_hibma * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
362642Sn_hibma *
462642Sn_hibma * Permission to use, copy, modify, and distribute this software for any
562642Sn_hibma * purpose with or without fee is hereby granted, provided that the above
662642Sn_hibma * copyright notice and this permission notice appear in all copies.
762642Sn_hibma *
862642Sn_hibma * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962642Sn_hibma * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062642Sn_hibma * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162642Sn_hibma * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262642Sn_hibma * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362642Sn_hibma * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462642Sn_hibma * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562642Sn_hibma */
1662642Sn_hibma
1762642Sn_hibma/* XXX: memleaks */
1862642Sn_hibma/* XXX: signed vs unsigned */
1962642Sn_hibma/* XXX: remove all logging, only return status codes */
2062642Sn_hibma/* XXX: copy between two remote sites */
2162642Sn_hibma
2262642Sn_hibma#include "includes.h"
2362642Sn_hibmaRCSID("$OpenBSD: sftp-client.c,v 1.51 2004/07/11 17:48:47 deraadt Exp $");
2462642Sn_hibma
2562642Sn_hibma#include "openbsd-compat/sys-queue.h"
2662642Sn_hibma
2762642Sn_hibma#include "buffer.h"
2862642Sn_hibma#include "bufaux.h"
2962642Sn_hibma#include "getput.h"
3062642Sn_hibma#include "xmalloc.h"
3162642Sn_hibma#include "log.h"
3262642Sn_hibma#include "atomicio.h"
3362642Sn_hibma#include "progressmeter.h"
3462642Sn_hibma
3562642Sn_hibma#include "sftp.h"
3662642Sn_hibma#include "sftp-common.h"
3762642Sn_hibma#include "sftp-client.h"
3862642Sn_hibma
3962642Sn_hibmaextern volatile sig_atomic_t interrupted;
4062642Sn_hibmaextern int showprogress;
4162642Sn_hibma
42113273Smdodd/* Minimum amount of data to read at at time */
43188945Sthompsa#define MIN_READ_SIZE	512
4462642Sn_hibma
45227196Sed/* Maximum packet size */
46225839Smav#define MAX_MSG_LENGTH	(256 * 1024)
47225839Smav
48225839Smavstruct sftp_conn {
49225839Smav	int fd_in;
50225839Smav	int fd_out;
51225839Smav	u_int transfer_buflen;
52225839Smav	u_int num_requests;
53227196Sed	u_int version;
54227196Sed	u_int msg_id;
55227196Sed};
56227196Sed
57227196Sedstatic void
5862642Sn_hibmasend_msg(int fd, Buffer *m)
59225839Smav{
60225839Smav	u_char mlen[4];
61225839Smav
62225839Smav	if (buffer_len(m) > MAX_MSG_LENGTH)
63225839Smav		fatal("Outbound message too long %u", buffer_len(m));
64225839Smav
6562642Sn_hibma	/* Send length first */
66225839Smav	PUT_32BIT(mlen, buffer_len(m));
67225839Smav	if (atomicio(vwrite, fd, mlen, sizeof(mlen)) <= 0)
6862642Sn_hibma		fatal("Couldn't send packet: %s", strerror(errno));
69225839Smav
70225839Smav	if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0)
71225839Smav		fatal("Couldn't send packet: %s", strerror(errno));
72225839Smav
73225839Smav	buffer_clear(m);
74225839Smav}
7562642Sn_hibma
76225839Smavstatic void
77225839Smavget_msg(int fd, Buffer *m)
78225839Smav{
79225839Smav	ssize_t len;
80225839Smav	u_int msg_len;
81225839Smav
82225839Smav	buffer_append_space(m, 4);
83225839Smav	len = atomicio(read, fd, buffer_ptr(m), 4);
84225839Smav	if (len == 0)
85225839Smav		fatal("Connection closed");
86225839Smav	else if (len == -1)
87225839Smav		fatal("Couldn't read packet: %s", strerror(errno));
88225839Smav
89225839Smav	msg_len = buffer_get_int(m);
90225839Smav	if (msg_len > MAX_MSG_LENGTH)
91225839Smav		fatal("Received message too long %u", msg_len);
92225839Smav
93225839Smav	buffer_append_space(m, msg_len);
94225839Smav	len = atomicio(read, fd, buffer_ptr(m), msg_len);
95225839Smav	if (len == 0)
96225839Smav		fatal("Connection closed");
97225839Smav	else if (len == -1)
98225839Smav		fatal("Read packet: %s", strerror(errno));
99225839Smav}
100225839Smav
101225839Smavstatic void
102225839Smavsend_string_request(int fd, u_int id, u_int code, char *s,
103225839Smav    u_int len)
104225839Smav{
105225839Smav	Buffer msg;
106225839Smav
107225839Smav	buffer_init(&msg);
108225839Smav	buffer_put_char(&msg, code);
109225839Smav	buffer_put_int(&msg, id);
110225839Smav	buffer_put_string(&msg, s, len);
111225839Smav	send_msg(fd, &msg);
112225839Smav	debug3("Sent message fd %d T:%u I:%u", fd, code, id);
113225839Smav	buffer_free(&msg);
114225839Smav}
115225839Smav
116225839Smavstatic void
117225839Smavsend_string_attrs_request(int fd, u_int id, u_int code, char *s,
118225839Smav    u_int len, Attrib *a)
119225839Smav{
120225839Smav	Buffer msg;
121225839Smav
122225839Smav	buffer_init(&msg);
123225839Smav	buffer_put_char(&msg, code);
124225839Smav	buffer_put_int(&msg, id);
125225839Smav	buffer_put_string(&msg, s, len);
126225839Smav	encode_attrib(&msg, a);
127225839Smav	send_msg(fd, &msg);
128225839Smav	debug3("Sent message fd %d T:%u I:%u", fd, code, id);
129225839Smav	buffer_free(&msg);
130225839Smav}
131225839Smav
13262642Sn_hibmastatic u_int
133225839Smavget_status(int fd, u_int expected_id)
134225839Smav{
135225839Smav	Buffer msg;
136225839Smav	u_int type, id, status;
137225839Smav
138225839Smav	buffer_init(&msg);
139225839Smav	get_msg(fd, &msg);
140225839Smav	type = buffer_get_char(&msg);
141225839Smav	id = buffer_get_int(&msg);
142225839Smav
143225839Smav	if (id != expected_id)
144225839Smav		fatal("ID mismatch (%u != %u)", id, expected_id);
145225839Smav	if (type != SSH2_FXP_STATUS)
146225839Smav		fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
147225839Smav		    SSH2_FXP_STATUS, type);
148225839Smav
149225839Smav	status = buffer_get_int(&msg);
150225839Smav	buffer_free(&msg);
151225839Smav
152225839Smav	debug3("SSH2_FXP_STATUS %u", status);
153225839Smav
154225839Smav	return(status);
155225839Smav}
156225839Smav
157225839Smavstatic char *
158225839Smavget_handle(int fd, u_int expected_id, u_int *len)
159225839Smav{
160225839Smav	Buffer msg;
161225839Smav	u_int type, id;
162225839Smav	char *handle;
163225839Smav
164225839Smav	buffer_init(&msg);
165225839Smav	get_msg(fd, &msg);
166225839Smav	type = buffer_get_char(&msg);
167225839Smav	id = buffer_get_int(&msg);
168225839Smav
169225839Smav	if (id != expected_id)
170225839Smav		fatal("ID mismatch (%u != %u)", id, expected_id);
171225839Smav	if (type == SSH2_FXP_STATUS) {
172225839Smav		int status = buffer_get_int(&msg);
173225839Smav
174225839Smav		error("Couldn't get handle: %s", fx2txt(status));
175225839Smav		return(NULL);
176225839Smav	} else if (type != SSH2_FXP_HANDLE)
17762642Sn_hibma		fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
17862642Sn_hibma		    SSH2_FXP_HANDLE, type);
179225839Smav
18062642Sn_hibma	handle = buffer_get_string(&msg, len);
18162642Sn_hibma	buffer_free(&msg);
18262642Sn_hibma
183164531Sgrog	return(handle);
184164531Sgrog}
185235519Smav
186194789Sdelphijstatic Attrib *
187164531Sgrogget_decode_stat(int fd, u_int expected_id, int quiet)
188164531Sgrog{
189235519Smav	Buffer msg;
190194789Sdelphij	u_int type, id;
191225839Smav	Attrib *a;
192225839Smav
193225839Smav	buffer_init(&msg);
194225839Smav	get_msg(fd, &msg);
19562642Sn_hibma
19662642Sn_hibma	type = buffer_get_char(&msg);
19762642Sn_hibma	id = buffer_get_int(&msg);
198225839Smav
19987699Smarkm	debug3("Received stat reply T:%u I:%u", type, id);
20062642Sn_hibma	if (id != expected_id)
20162642Sn_hibma		fatal("ID mismatch (%u != %u)", id, expected_id);
20262642Sn_hibma	if (type == SSH2_FXP_STATUS) {
203224511Smav		int status = buffer_get_int(&msg);
204224511Smav
205164531Sgrog		if (quiet)
20662642Sn_hibma			debug("Couldn't stat remote file: %s", fx2txt(status));
207224511Smav		else
208224511Smav			error("Couldn't stat remote file: %s", fx2txt(status));
20962642Sn_hibma		return(NULL);
21062642Sn_hibma	} else if (type != SSH2_FXP_ATTRS) {
21162642Sn_hibma		fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
21262642Sn_hibma		    SSH2_FXP_ATTRS, type);
21362642Sn_hibma	}
21462642Sn_hibma	a = decode_attrib(&msg);
21562642Sn_hibma	buffer_free(&msg);
21662642Sn_hibma
21762642Sn_hibma	return(a);
21862642Sn_hibma}
219224511Smav
220224511Smavstruct sftp_conn *
221224511Smavdo_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
222224511Smav{
223224511Smav	u_int type;
224224511Smav	int version;
225224511Smav	Buffer msg;
226224511Smav	struct sftp_conn *ret;
227224511Smav
228224511Smav	buffer_init(&msg);
229224511Smav	buffer_put_char(&msg, SSH2_FXP_INIT);
230224511Smav	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
231224511Smav	send_msg(fd_out, &msg);
232224511Smav
233224511Smav	buffer_clear(&msg);
234224511Smav
235224511Smav	get_msg(fd_in, &msg);
236224511Smav
237225839Smav	/* Expecting a VERSION reply */
23862642Sn_hibma	if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
23962642Sn_hibma		error("Invalid packet back from SSH2_FXP_INIT (type %u)",
24062642Sn_hibma		    type);
24162642Sn_hibma		buffer_free(&msg);
24267256Sn_hibma		return(NULL);
24362642Sn_hibma	}
244224511Smav	version = buffer_get_int(&msg);
24562642Sn_hibma
24662642Sn_hibma	debug2("Remote version: %d", version);
247224511Smav
248224511Smav	/* Check for extensions */
249164531Sgrog	while (buffer_len(&msg) > 0) {
25062642Sn_hibma		char *name = buffer_get_string(&msg, NULL);
25162642Sn_hibma		char *value = buffer_get_string(&msg, NULL);
25262642Sn_hibma
25362642Sn_hibma		debug2("Init extension: \"%s\"", name);
25462642Sn_hibma		xfree(name);
25562642Sn_hibma		xfree(value);
25662642Sn_hibma	}
25762642Sn_hibma
25862642Sn_hibma	buffer_free(&msg);
25962642Sn_hibma
26062642Sn_hibma	ret = xmalloc(sizeof(*ret));
26162642Sn_hibma	ret->fd_in = fd_in;
26262642Sn_hibma	ret->fd_out = fd_out;
26362642Sn_hibma	ret->transfer_buflen = transfer_buflen;
26462642Sn_hibma	ret->num_requests = num_requests;
26562642Sn_hibma	ret->version = version;
26662642Sn_hibma	ret->msg_id = 1;
267224511Smav
26875606Sn_hibma	/* Some filexfer v.0 servers don't support large packets */
26962642Sn_hibma	if (version == 0)
270224511Smav		ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
27167256Sn_hibma
27267256Sn_hibma	return(ret);
273224511Smav}
27467256Sn_hibma
27562642Sn_hibmau_int
27662642Sn_hibmasftp_proto_version(struct sftp_conn *conn)
277225839Smav{
27862642Sn_hibma	return(conn->version);
27962642Sn_hibma}
28062642Sn_hibma
28162642Sn_hibmaint
28262642Sn_hibmado_close(struct sftp_conn *conn, char *handle, u_int handle_len)
28362642Sn_hibma{
28462642Sn_hibma	u_int id, status;
28562642Sn_hibma	Buffer msg;
286224511Smav
287224511Smav	buffer_init(&msg);
28862642Sn_hibma
28962642Sn_hibma	id = conn->msg_id++;
29062642Sn_hibma	buffer_put_char(&msg, SSH2_FXP_CLOSE);
29162642Sn_hibma	buffer_put_int(&msg, id);
292164531Sgrog	buffer_put_string(&msg, handle, handle_len);
293164531Sgrog	send_msg(conn->fd_out, &msg);
294224511Smav	debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
29562642Sn_hibma
296224511Smav	status = get_status(conn->fd_in, id);
29762642Sn_hibma	if (status != SSH2_FX_OK)
29862642Sn_hibma		error("Couldn't close file: %s", fx2txt(status));
299225839Smav
30062642Sn_hibma	buffer_free(&msg);
30162642Sn_hibma
302225839Smav	return(status);
303225839Smav}
30462642Sn_hibma
305225839Smav
30662642Sn_hibmastatic int
307235519Smavdo_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
308225839Smav    SFTP_DIRENT ***dir)
309225839Smav{
310225839Smav	Buffer msg;
311225839Smav	u_int type, id, handle_len, i, expected_id, ents = 0;
312225839Smav	char *handle;
313225839Smav
314225839Smav	id = conn->msg_id++;
315225839Smav
316225839Smav	buffer_init(&msg);
317225839Smav	buffer_put_char(&msg, SSH2_FXP_OPENDIR);
318225839Smav	buffer_put_int(&msg, id);
319225839Smav	buffer_put_cstring(&msg, path);
320225839Smav	send_msg(conn->fd_out, &msg);
321225839Smav
322225839Smav	buffer_clear(&msg);
323225839Smav
324225839Smav	handle = get_handle(conn->fd_in, id, &handle_len);
325225839Smav	if (handle == NULL)
326225839Smav		return(-1);
327225839Smav
32862642Sn_hibma	if (dir) {
329225839Smav		ents = 0;
330225839Smav		*dir = xmalloc(sizeof(**dir));
331225839Smav		(*dir)[0] = NULL;
332225839Smav	}
333225839Smav
334225839Smav	for (; !interrupted;) {
335225839Smav		int count;
336225839Smav
337225839Smav		id = expected_id = conn->msg_id++;
338225839Smav
339225839Smav		debug3("Sending SSH2_FXP_READDIR I:%u", id);
340225839Smav
341225839Smav		buffer_clear(&msg);
342225839Smav		buffer_put_char(&msg, SSH2_FXP_READDIR);
343225839Smav		buffer_put_int(&msg, id);
34462642Sn_hibma		buffer_put_string(&msg, handle, handle_len);
345225839Smav		send_msg(conn->fd_out, &msg);
346225839Smav
347225839Smav		buffer_clear(&msg);
348225839Smav
349225839Smav		get_msg(conn->fd_in, &msg);
350225839Smav
351225839Smav		type = buffer_get_char(&msg);
352225839Smav		id = buffer_get_int(&msg);
353225839Smav
354225839Smav		debug3("Received reply T:%u I:%u", type, id);
35562642Sn_hibma
356225839Smav		if (id != expected_id)
357225839Smav			fatal("ID mismatch (%u != %u)", id, expected_id);
358225839Smav
359224511Smav		if (type == SSH2_FXP_STATUS) {
360225839Smav			int status = buffer_get_int(&msg);
361225839Smav
362225839Smav			debug3("Received SSH2_FXP_STATUS %d", status);
363225839Smav
364225839Smav			if (status == SSH2_FX_EOF) {
365225839Smav				break;
366225839Smav			} else {
367225839Smav				error("Couldn't read directory: %s",
368225839Smav				    fx2txt(status));
369225839Smav				do_close(conn, handle, handle_len);
370225839Smav				xfree(handle);
371225839Smav				return(status);
372225839Smav			}
373225839Smav		} else if (type != SSH2_FXP_NAME)
374225839Smav			fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
375225839Smav			    SSH2_FXP_NAME, type);
376225839Smav
377225839Smav		count = buffer_get_int(&msg);
378225839Smav		if (count == 0)
379225839Smav			break;
380225839Smav		debug3("Received %d SSH2_FXP_NAME responses", count);
381225839Smav		for (i = 0; i < count; i++) {
382225839Smav			char *filename, *longname;
383225839Smav			Attrib *a;
384225839Smav
385225839Smav			filename = buffer_get_string(&msg, NULL);
386225839Smav			longname = buffer_get_string(&msg, NULL);
387225839Smav			a = decode_attrib(&msg);
388225839Smav
389225839Smav			if (printflag)
390225839Smav				printf("%s\n", longname);
391225839Smav
392225839Smav			if (dir) {
393225839Smav				*dir = xrealloc(*dir, sizeof(**dir) *
394225839Smav				    (ents + 2));
395225839Smav				(*dir)[ents] = xmalloc(sizeof(***dir));
396225839Smav				(*dir)[ents]->filename = xstrdup(filename);
397225839Smav				(*dir)[ents]->longname = xstrdup(longname);
398225839Smav				memcpy(&(*dir)[ents]->a, a, sizeof(*a));
399225839Smav				(*dir)[++ents] = NULL;
400225839Smav			}
401225839Smav
402225839Smav			xfree(filename);
403225839Smav			xfree(longname);
404225839Smav		}
405225839Smav	}
406225839Smav
407225839Smav	buffer_free(&msg);
40862642Sn_hibma	do_close(conn, handle, handle_len);
40962642Sn_hibma	xfree(handle);
410225839Smav
411225839Smav	/* Don't return partial matches on interrupt */
412225839Smav	if (interrupted && dir != NULL && *dir != NULL) {
413225839Smav		free_sftp_dirents(*dir);
414225839Smav		*dir = xmalloc(sizeof(**dir));
415225839Smav		**dir = NULL;
416225839Smav	}
417225839Smav
418225839Smav	return(0);
41962642Sn_hibma}
420225839Smav
421225839Smavint
422225839Smavdo_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
423225839Smav{
424225839Smav	return(do_lsreaddir(conn, path, 0, dir));
425225839Smav}
426225839Smav
427225839Smavvoid free_sftp_dirents(SFTP_DIRENT **s)
428225839Smav{
429225839Smav	int i;
430225839Smav
431225839Smav	for (i = 0; s[i]; i++) {
43262642Sn_hibma		xfree(s[i]->filename);
433225839Smav		xfree(s[i]->longname);
434225839Smav		xfree(s[i]);
435225839Smav	}
436225839Smav	xfree(s);
437225839Smav}
438225839Smav
439225839Smavint
44062642Sn_hibmado_rm(struct sftp_conn *conn, char *path)
44162642Sn_hibma{
44262642Sn_hibma	u_int status, id;
44362642Sn_hibma
44462642Sn_hibma	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
445225839Smav
446225839Smav	id = conn->msg_id++;
447225839Smav	send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
44862642Sn_hibma	    strlen(path));
449225839Smav	status = get_status(conn->fd_in, id);
45062642Sn_hibma	if (status != SSH2_FX_OK)
45162642Sn_hibma		error("Couldn't delete file: %s", fx2txt(status));
45262642Sn_hibma	return(status);
45362642Sn_hibma}
454225839Smav
45562642Sn_hibmaint
45662642Sn_hibmado_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
45762642Sn_hibma{
45862642Sn_hibma	u_int status, id;
45962642Sn_hibma
46062642Sn_hibma	id = conn->msg_id++;
46162642Sn_hibma	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
46262642Sn_hibma	    strlen(path), a);
46362642Sn_hibma
46462642Sn_hibma	status = get_status(conn->fd_in, id);
46562642Sn_hibma	if (status != SSH2_FX_OK)
46662642Sn_hibma		error("Couldn't create directory: %s", fx2txt(status));
46762642Sn_hibma
46862642Sn_hibma	return(status);
46962642Sn_hibma}
47062642Sn_hibma
47162642Sn_hibmaint
47262642Sn_hibmado_rmdir(struct sftp_conn *conn, char *path)
47362642Sn_hibma{
47462642Sn_hibma	u_int status, id;
47562642Sn_hibma
47662642Sn_hibma	id = conn->msg_id++;
477225839Smav	send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
478225839Smav	    strlen(path));
479225839Smav
480164531Sgrog	status = get_status(conn->fd_in, id);
481164544Sgrog	if (status != SSH2_FX_OK)
482164531Sgrog		error("Couldn't remove directory: %s", fx2txt(status));
483225839Smav
484225839Smav	return(status);
485225839Smav}
48662642Sn_hibma
48762642Sn_hibmaAttrib *
48862642Sn_hibmado_stat(struct sftp_conn *conn, char *path, int quiet)
48962642Sn_hibma{
49062642Sn_hibma	u_int id;
49162642Sn_hibma
49262642Sn_hibma	id = conn->msg_id++;
493225839Smav
49462642Sn_hibma	send_string_request(conn->fd_out, id,
49562642Sn_hibma	    conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
496225839Smav	    path, strlen(path));
49762642Sn_hibma
49862642Sn_hibma	return(get_decode_stat(conn->fd_in, id, quiet));
49962642Sn_hibma}
50062642Sn_hibma
50187699SmarkmAttrib *
50262642Sn_hibmado_lstat(struct sftp_conn *conn, char *path, int quiet)
50387699Smarkm{
50487699Smarkm	u_int id;
50562642Sn_hibma
50662642Sn_hibma	if (conn->version == 0) {
50762642Sn_hibma		if (quiet)
50862642Sn_hibma			debug("Server version does not support lstat operation");
50962642Sn_hibma		else
51062642Sn_hibma			logit("Server version does not support lstat operation");
51162642Sn_hibma		return(do_stat(conn, path, quiet));
51262642Sn_hibma	}
51362642Sn_hibma
514164531Sgrog	id = conn->msg_id++;
51562642Sn_hibma	send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
516164531Sgrog	    strlen(path));
51762642Sn_hibma
51862642Sn_hibma	return(get_decode_stat(conn->fd_in, id, quiet));
51962642Sn_hibma}
52062642Sn_hibma
521225839SmavAttrib *
522225839Smavdo_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
523225839Smav{
524225839Smav	u_int id;
525225839Smav
526225839Smav	id = conn->msg_id++;
527225839Smav	send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
52862642Sn_hibma	    handle_len);
52962642Sn_hibma
53062642Sn_hibma	return(get_decode_stat(conn->fd_in, id, quiet));
53162642Sn_hibma}
532
533int
534do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
535{
536	u_int status, id;
537
538	id = conn->msg_id++;
539	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
540	    strlen(path), a);
541
542	status = get_status(conn->fd_in, id);
543	if (status != SSH2_FX_OK)
544		error("Couldn't setstat on \"%s\": %s", path,
545		    fx2txt(status));
546
547	return(status);
548}
549
550int
551do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
552    Attrib *a)
553{
554	u_int status, id;
555
556	id = conn->msg_id++;
557	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
558	    handle_len, a);
559
560	status = get_status(conn->fd_in, id);
561	if (status != SSH2_FX_OK)
562		error("Couldn't fsetstat: %s", fx2txt(status));
563
564	return(status);
565}
566
567char *
568do_realpath(struct sftp_conn *conn, char *path)
569{
570	Buffer msg;
571	u_int type, expected_id, count, id;
572	char *filename, *longname;
573	Attrib *a;
574
575	expected_id = id = conn->msg_id++;
576	send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
577	    strlen(path));
578
579	buffer_init(&msg);
580
581	get_msg(conn->fd_in, &msg);
582	type = buffer_get_char(&msg);
583	id = buffer_get_int(&msg);
584
585	if (id != expected_id)
586		fatal("ID mismatch (%u != %u)", id, expected_id);
587
588	if (type == SSH2_FXP_STATUS) {
589		u_int status = buffer_get_int(&msg);
590
591		error("Couldn't canonicalise: %s", fx2txt(status));
592		return(NULL);
593	} else if (type != SSH2_FXP_NAME)
594		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
595		    SSH2_FXP_NAME, type);
596
597	count = buffer_get_int(&msg);
598	if (count != 1)
599		fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
600
601	filename = buffer_get_string(&msg, NULL);
602	longname = buffer_get_string(&msg, NULL);
603	a = decode_attrib(&msg);
604
605	debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
606
607	xfree(longname);
608
609	buffer_free(&msg);
610
611	return(filename);
612}
613
614int
615do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
616{
617	Buffer msg;
618	u_int status, id;
619
620	buffer_init(&msg);
621
622	/* Send rename request */
623	id = conn->msg_id++;
624	buffer_put_char(&msg, SSH2_FXP_RENAME);
625	buffer_put_int(&msg, id);
626	buffer_put_cstring(&msg, oldpath);
627	buffer_put_cstring(&msg, newpath);
628	send_msg(conn->fd_out, &msg);
629	debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
630	    newpath);
631	buffer_free(&msg);
632
633	status = get_status(conn->fd_in, id);
634	if (status != SSH2_FX_OK)
635		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
636		    newpath, fx2txt(status));
637
638	return(status);
639}
640
641int
642do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
643{
644	Buffer msg;
645	u_int status, id;
646
647	if (conn->version < 3) {
648		error("This server does not support the symlink operation");
649		return(SSH2_FX_OP_UNSUPPORTED);
650	}
651
652	buffer_init(&msg);
653
654	/* Send symlink request */
655	id = conn->msg_id++;
656	buffer_put_char(&msg, SSH2_FXP_SYMLINK);
657	buffer_put_int(&msg, id);
658	buffer_put_cstring(&msg, oldpath);
659	buffer_put_cstring(&msg, newpath);
660	send_msg(conn->fd_out, &msg);
661	debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
662	    newpath);
663	buffer_free(&msg);
664
665	status = get_status(conn->fd_in, id);
666	if (status != SSH2_FX_OK)
667		error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
668		    newpath, fx2txt(status));
669
670	return(status);
671}
672
673char *
674do_readlink(struct sftp_conn *conn, char *path)
675{
676	Buffer msg;
677	u_int type, expected_id, count, id;
678	char *filename, *longname;
679	Attrib *a;
680
681	expected_id = id = conn->msg_id++;
682	send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
683	    strlen(path));
684
685	buffer_init(&msg);
686
687	get_msg(conn->fd_in, &msg);
688	type = buffer_get_char(&msg);
689	id = buffer_get_int(&msg);
690
691	if (id != expected_id)
692		fatal("ID mismatch (%u != %u)", id, expected_id);
693
694	if (type == SSH2_FXP_STATUS) {
695		u_int status = buffer_get_int(&msg);
696
697		error("Couldn't readlink: %s", fx2txt(status));
698		return(NULL);
699	} else if (type != SSH2_FXP_NAME)
700		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
701		    SSH2_FXP_NAME, type);
702
703	count = buffer_get_int(&msg);
704	if (count != 1)
705		fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
706
707	filename = buffer_get_string(&msg, NULL);
708	longname = buffer_get_string(&msg, NULL);
709	a = decode_attrib(&msg);
710
711	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
712
713	xfree(longname);
714
715	buffer_free(&msg);
716
717	return(filename);
718}
719
720static void
721send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
722    char *handle, u_int handle_len)
723{
724	Buffer msg;
725
726	buffer_init(&msg);
727	buffer_clear(&msg);
728	buffer_put_char(&msg, SSH2_FXP_READ);
729	buffer_put_int(&msg, id);
730	buffer_put_string(&msg, handle, handle_len);
731	buffer_put_int64(&msg, offset);
732	buffer_put_int(&msg, len);
733	send_msg(fd_out, &msg);
734	buffer_free(&msg);
735}
736
737int
738do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
739    int pflag)
740{
741	Attrib junk, *a;
742	Buffer msg;
743	char *handle;
744	int local_fd, status, num_req, max_req, write_error;
745	int read_error, write_errno;
746	u_int64_t offset, size;
747	u_int handle_len, mode, type, id, buflen;
748	off_t progress_counter;
749	struct request {
750		u_int id;
751		u_int len;
752		u_int64_t offset;
753		TAILQ_ENTRY(request) tq;
754	};
755	TAILQ_HEAD(reqhead, request) requests;
756	struct request *req;
757
758	TAILQ_INIT(&requests);
759
760	a = do_stat(conn, remote_path, 0);
761	if (a == NULL)
762		return(-1);
763
764	/* XXX: should we preserve set[ug]id? */
765	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
766		mode = a->perm & 0777;
767	else
768		mode = 0666;
769
770	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
771	    (!S_ISREG(a->perm))) {
772		error("Cannot download non-regular file: %s", remote_path);
773		return(-1);
774	}
775
776	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
777		size = a->size;
778	else
779		size = 0;
780
781	buflen = conn->transfer_buflen;
782	buffer_init(&msg);
783
784	/* Send open request */
785	id = conn->msg_id++;
786	buffer_put_char(&msg, SSH2_FXP_OPEN);
787	buffer_put_int(&msg, id);
788	buffer_put_cstring(&msg, remote_path);
789	buffer_put_int(&msg, SSH2_FXF_READ);
790	attrib_clear(&junk); /* Send empty attributes */
791	encode_attrib(&msg, &junk);
792	send_msg(conn->fd_out, &msg);
793	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
794
795	handle = get_handle(conn->fd_in, id, &handle_len);
796	if (handle == NULL) {
797		buffer_free(&msg);
798		return(-1);
799	}
800
801	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
802	    mode | S_IWRITE);
803	if (local_fd == -1) {
804		error("Couldn't open local file \"%s\" for writing: %s",
805		    local_path, strerror(errno));
806		buffer_free(&msg);
807		xfree(handle);
808		return(-1);
809	}
810
811	/* Read from remote and write to local */
812	write_error = read_error = write_errno = num_req = offset = 0;
813	max_req = 1;
814	progress_counter = 0;
815
816	if (showprogress && size != 0)
817		start_progress_meter(remote_path, size, &progress_counter);
818
819	while (num_req > 0 || max_req > 0) {
820		char *data;
821		u_int len;
822
823		/*
824		 * Simulate EOF on interrupt: stop sending new requests and
825		 * allow outstanding requests to drain gracefully
826		 */
827		if (interrupted) {
828			if (num_req == 0) /* If we haven't started yet... */
829				break;
830			max_req = 0;
831		}
832
833		/* Send some more requests */
834		while (num_req < max_req) {
835			debug3("Request range %llu -> %llu (%d/%d)",
836			    (unsigned long long)offset,
837			    (unsigned long long)offset + buflen - 1,
838			    num_req, max_req);
839			req = xmalloc(sizeof(*req));
840			req->id = conn->msg_id++;
841			req->len = buflen;
842			req->offset = offset;
843			offset += buflen;
844			num_req++;
845			TAILQ_INSERT_TAIL(&requests, req, tq);
846			send_read_request(conn->fd_out, req->id, req->offset,
847			    req->len, handle, handle_len);
848		}
849
850		buffer_clear(&msg);
851		get_msg(conn->fd_in, &msg);
852		type = buffer_get_char(&msg);
853		id = buffer_get_int(&msg);
854		debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
855
856		/* Find the request in our queue */
857		for(req = TAILQ_FIRST(&requests);
858		    req != NULL && req->id != id;
859		    req = TAILQ_NEXT(req, tq))
860			;
861		if (req == NULL)
862			fatal("Unexpected reply %u", id);
863
864		switch (type) {
865		case SSH2_FXP_STATUS:
866			status = buffer_get_int(&msg);
867			if (status != SSH2_FX_EOF)
868				read_error = 1;
869			max_req = 0;
870			TAILQ_REMOVE(&requests, req, tq);
871			xfree(req);
872			num_req--;
873			break;
874		case SSH2_FXP_DATA:
875			data = buffer_get_string(&msg, &len);
876			debug3("Received data %llu -> %llu",
877			    (unsigned long long)req->offset,
878			    (unsigned long long)req->offset + len - 1);
879			if (len > req->len)
880				fatal("Received more data than asked for "
881				    "%u > %u", len, req->len);
882			if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
883			    atomicio(vwrite, local_fd, data, len) != len) &&
884			    !write_error) {
885				write_errno = errno;
886				write_error = 1;
887				max_req = 0;
888			}
889			progress_counter += len;
890			xfree(data);
891
892			if (len == req->len) {
893				TAILQ_REMOVE(&requests, req, tq);
894				xfree(req);
895				num_req--;
896			} else {
897				/* Resend the request for the missing data */
898				debug3("Short data block, re-requesting "
899				    "%llu -> %llu (%2d)",
900				    (unsigned long long)req->offset + len,
901				    (unsigned long long)req->offset +
902				    req->len - 1, num_req);
903				req->id = conn->msg_id++;
904				req->len -= len;
905				req->offset += len;
906				send_read_request(conn->fd_out, req->id,
907				    req->offset, req->len, handle, handle_len);
908				/* Reduce the request size */
909				if (len < buflen)
910					buflen = MAX(MIN_READ_SIZE, len);
911			}
912			if (max_req > 0) { /* max_req = 0 iff EOF received */
913				if (size > 0 && offset > size) {
914					/* Only one request at a time
915					 * after the expected EOF */
916					debug3("Finish at %llu (%2d)",
917					    (unsigned long long)offset,
918					    num_req);
919					max_req = 1;
920				} else if (max_req <= conn->num_requests) {
921					++max_req;
922				}
923			}
924			break;
925		default:
926			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
927			    SSH2_FXP_DATA, type);
928		}
929	}
930
931	if (showprogress && size)
932		stop_progress_meter();
933
934	/* Sanity check */
935	if (TAILQ_FIRST(&requests) != NULL)
936		fatal("Transfer complete, but requests still in queue");
937
938	if (read_error) {
939		error("Couldn't read from remote file \"%s\" : %s",
940		    remote_path, fx2txt(status));
941		do_close(conn, handle, handle_len);
942	} else if (write_error) {
943		error("Couldn't write to \"%s\": %s", local_path,
944		    strerror(write_errno));
945		status = -1;
946		do_close(conn, handle, handle_len);
947	} else {
948		status = do_close(conn, handle, handle_len);
949
950		/* Override umask and utimes if asked */
951#ifdef HAVE_FCHMOD
952		if (pflag && fchmod(local_fd, mode) == -1)
953#else
954		if (pflag && chmod(local_path, mode) == -1)
955#endif /* HAVE_FCHMOD */
956			error("Couldn't set mode on \"%s\": %s", local_path,
957			    strerror(errno));
958		if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
959			struct timeval tv[2];
960			tv[0].tv_sec = a->atime;
961			tv[1].tv_sec = a->mtime;
962			tv[0].tv_usec = tv[1].tv_usec = 0;
963			if (utimes(local_path, tv) == -1)
964				error("Can't set times on \"%s\": %s",
965				    local_path, strerror(errno));
966		}
967	}
968	close(local_fd);
969	buffer_free(&msg);
970	xfree(handle);
971
972	return(status);
973}
974
975int
976do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
977    int pflag)
978{
979	int local_fd, status;
980	u_int handle_len, id, type;
981	u_int64_t offset;
982	char *handle, *data;
983	Buffer msg;
984	struct stat sb;
985	Attrib a;
986	u_int32_t startid;
987	u_int32_t ackid;
988	struct outstanding_ack {
989		u_int id;
990		u_int len;
991		u_int64_t offset;
992		TAILQ_ENTRY(outstanding_ack) tq;
993	};
994	TAILQ_HEAD(ackhead, outstanding_ack) acks;
995	struct outstanding_ack *ack = NULL;
996
997	TAILQ_INIT(&acks);
998
999	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1000		error("Couldn't open local file \"%s\" for reading: %s",
1001		    local_path, strerror(errno));
1002		return(-1);
1003	}
1004	if (fstat(local_fd, &sb) == -1) {
1005		error("Couldn't fstat local file \"%s\": %s",
1006		    local_path, strerror(errno));
1007		close(local_fd);
1008		return(-1);
1009	}
1010	if (!S_ISREG(sb.st_mode)) {
1011		error("%s is not a regular file", local_path);
1012		close(local_fd);
1013		return(-1);
1014	}
1015	stat_to_attrib(&sb, &a);
1016
1017	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1018	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1019	a.perm &= 0777;
1020	if (!pflag)
1021		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1022
1023	buffer_init(&msg);
1024
1025	/* Send open request */
1026	id = conn->msg_id++;
1027	buffer_put_char(&msg, SSH2_FXP_OPEN);
1028	buffer_put_int(&msg, id);
1029	buffer_put_cstring(&msg, remote_path);
1030	buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1031	encode_attrib(&msg, &a);
1032	send_msg(conn->fd_out, &msg);
1033	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1034
1035	buffer_clear(&msg);
1036
1037	handle = get_handle(conn->fd_in, id, &handle_len);
1038	if (handle == NULL) {
1039		close(local_fd);
1040		buffer_free(&msg);
1041		return(-1);
1042	}
1043
1044	startid = ackid = id + 1;
1045	data = xmalloc(conn->transfer_buflen);
1046
1047	/* Read from local and write to remote */
1048	offset = 0;
1049	if (showprogress)
1050		start_progress_meter(local_path, sb.st_size, &offset);
1051
1052	for (;;) {
1053		int len;
1054
1055		/*
1056		 * Can't use atomicio here because it returns 0 on EOF,
1057		 * thus losing the last block of the file.
1058		 * Simulate an EOF on interrupt, allowing ACKs from the
1059		 * server to drain.
1060		 */
1061		if (interrupted)
1062			len = 0;
1063		else do
1064			len = read(local_fd, data, conn->transfer_buflen);
1065		while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1066
1067		if (len == -1)
1068			fatal("Couldn't read from \"%s\": %s", local_path,
1069			    strerror(errno));
1070
1071		if (len != 0) {
1072			ack = xmalloc(sizeof(*ack));
1073			ack->id = ++id;
1074			ack->offset = offset;
1075			ack->len = len;
1076			TAILQ_INSERT_TAIL(&acks, ack, tq);
1077
1078			buffer_clear(&msg);
1079			buffer_put_char(&msg, SSH2_FXP_WRITE);
1080			buffer_put_int(&msg, ack->id);
1081			buffer_put_string(&msg, handle, handle_len);
1082			buffer_put_int64(&msg, offset);
1083			buffer_put_string(&msg, data, len);
1084			send_msg(conn->fd_out, &msg);
1085			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1086			    id, (unsigned long long)offset, len);
1087		} else if (TAILQ_FIRST(&acks) == NULL)
1088			break;
1089
1090		if (ack == NULL)
1091			fatal("Unexpected ACK %u", id);
1092
1093		if (id == startid || len == 0 ||
1094		    id - ackid >= conn->num_requests) {
1095			u_int r_id;
1096
1097			buffer_clear(&msg);
1098			get_msg(conn->fd_in, &msg);
1099			type = buffer_get_char(&msg);
1100			r_id = buffer_get_int(&msg);
1101
1102			if (type != SSH2_FXP_STATUS)
1103				fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1104				    "got %d", SSH2_FXP_STATUS, type);
1105
1106			status = buffer_get_int(&msg);
1107			debug3("SSH2_FXP_STATUS %d", status);
1108
1109			/* Find the request in our queue */
1110			for(ack = TAILQ_FIRST(&acks);
1111			    ack != NULL && ack->id != r_id;
1112			    ack = TAILQ_NEXT(ack, tq))
1113				;
1114			if (ack == NULL)
1115				fatal("Can't find request for ID %u", r_id);
1116			TAILQ_REMOVE(&acks, ack, tq);
1117
1118			if (status != SSH2_FX_OK) {
1119				error("Couldn't write to remote file \"%s\": %s",
1120				    remote_path, fx2txt(status));
1121				do_close(conn, handle, handle_len);
1122				close(local_fd);
1123				xfree(data);
1124				xfree(ack);
1125				goto done;
1126			}
1127			debug3("In write loop, ack for %u %u bytes at %llu",
1128			   ack->id, ack->len, (unsigned long long)ack->offset);
1129			++ackid;
1130			xfree(ack);
1131		}
1132		offset += len;
1133	}
1134	if (showprogress)
1135		stop_progress_meter();
1136	xfree(data);
1137
1138	if (close(local_fd) == -1) {
1139		error("Couldn't close local file \"%s\": %s", local_path,
1140		    strerror(errno));
1141		do_close(conn, handle, handle_len);
1142		status = -1;
1143		goto done;
1144	}
1145
1146	/* Override umask and utimes if asked */
1147	if (pflag)
1148		do_fsetstat(conn, handle, handle_len, &a);
1149
1150	status = do_close(conn, handle, handle_len);
1151
1152done:
1153	xfree(handle);
1154	buffer_free(&msg);
1155	return(status);
1156}
1157