blob: 6575eb941d41b719992ec9d10d860f601317dd16 [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 Lindstrom1addabd2001-03-05 07:09:11 +000025RCSID("$OpenBSD: sftp-server.c,v 1.22 2001/03/03 22:07:50 deraadt 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 Miller33804262001-02-04 23:20:18 +110034#include "sftp-common.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110035
36/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000037#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110038#define get_int() buffer_get_int(&iqueue);
39#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000040#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110041
Ben Lindstrom49a79c02000-11-17 03:47:20 +000042#ifdef HAVE___PROGNAME
43extern char *__progname;
44#else
45char *__progname;
46#endif
47
Damien Miller7b28dc52000-09-05 13:34:53 +110048/* input and output queue */
49Buffer iqueue;
50Buffer oqueue;
51
52/* portable attibutes, etc. */
53
Damien Miller7b28dc52000-09-05 13:34:53 +110054typedef struct Stat Stat;
55
Damien Miller33804262001-02-04 23:20:18 +110056struct Stat {
Damien Miller7b28dc52000-09-05 13:34:53 +110057 char *name;
58 char *long_name;
59 Attrib attrib;
60};
61
62int
63errno_to_portable(int unixerrno)
64{
65 int ret = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000066
Damien Miller7b28dc52000-09-05 13:34:53 +110067 switch (unixerrno) {
68 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000069 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110070 break;
71 case ENOENT:
72 case ENOTDIR:
73 case EBADF:
74 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000075 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110076 break;
77 case EPERM:
78 case EACCES:
79 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000080 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110081 break;
82 case ENAMETOOLONG:
83 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000084 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110085 break;
86 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000087 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110088 break;
89 }
90 return ret;
91}
92
93int
94flags_from_portable(int pflags)
95{
96 int flags = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000097
Ben Lindstrom36592512001-03-05 05:02:08 +000098 if ((pflags & SSH2_FXF_READ) &&
99 (pflags & SSH2_FXF_WRITE)) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100100 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000101 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100102 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000103 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100104 flags = O_WRONLY;
105 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000106 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100107 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000108 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100109 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000110 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100111 flags |= O_EXCL;
112 return flags;
113}
114
Damien Miller7b28dc52000-09-05 13:34:53 +1100115Attrib *
116get_attrib(void)
117{
118 return decode_attrib(&iqueue);
119}
120
121/* handle handles */
122
123typedef struct Handle Handle;
124struct Handle {
125 int use;
126 DIR *dirp;
127 int fd;
128 char *name;
129};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000130
Damien Miller7b28dc52000-09-05 13:34:53 +1100131enum {
132 HANDLE_UNUSED,
133 HANDLE_DIR,
134 HANDLE_FILE
135};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000136
Damien Miller7b28dc52000-09-05 13:34:53 +1100137Handle handles[100];
138
139void
140handle_init(void)
141{
142 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000143
Damien Miller7b28dc52000-09-05 13:34:53 +1100144 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
145 handles[i].use = HANDLE_UNUSED;
146}
147
148int
149handle_new(int use, char *name, int fd, DIR *dirp)
150{
151 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000152
Damien Miller7b28dc52000-09-05 13:34:53 +1100153 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
154 if (handles[i].use == HANDLE_UNUSED) {
155 handles[i].use = use;
156 handles[i].dirp = dirp;
157 handles[i].fd = fd;
158 handles[i].name = name;
159 return i;
160 }
161 }
162 return -1;
163}
164
165int
166handle_is_ok(int i, int type)
167{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000168 return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
169 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100170}
171
172int
173handle_to_string(int handle, char **stringp, int *hlenp)
174{
Damien Miller7b28dc52000-09-05 13:34:53 +1100175 if (stringp == NULL || hlenp == NULL)
176 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000177 *stringp = xmalloc(sizeof(int32_t));
178 PUT_32BIT(*stringp, handle);
179 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100180 return 0;
181}
182
183int
Damien Millere4340be2000-09-16 13:29:08 +1100184handle_from_string(char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100185{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000186 int val;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000187
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000188 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100189 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000190 val = GET_32BIT(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100191 if (handle_is_ok(val, HANDLE_FILE) ||
192 handle_is_ok(val, HANDLE_DIR))
193 return val;
194 return -1;
195}
196
197char *
198handle_to_name(int handle)
199{
200 if (handle_is_ok(handle, HANDLE_DIR)||
201 handle_is_ok(handle, HANDLE_FILE))
202 return handles[handle].name;
203 return NULL;
204}
205
206DIR *
207handle_to_dir(int handle)
208{
209 if (handle_is_ok(handle, HANDLE_DIR))
210 return handles[handle].dirp;
211 return NULL;
212}
213
214int
215handle_to_fd(int handle)
216{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000217 if (handle_is_ok(handle, HANDLE_FILE))
Damien Miller7b28dc52000-09-05 13:34:53 +1100218 return handles[handle].fd;
219 return -1;
220}
221
222int
223handle_close(int handle)
224{
225 int ret = -1;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000226
Damien Miller7b28dc52000-09-05 13:34:53 +1100227 if (handle_is_ok(handle, HANDLE_FILE)) {
228 ret = close(handles[handle].fd);
229 handles[handle].use = HANDLE_UNUSED;
230 } else if (handle_is_ok(handle, HANDLE_DIR)) {
231 ret = closedir(handles[handle].dirp);
232 handles[handle].use = HANDLE_UNUSED;
233 } else {
234 errno = ENOENT;
235 }
236 return ret;
237}
238
239int
240get_handle(void)
241{
242 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000243 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100244 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000245
Damien Miller7b28dc52000-09-05 13:34:53 +1100246 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000247 if (hlen < 256)
248 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100249 xfree(handle);
250 return val;
251}
252
253/* send replies */
254
255void
256send_msg(Buffer *m)
257{
258 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000259
Damien Miller7b28dc52000-09-05 13:34:53 +1100260 buffer_put_int(&oqueue, mlen);
261 buffer_append(&oqueue, buffer_ptr(m), mlen);
262 buffer_consume(m, mlen);
263}
264
265void
266send_status(u_int32_t id, u_int32_t error)
267{
268 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000269
Damien Miller7b28dc52000-09-05 13:34:53 +1100270 TRACE("sent status id %d error %d", id, error);
271 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000272 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100273 buffer_put_int(&msg, id);
274 buffer_put_int(&msg, error);
275 send_msg(&msg);
276 buffer_free(&msg);
277}
278void
279send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
280{
281 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000282
Damien Miller7b28dc52000-09-05 13:34:53 +1100283 buffer_init(&msg);
284 buffer_put_char(&msg, type);
285 buffer_put_int(&msg, id);
286 buffer_put_string(&msg, data, dlen);
287 send_msg(&msg);
288 buffer_free(&msg);
289}
290
291void
292send_data(u_int32_t id, char *data, int dlen)
293{
294 TRACE("sent data id %d len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000295 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100296}
297
298void
299send_handle(u_int32_t id, int handle)
300{
301 char *string;
302 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000303
Damien Miller7b28dc52000-09-05 13:34:53 +1100304 handle_to_string(handle, &string, &hlen);
305 TRACE("sent handle id %d handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000306 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100307 xfree(string);
308}
309
310void
311send_names(u_int32_t id, int count, Stat *stats)
312{
313 Buffer msg;
314 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000315
Damien Miller7b28dc52000-09-05 13:34:53 +1100316 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000317 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100318 buffer_put_int(&msg, id);
319 buffer_put_int(&msg, count);
320 TRACE("sent names id %d count %d", id, count);
321 for (i = 0; i < count; i++) {
322 buffer_put_cstring(&msg, stats[i].name);
323 buffer_put_cstring(&msg, stats[i].long_name);
324 encode_attrib(&msg, &stats[i].attrib);
325 }
326 send_msg(&msg);
327 buffer_free(&msg);
328}
329
330void
331send_attrib(u_int32_t id, Attrib *a)
332{
333 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000334
Damien Miller7b28dc52000-09-05 13:34:53 +1100335 TRACE("sent attrib id %d have 0x%x", id, a->flags);
336 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000337 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100338 buffer_put_int(&msg, id);
339 encode_attrib(&msg, a);
340 send_msg(&msg);
341 buffer_free(&msg);
342}
343
344/* parse incoming */
345
346void
347process_init(void)
348{
349 Buffer msg;
350 int version = buffer_get_int(&iqueue);
351
352 TRACE("client version %d", version);
353 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000354 buffer_put_char(&msg, SSH2_FXP_VERSION);
355 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100356 send_msg(&msg);
357 buffer_free(&msg);
358}
359
360void
361process_open(void)
362{
363 u_int32_t id, pflags;
364 Attrib *a;
365 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000366 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100367
368 id = get_int();
369 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000370 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100371 a = get_attrib();
372 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000373 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Damien Miller7b28dc52000-09-05 13:34:53 +1100374 TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
375 fd = open(name, flags, mode);
376 if (fd < 0) {
377 status = errno_to_portable(errno);
378 } else {
379 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
380 if (handle < 0) {
381 close(fd);
382 } else {
383 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000384 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100385 }
386 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000387 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100388 send_status(id, status);
389 xfree(name);
390}
391
392void
393process_close(void)
394{
395 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000396 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100397
398 id = get_int();
399 handle = get_handle();
400 TRACE("close id %d handle %d", id, handle);
401 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000402 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100403 send_status(id, status);
404}
405
406void
407process_read(void)
408{
409 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000410 u_int32_t id, len;
411 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100412 u_int64_t off;
413
414 id = get_int();
415 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000416 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100417 len = get_int();
418
Kevin Stevesec1c1402001-02-05 15:39:22 +0000419 TRACE("read id %d handle %d off %llu len %d", id, handle,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000420 (u_int64_t)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100421 if (len > sizeof buf) {
422 len = sizeof buf;
423 log("read change len %d", len);
424 }
425 fd = handle_to_fd(handle);
426 if (fd >= 0) {
427 if (lseek(fd, off, SEEK_SET) < 0) {
428 error("process_read: seek failed");
429 status = errno_to_portable(errno);
430 } else {
431 ret = read(fd, buf, len);
432 if (ret < 0) {
433 status = errno_to_portable(errno);
434 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000435 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100436 } else {
437 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000438 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100439 }
440 }
441 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000442 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100443 send_status(id, status);
444}
445
446void
447process_write(void)
448{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000449 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100450 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100451 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000452 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100453 char *data;
454
455 id = get_int();
456 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000457 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100458 data = get_string(&len);
459
Kevin Stevesec1c1402001-02-05 15:39:22 +0000460 TRACE("write id %d handle %d off %llu len %d", id, handle,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000461 (u_int64_t)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100462 fd = handle_to_fd(handle);
463 if (fd >= 0) {
464 if (lseek(fd, off, SEEK_SET) < 0) {
465 status = errno_to_portable(errno);
466 error("process_write: seek failed");
467 } else {
468/* XXX ATOMICIO ? */
469 ret = write(fd, data, len);
470 if (ret == -1) {
471 error("process_write: write failed");
472 status = errno_to_portable(errno);
473 } else if (ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000474 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100475 } else {
476 log("nothing at all written");
477 }
478 }
479 }
480 send_status(id, status);
481 xfree(data);
482}
483
484void
485process_do_stat(int do_lstat)
486{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000487 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100488 struct stat st;
489 u_int32_t id;
490 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000491 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100492
493 id = get_int();
494 name = get_string(NULL);
495 TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
496 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
497 if (ret < 0) {
498 status = errno_to_portable(errno);
499 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000500 stat_to_attrib(&st, &a);
501 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000502 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100503 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000504 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100505 send_status(id, status);
506 xfree(name);
507}
508
509void
510process_stat(void)
511{
512 process_do_stat(0);
513}
514
515void
516process_lstat(void)
517{
518 process_do_stat(1);
519}
520
521void
522process_fstat(void)
523{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000524 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100525 struct stat st;
526 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000527 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100528
529 id = get_int();
530 handle = get_handle();
531 TRACE("fstat id %d handle %d", id, handle);
532 fd = handle_to_fd(handle);
533 if (fd >= 0) {
534 ret = fstat(fd, &st);
535 if (ret < 0) {
536 status = errno_to_portable(errno);
537 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000538 stat_to_attrib(&st, &a);
539 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000540 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100541 }
542 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000543 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100544 send_status(id, status);
545}
546
547struct timeval *
548attrib_to_tv(Attrib *a)
549{
550 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000551
Damien Miller7b28dc52000-09-05 13:34:53 +1100552 tv[0].tv_sec = a->atime;
553 tv[0].tv_usec = 0;
554 tv[1].tv_sec = a->mtime;
555 tv[1].tv_usec = 0;
556 return tv;
557}
558
559void
560process_setstat(void)
561{
562 Attrib *a;
563 u_int32_t id;
564 char *name;
565 int ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000566 int status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100567
568 id = get_int();
569 name = get_string(NULL);
570 a = get_attrib();
571 TRACE("setstat id %d name %s", id, name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000572 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100573 ret = chmod(name, a->perm & 0777);
574 if (ret == -1)
575 status = errno_to_portable(errno);
576 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000577 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100578 ret = utimes(name, attrib_to_tv(a));
579 if (ret == -1)
580 status = errno_to_portable(errno);
581 }
Kevin Steves8e743932001-02-05 13:24:35 +0000582 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
583 ret = chown(name, a->uid, a->gid);
584 if (ret == -1)
585 status = errno_to_portable(errno);
586 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100587 send_status(id, status);
588 xfree(name);
589}
590
591void
592process_fsetstat(void)
593{
594 Attrib *a;
595 u_int32_t id;
596 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000597 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100598 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000599
Damien Miller7b28dc52000-09-05 13:34:53 +1100600 id = get_int();
601 handle = get_handle();
602 a = get_attrib();
603 TRACE("fsetstat id %d handle %d", id, handle);
604 fd = handle_to_fd(handle);
605 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000606 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000607 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100608 } else {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000609 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000610#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100611 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000612#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000613 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000614#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100615 if (ret == -1)
616 status = errno_to_portable(errno);
617 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000618 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100619#ifdef HAVE_FUTIMES
620 ret = futimes(fd, attrib_to_tv(a));
621#else
622 ret = utimes(name, attrib_to_tv(a));
623#endif
624 if (ret == -1)
625 status = errno_to_portable(errno);
626 }
Kevin Steves8e743932001-02-05 13:24:35 +0000627 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000628#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000629 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000630#else
631 ret = chown(name, a->uid, a->gid);
632#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000633 if (ret == -1)
634 status = errno_to_portable(errno);
635 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100636 }
637 send_status(id, status);
638}
639
640void
641process_opendir(void)
642{
643 DIR *dirp = NULL;
644 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000645 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100646 u_int32_t id;
647
648 id = get_int();
649 path = get_string(NULL);
650 TRACE("opendir id %d path %s", id, path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000651 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100652 if (dirp == NULL) {
653 status = errno_to_portable(errno);
654 } else {
655 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
656 if (handle < 0) {
657 closedir(dirp);
658 } else {
659 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000660 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100661 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000662
Damien Miller7b28dc52000-09-05 13:34:53 +1100663 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000664 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100665 send_status(id, status);
666 xfree(path);
667}
668
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000669/*
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000670 * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000671 */
Damien Miller7b28dc52000-09-05 13:34:53 +1100672char *
673ls_file(char *name, struct stat *st)
674{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000675 int sz = 0;
676 struct passwd *pw;
677 struct group *gr;
678 struct tm *ltime = localtime(&st->st_mtime);
679 char *user, *group;
680 char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
681
682 strmode(st->st_mode, mode);
683 if ((pw = getpwuid(st->st_uid)) != NULL) {
684 user = pw->pw_name;
685 } else {
686 snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
687 user = ubuf;
688 }
689 if ((gr = getgrgid(st->st_gid)) != NULL) {
690 group = gr->gr_name;
691 } else {
692 snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
693 group = gbuf;
694 }
695 if (ltime != NULL) {
696 if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
697 sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
698 else
699 sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime);
700 }
701 if (sz == 0)
702 tbuf[0] = '\0';
Ben Lindstrom7e9aff52001-02-10 23:00:22 +0000703 snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8llu %s %s", mode,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000704 st->st_nlink, user, group, (u_int64_t)st->st_size, tbuf, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100705 return xstrdup(buf);
706}
707
708void
709process_readdir(void)
710{
711 DIR *dirp;
712 struct dirent *dp;
713 char *path;
714 int handle;
715 u_int32_t id;
716
717 id = get_int();
718 handle = get_handle();
719 TRACE("readdir id %d handle %d", id, handle);
720 dirp = handle_to_dir(handle);
721 path = handle_to_name(handle);
722 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000723 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100724 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100725 struct stat st;
726 char pathname[1024];
727 Stat *stats;
728 int nstats = 10, count = 0, i;
729 stats = xmalloc(nstats * sizeof(Stat));
730 while ((dp = readdir(dirp)) != NULL) {
731 if (count >= nstats) {
732 nstats *= 2;
733 stats = xrealloc(stats, nstats * sizeof(Stat));
734 }
735/* XXX OVERFLOW ? */
736 snprintf(pathname, sizeof pathname,
737 "%s/%s", path, dp->d_name);
738 if (lstat(pathname, &st) < 0)
739 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000740 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100741 stats[count].name = xstrdup(dp->d_name);
742 stats[count].long_name = ls_file(dp->d_name, &st);
743 count++;
744 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000745 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100746 if (count == 100)
747 break;
748 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000749 if (count > 0) {
750 send_names(id, count, stats);
751 for(i = 0; i < count; i++) {
752 xfree(stats[i].name);
753 xfree(stats[i].long_name);
754 }
755 } else {
756 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100757 }
758 xfree(stats);
759 }
760}
761
762void
763process_remove(void)
764{
765 char *name;
766 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000767 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100768 int ret;
769
770 id = get_int();
771 name = get_string(NULL);
772 TRACE("remove id %d name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000773 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000774 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100775 send_status(id, status);
776 xfree(name);
777}
778
779void
780process_mkdir(void)
781{
782 Attrib *a;
783 u_int32_t id;
784 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000785 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100786
787 id = get_int();
788 name = get_string(NULL);
789 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000790 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
791 a->perm & 0777 : 0777;
Damien Miller7b28dc52000-09-05 13:34:53 +1100792 TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
793 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000794 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100795 send_status(id, status);
796 xfree(name);
797}
798
799void
800process_rmdir(void)
801{
802 u_int32_t id;
803 char *name;
804 int ret, status;
805
806 id = get_int();
807 name = get_string(NULL);
808 TRACE("rmdir id %d name %s", id, name);
809 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000810 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100811 send_status(id, status);
812 xfree(name);
813}
814
815void
816process_realpath(void)
817{
818 char resolvedname[MAXPATHLEN];
819 u_int32_t id;
820 char *path;
821
822 id = get_int();
823 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000824 if (path[0] == '\0') {
825 xfree(path);
826 path = xstrdup(".");
827 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100828 TRACE("realpath id %d path %s", id, path);
829 if (realpath(path, resolvedname) == NULL) {
830 send_status(id, errno_to_portable(errno));
831 } else {
832 Stat s;
833 attrib_clear(&s.attrib);
834 s.name = s.long_name = resolvedname;
835 send_names(id, 1, &s);
836 }
837 xfree(path);
838}
839
840void
841process_rename(void)
842{
843 u_int32_t id;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000844 struct stat st;
Damien Miller7b28dc52000-09-05 13:34:53 +1100845 char *oldpath, *newpath;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000846 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100847
848 id = get_int();
849 oldpath = get_string(NULL);
850 newpath = get_string(NULL);
851 TRACE("rename id %d old %s new %s", id, oldpath, newpath);
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000852 /* fail if 'newpath' exists */
853 if (stat(newpath, &st) == -1) {
854 ret = rename(oldpath, newpath);
855 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
856 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100857 send_status(id, status);
858 xfree(oldpath);
859 xfree(newpath);
860}
861
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000862void
863process_extended(void)
864{
865 u_int32_t id;
866 char *request;
867
868 id = get_int();
869 request = get_string(NULL);
870 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
871 xfree(request);
872}
Damien Miller7b28dc52000-09-05 13:34:53 +1100873
874/* stolen from ssh-agent */
875
876void
877process(void)
878{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000879 u_int msg_len;
880 u_int type;
881 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100882
883 if (buffer_len(&iqueue) < 5)
884 return; /* Incomplete message. */
Ben Lindstrom46c16222000-12-22 01:43:59 +0000885 cp = (u_char *) buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100886 msg_len = GET_32BIT(cp);
887 if (msg_len > 256 * 1024) {
888 error("bad message ");
889 exit(11);
890 }
891 if (buffer_len(&iqueue) < msg_len + 4)
892 return;
893 buffer_consume(&iqueue, 4);
894 type = buffer_get_char(&iqueue);
895 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000896 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100897 process_init();
898 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000899 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100900 process_open();
901 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000902 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100903 process_close();
904 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000905 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100906 process_read();
907 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000908 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100909 process_write();
910 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000911 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100912 process_lstat();
913 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000914 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100915 process_fstat();
916 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000917 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100918 process_setstat();
919 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000920 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100921 process_fsetstat();
922 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000923 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100924 process_opendir();
925 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000926 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100927 process_readdir();
928 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000929 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100930 process_remove();
931 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000932 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100933 process_mkdir();
934 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000935 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100936 process_rmdir();
937 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000938 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +1100939 process_realpath();
940 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000941 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100942 process_stat();
943 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000944 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +1100945 process_rename();
946 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000947 case SSH2_FXP_EXTENDED:
948 process_extended();
949 break;
Damien Miller7b28dc52000-09-05 13:34:53 +1100950 default:
951 error("Unknown message %d", type);
952 break;
953 }
954}
955
956int
957main(int ac, char **av)
958{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +0000959 fd_set *rset, *wset;
Damien Miller7b28dc52000-09-05 13:34:53 +1100960 int in, out, max;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +0000961 ssize_t len, olen, set_size;
Damien Miller7b28dc52000-09-05 13:34:53 +1100962
Ben Lindstrom49a79c02000-11-17 03:47:20 +0000963 __progname = get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +1100964 handle_init();
965
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000966#ifdef DEBUG_SFTP_SERVER
Kevin Stevesef4eea92001-02-05 12:42:17 +0000967 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000968#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000969
Damien Miller7b28dc52000-09-05 13:34:53 +1100970 in = dup(STDIN_FILENO);
971 out = dup(STDOUT_FILENO);
972
973 max = 0;
974 if (in > max)
975 max = in;
976 if (out > max)
977 max = out;
978
979 buffer_init(&iqueue);
980 buffer_init(&oqueue);
981
Ben Lindstromcb80bdf2001-03-05 07:06:12 +0000982 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
983 rset = (fd_set *)xmalloc(set_size);
984 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +1100985
Ben Lindstromcb80bdf2001-03-05 07:06:12 +0000986 for (;;) {
987 memset(rset, 0, set_size);
988 memset(wset, 0, set_size);
989
990 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +1100991 olen = buffer_len(&oqueue);
992 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +0000993 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +1100994
Ben Lindstromcb80bdf2001-03-05 07:06:12 +0000995 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100996 if (errno == EINTR)
997 continue;
998 exit(2);
999 }
1000
1001 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001002 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001003 char buf[4*4096];
1004 len = read(in, buf, sizeof buf);
1005 if (len == 0) {
1006 debug("read eof");
1007 exit(0);
1008 } else if (len < 0) {
1009 error("read error");
1010 exit(1);
1011 } else {
1012 buffer_append(&iqueue, buf, len);
1013 }
1014 }
1015 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001016 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001017 len = write(out, buffer_ptr(&oqueue), olen);
1018 if (len < 0) {
1019 error("write error");
1020 exit(1);
1021 } else {
1022 buffer_consume(&oqueue, len);
1023 }
1024 }
1025 /* process requests from client */
1026 process();
1027 }
1028}