xref: /freebsd/crypto/openssh/sftp-server.c (revision d95e11bf7e5a59b5c3f81bd8dfc2918ee7d3bada)
1b66f2d16SKris Kennaway /*
2ae1f160dSDag-Erling Smørgrav  * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
3b66f2d16SKris Kennaway  *
4b66f2d16SKris Kennaway  * Redistribution and use in source and binary forms, with or without
5b66f2d16SKris Kennaway  * modification, are permitted provided that the following conditions
6b66f2d16SKris Kennaway  * are met:
7b66f2d16SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
8b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
9b66f2d16SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
10b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
11b66f2d16SKris Kennaway  *    documentation and/or other materials provided with the distribution.
12b66f2d16SKris Kennaway  *
13b66f2d16SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14b66f2d16SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15b66f2d16SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16b66f2d16SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17b66f2d16SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18b66f2d16SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19b66f2d16SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20b66f2d16SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21b66f2d16SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22b66f2d16SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23b66f2d16SKris Kennaway  */
24b66f2d16SKris Kennaway #include "includes.h"
25d95e11bfSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.43 2003/06/25 22:39:36 miod Exp $");
26b66f2d16SKris Kennaway 
27b66f2d16SKris Kennaway #include "buffer.h"
28b66f2d16SKris Kennaway #include "bufaux.h"
29b66f2d16SKris Kennaway #include "getput.h"
301e8db6e2SBrian Feldman #include "log.h"
31b66f2d16SKris Kennaway #include "xmalloc.h"
32b66f2d16SKris Kennaway 
331e8db6e2SBrian Feldman #include "sftp.h"
341e8db6e2SBrian Feldman #include "sftp-common.h"
35b66f2d16SKris Kennaway 
36b66f2d16SKris Kennaway /* helper */
371e8db6e2SBrian Feldman #define get_int64()			buffer_get_int64(&iqueue);
38b66f2d16SKris Kennaway #define get_int()			buffer_get_int(&iqueue);
39b66f2d16SKris Kennaway #define get_string(lenp)		buffer_get_string(&iqueue, lenp);
401e8db6e2SBrian Feldman #define TRACE				debug
41b66f2d16SKris Kennaway 
4283d2307dSDag-Erling Smørgrav #ifdef HAVE___PROGNAME
4383d2307dSDag-Erling Smørgrav extern char *__progname;
4483d2307dSDag-Erling Smørgrav #else
4583d2307dSDag-Erling Smørgrav char *__progname;
4683d2307dSDag-Erling Smørgrav #endif
4783d2307dSDag-Erling Smørgrav 
48b66f2d16SKris Kennaway /* input and output queue */
49b66f2d16SKris Kennaway Buffer iqueue;
50b66f2d16SKris Kennaway Buffer oqueue;
51b66f2d16SKris Kennaway 
521e8db6e2SBrian Feldman /* Version of client */
531e8db6e2SBrian Feldman int version;
541e8db6e2SBrian Feldman 
55d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */
56b66f2d16SKris Kennaway 
57b66f2d16SKris Kennaway typedef struct Stat Stat;
58b66f2d16SKris Kennaway 
591e8db6e2SBrian Feldman struct Stat {
60b66f2d16SKris Kennaway 	char *name;
61b66f2d16SKris Kennaway 	char *long_name;
62b66f2d16SKris Kennaway 	Attrib attrib;
63b66f2d16SKris Kennaway };
64b66f2d16SKris Kennaway 
65ae1f160dSDag-Erling Smørgrav static int
66b66f2d16SKris Kennaway errno_to_portable(int unixerrno)
67b66f2d16SKris Kennaway {
68b66f2d16SKris Kennaway 	int ret = 0;
691e8db6e2SBrian Feldman 
70b66f2d16SKris Kennaway 	switch (unixerrno) {
71b66f2d16SKris Kennaway 	case 0:
721e8db6e2SBrian Feldman 		ret = SSH2_FX_OK;
73b66f2d16SKris Kennaway 		break;
74b66f2d16SKris Kennaway 	case ENOENT:
75b66f2d16SKris Kennaway 	case ENOTDIR:
76b66f2d16SKris Kennaway 	case EBADF:
77b66f2d16SKris Kennaway 	case ELOOP:
781e8db6e2SBrian Feldman 		ret = SSH2_FX_NO_SUCH_FILE;
79b66f2d16SKris Kennaway 		break;
80b66f2d16SKris Kennaway 	case EPERM:
81b66f2d16SKris Kennaway 	case EACCES:
82b66f2d16SKris Kennaway 	case EFAULT:
831e8db6e2SBrian Feldman 		ret = SSH2_FX_PERMISSION_DENIED;
84b66f2d16SKris Kennaway 		break;
85b66f2d16SKris Kennaway 	case ENAMETOOLONG:
86b66f2d16SKris Kennaway 	case EINVAL:
871e8db6e2SBrian Feldman 		ret = SSH2_FX_BAD_MESSAGE;
88b66f2d16SKris Kennaway 		break;
89b66f2d16SKris Kennaway 	default:
901e8db6e2SBrian Feldman 		ret = SSH2_FX_FAILURE;
91b66f2d16SKris Kennaway 		break;
92b66f2d16SKris Kennaway 	}
93b66f2d16SKris Kennaway 	return ret;
94b66f2d16SKris Kennaway }
95b66f2d16SKris Kennaway 
96ae1f160dSDag-Erling Smørgrav static int
97b66f2d16SKris Kennaway flags_from_portable(int pflags)
98b66f2d16SKris Kennaway {
99b66f2d16SKris Kennaway 	int flags = 0;
1001e8db6e2SBrian Feldman 
1011e8db6e2SBrian Feldman 	if ((pflags & SSH2_FXF_READ) &&
1021e8db6e2SBrian Feldman 	    (pflags & SSH2_FXF_WRITE)) {
103b66f2d16SKris Kennaway 		flags = O_RDWR;
1041e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_READ) {
105b66f2d16SKris Kennaway 		flags = O_RDONLY;
1061e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_WRITE) {
107b66f2d16SKris Kennaway 		flags = O_WRONLY;
108b66f2d16SKris Kennaway 	}
1091e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_CREAT)
110b66f2d16SKris Kennaway 		flags |= O_CREAT;
1111e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_TRUNC)
112b66f2d16SKris Kennaway 		flags |= O_TRUNC;
1131e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_EXCL)
114b66f2d16SKris Kennaway 		flags |= O_EXCL;
115b66f2d16SKris Kennaway 	return flags;
116b66f2d16SKris Kennaway }
117b66f2d16SKris Kennaway 
118ae1f160dSDag-Erling Smørgrav static Attrib *
119b66f2d16SKris Kennaway get_attrib(void)
120b66f2d16SKris Kennaway {
121b66f2d16SKris Kennaway 	return decode_attrib(&iqueue);
122b66f2d16SKris Kennaway }
123b66f2d16SKris Kennaway 
124b66f2d16SKris Kennaway /* handle handles */
125b66f2d16SKris Kennaway 
126b66f2d16SKris Kennaway typedef struct Handle Handle;
127b66f2d16SKris Kennaway struct Handle {
128b66f2d16SKris Kennaway 	int use;
129b66f2d16SKris Kennaway 	DIR *dirp;
130b66f2d16SKris Kennaway 	int fd;
131b66f2d16SKris Kennaway 	char *name;
132b66f2d16SKris Kennaway };
1331e8db6e2SBrian Feldman 
134b66f2d16SKris Kennaway enum {
135b66f2d16SKris Kennaway 	HANDLE_UNUSED,
136b66f2d16SKris Kennaway 	HANDLE_DIR,
137b66f2d16SKris Kennaway 	HANDLE_FILE
138b66f2d16SKris Kennaway };
1391e8db6e2SBrian Feldman 
140b66f2d16SKris Kennaway Handle	handles[100];
141b66f2d16SKris Kennaway 
142ae1f160dSDag-Erling Smørgrav static void
143b66f2d16SKris Kennaway handle_init(void)
144b66f2d16SKris Kennaway {
145b66f2d16SKris Kennaway 	int i;
1461e8db6e2SBrian Feldman 
147b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
148b66f2d16SKris Kennaway 		handles[i].use = HANDLE_UNUSED;
149b66f2d16SKris Kennaway }
150b66f2d16SKris Kennaway 
151ae1f160dSDag-Erling Smørgrav static int
152b66f2d16SKris Kennaway handle_new(int use, char *name, int fd, DIR *dirp)
153b66f2d16SKris Kennaway {
154b66f2d16SKris Kennaway 	int i;
1551e8db6e2SBrian Feldman 
156b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
157b66f2d16SKris Kennaway 		if (handles[i].use == HANDLE_UNUSED) {
158b66f2d16SKris Kennaway 			handles[i].use = use;
159b66f2d16SKris Kennaway 			handles[i].dirp = dirp;
160b66f2d16SKris Kennaway 			handles[i].fd = fd;
161d0c8c0bcSDag-Erling Smørgrav 			handles[i].name = xstrdup(name);
162b66f2d16SKris Kennaway 			return i;
163b66f2d16SKris Kennaway 		}
164b66f2d16SKris Kennaway 	}
165b66f2d16SKris Kennaway 	return -1;
166b66f2d16SKris Kennaway }
167b66f2d16SKris Kennaway 
168ae1f160dSDag-Erling Smørgrav static int
169b66f2d16SKris Kennaway handle_is_ok(int i, int type)
170b66f2d16SKris Kennaway {
1711e8db6e2SBrian Feldman 	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
1721e8db6e2SBrian Feldman 	    handles[i].use == type;
173b66f2d16SKris Kennaway }
174b66f2d16SKris Kennaway 
175ae1f160dSDag-Erling Smørgrav static int
176b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp)
177b66f2d16SKris Kennaway {
178b66f2d16SKris Kennaway 	if (stringp == NULL || hlenp == NULL)
179b66f2d16SKris Kennaway 		return -1;
1801e8db6e2SBrian Feldman 	*stringp = xmalloc(sizeof(int32_t));
1811e8db6e2SBrian Feldman 	PUT_32BIT(*stringp, handle);
1821e8db6e2SBrian Feldman 	*hlenp = sizeof(int32_t);
183b66f2d16SKris Kennaway 	return 0;
184b66f2d16SKris Kennaway }
185b66f2d16SKris Kennaway 
186ae1f160dSDag-Erling Smørgrav static int
187b66f2d16SKris Kennaway handle_from_string(char *handle, u_int hlen)
188b66f2d16SKris Kennaway {
1891e8db6e2SBrian Feldman 	int val;
1901e8db6e2SBrian Feldman 
1911e8db6e2SBrian Feldman 	if (hlen != sizeof(int32_t))
192b66f2d16SKris Kennaway 		return -1;
1931e8db6e2SBrian Feldman 	val = GET_32BIT(handle);
194b66f2d16SKris Kennaway 	if (handle_is_ok(val, HANDLE_FILE) ||
195b66f2d16SKris Kennaway 	    handle_is_ok(val, HANDLE_DIR))
196b66f2d16SKris Kennaway 		return val;
197b66f2d16SKris Kennaway 	return -1;
198b66f2d16SKris Kennaway }
199b66f2d16SKris Kennaway 
200ae1f160dSDag-Erling Smørgrav static char *
201b66f2d16SKris Kennaway handle_to_name(int handle)
202b66f2d16SKris Kennaway {
203b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR)||
204b66f2d16SKris Kennaway 	    handle_is_ok(handle, HANDLE_FILE))
205b66f2d16SKris Kennaway 		return handles[handle].name;
206b66f2d16SKris Kennaway 	return NULL;
207b66f2d16SKris Kennaway }
208b66f2d16SKris Kennaway 
209ae1f160dSDag-Erling Smørgrav static DIR *
210b66f2d16SKris Kennaway handle_to_dir(int handle)
211b66f2d16SKris Kennaway {
212b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR))
213b66f2d16SKris Kennaway 		return handles[handle].dirp;
214b66f2d16SKris Kennaway 	return NULL;
215b66f2d16SKris Kennaway }
216b66f2d16SKris Kennaway 
217ae1f160dSDag-Erling Smørgrav static int
218b66f2d16SKris Kennaway handle_to_fd(int handle)
219b66f2d16SKris Kennaway {
220b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE))
221b66f2d16SKris Kennaway 		return handles[handle].fd;
222b66f2d16SKris Kennaway 	return -1;
223b66f2d16SKris Kennaway }
224b66f2d16SKris Kennaway 
225ae1f160dSDag-Erling Smørgrav static int
226b66f2d16SKris Kennaway handle_close(int handle)
227b66f2d16SKris Kennaway {
228b66f2d16SKris Kennaway 	int ret = -1;
2291e8db6e2SBrian Feldman 
230b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE)) {
231b66f2d16SKris Kennaway 		ret = close(handles[handle].fd);
232b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
233d0c8c0bcSDag-Erling Smørgrav 		xfree(handles[handle].name);
234b66f2d16SKris Kennaway 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
235b66f2d16SKris Kennaway 		ret = closedir(handles[handle].dirp);
236b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
237d0c8c0bcSDag-Erling Smørgrav 		xfree(handles[handle].name);
238b66f2d16SKris Kennaway 	} else {
239b66f2d16SKris Kennaway 		errno = ENOENT;
240b66f2d16SKris Kennaway 	}
241b66f2d16SKris Kennaway 	return ret;
242b66f2d16SKris Kennaway }
243b66f2d16SKris Kennaway 
244ae1f160dSDag-Erling Smørgrav static int
245b66f2d16SKris Kennaway get_handle(void)
246b66f2d16SKris Kennaway {
247b66f2d16SKris Kennaway 	char *handle;
2481e8db6e2SBrian Feldman 	int val = -1;
249b66f2d16SKris Kennaway 	u_int hlen;
2501e8db6e2SBrian Feldman 
251b66f2d16SKris Kennaway 	handle = get_string(&hlen);
2521e8db6e2SBrian Feldman 	if (hlen < 256)
253b66f2d16SKris Kennaway 		val = handle_from_string(handle, hlen);
254b66f2d16SKris Kennaway 	xfree(handle);
255b66f2d16SKris Kennaway 	return val;
256b66f2d16SKris Kennaway }
257b66f2d16SKris Kennaway 
258b66f2d16SKris Kennaway /* send replies */
259b66f2d16SKris Kennaway 
260ae1f160dSDag-Erling Smørgrav static void
261b66f2d16SKris Kennaway send_msg(Buffer *m)
262b66f2d16SKris Kennaway {
263b66f2d16SKris Kennaway 	int mlen = buffer_len(m);
2641e8db6e2SBrian Feldman 
265b66f2d16SKris Kennaway 	buffer_put_int(&oqueue, mlen);
266b66f2d16SKris Kennaway 	buffer_append(&oqueue, buffer_ptr(m), mlen);
267b66f2d16SKris Kennaway 	buffer_consume(m, mlen);
268b66f2d16SKris Kennaway }
269b66f2d16SKris Kennaway 
270ae1f160dSDag-Erling Smørgrav static void
271b66f2d16SKris Kennaway send_status(u_int32_t id, u_int32_t error)
272b66f2d16SKris Kennaway {
273b66f2d16SKris Kennaway 	Buffer msg;
2741e8db6e2SBrian Feldman 	const char *status_messages[] = {
2751e8db6e2SBrian Feldman 		"Success",			/* SSH_FX_OK */
2761e8db6e2SBrian Feldman 		"End of file",			/* SSH_FX_EOF */
2771e8db6e2SBrian Feldman 		"No such file",			/* SSH_FX_NO_SUCH_FILE */
2781e8db6e2SBrian Feldman 		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
2791e8db6e2SBrian Feldman 		"Failure",			/* SSH_FX_FAILURE */
2801e8db6e2SBrian Feldman 		"Bad message",			/* SSH_FX_BAD_MESSAGE */
2811e8db6e2SBrian Feldman 		"No connection",		/* SSH_FX_NO_CONNECTION */
2821e8db6e2SBrian Feldman 		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
2831e8db6e2SBrian Feldman 		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
2841e8db6e2SBrian Feldman 		"Unknown error"			/* Others */
2851e8db6e2SBrian Feldman 	};
2861e8db6e2SBrian Feldman 
287ee21a45fSDag-Erling Smørgrav 	TRACE("sent status id %u error %u", id, error);
288b66f2d16SKris Kennaway 	buffer_init(&msg);
2891e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_STATUS);
290b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
291b66f2d16SKris Kennaway 	buffer_put_int(&msg, error);
2921e8db6e2SBrian Feldman 	if (version >= 3) {
2931e8db6e2SBrian Feldman 		buffer_put_cstring(&msg,
2941e8db6e2SBrian Feldman 		    status_messages[MIN(error,SSH2_FX_MAX)]);
2951e8db6e2SBrian Feldman 		buffer_put_cstring(&msg, "");
2961e8db6e2SBrian Feldman 	}
297b66f2d16SKris Kennaway 	send_msg(&msg);
298b66f2d16SKris Kennaway 	buffer_free(&msg);
299b66f2d16SKris Kennaway }
300ae1f160dSDag-Erling Smørgrav static void
301b66f2d16SKris Kennaway send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
302b66f2d16SKris Kennaway {
303b66f2d16SKris Kennaway 	Buffer msg;
3041e8db6e2SBrian Feldman 
305b66f2d16SKris Kennaway 	buffer_init(&msg);
306b66f2d16SKris Kennaway 	buffer_put_char(&msg, type);
307b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
308b66f2d16SKris Kennaway 	buffer_put_string(&msg, data, dlen);
309b66f2d16SKris Kennaway 	send_msg(&msg);
310b66f2d16SKris Kennaway 	buffer_free(&msg);
311b66f2d16SKris Kennaway }
312b66f2d16SKris Kennaway 
313ae1f160dSDag-Erling Smørgrav static void
314b66f2d16SKris Kennaway send_data(u_int32_t id, char *data, int dlen)
315b66f2d16SKris Kennaway {
316ee21a45fSDag-Erling Smørgrav 	TRACE("sent data id %u len %d", id, dlen);
3171e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
318b66f2d16SKris Kennaway }
319b66f2d16SKris Kennaway 
320ae1f160dSDag-Erling Smørgrav static void
321b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle)
322b66f2d16SKris Kennaway {
323b66f2d16SKris Kennaway 	char *string;
324b66f2d16SKris Kennaway 	int hlen;
3251e8db6e2SBrian Feldman 
326b66f2d16SKris Kennaway 	handle_to_string(handle, &string, &hlen);
327ee21a45fSDag-Erling Smørgrav 	TRACE("sent handle id %u handle %d", id, handle);
3281e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
329b66f2d16SKris Kennaway 	xfree(string);
330b66f2d16SKris Kennaway }
331b66f2d16SKris Kennaway 
332ae1f160dSDag-Erling Smørgrav static void
333b66f2d16SKris Kennaway send_names(u_int32_t id, int count, Stat *stats)
334b66f2d16SKris Kennaway {
335b66f2d16SKris Kennaway 	Buffer msg;
336b66f2d16SKris Kennaway 	int i;
3371e8db6e2SBrian Feldman 
338b66f2d16SKris Kennaway 	buffer_init(&msg);
3391e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_NAME);
340b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
341b66f2d16SKris Kennaway 	buffer_put_int(&msg, count);
342ee21a45fSDag-Erling Smørgrav 	TRACE("sent names id %u count %d", id, count);
343b66f2d16SKris Kennaway 	for (i = 0; i < count; i++) {
344b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].name);
345b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].long_name);
346b66f2d16SKris Kennaway 		encode_attrib(&msg, &stats[i].attrib);
347b66f2d16SKris Kennaway 	}
348b66f2d16SKris Kennaway 	send_msg(&msg);
349b66f2d16SKris Kennaway 	buffer_free(&msg);
350b66f2d16SKris Kennaway }
351b66f2d16SKris Kennaway 
352ae1f160dSDag-Erling Smørgrav static void
353b66f2d16SKris Kennaway send_attrib(u_int32_t id, Attrib *a)
354b66f2d16SKris Kennaway {
355b66f2d16SKris Kennaway 	Buffer msg;
3561e8db6e2SBrian Feldman 
357ee21a45fSDag-Erling Smørgrav 	TRACE("sent attrib id %u have 0x%x", id, a->flags);
358b66f2d16SKris Kennaway 	buffer_init(&msg);
3591e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_ATTRS);
360b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
361b66f2d16SKris Kennaway 	encode_attrib(&msg, a);
362b66f2d16SKris Kennaway 	send_msg(&msg);
363b66f2d16SKris Kennaway 	buffer_free(&msg);
364b66f2d16SKris Kennaway }
365b66f2d16SKris Kennaway 
366b66f2d16SKris Kennaway /* parse incoming */
367b66f2d16SKris Kennaway 
368ae1f160dSDag-Erling Smørgrav static void
369b66f2d16SKris Kennaway process_init(void)
370b66f2d16SKris Kennaway {
371b66f2d16SKris Kennaway 	Buffer msg;
372b66f2d16SKris Kennaway 
373545d5ecaSDag-Erling Smørgrav 	version = get_int();
374b66f2d16SKris Kennaway 	TRACE("client version %d", version);
375b66f2d16SKris Kennaway 	buffer_init(&msg);
3761e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_VERSION);
3771e8db6e2SBrian Feldman 	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
378b66f2d16SKris Kennaway 	send_msg(&msg);
379b66f2d16SKris Kennaway 	buffer_free(&msg);
380b66f2d16SKris Kennaway }
381b66f2d16SKris Kennaway 
382ae1f160dSDag-Erling Smørgrav static void
383b66f2d16SKris Kennaway process_open(void)
384b66f2d16SKris Kennaway {
385b66f2d16SKris Kennaway 	u_int32_t id, pflags;
386b66f2d16SKris Kennaway 	Attrib *a;
387b66f2d16SKris Kennaway 	char *name;
3881e8db6e2SBrian Feldman 	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
389b66f2d16SKris Kennaway 
390b66f2d16SKris Kennaway 	id = get_int();
391b66f2d16SKris Kennaway 	name = get_string(NULL);
3921e8db6e2SBrian Feldman 	pflags = get_int();		/* portable flags */
393b66f2d16SKris Kennaway 	a = get_attrib();
394b66f2d16SKris Kennaway 	flags = flags_from_portable(pflags);
3951e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
396ee21a45fSDag-Erling Smørgrav 	TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
397b66f2d16SKris Kennaway 	fd = open(name, flags, mode);
398b66f2d16SKris Kennaway 	if (fd < 0) {
399b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
400b66f2d16SKris Kennaway 	} else {
401d0c8c0bcSDag-Erling Smørgrav 		handle = handle_new(HANDLE_FILE, name, fd, NULL);
402b66f2d16SKris Kennaway 		if (handle < 0) {
403b66f2d16SKris Kennaway 			close(fd);
404b66f2d16SKris Kennaway 		} else {
405b66f2d16SKris Kennaway 			send_handle(id, handle);
4061e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
407b66f2d16SKris Kennaway 		}
408b66f2d16SKris Kennaway 	}
4091e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
410b66f2d16SKris Kennaway 		send_status(id, status);
411b66f2d16SKris Kennaway 	xfree(name);
412b66f2d16SKris Kennaway }
413b66f2d16SKris Kennaway 
414ae1f160dSDag-Erling Smørgrav static void
415b66f2d16SKris Kennaway process_close(void)
416b66f2d16SKris Kennaway {
417b66f2d16SKris Kennaway 	u_int32_t id;
4181e8db6e2SBrian Feldman 	int handle, ret, status = SSH2_FX_FAILURE;
419b66f2d16SKris Kennaway 
420b66f2d16SKris Kennaway 	id = get_int();
421b66f2d16SKris Kennaway 	handle = get_handle();
422ee21a45fSDag-Erling Smørgrav 	TRACE("close id %u handle %d", id, handle);
423b66f2d16SKris Kennaway 	ret = handle_close(handle);
4241e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
425b66f2d16SKris Kennaway 	send_status(id, status);
426b66f2d16SKris Kennaway }
427b66f2d16SKris Kennaway 
428ae1f160dSDag-Erling Smørgrav static void
429b66f2d16SKris Kennaway process_read(void)
430b66f2d16SKris Kennaway {
431b66f2d16SKris Kennaway 	char buf[64*1024];
4321e8db6e2SBrian Feldman 	u_int32_t id, len;
4331e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
434b66f2d16SKris Kennaway 	u_int64_t off;
435b66f2d16SKris Kennaway 
436b66f2d16SKris Kennaway 	id = get_int();
437b66f2d16SKris Kennaway 	handle = get_handle();
4381e8db6e2SBrian Feldman 	off = get_int64();
439b66f2d16SKris Kennaway 	len = get_int();
440b66f2d16SKris Kennaway 
441ee21a45fSDag-Erling Smørgrav 	TRACE("read id %u handle %d off %llu len %d", id, handle,
44283d2307dSDag-Erling Smørgrav 	    (u_int64_t)off, len);
443b66f2d16SKris Kennaway 	if (len > sizeof buf) {
444b66f2d16SKris Kennaway 		len = sizeof buf;
445d95e11bfSDag-Erling Smørgrav 		logit("read change len %d", len);
446b66f2d16SKris Kennaway 	}
447b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
448b66f2d16SKris Kennaway 	if (fd >= 0) {
449b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
450b66f2d16SKris Kennaway 			error("process_read: seek failed");
451b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
452b66f2d16SKris Kennaway 		} else {
453b66f2d16SKris Kennaway 			ret = read(fd, buf, len);
454b66f2d16SKris Kennaway 			if (ret < 0) {
455b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
456b66f2d16SKris Kennaway 			} else if (ret == 0) {
4571e8db6e2SBrian Feldman 				status = SSH2_FX_EOF;
458b66f2d16SKris Kennaway 			} else {
459b66f2d16SKris Kennaway 				send_data(id, buf, ret);
4601e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
461b66f2d16SKris Kennaway 			}
462b66f2d16SKris Kennaway 		}
463b66f2d16SKris Kennaway 	}
4641e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
465b66f2d16SKris Kennaway 		send_status(id, status);
466b66f2d16SKris Kennaway }
467b66f2d16SKris Kennaway 
468ae1f160dSDag-Erling Smørgrav static void
469b66f2d16SKris Kennaway process_write(void)
470b66f2d16SKris Kennaway {
4711e8db6e2SBrian Feldman 	u_int32_t id;
472b66f2d16SKris Kennaway 	u_int64_t off;
473b66f2d16SKris Kennaway 	u_int len;
4741e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
475b66f2d16SKris Kennaway 	char *data;
476b66f2d16SKris Kennaway 
477b66f2d16SKris Kennaway 	id = get_int();
478b66f2d16SKris Kennaway 	handle = get_handle();
4791e8db6e2SBrian Feldman 	off = get_int64();
480b66f2d16SKris Kennaway 	data = get_string(&len);
481b66f2d16SKris Kennaway 
482ee21a45fSDag-Erling Smørgrav 	TRACE("write id %u handle %d off %llu len %d", id, handle,
48383d2307dSDag-Erling Smørgrav 	    (u_int64_t)off, len);
484b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
485b66f2d16SKris Kennaway 	if (fd >= 0) {
486b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
487b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
488b66f2d16SKris Kennaway 			error("process_write: seek failed");
489b66f2d16SKris Kennaway 		} else {
490b66f2d16SKris Kennaway /* XXX ATOMICIO ? */
491b66f2d16SKris Kennaway 			ret = write(fd, data, len);
492b66f2d16SKris Kennaway 			if (ret == -1) {
493b66f2d16SKris Kennaway 				error("process_write: write failed");
494b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
495b66f2d16SKris Kennaway 			} else if (ret == len) {
4961e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
497b66f2d16SKris Kennaway 			} else {
498d95e11bfSDag-Erling Smørgrav 				logit("nothing at all written");
499b66f2d16SKris Kennaway 			}
500b66f2d16SKris Kennaway 		}
501b66f2d16SKris Kennaway 	}
502b66f2d16SKris Kennaway 	send_status(id, status);
503b66f2d16SKris Kennaway 	xfree(data);
504b66f2d16SKris Kennaway }
505b66f2d16SKris Kennaway 
506ae1f160dSDag-Erling Smørgrav static void
507b66f2d16SKris Kennaway process_do_stat(int do_lstat)
508b66f2d16SKris Kennaway {
5091e8db6e2SBrian Feldman 	Attrib a;
510b66f2d16SKris Kennaway 	struct stat st;
511b66f2d16SKris Kennaway 	u_int32_t id;
512b66f2d16SKris Kennaway 	char *name;
5131e8db6e2SBrian Feldman 	int ret, status = SSH2_FX_FAILURE;
514b66f2d16SKris Kennaway 
515b66f2d16SKris Kennaway 	id = get_int();
516b66f2d16SKris Kennaway 	name = get_string(NULL);
517ee21a45fSDag-Erling Smørgrav 	TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
518b66f2d16SKris Kennaway 	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
519b66f2d16SKris Kennaway 	if (ret < 0) {
520b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
521b66f2d16SKris Kennaway 	} else {
5221e8db6e2SBrian Feldman 		stat_to_attrib(&st, &a);
5231e8db6e2SBrian Feldman 		send_attrib(id, &a);
5241e8db6e2SBrian Feldman 		status = SSH2_FX_OK;
525b66f2d16SKris Kennaway 	}
5261e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
527b66f2d16SKris Kennaway 		send_status(id, status);
528b66f2d16SKris Kennaway 	xfree(name);
529b66f2d16SKris Kennaway }
530b66f2d16SKris Kennaway 
531ae1f160dSDag-Erling Smørgrav static void
532b66f2d16SKris Kennaway process_stat(void)
533b66f2d16SKris Kennaway {
534b66f2d16SKris Kennaway 	process_do_stat(0);
535b66f2d16SKris Kennaway }
536b66f2d16SKris Kennaway 
537ae1f160dSDag-Erling Smørgrav static void
538b66f2d16SKris Kennaway process_lstat(void)
539b66f2d16SKris Kennaway {
540b66f2d16SKris Kennaway 	process_do_stat(1);
541b66f2d16SKris Kennaway }
542b66f2d16SKris Kennaway 
543ae1f160dSDag-Erling Smørgrav static void
544b66f2d16SKris Kennaway process_fstat(void)
545b66f2d16SKris Kennaway {
5461e8db6e2SBrian Feldman 	Attrib a;
547b66f2d16SKris Kennaway 	struct stat st;
548b66f2d16SKris Kennaway 	u_int32_t id;
5491e8db6e2SBrian Feldman 	int fd, ret, handle, status = SSH2_FX_FAILURE;
550b66f2d16SKris Kennaway 
551b66f2d16SKris Kennaway 	id = get_int();
552b66f2d16SKris Kennaway 	handle = get_handle();
553ee21a45fSDag-Erling Smørgrav 	TRACE("fstat id %u handle %d", id, handle);
554b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
555b66f2d16SKris Kennaway 	if (fd  >= 0) {
556b66f2d16SKris Kennaway 		ret = fstat(fd, &st);
557b66f2d16SKris Kennaway 		if (ret < 0) {
558b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
559b66f2d16SKris Kennaway 		} else {
5601e8db6e2SBrian Feldman 			stat_to_attrib(&st, &a);
5611e8db6e2SBrian Feldman 			send_attrib(id, &a);
5621e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
563b66f2d16SKris Kennaway 		}
564b66f2d16SKris Kennaway 	}
5651e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
566b66f2d16SKris Kennaway 		send_status(id, status);
567b66f2d16SKris Kennaway }
568b66f2d16SKris Kennaway 
569ae1f160dSDag-Erling Smørgrav static struct timeval *
570b66f2d16SKris Kennaway attrib_to_tv(Attrib *a)
571b66f2d16SKris Kennaway {
572b66f2d16SKris Kennaway 	static struct timeval tv[2];
5731e8db6e2SBrian Feldman 
574b66f2d16SKris Kennaway 	tv[0].tv_sec = a->atime;
575b66f2d16SKris Kennaway 	tv[0].tv_usec = 0;
576b66f2d16SKris Kennaway 	tv[1].tv_sec = a->mtime;
577b66f2d16SKris Kennaway 	tv[1].tv_usec = 0;
578b66f2d16SKris Kennaway 	return tv;
579b66f2d16SKris Kennaway }
580b66f2d16SKris Kennaway 
581ae1f160dSDag-Erling Smørgrav static void
582b66f2d16SKris Kennaway process_setstat(void)
583b66f2d16SKris Kennaway {
584b66f2d16SKris Kennaway 	Attrib *a;
585b66f2d16SKris Kennaway 	u_int32_t id;
586b66f2d16SKris Kennaway 	char *name;
587ee21a45fSDag-Erling Smørgrav 	int status = SSH2_FX_OK, ret;
588b66f2d16SKris Kennaway 
589b66f2d16SKris Kennaway 	id = get_int();
590b66f2d16SKris Kennaway 	name = get_string(NULL);
591b66f2d16SKris Kennaway 	a = get_attrib();
592ee21a45fSDag-Erling Smørgrav 	TRACE("setstat id %u name %s", id, name);
593ae1f160dSDag-Erling Smørgrav 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
594ae1f160dSDag-Erling Smørgrav 		ret = truncate(name, a->size);
595ae1f160dSDag-Erling Smørgrav 		if (ret == -1)
596ae1f160dSDag-Erling Smørgrav 			status = errno_to_portable(errno);
597ae1f160dSDag-Erling Smørgrav 	}
5981e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
599b66f2d16SKris Kennaway 		ret = chmod(name, a->perm & 0777);
600b66f2d16SKris Kennaway 		if (ret == -1)
601b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
602b66f2d16SKris Kennaway 	}
6031e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
604b66f2d16SKris Kennaway 		ret = utimes(name, attrib_to_tv(a));
605b66f2d16SKris Kennaway 		if (ret == -1)
606b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
607b66f2d16SKris Kennaway 	}
6081e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
6091e8db6e2SBrian Feldman 		ret = chown(name, a->uid, a->gid);
6101e8db6e2SBrian Feldman 		if (ret == -1)
6111e8db6e2SBrian Feldman 			status = errno_to_portable(errno);
6121e8db6e2SBrian Feldman 	}
613b66f2d16SKris Kennaway 	send_status(id, status);
614b66f2d16SKris Kennaway 	xfree(name);
615b66f2d16SKris Kennaway }
616b66f2d16SKris Kennaway 
617ae1f160dSDag-Erling Smørgrav static void
618b66f2d16SKris Kennaway process_fsetstat(void)
619b66f2d16SKris Kennaway {
620b66f2d16SKris Kennaway 	Attrib *a;
621b66f2d16SKris Kennaway 	u_int32_t id;
622b66f2d16SKris Kennaway 	int handle, fd, ret;
6231e8db6e2SBrian Feldman 	int status = SSH2_FX_OK;
62483d2307dSDag-Erling Smørgrav 	char *name;
625b66f2d16SKris Kennaway 
626b66f2d16SKris Kennaway 	id = get_int();
627b66f2d16SKris Kennaway 	handle = get_handle();
628b66f2d16SKris Kennaway 	a = get_attrib();
629ee21a45fSDag-Erling Smørgrav 	TRACE("fsetstat id %u handle %d", id, handle);
630b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
63183d2307dSDag-Erling Smørgrav 	name = handle_to_name(handle);
63283d2307dSDag-Erling Smørgrav 	if (fd < 0 || name == NULL) {
6331e8db6e2SBrian Feldman 		status = SSH2_FX_FAILURE;
634b66f2d16SKris Kennaway 	} else {
635ae1f160dSDag-Erling Smørgrav 		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
636ae1f160dSDag-Erling Smørgrav 			ret = ftruncate(fd, a->size);
637ae1f160dSDag-Erling Smørgrav 			if (ret == -1)
638ae1f160dSDag-Erling Smørgrav 				status = errno_to_portable(errno);
639ae1f160dSDag-Erling Smørgrav 		}
6401e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
64183d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD
642b66f2d16SKris Kennaway 			ret = fchmod(fd, a->perm & 0777);
64383d2307dSDag-Erling Smørgrav #else
64483d2307dSDag-Erling Smørgrav 			ret = chmod(name, a->perm & 0777);
64583d2307dSDag-Erling Smørgrav #endif
646b66f2d16SKris Kennaway 			if (ret == -1)
647b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
648b66f2d16SKris Kennaway 		}
6491e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
65083d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES
651b66f2d16SKris Kennaway 			ret = futimes(fd, attrib_to_tv(a));
65283d2307dSDag-Erling Smørgrav #else
65383d2307dSDag-Erling Smørgrav 			ret = utimes(name, attrib_to_tv(a));
65483d2307dSDag-Erling Smørgrav #endif
655b66f2d16SKris Kennaway 			if (ret == -1)
656b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
657b66f2d16SKris Kennaway 		}
6581e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
65983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN
6601e8db6e2SBrian Feldman 			ret = fchown(fd, a->uid, a->gid);
66183d2307dSDag-Erling Smørgrav #else
66283d2307dSDag-Erling Smørgrav 			ret = chown(name, a->uid, a->gid);
66383d2307dSDag-Erling Smørgrav #endif
6641e8db6e2SBrian Feldman 			if (ret == -1)
6651e8db6e2SBrian Feldman 				status = errno_to_portable(errno);
6661e8db6e2SBrian Feldman 		}
667b66f2d16SKris Kennaway 	}
668b66f2d16SKris Kennaway 	send_status(id, status);
669b66f2d16SKris Kennaway }
670b66f2d16SKris Kennaway 
671ae1f160dSDag-Erling Smørgrav static void
672b66f2d16SKris Kennaway process_opendir(void)
673b66f2d16SKris Kennaway {
674b66f2d16SKris Kennaway 	DIR *dirp = NULL;
675b66f2d16SKris Kennaway 	char *path;
6761e8db6e2SBrian Feldman 	int handle, status = SSH2_FX_FAILURE;
677b66f2d16SKris Kennaway 	u_int32_t id;
678b66f2d16SKris Kennaway 
679b66f2d16SKris Kennaway 	id = get_int();
680b66f2d16SKris Kennaway 	path = get_string(NULL);
681ee21a45fSDag-Erling Smørgrav 	TRACE("opendir id %u path %s", id, path);
682b66f2d16SKris Kennaway 	dirp = opendir(path);
683b66f2d16SKris Kennaway 	if (dirp == NULL) {
684b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
685b66f2d16SKris Kennaway 	} else {
686d0c8c0bcSDag-Erling Smørgrav 		handle = handle_new(HANDLE_DIR, path, 0, dirp);
687b66f2d16SKris Kennaway 		if (handle < 0) {
688b66f2d16SKris Kennaway 			closedir(dirp);
689b66f2d16SKris Kennaway 		} else {
690b66f2d16SKris Kennaway 			send_handle(id, handle);
6911e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
692b66f2d16SKris Kennaway 		}
693b66f2d16SKris Kennaway 
694b66f2d16SKris Kennaway 	}
6951e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
696b66f2d16SKris Kennaway 		send_status(id, status);
697b66f2d16SKris Kennaway 	xfree(path);
698b66f2d16SKris Kennaway }
699b66f2d16SKris Kennaway 
700ae1f160dSDag-Erling Smørgrav static void
701b66f2d16SKris Kennaway process_readdir(void)
702b66f2d16SKris Kennaway {
703b66f2d16SKris Kennaway 	DIR *dirp;
704b66f2d16SKris Kennaway 	struct dirent *dp;
705b66f2d16SKris Kennaway 	char *path;
706b66f2d16SKris Kennaway 	int handle;
707b66f2d16SKris Kennaway 	u_int32_t id;
708b66f2d16SKris Kennaway 
709b66f2d16SKris Kennaway 	id = get_int();
710b66f2d16SKris Kennaway 	handle = get_handle();
711ee21a45fSDag-Erling Smørgrav 	TRACE("readdir id %u handle %d", id, handle);
712b66f2d16SKris Kennaway 	dirp = handle_to_dir(handle);
713b66f2d16SKris Kennaway 	path = handle_to_name(handle);
714b66f2d16SKris Kennaway 	if (dirp == NULL || path == NULL) {
7151e8db6e2SBrian Feldman 		send_status(id, SSH2_FX_FAILURE);
716b66f2d16SKris Kennaway 	} else {
717b66f2d16SKris Kennaway 		struct stat st;
718b66f2d16SKris Kennaway 		char pathname[1024];
719b66f2d16SKris Kennaway 		Stat *stats;
720b66f2d16SKris Kennaway 		int nstats = 10, count = 0, i;
721ee21a45fSDag-Erling Smørgrav 
722b66f2d16SKris Kennaway 		stats = xmalloc(nstats * sizeof(Stat));
723b66f2d16SKris Kennaway 		while ((dp = readdir(dirp)) != NULL) {
724b66f2d16SKris Kennaway 			if (count >= nstats) {
725b66f2d16SKris Kennaway 				nstats *= 2;
726b66f2d16SKris Kennaway 				stats = xrealloc(stats, nstats * sizeof(Stat));
727b66f2d16SKris Kennaway 			}
728b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
729ae1f160dSDag-Erling Smørgrav 			snprintf(pathname, sizeof pathname, "%s%s%s", path,
730ae1f160dSDag-Erling Smørgrav 			    strcmp(path, "/") ? "/" : "", dp->d_name);
731b66f2d16SKris Kennaway 			if (lstat(pathname, &st) < 0)
732b66f2d16SKris Kennaway 				continue;
7331e8db6e2SBrian Feldman 			stat_to_attrib(&st, &(stats[count].attrib));
734b66f2d16SKris Kennaway 			stats[count].name = xstrdup(dp->d_name);
7354b17dab0SDag-Erling Smørgrav 			stats[count].long_name = ls_file(dp->d_name, &st, 0);
736b66f2d16SKris Kennaway 			count++;
737b66f2d16SKris Kennaway 			/* send up to 100 entries in one message */
7381e8db6e2SBrian Feldman 			/* XXX check packet size instead */
739b66f2d16SKris Kennaway 			if (count == 100)
740b66f2d16SKris Kennaway 				break;
741b66f2d16SKris Kennaway 		}
7421e8db6e2SBrian Feldman 		if (count > 0) {
743b66f2d16SKris Kennaway 			send_names(id, count, stats);
744b66f2d16SKris Kennaway 			for (i = 0; i < count; i++) {
745b66f2d16SKris Kennaway 				xfree(stats[i].name);
746b66f2d16SKris Kennaway 				xfree(stats[i].long_name);
747b66f2d16SKris Kennaway 			}
7481e8db6e2SBrian Feldman 		} else {
7491e8db6e2SBrian Feldman 			send_status(id, SSH2_FX_EOF);
7501e8db6e2SBrian Feldman 		}
751b66f2d16SKris Kennaway 		xfree(stats);
752b66f2d16SKris Kennaway 	}
753b66f2d16SKris Kennaway }
754b66f2d16SKris Kennaway 
755ae1f160dSDag-Erling Smørgrav static void
756b66f2d16SKris Kennaway process_remove(void)
757b66f2d16SKris Kennaway {
758b66f2d16SKris Kennaway 	char *name;
759b66f2d16SKris Kennaway 	u_int32_t id;
7601e8db6e2SBrian Feldman 	int status = SSH2_FX_FAILURE;
761b66f2d16SKris Kennaway 	int ret;
762b66f2d16SKris Kennaway 
763b66f2d16SKris Kennaway 	id = get_int();
764b66f2d16SKris Kennaway 	name = get_string(NULL);
765ee21a45fSDag-Erling Smørgrav 	TRACE("remove id %u name %s", id, name);
7661e8db6e2SBrian Feldman 	ret = unlink(name);
7671e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
768b66f2d16SKris Kennaway 	send_status(id, status);
769b66f2d16SKris Kennaway 	xfree(name);
770b66f2d16SKris Kennaway }
771b66f2d16SKris Kennaway 
772ae1f160dSDag-Erling Smørgrav static void
773b66f2d16SKris Kennaway process_mkdir(void)
774b66f2d16SKris Kennaway {
775b66f2d16SKris Kennaway 	Attrib *a;
776b66f2d16SKris Kennaway 	u_int32_t id;
777b66f2d16SKris Kennaway 	char *name;
7781e8db6e2SBrian Feldman 	int ret, mode, status = SSH2_FX_FAILURE;
779b66f2d16SKris Kennaway 
780b66f2d16SKris Kennaway 	id = get_int();
781b66f2d16SKris Kennaway 	name = get_string(NULL);
782b66f2d16SKris Kennaway 	a = get_attrib();
7831e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
7841e8db6e2SBrian Feldman 	    a->perm & 0777 : 0777;
785ee21a45fSDag-Erling Smørgrav 	TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
786b66f2d16SKris Kennaway 	ret = mkdir(name, mode);
7871e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
788b66f2d16SKris Kennaway 	send_status(id, status);
789b66f2d16SKris Kennaway 	xfree(name);
790b66f2d16SKris Kennaway }
791b66f2d16SKris Kennaway 
792ae1f160dSDag-Erling Smørgrav static void
793b66f2d16SKris Kennaway process_rmdir(void)
794b66f2d16SKris Kennaway {
795b66f2d16SKris Kennaway 	u_int32_t id;
796b66f2d16SKris Kennaway 	char *name;
797b66f2d16SKris Kennaway 	int ret, status;
798b66f2d16SKris Kennaway 
799b66f2d16SKris Kennaway 	id = get_int();
800b66f2d16SKris Kennaway 	name = get_string(NULL);
801ee21a45fSDag-Erling Smørgrav 	TRACE("rmdir id %u name %s", id, name);
802b66f2d16SKris Kennaway 	ret = rmdir(name);
8031e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
804b66f2d16SKris Kennaway 	send_status(id, status);
805b66f2d16SKris Kennaway 	xfree(name);
806b66f2d16SKris Kennaway }
807b66f2d16SKris Kennaway 
808ae1f160dSDag-Erling Smørgrav static void
809b66f2d16SKris Kennaway process_realpath(void)
810b66f2d16SKris Kennaway {
811b66f2d16SKris Kennaway 	char resolvedname[MAXPATHLEN];
812b66f2d16SKris Kennaway 	u_int32_t id;
813b66f2d16SKris Kennaway 	char *path;
814b66f2d16SKris Kennaway 
815b66f2d16SKris Kennaway 	id = get_int();
816b66f2d16SKris Kennaway 	path = get_string(NULL);
8171e8db6e2SBrian Feldman 	if (path[0] == '\0') {
8181e8db6e2SBrian Feldman 		xfree(path);
8191e8db6e2SBrian Feldman 		path = xstrdup(".");
8201e8db6e2SBrian Feldman 	}
821ee21a45fSDag-Erling Smørgrav 	TRACE("realpath id %u path %s", id, path);
822b66f2d16SKris Kennaway 	if (realpath(path, resolvedname) == NULL) {
823b66f2d16SKris Kennaway 		send_status(id, errno_to_portable(errno));
824b66f2d16SKris Kennaway 	} else {
825b66f2d16SKris Kennaway 		Stat s;
826b66f2d16SKris Kennaway 		attrib_clear(&s.attrib);
827b66f2d16SKris Kennaway 		s.name = s.long_name = resolvedname;
828b66f2d16SKris Kennaway 		send_names(id, 1, &s);
829b66f2d16SKris Kennaway 	}
830b66f2d16SKris Kennaway 	xfree(path);
831b66f2d16SKris Kennaway }
832b66f2d16SKris Kennaway 
833ae1f160dSDag-Erling Smørgrav static void
834b66f2d16SKris Kennaway process_rename(void)
835b66f2d16SKris Kennaway {
836b66f2d16SKris Kennaway 	u_int32_t id;
837b66f2d16SKris Kennaway 	char *oldpath, *newpath;
838d0c8c0bcSDag-Erling Smørgrav 	int status;
839d0c8c0bcSDag-Erling Smørgrav 	struct stat sb;
840b66f2d16SKris Kennaway 
841b66f2d16SKris Kennaway 	id = get_int();
842b66f2d16SKris Kennaway 	oldpath = get_string(NULL);
843b66f2d16SKris Kennaway 	newpath = get_string(NULL);
844ee21a45fSDag-Erling Smørgrav 	TRACE("rename id %u old %s new %s", id, oldpath, newpath);
845d0c8c0bcSDag-Erling Smørgrav 	status = SSH2_FX_FAILURE;
846d0c8c0bcSDag-Erling Smørgrav 	if (lstat(oldpath, &sb) == -1)
847d0c8c0bcSDag-Erling Smørgrav 		status = errno_to_portable(errno);
848d0c8c0bcSDag-Erling Smørgrav 	else if (S_ISREG(sb.st_mode)) {
849d0c8c0bcSDag-Erling Smørgrav 		/* Race-free rename of regular files */
850d0c8c0bcSDag-Erling Smørgrav 		if (link(oldpath, newpath) == -1)
851d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
852d0c8c0bcSDag-Erling Smørgrav 		else if (unlink(oldpath) == -1) {
853d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
854d0c8c0bcSDag-Erling Smørgrav 			/* clean spare link */
855d0c8c0bcSDag-Erling Smørgrav 			unlink(newpath);
856d0c8c0bcSDag-Erling Smørgrav 		} else
857d0c8c0bcSDag-Erling Smørgrav 			status = SSH2_FX_OK;
858d0c8c0bcSDag-Erling Smørgrav 	} else if (stat(newpath, &sb) == -1) {
859d0c8c0bcSDag-Erling Smørgrav 		if (rename(oldpath, newpath) == -1)
860d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
861d0c8c0bcSDag-Erling Smørgrav 		else
862d0c8c0bcSDag-Erling Smørgrav 			status = SSH2_FX_OK;
8631e8db6e2SBrian Feldman 	}
864b66f2d16SKris Kennaway 	send_status(id, status);
865b66f2d16SKris Kennaway 	xfree(oldpath);
866b66f2d16SKris Kennaway 	xfree(newpath);
867b66f2d16SKris Kennaway }
868b66f2d16SKris Kennaway 
869ae1f160dSDag-Erling Smørgrav static void
8701e8db6e2SBrian Feldman process_readlink(void)
8711e8db6e2SBrian Feldman {
8721e8db6e2SBrian Feldman 	u_int32_t id;
873ae1f160dSDag-Erling Smørgrav 	int len;
8741e8db6e2SBrian Feldman 	char link[MAXPATHLEN];
8751e8db6e2SBrian Feldman 	char *path;
8761e8db6e2SBrian Feldman 
8771e8db6e2SBrian Feldman 	id = get_int();
8781e8db6e2SBrian Feldman 	path = get_string(NULL);
879ee21a45fSDag-Erling Smørgrav 	TRACE("readlink id %u path %s", id, path);
880ae1f160dSDag-Erling Smørgrav 	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
8811e8db6e2SBrian Feldman 		send_status(id, errno_to_portable(errno));
8821e8db6e2SBrian Feldman 	else {
8831e8db6e2SBrian Feldman 		Stat s;
8841e8db6e2SBrian Feldman 
885ae1f160dSDag-Erling Smørgrav 		link[len] = '\0';
8861e8db6e2SBrian Feldman 		attrib_clear(&s.attrib);
8871e8db6e2SBrian Feldman 		s.name = s.long_name = link;
8881e8db6e2SBrian Feldman 		send_names(id, 1, &s);
8891e8db6e2SBrian Feldman 	}
8901e8db6e2SBrian Feldman 	xfree(path);
8911e8db6e2SBrian Feldman }
8921e8db6e2SBrian Feldman 
893ae1f160dSDag-Erling Smørgrav static void
8941e8db6e2SBrian Feldman process_symlink(void)
8951e8db6e2SBrian Feldman {
8961e8db6e2SBrian Feldman 	u_int32_t id;
8971e8db6e2SBrian Feldman 	char *oldpath, *newpath;
898d0c8c0bcSDag-Erling Smørgrav 	int ret, status;
8991e8db6e2SBrian Feldman 
9001e8db6e2SBrian Feldman 	id = get_int();
9011e8db6e2SBrian Feldman 	oldpath = get_string(NULL);
9021e8db6e2SBrian Feldman 	newpath = get_string(NULL);
903ee21a45fSDag-Erling Smørgrav 	TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
904d0c8c0bcSDag-Erling Smørgrav 	/* this will fail if 'newpath' exists */
9051e8db6e2SBrian Feldman 	ret = symlink(oldpath, newpath);
9061e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
9071e8db6e2SBrian Feldman 	send_status(id, status);
9081e8db6e2SBrian Feldman 	xfree(oldpath);
9091e8db6e2SBrian Feldman 	xfree(newpath);
9101e8db6e2SBrian Feldman }
9111e8db6e2SBrian Feldman 
912ae1f160dSDag-Erling Smørgrav static void
9131e8db6e2SBrian Feldman process_extended(void)
9141e8db6e2SBrian Feldman {
9151e8db6e2SBrian Feldman 	u_int32_t id;
9161e8db6e2SBrian Feldman 	char *request;
9171e8db6e2SBrian Feldman 
9181e8db6e2SBrian Feldman 	id = get_int();
9191e8db6e2SBrian Feldman 	request = get_string(NULL);
9201e8db6e2SBrian Feldman 	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
9211e8db6e2SBrian Feldman 	xfree(request);
9221e8db6e2SBrian Feldman }
923b66f2d16SKris Kennaway 
924b66f2d16SKris Kennaway /* stolen from ssh-agent */
925b66f2d16SKris Kennaway 
926ae1f160dSDag-Erling Smørgrav static void
927b66f2d16SKris Kennaway process(void)
928b66f2d16SKris Kennaway {
9291e8db6e2SBrian Feldman 	u_int msg_len;
930545d5ecaSDag-Erling Smørgrav 	u_int buf_len;
931545d5ecaSDag-Erling Smørgrav 	u_int consumed;
9321e8db6e2SBrian Feldman 	u_int type;
9331e8db6e2SBrian Feldman 	u_char *cp;
934b66f2d16SKris Kennaway 
935545d5ecaSDag-Erling Smørgrav 	buf_len = buffer_len(&iqueue);
936545d5ecaSDag-Erling Smørgrav 	if (buf_len < 5)
937b66f2d16SKris Kennaway 		return;		/* Incomplete message. */
938ae1f160dSDag-Erling Smørgrav 	cp = buffer_ptr(&iqueue);
939b66f2d16SKris Kennaway 	msg_len = GET_32BIT(cp);
940b66f2d16SKris Kennaway 	if (msg_len > 256 * 1024) {
941b66f2d16SKris Kennaway 		error("bad message ");
942b66f2d16SKris Kennaway 		exit(11);
943b66f2d16SKris Kennaway 	}
944545d5ecaSDag-Erling Smørgrav 	if (buf_len < msg_len + 4)
945b66f2d16SKris Kennaway 		return;
946b66f2d16SKris Kennaway 	buffer_consume(&iqueue, 4);
947545d5ecaSDag-Erling Smørgrav 	buf_len -= 4;
948b66f2d16SKris Kennaway 	type = buffer_get_char(&iqueue);
949b66f2d16SKris Kennaway 	switch (type) {
9501e8db6e2SBrian Feldman 	case SSH2_FXP_INIT:
951b66f2d16SKris Kennaway 		process_init();
952b66f2d16SKris Kennaway 		break;
9531e8db6e2SBrian Feldman 	case SSH2_FXP_OPEN:
954b66f2d16SKris Kennaway 		process_open();
955b66f2d16SKris Kennaway 		break;
9561e8db6e2SBrian Feldman 	case SSH2_FXP_CLOSE:
957b66f2d16SKris Kennaway 		process_close();
958b66f2d16SKris Kennaway 		break;
9591e8db6e2SBrian Feldman 	case SSH2_FXP_READ:
960b66f2d16SKris Kennaway 		process_read();
961b66f2d16SKris Kennaway 		break;
9621e8db6e2SBrian Feldman 	case SSH2_FXP_WRITE:
963b66f2d16SKris Kennaway 		process_write();
964b66f2d16SKris Kennaway 		break;
9651e8db6e2SBrian Feldman 	case SSH2_FXP_LSTAT:
966b66f2d16SKris Kennaway 		process_lstat();
967b66f2d16SKris Kennaway 		break;
9681e8db6e2SBrian Feldman 	case SSH2_FXP_FSTAT:
969b66f2d16SKris Kennaway 		process_fstat();
970b66f2d16SKris Kennaway 		break;
9711e8db6e2SBrian Feldman 	case SSH2_FXP_SETSTAT:
972b66f2d16SKris Kennaway 		process_setstat();
973b66f2d16SKris Kennaway 		break;
9741e8db6e2SBrian Feldman 	case SSH2_FXP_FSETSTAT:
975b66f2d16SKris Kennaway 		process_fsetstat();
976b66f2d16SKris Kennaway 		break;
9771e8db6e2SBrian Feldman 	case SSH2_FXP_OPENDIR:
978b66f2d16SKris Kennaway 		process_opendir();
979b66f2d16SKris Kennaway 		break;
9801e8db6e2SBrian Feldman 	case SSH2_FXP_READDIR:
981b66f2d16SKris Kennaway 		process_readdir();
982b66f2d16SKris Kennaway 		break;
9831e8db6e2SBrian Feldman 	case SSH2_FXP_REMOVE:
984b66f2d16SKris Kennaway 		process_remove();
985b66f2d16SKris Kennaway 		break;
9861e8db6e2SBrian Feldman 	case SSH2_FXP_MKDIR:
987b66f2d16SKris Kennaway 		process_mkdir();
988b66f2d16SKris Kennaway 		break;
9891e8db6e2SBrian Feldman 	case SSH2_FXP_RMDIR:
990b66f2d16SKris Kennaway 		process_rmdir();
991b66f2d16SKris Kennaway 		break;
9921e8db6e2SBrian Feldman 	case SSH2_FXP_REALPATH:
993b66f2d16SKris Kennaway 		process_realpath();
994b66f2d16SKris Kennaway 		break;
9951e8db6e2SBrian Feldman 	case SSH2_FXP_STAT:
996b66f2d16SKris Kennaway 		process_stat();
997b66f2d16SKris Kennaway 		break;
9981e8db6e2SBrian Feldman 	case SSH2_FXP_RENAME:
999b66f2d16SKris Kennaway 		process_rename();
1000b66f2d16SKris Kennaway 		break;
10011e8db6e2SBrian Feldman 	case SSH2_FXP_READLINK:
10021e8db6e2SBrian Feldman 		process_readlink();
10031e8db6e2SBrian Feldman 		break;
10041e8db6e2SBrian Feldman 	case SSH2_FXP_SYMLINK:
10051e8db6e2SBrian Feldman 		process_symlink();
10061e8db6e2SBrian Feldman 		break;
10071e8db6e2SBrian Feldman 	case SSH2_FXP_EXTENDED:
10081e8db6e2SBrian Feldman 		process_extended();
10091e8db6e2SBrian Feldman 		break;
1010b66f2d16SKris Kennaway 	default:
1011b66f2d16SKris Kennaway 		error("Unknown message %d", type);
1012b66f2d16SKris Kennaway 		break;
1013b66f2d16SKris Kennaway 	}
1014545d5ecaSDag-Erling Smørgrav 	/* discard the remaining bytes from the current packet */
1015545d5ecaSDag-Erling Smørgrav 	if (buf_len < buffer_len(&iqueue))
1016545d5ecaSDag-Erling Smørgrav 		fatal("iqueue grows");
1017545d5ecaSDag-Erling Smørgrav 	consumed = buf_len - buffer_len(&iqueue);
1018545d5ecaSDag-Erling Smørgrav 	if (msg_len < consumed)
1019545d5ecaSDag-Erling Smørgrav 		fatal("msg_len %d < consumed %d", msg_len, consumed);
1020545d5ecaSDag-Erling Smørgrav 	if (msg_len > consumed)
1021545d5ecaSDag-Erling Smørgrav 		buffer_consume(&iqueue, msg_len - consumed);
1022b66f2d16SKris Kennaway }
1023b66f2d16SKris Kennaway 
1024b66f2d16SKris Kennaway int
1025b66f2d16SKris Kennaway main(int ac, char **av)
1026b66f2d16SKris Kennaway {
10271e8db6e2SBrian Feldman 	fd_set *rset, *wset;
1028b66f2d16SKris Kennaway 	int in, out, max;
10291e8db6e2SBrian Feldman 	ssize_t len, olen, set_size;
10301e8db6e2SBrian Feldman 
10311e8db6e2SBrian Feldman 	/* XXX should use getopt */
1032b66f2d16SKris Kennaway 
1033d95e11bfSDag-Erling Smørgrav 	__progname = ssh_get_progname(av[0]);
1034b66f2d16SKris Kennaway 	handle_init();
1035b66f2d16SKris Kennaway 
10361e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER
10371e8db6e2SBrian Feldman 	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
10381e8db6e2SBrian Feldman #endif
10391e8db6e2SBrian Feldman 
1040b66f2d16SKris Kennaway 	in = dup(STDIN_FILENO);
1041b66f2d16SKris Kennaway 	out = dup(STDOUT_FILENO);
1042b66f2d16SKris Kennaway 
104383d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN
104483d2307dSDag-Erling Smørgrav 	setmode(in, O_BINARY);
104583d2307dSDag-Erling Smørgrav 	setmode(out, O_BINARY);
104683d2307dSDag-Erling Smørgrav #endif
104783d2307dSDag-Erling Smørgrav 
1048b66f2d16SKris Kennaway 	max = 0;
1049b66f2d16SKris Kennaway 	if (in > max)
1050b66f2d16SKris Kennaway 		max = in;
1051b66f2d16SKris Kennaway 	if (out > max)
1052b66f2d16SKris Kennaway 		max = out;
1053b66f2d16SKris Kennaway 
1054b66f2d16SKris Kennaway 	buffer_init(&iqueue);
1055b66f2d16SKris Kennaway 	buffer_init(&oqueue);
1056b66f2d16SKris Kennaway 
10571e8db6e2SBrian Feldman 	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
10581e8db6e2SBrian Feldman 	rset = (fd_set *)xmalloc(set_size);
10591e8db6e2SBrian Feldman 	wset = (fd_set *)xmalloc(set_size);
1060b66f2d16SKris Kennaway 
10611e8db6e2SBrian Feldman 	for (;;) {
10621e8db6e2SBrian Feldman 		memset(rset, 0, set_size);
10631e8db6e2SBrian Feldman 		memset(wset, 0, set_size);
10641e8db6e2SBrian Feldman 
10651e8db6e2SBrian Feldman 		FD_SET(in, rset);
1066b66f2d16SKris Kennaway 		olen = buffer_len(&oqueue);
1067b66f2d16SKris Kennaway 		if (olen > 0)
10681e8db6e2SBrian Feldman 			FD_SET(out, wset);
1069b66f2d16SKris Kennaway 
10701e8db6e2SBrian Feldman 		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1071b66f2d16SKris Kennaway 			if (errno == EINTR)
1072b66f2d16SKris Kennaway 				continue;
1073b66f2d16SKris Kennaway 			exit(2);
1074b66f2d16SKris Kennaway 		}
1075b66f2d16SKris Kennaway 
1076b66f2d16SKris Kennaway 		/* copy stdin to iqueue */
10771e8db6e2SBrian Feldman 		if (FD_ISSET(in, rset)) {
1078b66f2d16SKris Kennaway 			char buf[4*4096];
1079b66f2d16SKris Kennaway 			len = read(in, buf, sizeof buf);
1080b66f2d16SKris Kennaway 			if (len == 0) {
1081b66f2d16SKris Kennaway 				debug("read eof");
1082b66f2d16SKris Kennaway 				exit(0);
1083b66f2d16SKris Kennaway 			} else if (len < 0) {
1084b66f2d16SKris Kennaway 				error("read error");
1085b66f2d16SKris Kennaway 				exit(1);
1086b66f2d16SKris Kennaway 			} else {
1087b66f2d16SKris Kennaway 				buffer_append(&iqueue, buf, len);
1088b66f2d16SKris Kennaway 			}
1089b66f2d16SKris Kennaway 		}
1090b66f2d16SKris Kennaway 		/* send oqueue to stdout */
10911e8db6e2SBrian Feldman 		if (FD_ISSET(out, wset)) {
1092b66f2d16SKris Kennaway 			len = write(out, buffer_ptr(&oqueue), olen);
1093b66f2d16SKris Kennaway 			if (len < 0) {
1094b66f2d16SKris Kennaway 				error("write error");
1095b66f2d16SKris Kennaway 				exit(1);
1096b66f2d16SKris Kennaway 			} else {
1097b66f2d16SKris Kennaway 				buffer_consume(&oqueue, len);
1098b66f2d16SKris Kennaway 			}
1099b66f2d16SKris Kennaway 		}
1100b66f2d16SKris Kennaway 		/* process requests from client */
1101b66f2d16SKris Kennaway 		process();
1102b66f2d16SKris Kennaway 	}
1103b66f2d16SKris Kennaway }
1104