sftp-server.c revision 106121
176278Sjoerg/*
276278Sjoerg * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
376278Sjoerg *
476278Sjoerg * Redistribution and use in source and binary forms, with or without
576278Sjoerg * modification, are permitted provided that the following conditions
676278Sjoerg * are met:
776278Sjoerg * 1. Redistributions of source code must retain the above copyright
876278Sjoerg *    notice, this list of conditions and the following disclaimer.
976278Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1076278Sjoerg *    notice, this list of conditions and the following disclaimer in the
1176278Sjoerg *    documentation and/or other materials provided with the distribution.
1276278Sjoerg *
1376278Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1476278Sjoerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1576278Sjoerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1676278Sjoerg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1776278Sjoerg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1876278Sjoerg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1976278Sjoerg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2076278Sjoerg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2176278Sjoerg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2276278Sjoerg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2376278Sjoerg */
2476278Sjoerg#include "includes.h"
2576278SjoergRCSID("$OpenBSD: sftp-server.c,v 1.38 2002/09/11 22:41:50 djm Exp $");
2676278Sjoerg
2776278Sjoerg#include "buffer.h"
2876278Sjoerg#include "bufaux.h"
2976278Sjoerg#include "getput.h"
3076278Sjoerg#include "log.h"
3176278Sjoerg#include "xmalloc.h"
3276278Sjoerg
3376278Sjoerg#include "sftp.h"
3476278Sjoerg#include "sftp-common.h"
3576278Sjoerg
3676278Sjoerg/* helper */
3776278Sjoerg#define get_int64()			buffer_get_int64(&iqueue);
3876278Sjoerg#define get_int()			buffer_get_int(&iqueue);
39#define get_string(lenp)		buffer_get_string(&iqueue, lenp);
40#define TRACE				debug
41
42#ifdef HAVE___PROGNAME
43extern char *__progname;
44#else
45char *__progname;
46#endif
47
48/* input and output queue */
49Buffer iqueue;
50Buffer oqueue;
51
52/* Version of client */
53int version;
54
55/* portable attibutes, etc. */
56
57typedef struct Stat Stat;
58
59struct Stat {
60	char *name;
61	char *long_name;
62	Attrib attrib;
63};
64
65static int
66errno_to_portable(int unixerrno)
67{
68	int ret = 0;
69
70	switch (unixerrno) {
71	case 0:
72		ret = SSH2_FX_OK;
73		break;
74	case ENOENT:
75	case ENOTDIR:
76	case EBADF:
77	case ELOOP:
78		ret = SSH2_FX_NO_SUCH_FILE;
79		break;
80	case EPERM:
81	case EACCES:
82	case EFAULT:
83		ret = SSH2_FX_PERMISSION_DENIED;
84		break;
85	case ENAMETOOLONG:
86	case EINVAL:
87		ret = SSH2_FX_BAD_MESSAGE;
88		break;
89	default:
90		ret = SSH2_FX_FAILURE;
91		break;
92	}
93	return ret;
94}
95
96static int
97flags_from_portable(int pflags)
98{
99	int flags = 0;
100
101	if ((pflags & SSH2_FXF_READ) &&
102	    (pflags & SSH2_FXF_WRITE)) {
103		flags = O_RDWR;
104	} else if (pflags & SSH2_FXF_READ) {
105		flags = O_RDONLY;
106	} else if (pflags & SSH2_FXF_WRITE) {
107		flags = O_WRONLY;
108	}
109	if (pflags & SSH2_FXF_CREAT)
110		flags |= O_CREAT;
111	if (pflags & SSH2_FXF_TRUNC)
112		flags |= O_TRUNC;
113	if (pflags & SSH2_FXF_EXCL)
114		flags |= O_EXCL;
115	return flags;
116}
117
118static Attrib *
119get_attrib(void)
120{
121	return decode_attrib(&iqueue);
122}
123
124/* handle handles */
125
126typedef struct Handle Handle;
127struct Handle {
128	int use;
129	DIR *dirp;
130	int fd;
131	char *name;
132};
133
134enum {
135	HANDLE_UNUSED,
136	HANDLE_DIR,
137	HANDLE_FILE
138};
139
140Handle	handles[100];
141
142static void
143handle_init(void)
144{
145	int i;
146
147	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
148		handles[i].use = HANDLE_UNUSED;
149}
150
151static int
152handle_new(int use, char *name, int fd, DIR *dirp)
153{
154	int i;
155
156	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
157		if (handles[i].use == HANDLE_UNUSED) {
158			handles[i].use = use;
159			handles[i].dirp = dirp;
160			handles[i].fd = fd;
161			handles[i].name = name;
162			return i;
163		}
164	}
165	return -1;
166}
167
168static int
169handle_is_ok(int i, int type)
170{
171	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
172	    handles[i].use == type;
173}
174
175static int
176handle_to_string(int handle, char **stringp, int *hlenp)
177{
178	if (stringp == NULL || hlenp == NULL)
179		return -1;
180	*stringp = xmalloc(sizeof(int32_t));
181	PUT_32BIT(*stringp, handle);
182	*hlenp = sizeof(int32_t);
183	return 0;
184}
185
186static int
187handle_from_string(char *handle, u_int hlen)
188{
189	int val;
190
191	if (hlen != sizeof(int32_t))
192		return -1;
193	val = GET_32BIT(handle);
194	if (handle_is_ok(val, HANDLE_FILE) ||
195	    handle_is_ok(val, HANDLE_DIR))
196		return val;
197	return -1;
198}
199
200static char *
201handle_to_name(int handle)
202{
203	if (handle_is_ok(handle, HANDLE_DIR)||
204	    handle_is_ok(handle, HANDLE_FILE))
205		return handles[handle].name;
206	return NULL;
207}
208
209static DIR *
210handle_to_dir(int handle)
211{
212	if (handle_is_ok(handle, HANDLE_DIR))
213		return handles[handle].dirp;
214	return NULL;
215}
216
217static int
218handle_to_fd(int handle)
219{
220	if (handle_is_ok(handle, HANDLE_FILE))
221		return handles[handle].fd;
222	return -1;
223}
224
225static int
226handle_close(int handle)
227{
228	int ret = -1;
229
230	if (handle_is_ok(handle, HANDLE_FILE)) {
231		ret = close(handles[handle].fd);
232		handles[handle].use = HANDLE_UNUSED;
233	} else if (handle_is_ok(handle, HANDLE_DIR)) {
234		ret = closedir(handles[handle].dirp);
235		handles[handle].use = HANDLE_UNUSED;
236	} else {
237		errno = ENOENT;
238	}
239	return ret;
240}
241
242static int
243get_handle(void)
244{
245	char *handle;
246	int val = -1;
247	u_int hlen;
248
249	handle = get_string(&hlen);
250	if (hlen < 256)
251		val = handle_from_string(handle, hlen);
252	xfree(handle);
253	return val;
254}
255
256/* send replies */
257
258static void
259send_msg(Buffer *m)
260{
261	int mlen = buffer_len(m);
262
263	buffer_put_int(&oqueue, mlen);
264	buffer_append(&oqueue, buffer_ptr(m), mlen);
265	buffer_consume(m, mlen);
266}
267
268static void
269send_status(u_int32_t id, u_int32_t error)
270{
271	Buffer msg;
272	const char *status_messages[] = {
273		"Success",			/* SSH_FX_OK */
274		"End of file",			/* SSH_FX_EOF */
275		"No such file",			/* SSH_FX_NO_SUCH_FILE */
276		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
277		"Failure",			/* SSH_FX_FAILURE */
278		"Bad message",			/* SSH_FX_BAD_MESSAGE */
279		"No connection",		/* SSH_FX_NO_CONNECTION */
280		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
281		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
282		"Unknown error"			/* Others */
283	};
284
285	TRACE("sent status id %u error %u", id, error);
286	buffer_init(&msg);
287	buffer_put_char(&msg, SSH2_FXP_STATUS);
288	buffer_put_int(&msg, id);
289	buffer_put_int(&msg, error);
290	if (version >= 3) {
291		buffer_put_cstring(&msg,
292		    status_messages[MIN(error,SSH2_FX_MAX)]);
293		buffer_put_cstring(&msg, "");
294	}
295	send_msg(&msg);
296	buffer_free(&msg);
297}
298static void
299send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
300{
301	Buffer msg;
302
303	buffer_init(&msg);
304	buffer_put_char(&msg, type);
305	buffer_put_int(&msg, id);
306	buffer_put_string(&msg, data, dlen);
307	send_msg(&msg);
308	buffer_free(&msg);
309}
310
311static void
312send_data(u_int32_t id, char *data, int dlen)
313{
314	TRACE("sent data id %u len %d", id, dlen);
315	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
316}
317
318static void
319send_handle(u_int32_t id, int handle)
320{
321	char *string;
322	int hlen;
323
324	handle_to_string(handle, &string, &hlen);
325	TRACE("sent handle id %u handle %d", id, handle);
326	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
327	xfree(string);
328}
329
330static void
331send_names(u_int32_t id, int count, Stat *stats)
332{
333	Buffer msg;
334	int i;
335
336	buffer_init(&msg);
337	buffer_put_char(&msg, SSH2_FXP_NAME);
338	buffer_put_int(&msg, id);
339	buffer_put_int(&msg, count);
340	TRACE("sent names id %u count %d", id, count);
341	for (i = 0; i < count; i++) {
342		buffer_put_cstring(&msg, stats[i].name);
343		buffer_put_cstring(&msg, stats[i].long_name);
344		encode_attrib(&msg, &stats[i].attrib);
345	}
346	send_msg(&msg);
347	buffer_free(&msg);
348}
349
350static void
351send_attrib(u_int32_t id, Attrib *a)
352{
353	Buffer msg;
354
355	TRACE("sent attrib id %u have 0x%x", id, a->flags);
356	buffer_init(&msg);
357	buffer_put_char(&msg, SSH2_FXP_ATTRS);
358	buffer_put_int(&msg, id);
359	encode_attrib(&msg, a);
360	send_msg(&msg);
361	buffer_free(&msg);
362}
363
364/* parse incoming */
365
366static void
367process_init(void)
368{
369	Buffer msg;
370
371	version = get_int();
372	TRACE("client version %d", version);
373	buffer_init(&msg);
374	buffer_put_char(&msg, SSH2_FXP_VERSION);
375	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
376	send_msg(&msg);
377	buffer_free(&msg);
378}
379
380static void
381process_open(void)
382{
383	u_int32_t id, pflags;
384	Attrib *a;
385	char *name;
386	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
387
388	id = get_int();
389	name = get_string(NULL);
390	pflags = get_int();		/* portable flags */
391	a = get_attrib();
392	flags = flags_from_portable(pflags);
393	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
394	TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
395	fd = open(name, flags, mode);
396	if (fd < 0) {
397		status = errno_to_portable(errno);
398	} else {
399		handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
400		if (handle < 0) {
401			close(fd);
402		} else {
403			send_handle(id, handle);
404			status = SSH2_FX_OK;
405		}
406	}
407	if (status != SSH2_FX_OK)
408		send_status(id, status);
409	xfree(name);
410}
411
412static void
413process_close(void)
414{
415	u_int32_t id;
416	int handle, ret, status = SSH2_FX_FAILURE;
417
418	id = get_int();
419	handle = get_handle();
420	TRACE("close id %u handle %d", id, handle);
421	ret = handle_close(handle);
422	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
423	send_status(id, status);
424}
425
426static void
427process_read(void)
428{
429	char buf[64*1024];
430	u_int32_t id, len;
431	int handle, fd, ret, status = SSH2_FX_FAILURE;
432	u_int64_t off;
433
434	id = get_int();
435	handle = get_handle();
436	off = get_int64();
437	len = get_int();
438
439	TRACE("read id %u handle %d off %llu len %d", id, handle,
440	    (u_int64_t)off, len);
441	if (len > sizeof buf) {
442		len = sizeof buf;
443		log("read change len %d", len);
444	}
445	fd = handle_to_fd(handle);
446	if (fd >= 0) {
447		if (lseek(fd, off, SEEK_SET) < 0) {
448			error("process_read: seek failed");
449			status = errno_to_portable(errno);
450		} else {
451			ret = read(fd, buf, len);
452			if (ret < 0) {
453				status = errno_to_portable(errno);
454			} else if (ret == 0) {
455				status = SSH2_FX_EOF;
456			} else {
457				send_data(id, buf, ret);
458				status = SSH2_FX_OK;
459			}
460		}
461	}
462	if (status != SSH2_FX_OK)
463		send_status(id, status);
464}
465
466static void
467process_write(void)
468{
469	u_int32_t id;
470	u_int64_t off;
471	u_int len;
472	int handle, fd, ret, status = SSH2_FX_FAILURE;
473	char *data;
474
475	id = get_int();
476	handle = get_handle();
477	off = get_int64();
478	data = get_string(&len);
479
480	TRACE("write id %u handle %d off %llu len %d", id, handle,
481	    (u_int64_t)off, len);
482	fd = handle_to_fd(handle);
483	if (fd >= 0) {
484		if (lseek(fd, off, SEEK_SET) < 0) {
485			status = errno_to_portable(errno);
486			error("process_write: seek failed");
487		} else {
488/* XXX ATOMICIO ? */
489			ret = write(fd, data, len);
490			if (ret == -1) {
491				error("process_write: write failed");
492				status = errno_to_portable(errno);
493			} else if (ret == len) {
494				status = SSH2_FX_OK;
495			} else {
496				log("nothing at all written");
497			}
498		}
499	}
500	send_status(id, status);
501	xfree(data);
502}
503
504static void
505process_do_stat(int do_lstat)
506{
507	Attrib a;
508	struct stat st;
509	u_int32_t id;
510	char *name;
511	int ret, status = SSH2_FX_FAILURE;
512
513	id = get_int();
514	name = get_string(NULL);
515	TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
516	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
517	if (ret < 0) {
518		status = errno_to_portable(errno);
519	} else {
520		stat_to_attrib(&st, &a);
521		send_attrib(id, &a);
522		status = SSH2_FX_OK;
523	}
524	if (status != SSH2_FX_OK)
525		send_status(id, status);
526	xfree(name);
527}
528
529static void
530process_stat(void)
531{
532	process_do_stat(0);
533}
534
535static void
536process_lstat(void)
537{
538	process_do_stat(1);
539}
540
541static void
542process_fstat(void)
543{
544	Attrib a;
545	struct stat st;
546	u_int32_t id;
547	int fd, ret, handle, status = SSH2_FX_FAILURE;
548
549	id = get_int();
550	handle = get_handle();
551	TRACE("fstat id %u handle %d", id, handle);
552	fd = handle_to_fd(handle);
553	if (fd  >= 0) {
554		ret = fstat(fd, &st);
555		if (ret < 0) {
556			status = errno_to_portable(errno);
557		} else {
558			stat_to_attrib(&st, &a);
559			send_attrib(id, &a);
560			status = SSH2_FX_OK;
561		}
562	}
563	if (status != SSH2_FX_OK)
564		send_status(id, status);
565}
566
567static struct timeval *
568attrib_to_tv(Attrib *a)
569{
570	static struct timeval tv[2];
571
572	tv[0].tv_sec = a->atime;
573	tv[0].tv_usec = 0;
574	tv[1].tv_sec = a->mtime;
575	tv[1].tv_usec = 0;
576	return tv;
577}
578
579static void
580process_setstat(void)
581{
582	Attrib *a;
583	u_int32_t id;
584	char *name;
585	int status = SSH2_FX_OK, ret;
586
587	id = get_int();
588	name = get_string(NULL);
589	a = get_attrib();
590	TRACE("setstat id %u name %s", id, name);
591	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
592		ret = truncate(name, a->size);
593		if (ret == -1)
594			status = errno_to_portable(errno);
595	}
596	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
597		ret = chmod(name, a->perm & 0777);
598		if (ret == -1)
599			status = errno_to_portable(errno);
600	}
601	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
602		ret = utimes(name, attrib_to_tv(a));
603		if (ret == -1)
604			status = errno_to_portable(errno);
605	}
606	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
607		ret = chown(name, a->uid, a->gid);
608		if (ret == -1)
609			status = errno_to_portable(errno);
610	}
611	send_status(id, status);
612	xfree(name);
613}
614
615static void
616process_fsetstat(void)
617{
618	Attrib *a;
619	u_int32_t id;
620	int handle, fd, ret;
621	int status = SSH2_FX_OK;
622	char *name;
623
624	id = get_int();
625	handle = get_handle();
626	a = get_attrib();
627	TRACE("fsetstat id %u handle %d", id, handle);
628	fd = handle_to_fd(handle);
629	name = handle_to_name(handle);
630	if (fd < 0 || name == NULL) {
631		status = SSH2_FX_FAILURE;
632	} else {
633		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
634			ret = ftruncate(fd, a->size);
635			if (ret == -1)
636				status = errno_to_portable(errno);
637		}
638		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
639#ifdef HAVE_FCHMOD
640			ret = fchmod(fd, a->perm & 0777);
641#else
642			ret = chmod(name, a->perm & 0777);
643#endif
644			if (ret == -1)
645				status = errno_to_portable(errno);
646		}
647		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
648#ifdef HAVE_FUTIMES
649			ret = futimes(fd, attrib_to_tv(a));
650#else
651			ret = utimes(name, attrib_to_tv(a));
652#endif
653			if (ret == -1)
654				status = errno_to_portable(errno);
655		}
656		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
657#ifdef HAVE_FCHOWN
658			ret = fchown(fd, a->uid, a->gid);
659#else
660			ret = chown(name, a->uid, a->gid);
661#endif
662			if (ret == -1)
663				status = errno_to_portable(errno);
664		}
665	}
666	send_status(id, status);
667}
668
669static void
670process_opendir(void)
671{
672	DIR *dirp = NULL;
673	char *path;
674	int handle, status = SSH2_FX_FAILURE;
675	u_int32_t id;
676
677	id = get_int();
678	path = get_string(NULL);
679	TRACE("opendir id %u path %s", id, path);
680	dirp = opendir(path);
681	if (dirp == NULL) {
682		status = errno_to_portable(errno);
683	} else {
684		handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
685		if (handle < 0) {
686			closedir(dirp);
687		} else {
688			send_handle(id, handle);
689			status = SSH2_FX_OK;
690		}
691
692	}
693	if (status != SSH2_FX_OK)
694		send_status(id, status);
695	xfree(path);
696}
697
698static void
699process_readdir(void)
700{
701	DIR *dirp;
702	struct dirent *dp;
703	char *path;
704	int handle;
705	u_int32_t id;
706
707	id = get_int();
708	handle = get_handle();
709	TRACE("readdir id %u handle %d", id, handle);
710	dirp = handle_to_dir(handle);
711	path = handle_to_name(handle);
712	if (dirp == NULL || path == NULL) {
713		send_status(id, SSH2_FX_FAILURE);
714	} else {
715		struct stat st;
716		char pathname[1024];
717		Stat *stats;
718		int nstats = 10, count = 0, i;
719
720		stats = xmalloc(nstats * sizeof(Stat));
721		while ((dp = readdir(dirp)) != NULL) {
722			if (count >= nstats) {
723				nstats *= 2;
724				stats = xrealloc(stats, nstats * sizeof(Stat));
725			}
726/* XXX OVERFLOW ? */
727			snprintf(pathname, sizeof pathname, "%s%s%s", path,
728			    strcmp(path, "/") ? "/" : "", dp->d_name);
729			if (lstat(pathname, &st) < 0)
730				continue;
731			stat_to_attrib(&st, &(stats[count].attrib));
732			stats[count].name = xstrdup(dp->d_name);
733			stats[count].long_name = ls_file(dp->d_name, &st, 0);
734			count++;
735			/* send up to 100 entries in one message */
736			/* XXX check packet size instead */
737			if (count == 100)
738				break;
739		}
740		if (count > 0) {
741			send_names(id, count, stats);
742			for (i = 0; i < count; i++) {
743				xfree(stats[i].name);
744				xfree(stats[i].long_name);
745			}
746		} else {
747			send_status(id, SSH2_FX_EOF);
748		}
749		xfree(stats);
750	}
751}
752
753static void
754process_remove(void)
755{
756	char *name;
757	u_int32_t id;
758	int status = SSH2_FX_FAILURE;
759	int ret;
760
761	id = get_int();
762	name = get_string(NULL);
763	TRACE("remove id %u name %s", id, name);
764	ret = unlink(name);
765	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
766	send_status(id, status);
767	xfree(name);
768}
769
770static void
771process_mkdir(void)
772{
773	Attrib *a;
774	u_int32_t id;
775	char *name;
776	int ret, mode, status = SSH2_FX_FAILURE;
777
778	id = get_int();
779	name = get_string(NULL);
780	a = get_attrib();
781	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
782	    a->perm & 0777 : 0777;
783	TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
784	ret = mkdir(name, mode);
785	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
786	send_status(id, status);
787	xfree(name);
788}
789
790static void
791process_rmdir(void)
792{
793	u_int32_t id;
794	char *name;
795	int ret, status;
796
797	id = get_int();
798	name = get_string(NULL);
799	TRACE("rmdir id %u name %s", id, name);
800	ret = rmdir(name);
801	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
802	send_status(id, status);
803	xfree(name);
804}
805
806static void
807process_realpath(void)
808{
809	char resolvedname[MAXPATHLEN];
810	u_int32_t id;
811	char *path;
812
813	id = get_int();
814	path = get_string(NULL);
815	if (path[0] == '\0') {
816		xfree(path);
817		path = xstrdup(".");
818	}
819	TRACE("realpath id %u path %s", id, path);
820	if (realpath(path, resolvedname) == NULL) {
821		send_status(id, errno_to_portable(errno));
822	} else {
823		Stat s;
824		attrib_clear(&s.attrib);
825		s.name = s.long_name = resolvedname;
826		send_names(id, 1, &s);
827	}
828	xfree(path);
829}
830
831static void
832process_rename(void)
833{
834	u_int32_t id;
835	struct stat st;
836	char *oldpath, *newpath;
837	int ret, status = SSH2_FX_FAILURE;
838
839	id = get_int();
840	oldpath = get_string(NULL);
841	newpath = get_string(NULL);
842	TRACE("rename id %u old %s new %s", id, oldpath, newpath);
843	/* fail if 'newpath' exists */
844	if (stat(newpath, &st) == -1) {
845		ret = rename(oldpath, newpath);
846		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
847	}
848	send_status(id, status);
849	xfree(oldpath);
850	xfree(newpath);
851}
852
853static void
854process_readlink(void)
855{
856	u_int32_t id;
857	int len;
858	char link[MAXPATHLEN];
859	char *path;
860
861	id = get_int();
862	path = get_string(NULL);
863	TRACE("readlink id %u path %s", id, path);
864	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
865		send_status(id, errno_to_portable(errno));
866	else {
867		Stat s;
868
869		link[len] = '\0';
870		attrib_clear(&s.attrib);
871		s.name = s.long_name = link;
872		send_names(id, 1, &s);
873	}
874	xfree(path);
875}
876
877static void
878process_symlink(void)
879{
880	u_int32_t id;
881	struct stat st;
882	char *oldpath, *newpath;
883	int ret, status = SSH2_FX_FAILURE;
884
885	id = get_int();
886	oldpath = get_string(NULL);
887	newpath = get_string(NULL);
888	TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
889	/* fail if 'newpath' exists */
890	if (stat(newpath, &st) == -1) {
891		ret = symlink(oldpath, newpath);
892		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
893	}
894	send_status(id, status);
895	xfree(oldpath);
896	xfree(newpath);
897}
898
899static void
900process_extended(void)
901{
902	u_int32_t id;
903	char *request;
904
905	id = get_int();
906	request = get_string(NULL);
907	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
908	xfree(request);
909}
910
911/* stolen from ssh-agent */
912
913static void
914process(void)
915{
916	u_int msg_len;
917	u_int buf_len;
918	u_int consumed;
919	u_int type;
920	u_char *cp;
921
922	buf_len = buffer_len(&iqueue);
923	if (buf_len < 5)
924		return;		/* Incomplete message. */
925	cp = buffer_ptr(&iqueue);
926	msg_len = GET_32BIT(cp);
927	if (msg_len > 256 * 1024) {
928		error("bad message ");
929		exit(11);
930	}
931	if (buf_len < msg_len + 4)
932		return;
933	buffer_consume(&iqueue, 4);
934	buf_len -= 4;
935	type = buffer_get_char(&iqueue);
936	switch (type) {
937	case SSH2_FXP_INIT:
938		process_init();
939		break;
940	case SSH2_FXP_OPEN:
941		process_open();
942		break;
943	case SSH2_FXP_CLOSE:
944		process_close();
945		break;
946	case SSH2_FXP_READ:
947		process_read();
948		break;
949	case SSH2_FXP_WRITE:
950		process_write();
951		break;
952	case SSH2_FXP_LSTAT:
953		process_lstat();
954		break;
955	case SSH2_FXP_FSTAT:
956		process_fstat();
957		break;
958	case SSH2_FXP_SETSTAT:
959		process_setstat();
960		break;
961	case SSH2_FXP_FSETSTAT:
962		process_fsetstat();
963		break;
964	case SSH2_FXP_OPENDIR:
965		process_opendir();
966		break;
967	case SSH2_FXP_READDIR:
968		process_readdir();
969		break;
970	case SSH2_FXP_REMOVE:
971		process_remove();
972		break;
973	case SSH2_FXP_MKDIR:
974		process_mkdir();
975		break;
976	case SSH2_FXP_RMDIR:
977		process_rmdir();
978		break;
979	case SSH2_FXP_REALPATH:
980		process_realpath();
981		break;
982	case SSH2_FXP_STAT:
983		process_stat();
984		break;
985	case SSH2_FXP_RENAME:
986		process_rename();
987		break;
988	case SSH2_FXP_READLINK:
989		process_readlink();
990		break;
991	case SSH2_FXP_SYMLINK:
992		process_symlink();
993		break;
994	case SSH2_FXP_EXTENDED:
995		process_extended();
996		break;
997	default:
998		error("Unknown message %d", type);
999		break;
1000	}
1001	/* discard the remaining bytes from the current packet */
1002	if (buf_len < buffer_len(&iqueue))
1003		fatal("iqueue grows");
1004	consumed = buf_len - buffer_len(&iqueue);
1005	if (msg_len < consumed)
1006		fatal("msg_len %d < consumed %d", msg_len, consumed);
1007	if (msg_len > consumed)
1008		buffer_consume(&iqueue, msg_len - consumed);
1009}
1010
1011int
1012main(int ac, char **av)
1013{
1014	fd_set *rset, *wset;
1015	int in, out, max;
1016	ssize_t len, olen, set_size;
1017
1018	/* XXX should use getopt */
1019
1020	__progname = get_progname(av[0]);
1021	handle_init();
1022
1023#ifdef DEBUG_SFTP_SERVER
1024	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1025#endif
1026
1027	in = dup(STDIN_FILENO);
1028	out = dup(STDOUT_FILENO);
1029
1030#ifdef HAVE_CYGWIN
1031	setmode(in, O_BINARY);
1032	setmode(out, O_BINARY);
1033#endif
1034
1035	max = 0;
1036	if (in > max)
1037		max = in;
1038	if (out > max)
1039		max = out;
1040
1041	buffer_init(&iqueue);
1042	buffer_init(&oqueue);
1043
1044	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1045	rset = (fd_set *)xmalloc(set_size);
1046	wset = (fd_set *)xmalloc(set_size);
1047
1048	for (;;) {
1049		memset(rset, 0, set_size);
1050		memset(wset, 0, set_size);
1051
1052		FD_SET(in, rset);
1053		olen = buffer_len(&oqueue);
1054		if (olen > 0)
1055			FD_SET(out, wset);
1056
1057		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1058			if (errno == EINTR)
1059				continue;
1060			exit(2);
1061		}
1062
1063		/* copy stdin to iqueue */
1064		if (FD_ISSET(in, rset)) {
1065			char buf[4*4096];
1066			len = read(in, buf, sizeof buf);
1067			if (len == 0) {
1068				debug("read eof");
1069				exit(0);
1070			} else if (len < 0) {
1071				error("read error");
1072				exit(1);
1073			} else {
1074				buffer_append(&iqueue, buf, len);
1075			}
1076		}
1077		/* send oqueue to stdout */
1078		if (FD_ISSET(out, wset)) {
1079			len = write(out, buffer_ptr(&oqueue), olen);
1080			if (len < 0) {
1081				error("write error");
1082				exit(1);
1083			} else {
1084				buffer_consume(&oqueue, len);
1085			}
1086		}
1087		/* process requests from client */
1088		process();
1089	}
1090}
1091