blob: a3e11ce5b7b852f228f2a5276e9de0795d04bfb0 [file] [log] [blame]
Damien Miller7b28dc52000-09-05 13:34:53 +11001/*
2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
Damien Miller7b28dc52000-09-05 13:34:53 +110012 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24#include "includes.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000025RCSID("$OpenBSD: sftp-server.c,v 1.14 2001/01/21 19:05:56 markus Exp $");
Damien Miller7b28dc52000-09-05 13:34:53 +110026
Damien Miller7b28dc52000-09-05 13:34:53 +110027#include "buffer.h"
28#include "bufaux.h"
29#include "getput.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000030#include "log.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110031#include "xmalloc.h"
32
Ben Lindstrom2f959b42001-01-11 06:20:23 +000033#include "sftp.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110034
35/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000036#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110037#define get_int() buffer_get_int(&iqueue);
38#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000039#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110040
Ben Lindstrom49a79c02000-11-17 03:47:20 +000041#ifdef HAVE___PROGNAME
42extern char *__progname;
43#else
44char *__progname;
45#endif
46
Damien Miller7b28dc52000-09-05 13:34:53 +110047/* input and output queue */
48Buffer iqueue;
49Buffer oqueue;
50
51/* portable attibutes, etc. */
52
53typedef struct Attrib Attrib;
54typedef struct Stat Stat;
55
56struct Attrib
57{
58 u_int32_t flags;
Damien Miller7b28dc52000-09-05 13:34:53 +110059 u_int64_t size;
60 u_int32_t uid;
61 u_int32_t gid;
62 u_int32_t perm;
63 u_int32_t atime;
64 u_int32_t mtime;
65};
66
67struct Stat
68{
69 char *name;
70 char *long_name;
71 Attrib attrib;
72};
73
74int
75errno_to_portable(int unixerrno)
76{
77 int ret = 0;
78 switch (unixerrno) {
79 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000080 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110081 break;
82 case ENOENT:
83 case ENOTDIR:
84 case EBADF:
85 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000086 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110087 break;
88 case EPERM:
89 case EACCES:
90 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000091 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110092 break;
93 case ENAMETOOLONG:
94 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000095 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110096 break;
97 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000098 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110099 break;
100 }
101 return ret;
102}
103
104int
105flags_from_portable(int pflags)
106{
107 int flags = 0;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000108 if (pflags & SSH2_FXF_READ &&
109 pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100110 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000111 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100112 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000113 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100114 flags = O_WRONLY;
115 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000116 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100117 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000118 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100119 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000120 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100121 flags |= O_EXCL;
122 return flags;
123}
124
125void
126attrib_clear(Attrib *a)
127{
128 a->flags = 0;
Damien Miller7b28dc52000-09-05 13:34:53 +1100129 a->size = 0;
130 a->uid = 0;
131 a->gid = 0;
132 a->perm = 0;
133 a->atime = 0;
134 a->mtime = 0;
135}
136
137Attrib *
138decode_attrib(Buffer *b)
139{
140 static Attrib a;
141 attrib_clear(&a);
Damien Millere4340be2000-09-16 13:29:08 +1100142 a.flags = buffer_get_int(b);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000143 if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
144 a.size = buffer_get_int64(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100145 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000146 if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
Damien Millere4340be2000-09-16 13:29:08 +1100147 a.uid = buffer_get_int(b);
148 a.gid = buffer_get_int(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100149 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000150 if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Millere4340be2000-09-16 13:29:08 +1100151 a.perm = buffer_get_int(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100152 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000153 if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Millere4340be2000-09-16 13:29:08 +1100154 a.atime = buffer_get_int(b);
155 a.mtime = buffer_get_int(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100156 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000157 /* vendor-specific extensions */
158 if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
159 char *type, *data;
160 int i, count;
161 count = buffer_get_int(b);
162 for (i = 0; i < count; i++) {
163 type = buffer_get_string(b, NULL);
164 data = buffer_get_string(b, NULL);
165 xfree(type);
166 xfree(data);
167 }
168 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100169 return &a;
170}
171
172void
173encode_attrib(Buffer *b, Attrib *a)
174{
175 buffer_put_int(b, a->flags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000176 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
177 buffer_put_int64(b, a->size);
Damien Miller7b28dc52000-09-05 13:34:53 +1100178 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000179 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100180 buffer_put_int(b, a->uid);
181 buffer_put_int(b, a->gid);
182 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000183 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100184 buffer_put_int(b, a->perm);
185 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000186 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100187 buffer_put_int(b, a->atime);
188 buffer_put_int(b, a->mtime);
189 }
190}
191
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000192void
193stat_to_attrib(struct stat *st, Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100194{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000195 attrib_clear(a);
196 a->flags = 0;
197 a->flags |= SSH2_FILEXFER_ATTR_SIZE;
198 a->size = st->st_size;
199 a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
200 a->uid = st->st_uid;
201 a->gid = st->st_gid;
202 a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
203 a->perm = st->st_mode;
204 a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
205 a->atime = st->st_atime;
206 a->mtime = st->st_mtime;
Damien Miller7b28dc52000-09-05 13:34:53 +1100207}
208
209Attrib *
210get_attrib(void)
211{
212 return decode_attrib(&iqueue);
213}
214
215/* handle handles */
216
217typedef struct Handle Handle;
218struct Handle {
219 int use;
220 DIR *dirp;
221 int fd;
222 char *name;
223};
224enum {
225 HANDLE_UNUSED,
226 HANDLE_DIR,
227 HANDLE_FILE
228};
229Handle handles[100];
230
231void
232handle_init(void)
233{
234 int i;
235 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
236 handles[i].use = HANDLE_UNUSED;
237}
238
239int
240handle_new(int use, char *name, int fd, DIR *dirp)
241{
242 int i;
243 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
244 if (handles[i].use == HANDLE_UNUSED) {
245 handles[i].use = use;
246 handles[i].dirp = dirp;
247 handles[i].fd = fd;
248 handles[i].name = name;
249 return i;
250 }
251 }
252 return -1;
253}
254
255int
256handle_is_ok(int i, int type)
257{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000258 return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
259 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100260}
261
262int
263handle_to_string(int handle, char **stringp, int *hlenp)
264{
Damien Miller7b28dc52000-09-05 13:34:53 +1100265 if (stringp == NULL || hlenp == NULL)
266 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000267 *stringp = xmalloc(sizeof(int32_t));
268 PUT_32BIT(*stringp, handle);
269 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100270 return 0;
271}
272
273int
Damien Millere4340be2000-09-16 13:29:08 +1100274handle_from_string(char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100275{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000276 int val;
277 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100278 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000279 val = GET_32BIT(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100280 if (handle_is_ok(val, HANDLE_FILE) ||
281 handle_is_ok(val, HANDLE_DIR))
282 return val;
283 return -1;
284}
285
286char *
287handle_to_name(int handle)
288{
289 if (handle_is_ok(handle, HANDLE_DIR)||
290 handle_is_ok(handle, HANDLE_FILE))
291 return handles[handle].name;
292 return NULL;
293}
294
295DIR *
296handle_to_dir(int handle)
297{
298 if (handle_is_ok(handle, HANDLE_DIR))
299 return handles[handle].dirp;
300 return NULL;
301}
302
303int
304handle_to_fd(int handle)
305{
306 if (handle_is_ok(handle, HANDLE_FILE))
307 return handles[handle].fd;
308 return -1;
309}
310
311int
312handle_close(int handle)
313{
314 int ret = -1;
315 if (handle_is_ok(handle, HANDLE_FILE)) {
316 ret = close(handles[handle].fd);
317 handles[handle].use = HANDLE_UNUSED;
318 } else if (handle_is_ok(handle, HANDLE_DIR)) {
319 ret = closedir(handles[handle].dirp);
320 handles[handle].use = HANDLE_UNUSED;
321 } else {
322 errno = ENOENT;
323 }
324 return ret;
325}
326
327int
328get_handle(void)
329{
330 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000331 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100332 u_int hlen;
Damien Miller7b28dc52000-09-05 13:34:53 +1100333 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000334 if (hlen < 256)
335 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100336 xfree(handle);
337 return val;
338}
339
340/* send replies */
341
342void
343send_msg(Buffer *m)
344{
345 int mlen = buffer_len(m);
346 buffer_put_int(&oqueue, mlen);
347 buffer_append(&oqueue, buffer_ptr(m), mlen);
348 buffer_consume(m, mlen);
349}
350
351void
352send_status(u_int32_t id, u_int32_t error)
353{
354 Buffer msg;
355 TRACE("sent status id %d error %d", id, error);
356 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000357 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100358 buffer_put_int(&msg, id);
359 buffer_put_int(&msg, error);
360 send_msg(&msg);
361 buffer_free(&msg);
362}
363void
364send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
365{
366 Buffer msg;
367 buffer_init(&msg);
368 buffer_put_char(&msg, type);
369 buffer_put_int(&msg, id);
370 buffer_put_string(&msg, data, dlen);
371 send_msg(&msg);
372 buffer_free(&msg);
373}
374
375void
376send_data(u_int32_t id, char *data, int dlen)
377{
378 TRACE("sent data id %d len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000379 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100380}
381
382void
383send_handle(u_int32_t id, int handle)
384{
385 char *string;
386 int hlen;
387 handle_to_string(handle, &string, &hlen);
388 TRACE("sent handle id %d handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000389 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100390 xfree(string);
391}
392
393void
394send_names(u_int32_t id, int count, Stat *stats)
395{
396 Buffer msg;
397 int i;
398 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000399 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100400 buffer_put_int(&msg, id);
401 buffer_put_int(&msg, count);
402 TRACE("sent names id %d count %d", id, count);
403 for (i = 0; i < count; i++) {
404 buffer_put_cstring(&msg, stats[i].name);
405 buffer_put_cstring(&msg, stats[i].long_name);
406 encode_attrib(&msg, &stats[i].attrib);
407 }
408 send_msg(&msg);
409 buffer_free(&msg);
410}
411
412void
413send_attrib(u_int32_t id, Attrib *a)
414{
415 Buffer msg;
416 TRACE("sent attrib id %d have 0x%x", id, a->flags);
417 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000418 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100419 buffer_put_int(&msg, id);
420 encode_attrib(&msg, a);
421 send_msg(&msg);
422 buffer_free(&msg);
423}
424
425/* parse incoming */
426
427void
428process_init(void)
429{
430 Buffer msg;
431 int version = buffer_get_int(&iqueue);
432
433 TRACE("client version %d", version);
434 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000435 buffer_put_char(&msg, SSH2_FXP_VERSION);
436 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100437 send_msg(&msg);
438 buffer_free(&msg);
439}
440
441void
442process_open(void)
443{
444 u_int32_t id, pflags;
445 Attrib *a;
446 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000447 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100448
449 id = get_int();
450 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000451 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100452 a = get_attrib();
453 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000454 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Damien Miller7b28dc52000-09-05 13:34:53 +1100455 TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
456 fd = open(name, flags, mode);
457 if (fd < 0) {
458 status = errno_to_portable(errno);
459 } else {
460 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
461 if (handle < 0) {
462 close(fd);
463 } else {
464 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000465 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100466 }
467 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000468 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100469 send_status(id, status);
470 xfree(name);
471}
472
473void
474process_close(void)
475{
476 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000477 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100478
479 id = get_int();
480 handle = get_handle();
481 TRACE("close id %d handle %d", id, handle);
482 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000483 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100484 send_status(id, status);
485}
486
487void
488process_read(void)
489{
490 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000491 u_int32_t id, len;
492 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100493 u_int64_t off;
494
495 id = get_int();
496 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000497 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100498 len = get_int();
499
Damien Millerf056e232000-09-23 14:58:32 +1100500 TRACE("read id %d handle %d off %lld len %d", id, handle, off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100501 if (len > sizeof buf) {
502 len = sizeof buf;
503 log("read change len %d", len);
504 }
505 fd = handle_to_fd(handle);
506 if (fd >= 0) {
507 if (lseek(fd, off, SEEK_SET) < 0) {
508 error("process_read: seek failed");
509 status = errno_to_portable(errno);
510 } else {
511 ret = read(fd, buf, len);
512 if (ret < 0) {
513 status = errno_to_portable(errno);
514 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000515 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100516 } else {
517 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000518 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100519 }
520 }
521 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000522 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100523 send_status(id, status);
524}
525
526void
527process_write(void)
528{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000529 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100530 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100531 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000532 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100533 char *data;
534
535 id = get_int();
536 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000537 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100538 data = get_string(&len);
539
Damien Millerf056e232000-09-23 14:58:32 +1100540 TRACE("write id %d handle %d off %lld len %d", id, handle, off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100541 fd = handle_to_fd(handle);
542 if (fd >= 0) {
543 if (lseek(fd, off, SEEK_SET) < 0) {
544 status = errno_to_portable(errno);
545 error("process_write: seek failed");
546 } else {
547/* XXX ATOMICIO ? */
548 ret = write(fd, data, len);
549 if (ret == -1) {
550 error("process_write: write failed");
551 status = errno_to_portable(errno);
552 } else if (ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000553 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100554 } else {
555 log("nothing at all written");
556 }
557 }
558 }
559 send_status(id, status);
560 xfree(data);
561}
562
563void
564process_do_stat(int do_lstat)
565{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000566 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100567 struct stat st;
568 u_int32_t id;
569 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000570 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100571
572 id = get_int();
573 name = get_string(NULL);
574 TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
575 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
576 if (ret < 0) {
577 status = errno_to_portable(errno);
578 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000579 stat_to_attrib(&st, &a);
580 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000581 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100582 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000583 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100584 send_status(id, status);
585 xfree(name);
586}
587
588void
589process_stat(void)
590{
591 process_do_stat(0);
592}
593
594void
595process_lstat(void)
596{
597 process_do_stat(1);
598}
599
600void
601process_fstat(void)
602{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000603 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100604 struct stat st;
605 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000606 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100607
608 id = get_int();
609 handle = get_handle();
610 TRACE("fstat id %d handle %d", id, handle);
611 fd = handle_to_fd(handle);
612 if (fd >= 0) {
613 ret = fstat(fd, &st);
614 if (ret < 0) {
615 status = errno_to_portable(errno);
616 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000617 stat_to_attrib(&st, &a);
618 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000619 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100620 }
621 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000622 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100623 send_status(id, status);
624}
625
626struct timeval *
627attrib_to_tv(Attrib *a)
628{
629 static struct timeval tv[2];
630 tv[0].tv_sec = a->atime;
631 tv[0].tv_usec = 0;
632 tv[1].tv_sec = a->mtime;
633 tv[1].tv_usec = 0;
634 return tv;
635}
636
637void
638process_setstat(void)
639{
640 Attrib *a;
641 u_int32_t id;
642 char *name;
643 int ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000644 int status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100645
646 id = get_int();
647 name = get_string(NULL);
648 a = get_attrib();
649 TRACE("setstat id %d name %s", id, name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000650 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100651 ret = chmod(name, a->perm & 0777);
652 if (ret == -1)
653 status = errno_to_portable(errno);
654 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000655 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100656 ret = utimes(name, attrib_to_tv(a));
657 if (ret == -1)
658 status = errno_to_portable(errno);
659 }
660 send_status(id, status);
661 xfree(name);
662}
663
664void
665process_fsetstat(void)
666{
667 Attrib *a;
668 u_int32_t id;
669 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000670 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100671 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000672
Damien Miller7b28dc52000-09-05 13:34:53 +1100673 id = get_int();
674 handle = get_handle();
675 a = get_attrib();
676 TRACE("fsetstat id %d handle %d", id, handle);
677 fd = handle_to_fd(handle);
678 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000679 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000680 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100681 } else {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000682 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000683#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100684 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000685#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000686 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000687#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100688 if (ret == -1)
689 status = errno_to_portable(errno);
690 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000691 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100692#ifdef HAVE_FUTIMES
693 ret = futimes(fd, attrib_to_tv(a));
694#else
695 ret = utimes(name, attrib_to_tv(a));
696#endif
697 if (ret == -1)
698 status = errno_to_portable(errno);
699 }
700 }
701 send_status(id, status);
702}
703
704void
705process_opendir(void)
706{
707 DIR *dirp = NULL;
708 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000709 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100710 u_int32_t id;
711
712 id = get_int();
713 path = get_string(NULL);
714 TRACE("opendir id %d path %s", id, path);
715 dirp = opendir(path);
716 if (dirp == NULL) {
717 status = errno_to_portable(errno);
718 } else {
719 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
720 if (handle < 0) {
721 closedir(dirp);
722 } else {
723 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000724 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100725 }
726
727 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000728 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100729 send_status(id, status);
730 xfree(path);
731}
732
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000733/*
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000734 * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000735 */
Damien Miller7b28dc52000-09-05 13:34:53 +1100736char *
737ls_file(char *name, struct stat *st)
738{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000739 int sz = 0;
740 struct passwd *pw;
741 struct group *gr;
742 struct tm *ltime = localtime(&st->st_mtime);
743 char *user, *group;
744 char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
745
746 strmode(st->st_mode, mode);
747 if ((pw = getpwuid(st->st_uid)) != NULL) {
748 user = pw->pw_name;
749 } else {
750 snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
751 user = ubuf;
752 }
753 if ((gr = getgrgid(st->st_gid)) != NULL) {
754 group = gr->gr_name;
755 } else {
756 snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
757 group = gbuf;
758 }
759 if (ltime != NULL) {
760 if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
761 sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
762 else
763 sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime);
764 }
765 if (sz == 0)
766 tbuf[0] = '\0';
Kevin Stevesb71eb582001-01-29 16:57:27 +0000767 snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8lld %s %s", mode,
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000768 st->st_nlink, user, group, (long long)st->st_size, tbuf, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100769 return xstrdup(buf);
770}
771
772void
773process_readdir(void)
774{
775 DIR *dirp;
776 struct dirent *dp;
777 char *path;
778 int handle;
779 u_int32_t id;
780
781 id = get_int();
782 handle = get_handle();
783 TRACE("readdir id %d handle %d", id, handle);
784 dirp = handle_to_dir(handle);
785 path = handle_to_name(handle);
786 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000787 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100788 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100789 struct stat st;
790 char pathname[1024];
791 Stat *stats;
792 int nstats = 10, count = 0, i;
793 stats = xmalloc(nstats * sizeof(Stat));
794 while ((dp = readdir(dirp)) != NULL) {
795 if (count >= nstats) {
796 nstats *= 2;
797 stats = xrealloc(stats, nstats * sizeof(Stat));
798 }
799/* XXX OVERFLOW ? */
800 snprintf(pathname, sizeof pathname,
801 "%s/%s", path, dp->d_name);
802 if (lstat(pathname, &st) < 0)
803 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000804 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100805 stats[count].name = xstrdup(dp->d_name);
806 stats[count].long_name = ls_file(dp->d_name, &st);
807 count++;
808 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000809 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100810 if (count == 100)
811 break;
812 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000813 if (count > 0) {
814 send_names(id, count, stats);
815 for(i = 0; i < count; i++) {
816 xfree(stats[i].name);
817 xfree(stats[i].long_name);
818 }
819 } else {
820 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100821 }
822 xfree(stats);
823 }
824}
825
826void
827process_remove(void)
828{
829 char *name;
830 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000831 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100832 int ret;
833
834 id = get_int();
835 name = get_string(NULL);
836 TRACE("remove id %d name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000837 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000838 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100839 send_status(id, status);
840 xfree(name);
841}
842
843void
844process_mkdir(void)
845{
846 Attrib *a;
847 u_int32_t id;
848 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000849 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100850
851 id = get_int();
852 name = get_string(NULL);
853 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000854 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
855 a->perm & 0777 : 0777;
Damien Miller7b28dc52000-09-05 13:34:53 +1100856 TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
857 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000858 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100859 send_status(id, status);
860 xfree(name);
861}
862
863void
864process_rmdir(void)
865{
866 u_int32_t id;
867 char *name;
868 int ret, status;
869
870 id = get_int();
871 name = get_string(NULL);
872 TRACE("rmdir id %d name %s", id, name);
873 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000874 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100875 send_status(id, status);
876 xfree(name);
877}
878
879void
880process_realpath(void)
881{
882 char resolvedname[MAXPATHLEN];
883 u_int32_t id;
884 char *path;
885
886 id = get_int();
887 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000888 if (path[0] == '\0') {
889 xfree(path);
890 path = xstrdup(".");
891 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100892 TRACE("realpath id %d path %s", id, path);
893 if (realpath(path, resolvedname) == NULL) {
894 send_status(id, errno_to_portable(errno));
895 } else {
896 Stat s;
897 attrib_clear(&s.attrib);
898 s.name = s.long_name = resolvedname;
899 send_names(id, 1, &s);
900 }
901 xfree(path);
902}
903
904void
905process_rename(void)
906{
907 u_int32_t id;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000908 struct stat st;
Damien Miller7b28dc52000-09-05 13:34:53 +1100909 char *oldpath, *newpath;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000910 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100911
912 id = get_int();
913 oldpath = get_string(NULL);
914 newpath = get_string(NULL);
915 TRACE("rename id %d old %s new %s", id, oldpath, newpath);
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000916 /* fail if 'newpath' exists */
917 if (stat(newpath, &st) == -1) {
918 ret = rename(oldpath, newpath);
919 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
920 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100921 send_status(id, status);
922 xfree(oldpath);
923 xfree(newpath);
924}
925
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000926void
927process_extended(void)
928{
929 u_int32_t id;
930 char *request;
931
932 id = get_int();
933 request = get_string(NULL);
934 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
935 xfree(request);
936}
Damien Miller7b28dc52000-09-05 13:34:53 +1100937
938/* stolen from ssh-agent */
939
940void
941process(void)
942{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000943 u_int msg_len;
944 u_int type;
945 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100946
947 if (buffer_len(&iqueue) < 5)
948 return; /* Incomplete message. */
Ben Lindstrom46c16222000-12-22 01:43:59 +0000949 cp = (u_char *) buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100950 msg_len = GET_32BIT(cp);
951 if (msg_len > 256 * 1024) {
952 error("bad message ");
953 exit(11);
954 }
955 if (buffer_len(&iqueue) < msg_len + 4)
956 return;
957 buffer_consume(&iqueue, 4);
958 type = buffer_get_char(&iqueue);
959 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000960 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100961 process_init();
962 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000963 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100964 process_open();
965 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000966 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100967 process_close();
968 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000969 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100970 process_read();
971 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000972 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100973 process_write();
974 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000975 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100976 process_lstat();
977 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000978 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100979 process_fstat();
980 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000981 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100982 process_setstat();
983 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000984 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100985 process_fsetstat();
986 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000987 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100988 process_opendir();
989 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000990 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100991 process_readdir();
992 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000993 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100994 process_remove();
995 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000996 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100997 process_mkdir();
998 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000999 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001000 process_rmdir();
1001 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001002 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +11001003 process_realpath();
1004 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001005 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001006 process_stat();
1007 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001008 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +11001009 process_rename();
1010 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001011 case SSH2_FXP_EXTENDED:
1012 process_extended();
1013 break;
Damien Miller7b28dc52000-09-05 13:34:53 +11001014 default:
1015 error("Unknown message %d", type);
1016 break;
1017 }
1018}
1019
1020int
1021main(int ac, char **av)
1022{
1023 fd_set rset, wset;
1024 int in, out, max;
Damien Millere4340be2000-09-16 13:29:08 +11001025 ssize_t len, olen;
Damien Miller7b28dc52000-09-05 13:34:53 +11001026
Ben Lindstrom49a79c02000-11-17 03:47:20 +00001027 __progname = get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001028 handle_init();
1029
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001030#ifdef DEBUG_SFTP_SERVER
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001031 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001032#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001033
Damien Miller7b28dc52000-09-05 13:34:53 +11001034 in = dup(STDIN_FILENO);
1035 out = dup(STDOUT_FILENO);
1036
1037 max = 0;
1038 if (in > max)
1039 max = in;
1040 if (out > max)
1041 max = out;
1042
1043 buffer_init(&iqueue);
1044 buffer_init(&oqueue);
1045
1046 for (;;) {
1047 FD_ZERO(&rset);
1048 FD_ZERO(&wset);
1049
1050 FD_SET(in, &rset);
1051 olen = buffer_len(&oqueue);
1052 if (olen > 0)
1053 FD_SET(out, &wset);
1054
1055 if (select(max+1, &rset, &wset, NULL, NULL) < 0) {
1056 if (errno == EINTR)
1057 continue;
1058 exit(2);
1059 }
1060
1061 /* copy stdin to iqueue */
1062 if (FD_ISSET(in, &rset)) {
1063 char buf[4*4096];
1064 len = read(in, buf, sizeof buf);
1065 if (len == 0) {
1066 debug("read eof");
1067 exit(0);
1068 } else if (len < 0) {
1069 error("read error");
1070 exit(1);
1071 } else {
1072 buffer_append(&iqueue, buf, len);
1073 }
1074 }
1075 /* send oqueue to stdout */
1076 if (FD_ISSET(out, &wset)) {
1077 len = write(out, buffer_ptr(&oqueue), olen);
1078 if (len < 0) {
1079 error("write error");
1080 exit(1);
1081 } else {
1082 buffer_consume(&oqueue, len);
1083 }
1084 }
1085 /* process requests from client */
1086 process();
1087 }
1088}