Annotation of src/usr.bin/ssh/sftp-server.c, Revision 1.63
1.63 ! stevesk 1: /* $OpenBSD: sftp-server.c,v 1.62 2006/07/11 20:07:25 stevesk Exp $ */
1.1 markus 2: /*
1.45 markus 3: * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
1.1 markus 4: *
1.45 markus 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 markus 8: *
1.45 markus 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 markus 16: */
17: #include "includes.h"
1.52 stevesk 18:
19: #include <sys/types.h>
20: #include <sys/stat.h>
1.51 stevesk 21:
22: #include <dirent.h>
1.62 stevesk 23: #include <errno.h>
1.59 stevesk 24: #include <fcntl.h>
1.63 ! stevesk 25: #include <unistd.h>
1.1 markus 26:
27: #include "buffer.h"
28: #include "bufaux.h"
1.14 markus 29: #include "log.h"
1.1 markus 30: #include "xmalloc.h"
1.49 djm 31: #include "misc.h"
1.58 djm 32: #include "uidswap.h"
1.1 markus 33:
1.10 markus 34: #include "sftp.h"
1.15 djm 35: #include "sftp-common.h"
1.1 markus 36:
37: /* helper */
1.10 markus 38: #define get_int64() buffer_get_int64(&iqueue);
1.1 markus 39: #define get_int() buffer_get_int(&iqueue);
40: #define get_string(lenp) buffer_get_string(&iqueue, lenp);
1.58 djm 41:
42: /* Our verbosity */
43: LogLevel log_level = SYSLOG_LEVEL_ERROR;
44:
45: /* Our client */
46: struct passwd *pw = NULL;
47: char *client_addr = NULL;
1.1 markus 48:
49: /* input and output queue */
50: Buffer iqueue;
51: Buffer oqueue;
52:
1.23 djm 53: /* Version of client */
54: int version;
55:
1.43 miod 56: /* portable attributes, etc. */
1.1 markus 57:
58: typedef struct Stat Stat;
59:
1.15 djm 60: struct Stat {
1.1 markus 61: char *name;
62: char *long_name;
63: Attrib attrib;
64: };
65:
1.28 itojun 66: static int
1.2 markus 67: errno_to_portable(int unixerrno)
1.1 markus 68: {
69: int ret = 0;
1.22 deraadt 70:
1.2 markus 71: switch (unixerrno) {
1.1 markus 72: case 0:
1.10 markus 73: ret = SSH2_FX_OK;
1.1 markus 74: break;
75: case ENOENT:
76: case ENOTDIR:
77: case EBADF:
78: case ELOOP:
1.10 markus 79: ret = SSH2_FX_NO_SUCH_FILE;
1.1 markus 80: break;
81: case EPERM:
82: case EACCES:
83: case EFAULT:
1.10 markus 84: ret = SSH2_FX_PERMISSION_DENIED;
1.1 markus 85: break;
86: case ENAMETOOLONG:
87: case EINVAL:
1.10 markus 88: ret = SSH2_FX_BAD_MESSAGE;
1.1 markus 89: break;
90: default:
1.10 markus 91: ret = SSH2_FX_FAILURE;
1.1 markus 92: break;
93: }
94: return ret;
95: }
96:
1.28 itojun 97: static int
1.1 markus 98: flags_from_portable(int pflags)
99: {
100: int flags = 0;
1.22 deraadt 101:
1.20 deraadt 102: if ((pflags & SSH2_FXF_READ) &&
103: (pflags & SSH2_FXF_WRITE)) {
1.1 markus 104: flags = O_RDWR;
1.10 markus 105: } else if (pflags & SSH2_FXF_READ) {
1.1 markus 106: flags = O_RDONLY;
1.10 markus 107: } else if (pflags & SSH2_FXF_WRITE) {
1.1 markus 108: flags = O_WRONLY;
109: }
1.10 markus 110: if (pflags & SSH2_FXF_CREAT)
1.1 markus 111: flags |= O_CREAT;
1.10 markus 112: if (pflags & SSH2_FXF_TRUNC)
1.1 markus 113: flags |= O_TRUNC;
1.10 markus 114: if (pflags & SSH2_FXF_EXCL)
1.1 markus 115: flags |= O_EXCL;
116: return flags;
117: }
118:
1.58 djm 119: static const char *
120: string_from_portable(int pflags)
121: {
122: static char ret[128];
123:
124: *ret = '\0';
125:
126: #define PAPPEND(str) { \
127: if (*ret != '\0') \
128: strlcat(ret, ",", sizeof(ret)); \
129: strlcat(ret, str, sizeof(ret)); \
130: }
131:
132: if (pflags & SSH2_FXF_READ)
133: PAPPEND("READ")
134: if (pflags & SSH2_FXF_WRITE)
135: PAPPEND("WRITE")
136: if (pflags & SSH2_FXF_CREAT)
137: PAPPEND("CREATE")
138: if (pflags & SSH2_FXF_TRUNC)
139: PAPPEND("TRUNCATE")
140: if (pflags & SSH2_FXF_EXCL)
141: PAPPEND("EXCL")
142:
143: return ret;
144: }
145:
1.28 itojun 146: static Attrib *
1.1 markus 147: get_attrib(void)
148: {
149: return decode_attrib(&iqueue);
150: }
151:
152: /* handle handles */
153:
154: typedef struct Handle Handle;
155: struct Handle {
156: int use;
157: DIR *dirp;
158: int fd;
159: char *name;
1.58 djm 160: u_int64_t bytes_read, bytes_write;
1.1 markus 161: };
1.22 deraadt 162:
1.1 markus 163: enum {
164: HANDLE_UNUSED,
165: HANDLE_DIR,
166: HANDLE_FILE
167: };
1.22 deraadt 168:
1.1 markus 169: Handle handles[100];
170:
1.28 itojun 171: static void
1.1 markus 172: handle_init(void)
173: {
1.48 djm 174: u_int i;
1.22 deraadt 175:
1.31 deraadt 176: for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
1.1 markus 177: handles[i].use = HANDLE_UNUSED;
178: }
179:
1.28 itojun 180: static int
1.44 jakob 181: handle_new(int use, const char *name, int fd, DIR *dirp)
1.1 markus 182: {
1.48 djm 183: u_int i;
1.22 deraadt 184:
1.31 deraadt 185: for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
1.1 markus 186: if (handles[i].use == HANDLE_UNUSED) {
187: handles[i].use = use;
188: handles[i].dirp = dirp;
189: handles[i].fd = fd;
1.40 markus 190: handles[i].name = xstrdup(name);
1.58 djm 191: handles[i].bytes_read = handles[i].bytes_write = 0;
1.1 markus 192: return i;
193: }
194: }
195: return -1;
196: }
197:
1.28 itojun 198: static int
1.1 markus 199: handle_is_ok(int i, int type)
200: {
1.48 djm 201: return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
1.10 markus 202: handles[i].use == type;
1.1 markus 203: }
204:
1.28 itojun 205: static int
1.1 markus 206: handle_to_string(int handle, char **stringp, int *hlenp)
207: {
208: if (stringp == NULL || hlenp == NULL)
209: return -1;
1.13 markus 210: *stringp = xmalloc(sizeof(int32_t));
1.57 djm 211: put_u32(*stringp, handle);
1.13 markus 212: *hlenp = sizeof(int32_t);
1.1 markus 213: return 0;
214: }
215:
1.28 itojun 216: static int
1.44 jakob 217: handle_from_string(const char *handle, u_int hlen)
1.1 markus 218: {
1.13 markus 219: int val;
1.22 deraadt 220:
1.13 markus 221: if (hlen != sizeof(int32_t))
1.1 markus 222: return -1;
1.57 djm 223: val = get_u32(handle);
1.1 markus 224: if (handle_is_ok(val, HANDLE_FILE) ||
225: handle_is_ok(val, HANDLE_DIR))
226: return val;
227: return -1;
228: }
229:
1.28 itojun 230: static char *
1.1 markus 231: handle_to_name(int handle)
232: {
233: if (handle_is_ok(handle, HANDLE_DIR)||
234: handle_is_ok(handle, HANDLE_FILE))
235: return handles[handle].name;
236: return NULL;
237: }
238:
1.28 itojun 239: static DIR *
1.1 markus 240: handle_to_dir(int handle)
241: {
242: if (handle_is_ok(handle, HANDLE_DIR))
243: return handles[handle].dirp;
244: return NULL;
245: }
246:
1.28 itojun 247: static int
1.1 markus 248: handle_to_fd(int handle)
249: {
1.17 stevesk 250: if (handle_is_ok(handle, HANDLE_FILE))
1.1 markus 251: return handles[handle].fd;
252: return -1;
253: }
254:
1.58 djm 255: static void
256: handle_update_read(int handle, ssize_t bytes)
257: {
258: if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
259: handles[handle].bytes_read += bytes;
260: }
261:
262: static void
263: handle_update_write(int handle, ssize_t bytes)
264: {
265: if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
266: handles[handle].bytes_write += bytes;
267: }
268:
269: static u_int64_t
270: handle_bytes_read(int handle)
271: {
272: if (handle_is_ok(handle, HANDLE_FILE))
273: return (handles[handle].bytes_read);
274: return 0;
275: }
276:
277: static u_int64_t
278: handle_bytes_write(int handle)
279: {
280: if (handle_is_ok(handle, HANDLE_FILE))
281: return (handles[handle].bytes_write);
282: return 0;
283: }
284:
1.28 itojun 285: static int
1.1 markus 286: handle_close(int handle)
287: {
288: int ret = -1;
1.22 deraadt 289:
1.1 markus 290: if (handle_is_ok(handle, HANDLE_FILE)) {
291: ret = close(handles[handle].fd);
292: handles[handle].use = HANDLE_UNUSED;
1.40 markus 293: xfree(handles[handle].name);
1.1 markus 294: } else if (handle_is_ok(handle, HANDLE_DIR)) {
295: ret = closedir(handles[handle].dirp);
296: handles[handle].use = HANDLE_UNUSED;
1.40 markus 297: xfree(handles[handle].name);
1.1 markus 298: } else {
299: errno = ENOENT;
300: }
301: return ret;
302: }
303:
1.58 djm 304: static void
305: handle_log_close(int handle, char *emsg)
306: {
307: if (handle_is_ok(handle, HANDLE_FILE)) {
308: logit("%s%sclose \"%s\" bytes read %llu written %llu",
309: emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
310: handle_to_name(handle),
311: handle_bytes_read(handle), handle_bytes_write(handle));
312: } else {
313: logit("%s%sclosedir \"%s\"",
314: emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
315: handle_to_name(handle));
316: }
317: }
318:
319: static void
320: handle_log_exit(void)
321: {
322: u_int i;
323:
324: for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
325: if (handles[i].use != HANDLE_UNUSED)
326: handle_log_close(i, "forced");
327: }
328:
1.28 itojun 329: static int
1.1 markus 330: get_handle(void)
331: {
332: char *handle;
1.10 markus 333: int val = -1;
1.5 markus 334: u_int hlen;
1.22 deraadt 335:
1.1 markus 336: handle = get_string(&hlen);
1.10 markus 337: if (hlen < 256)
338: val = handle_from_string(handle, hlen);
1.1 markus 339: xfree(handle);
340: return val;
341: }
342:
343: /* send replies */
344:
1.28 itojun 345: static void
1.1 markus 346: send_msg(Buffer *m)
347: {
348: int mlen = buffer_len(m);
1.22 deraadt 349:
1.1 markus 350: buffer_put_int(&oqueue, mlen);
351: buffer_append(&oqueue, buffer_ptr(m), mlen);
352: buffer_consume(m, mlen);
353: }
354:
1.58 djm 355: static const char *
356: status_to_message(u_int32_t status)
1.1 markus 357: {
1.23 djm 358: const char *status_messages[] = {
359: "Success", /* SSH_FX_OK */
360: "End of file", /* SSH_FX_EOF */
361: "No such file", /* SSH_FX_NO_SUCH_FILE */
362: "Permission denied", /* SSH_FX_PERMISSION_DENIED */
363: "Failure", /* SSH_FX_FAILURE */
364: "Bad message", /* SSH_FX_BAD_MESSAGE */
365: "No connection", /* SSH_FX_NO_CONNECTION */
366: "Connection lost", /* SSH_FX_CONNECTION_LOST */
367: "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
368: "Unknown error" /* Others */
369: };
1.58 djm 370: return (status_messages[MIN(status,SSH2_FX_MAX)]);
371: }
1.22 deraadt 372:
1.58 djm 373: static void
374: send_status(u_int32_t id, u_int32_t status)
375: {
376: Buffer msg;
377:
378: debug3("request %u: sent status %u", id, status);
379: if (log_level > SYSLOG_LEVEL_VERBOSE ||
380: (status != SSH2_FX_OK && status != SSH2_FX_EOF))
381: logit("sent status %s", status_to_message(status));
1.1 markus 382: buffer_init(&msg);
1.10 markus 383: buffer_put_char(&msg, SSH2_FXP_STATUS);
1.1 markus 384: buffer_put_int(&msg, id);
1.46 avsm 385: buffer_put_int(&msg, status);
1.23 djm 386: if (version >= 3) {
1.58 djm 387: buffer_put_cstring(&msg, status_to_message(status));
1.23 djm 388: buffer_put_cstring(&msg, "");
389: }
1.1 markus 390: send_msg(&msg);
391: buffer_free(&msg);
392: }
1.28 itojun 393: static void
1.44 jakob 394: send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
1.1 markus 395: {
396: Buffer msg;
1.22 deraadt 397:
1.1 markus 398: buffer_init(&msg);
399: buffer_put_char(&msg, type);
400: buffer_put_int(&msg, id);
401: buffer_put_string(&msg, data, dlen);
402: send_msg(&msg);
403: buffer_free(&msg);
404: }
405:
1.28 itojun 406: static void
1.44 jakob 407: send_data(u_int32_t id, const char *data, int dlen)
1.1 markus 408: {
1.58 djm 409: debug("request %u: sent data len %d", id, dlen);
1.10 markus 410: send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
1.1 markus 411: }
412:
1.28 itojun 413: static void
1.1 markus 414: send_handle(u_int32_t id, int handle)
415: {
416: char *string;
417: int hlen;
1.22 deraadt 418:
1.1 markus 419: handle_to_string(handle, &string, &hlen);
1.58 djm 420: debug("request %u: sent handle handle %d", id, handle);
1.10 markus 421: send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
1.1 markus 422: xfree(string);
423: }
424:
1.28 itojun 425: static void
1.44 jakob 426: send_names(u_int32_t id, int count, const Stat *stats)
1.1 markus 427: {
428: Buffer msg;
429: int i;
1.22 deraadt 430:
1.1 markus 431: buffer_init(&msg);
1.10 markus 432: buffer_put_char(&msg, SSH2_FXP_NAME);
1.1 markus 433: buffer_put_int(&msg, id);
434: buffer_put_int(&msg, count);
1.58 djm 435: debug("request %u: sent names count %d", id, count);
1.1 markus 436: for (i = 0; i < count; i++) {
437: buffer_put_cstring(&msg, stats[i].name);
438: buffer_put_cstring(&msg, stats[i].long_name);
439: encode_attrib(&msg, &stats[i].attrib);
440: }
441: send_msg(&msg);
442: buffer_free(&msg);
443: }
444:
1.28 itojun 445: static void
1.44 jakob 446: send_attrib(u_int32_t id, const Attrib *a)
1.1 markus 447: {
448: Buffer msg;
1.22 deraadt 449:
1.58 djm 450: debug("request %u: sent attrib have 0x%x", id, a->flags);
1.1 markus 451: buffer_init(&msg);
1.10 markus 452: buffer_put_char(&msg, SSH2_FXP_ATTRS);
1.1 markus 453: buffer_put_int(&msg, id);
454: encode_attrib(&msg, a);
455: send_msg(&msg);
456: buffer_free(&msg);
457: }
458:
459: /* parse incoming */
460:
1.28 itojun 461: static void
1.1 markus 462: process_init(void)
463: {
464: Buffer msg;
465:
1.35 markus 466: version = get_int();
1.58 djm 467: verbose("received client version %d", version);
1.1 markus 468: buffer_init(&msg);
1.10 markus 469: buffer_put_char(&msg, SSH2_FXP_VERSION);
470: buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
1.1 markus 471: send_msg(&msg);
472: buffer_free(&msg);
473: }
474:
1.28 itojun 475: static void
1.1 markus 476: process_open(void)
477: {
478: u_int32_t id, pflags;
479: Attrib *a;
480: char *name;
1.10 markus 481: int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
1.1 markus 482:
483: id = get_int();
484: name = get_string(NULL);
1.10 markus 485: pflags = get_int(); /* portable flags */
1.61 djm 486: debug3("request %u: open flags %d", id, pflags);
1.1 markus 487: a = get_attrib();
488: flags = flags_from_portable(pflags);
1.10 markus 489: mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
1.58 djm 490: logit("open \"%s\" flags %s mode 0%o",
491: name, string_from_portable(pflags), mode);
1.1 markus 492: fd = open(name, flags, mode);
493: if (fd < 0) {
494: status = errno_to_portable(errno);
495: } else {
1.40 markus 496: handle = handle_new(HANDLE_FILE, name, fd, NULL);
1.1 markus 497: if (handle < 0) {
498: close(fd);
499: } else {
500: send_handle(id, handle);
1.10 markus 501: status = SSH2_FX_OK;
1.1 markus 502: }
503: }
1.10 markus 504: if (status != SSH2_FX_OK)
1.1 markus 505: send_status(id, status);
506: xfree(name);
507: }
508:
1.28 itojun 509: static void
1.1 markus 510: process_close(void)
511: {
512: u_int32_t id;
1.10 markus 513: int handle, ret, status = SSH2_FX_FAILURE;
1.1 markus 514:
515: id = get_int();
516: handle = get_handle();
1.58 djm 517: debug3("request %u: close handle %u", id, handle);
518: handle_log_close(handle, NULL);
1.1 markus 519: ret = handle_close(handle);
1.10 markus 520: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 521: send_status(id, status);
522: }
523:
1.28 itojun 524: static void
1.1 markus 525: process_read(void)
526: {
527: char buf[64*1024];
1.10 markus 528: u_int32_t id, len;
529: int handle, fd, ret, status = SSH2_FX_FAILURE;
1.1 markus 530: u_int64_t off;
531:
532: id = get_int();
533: handle = get_handle();
1.10 markus 534: off = get_int64();
1.1 markus 535: len = get_int();
536:
1.58 djm 537: debug("request %u: read \"%s\" (handle %d) off %llu len %d",
538: id, handle_to_name(handle), handle, (unsigned long long)off, len);
1.1 markus 539: if (len > sizeof buf) {
540: len = sizeof buf;
1.58 djm 541: debug2("read change len %d", len);
1.1 markus 542: }
543: fd = handle_to_fd(handle);
544: if (fd >= 0) {
545: if (lseek(fd, off, SEEK_SET) < 0) {
546: error("process_read: seek failed");
547: status = errno_to_portable(errno);
548: } else {
549: ret = read(fd, buf, len);
550: if (ret < 0) {
551: status = errno_to_portable(errno);
552: } else if (ret == 0) {
1.10 markus 553: status = SSH2_FX_EOF;
1.1 markus 554: } else {
555: send_data(id, buf, ret);
1.10 markus 556: status = SSH2_FX_OK;
1.58 djm 557: handle_update_read(handle, ret);
1.1 markus 558: }
559: }
560: }
1.10 markus 561: if (status != SSH2_FX_OK)
1.1 markus 562: send_status(id, status);
563: }
564:
1.28 itojun 565: static void
1.1 markus 566: process_write(void)
567: {
1.10 markus 568: u_int32_t id;
1.1 markus 569: u_int64_t off;
1.5 markus 570: u_int len;
1.10 markus 571: int handle, fd, ret, status = SSH2_FX_FAILURE;
1.1 markus 572: char *data;
573:
574: id = get_int();
575: handle = get_handle();
1.10 markus 576: off = get_int64();
1.1 markus 577: data = get_string(&len);
578:
1.58 djm 579: debug("request %u: write \"%s\" (handle %d) off %llu len %d",
580: id, handle_to_name(handle), handle, (unsigned long long)off, len);
1.1 markus 581: fd = handle_to_fd(handle);
582: if (fd >= 0) {
583: if (lseek(fd, off, SEEK_SET) < 0) {
584: status = errno_to_portable(errno);
585: error("process_write: seek failed");
586: } else {
587: /* XXX ATOMICIO ? */
588: ret = write(fd, data, len);
1.48 djm 589: if (ret < 0) {
1.1 markus 590: error("process_write: write failed");
591: status = errno_to_portable(errno);
1.48 djm 592: } else if ((size_t)ret == len) {
1.10 markus 593: status = SSH2_FX_OK;
1.58 djm 594: handle_update_write(handle, ret);
1.1 markus 595: } else {
1.58 djm 596: debug2("nothing at all written");
1.1 markus 597: }
598: }
599: }
600: send_status(id, status);
601: xfree(data);
602: }
603:
1.28 itojun 604: static void
1.1 markus 605: process_do_stat(int do_lstat)
606: {
1.13 markus 607: Attrib a;
1.1 markus 608: struct stat st;
609: u_int32_t id;
610: char *name;
1.10 markus 611: int ret, status = SSH2_FX_FAILURE;
1.1 markus 612:
613: id = get_int();
614: name = get_string(NULL);
1.58 djm 615: debug3("request %u: %sstat", id, do_lstat ? "l" : "");
616: verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
1.1 markus 617: ret = do_lstat ? lstat(name, &st) : stat(name, &st);
618: if (ret < 0) {
619: status = errno_to_portable(errno);
620: } else {
1.13 markus 621: stat_to_attrib(&st, &a);
622: send_attrib(id, &a);
1.10 markus 623: status = SSH2_FX_OK;
1.1 markus 624: }
1.10 markus 625: if (status != SSH2_FX_OK)
1.1 markus 626: send_status(id, status);
627: xfree(name);
628: }
629:
1.28 itojun 630: static void
1.1 markus 631: process_stat(void)
632: {
633: process_do_stat(0);
634: }
635:
1.28 itojun 636: static void
1.1 markus 637: process_lstat(void)
638: {
639: process_do_stat(1);
640: }
641:
1.28 itojun 642: static void
1.1 markus 643: process_fstat(void)
644: {
1.13 markus 645: Attrib a;
1.1 markus 646: struct stat st;
647: u_int32_t id;
1.10 markus 648: int fd, ret, handle, status = SSH2_FX_FAILURE;
1.1 markus 649:
650: id = get_int();
651: handle = get_handle();
1.58 djm 652: debug("request %u: fstat \"%s\" (handle %u)",
653: id, handle_to_name(handle), handle);
1.1 markus 654: fd = handle_to_fd(handle);
655: if (fd >= 0) {
656: ret = fstat(fd, &st);
657: if (ret < 0) {
658: status = errno_to_portable(errno);
659: } else {
1.13 markus 660: stat_to_attrib(&st, &a);
661: send_attrib(id, &a);
1.10 markus 662: status = SSH2_FX_OK;
1.1 markus 663: }
664: }
1.10 markus 665: if (status != SSH2_FX_OK)
1.1 markus 666: send_status(id, status);
667: }
668:
1.28 itojun 669: static struct timeval *
1.44 jakob 670: attrib_to_tv(const Attrib *a)
1.1 markus 671: {
672: static struct timeval tv[2];
1.22 deraadt 673:
1.1 markus 674: tv[0].tv_sec = a->atime;
675: tv[0].tv_usec = 0;
676: tv[1].tv_sec = a->mtime;
677: tv[1].tv_usec = 0;
678: return tv;
679: }
680:
1.28 itojun 681: static void
1.1 markus 682: process_setstat(void)
683: {
684: Attrib *a;
685: u_int32_t id;
686: char *name;
1.36 deraadt 687: int status = SSH2_FX_OK, ret;
1.1 markus 688:
689: id = get_int();
690: name = get_string(NULL);
691: a = get_attrib();
1.58 djm 692: debug("request %u: setstat name \"%s\"", id, name);
1.33 markus 693: if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
1.58 djm 694: logit("set \"%s\" size %llu", name, a->size);
1.33 markus 695: ret = truncate(name, a->size);
696: if (ret == -1)
697: status = errno_to_portable(errno);
698: }
1.10 markus 699: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1.58 djm 700: logit("set \"%s\" mode %04o", name, a->perm);
1.1 markus 701: ret = chmod(name, a->perm & 0777);
702: if (ret == -1)
703: status = errno_to_portable(errno);
704: }
1.10 markus 705: if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1.58 djm 706: char buf[64];
707: time_t t = a->mtime;
708:
709: strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
710: localtime(&t));
711: logit("set \"%s\" modtime %s", name, buf);
1.1 markus 712: ret = utimes(name, attrib_to_tv(a));
713: if (ret == -1)
714: status = errno_to_portable(errno);
715: }
1.18 stevesk 716: if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
1.58 djm 717: logit("set \"%s\" owner %lu group %lu", name,
718: (u_long)a->uid, (u_long)a->gid);
1.18 stevesk 719: ret = chown(name, a->uid, a->gid);
720: if (ret == -1)
721: status = errno_to_portable(errno);
722: }
1.1 markus 723: send_status(id, status);
724: xfree(name);
725: }
726:
1.28 itojun 727: static void
1.1 markus 728: process_fsetstat(void)
729: {
730: Attrib *a;
731: u_int32_t id;
732: int handle, fd, ret;
1.10 markus 733: int status = SSH2_FX_OK;
1.1 markus 734:
735: id = get_int();
736: handle = get_handle();
737: a = get_attrib();
1.58 djm 738: debug("request %u: fsetstat handle %d", id, handle);
1.1 markus 739: fd = handle_to_fd(handle);
740: if (fd < 0) {
1.10 markus 741: status = SSH2_FX_FAILURE;
1.1 markus 742: } else {
1.58 djm 743: char *name = handle_to_name(handle);
744:
1.33 markus 745: if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
1.58 djm 746: logit("set \"%s\" size %llu", name, a->size);
1.33 markus 747: ret = ftruncate(fd, a->size);
748: if (ret == -1)
749: status = errno_to_portable(errno);
750: }
1.10 markus 751: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1.58 djm 752: logit("set \"%s\" mode %04o", name, a->perm);
1.1 markus 753: ret = fchmod(fd, a->perm & 0777);
754: if (ret == -1)
755: status = errno_to_portable(errno);
756: }
1.10 markus 757: if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1.58 djm 758: char buf[64];
759: time_t t = a->mtime;
760:
761: strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
762: localtime(&t));
763: logit("set \"%s\" modtime %s", name, buf);
1.1 markus 764: ret = futimes(fd, attrib_to_tv(a));
1.18 stevesk 765: if (ret == -1)
766: status = errno_to_portable(errno);
767: }
768: if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
1.58 djm 769: logit("set \"%s\" owner %lu group %lu", name,
770: (u_long)a->uid, (u_long)a->gid);
1.18 stevesk 771: ret = fchown(fd, a->uid, a->gid);
1.1 markus 772: if (ret == -1)
773: status = errno_to_portable(errno);
774: }
775: }
776: send_status(id, status);
777: }
778:
1.28 itojun 779: static void
1.1 markus 780: process_opendir(void)
781: {
782: DIR *dirp = NULL;
783: char *path;
1.10 markus 784: int handle, status = SSH2_FX_FAILURE;
1.1 markus 785: u_int32_t id;
786:
787: id = get_int();
788: path = get_string(NULL);
1.58 djm 789: debug3("request %u: opendir", id);
790: logit("opendir \"%s\"", path);
1.17 stevesk 791: dirp = opendir(path);
1.1 markus 792: if (dirp == NULL) {
793: status = errno_to_portable(errno);
794: } else {
1.40 markus 795: handle = handle_new(HANDLE_DIR, path, 0, dirp);
1.1 markus 796: if (handle < 0) {
797: closedir(dirp);
798: } else {
799: send_handle(id, handle);
1.10 markus 800: status = SSH2_FX_OK;
1.1 markus 801: }
1.17 stevesk 802:
1.1 markus 803: }
1.10 markus 804: if (status != SSH2_FX_OK)
1.1 markus 805: send_status(id, status);
806: xfree(path);
807: }
808:
1.28 itojun 809: static void
1.1 markus 810: process_readdir(void)
811: {
812: DIR *dirp;
813: struct dirent *dp;
814: char *path;
815: int handle;
816: u_int32_t id;
817:
818: id = get_int();
819: handle = get_handle();
1.58 djm 820: debug("request %u: readdir \"%s\" (handle %d)", id,
821: handle_to_name(handle), handle);
1.1 markus 822: dirp = handle_to_dir(handle);
823: path = handle_to_name(handle);
824: if (dirp == NULL || path == NULL) {
1.10 markus 825: send_status(id, SSH2_FX_FAILURE);
1.1 markus 826: } else {
827: struct stat st;
1.58 djm 828: char pathname[MAXPATHLEN];
1.1 markus 829: Stat *stats;
830: int nstats = 10, count = 0, i;
1.36 deraadt 831:
1.54 djm 832: stats = xcalloc(nstats, sizeof(Stat));
1.1 markus 833: while ((dp = readdir(dirp)) != NULL) {
834: if (count >= nstats) {
835: nstats *= 2;
1.55 djm 836: stats = xrealloc(stats, nstats, sizeof(Stat));
1.1 markus 837: }
838: /* XXX OVERFLOW ? */
1.30 jakob 839: snprintf(pathname, sizeof pathname, "%s%s%s", path,
840: strcmp(path, "/") ? "/" : "", dp->d_name);
1.1 markus 841: if (lstat(pathname, &st) < 0)
842: continue;
1.13 markus 843: stat_to_attrib(&st, &(stats[count].attrib));
1.1 markus 844: stats[count].name = xstrdup(dp->d_name);
1.38 djm 845: stats[count].long_name = ls_file(dp->d_name, &st, 0);
1.1 markus 846: count++;
847: /* send up to 100 entries in one message */
1.11 markus 848: /* XXX check packet size instead */
1.1 markus 849: if (count == 100)
850: break;
851: }
1.10 markus 852: if (count > 0) {
853: send_names(id, count, stats);
1.31 deraadt 854: for (i = 0; i < count; i++) {
1.10 markus 855: xfree(stats[i].name);
856: xfree(stats[i].long_name);
857: }
858: } else {
859: send_status(id, SSH2_FX_EOF);
1.1 markus 860: }
861: xfree(stats);
862: }
863: }
864:
1.28 itojun 865: static void
1.1 markus 866: process_remove(void)
867: {
868: char *name;
869: u_int32_t id;
1.10 markus 870: int status = SSH2_FX_FAILURE;
1.1 markus 871: int ret;
872:
873: id = get_int();
874: name = get_string(NULL);
1.58 djm 875: debug3("request %u: remove", id);
876: logit("remove name \"%s\"", name);
1.8 markus 877: ret = unlink(name);
1.10 markus 878: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 879: send_status(id, status);
880: xfree(name);
881: }
882:
1.28 itojun 883: static void
1.1 markus 884: process_mkdir(void)
885: {
886: Attrib *a;
887: u_int32_t id;
888: char *name;
1.10 markus 889: int ret, mode, status = SSH2_FX_FAILURE;
1.1 markus 890:
891: id = get_int();
892: name = get_string(NULL);
893: a = get_attrib();
1.10 markus 894: mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
895: a->perm & 0777 : 0777;
1.58 djm 896: debug3("request %u: mkdir", id);
897: logit("mkdir name \"%s\" mode 0%o", name, mode);
1.1 markus 898: ret = mkdir(name, mode);
1.10 markus 899: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 900: send_status(id, status);
901: xfree(name);
902: }
903:
1.28 itojun 904: static void
1.1 markus 905: process_rmdir(void)
906: {
907: u_int32_t id;
908: char *name;
909: int ret, status;
910:
911: id = get_int();
912: name = get_string(NULL);
1.58 djm 913: debug3("request %u: rmdir", id);
914: logit("rmdir name \"%s\"", name);
1.1 markus 915: ret = rmdir(name);
1.10 markus 916: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 917: send_status(id, status);
918: xfree(name);
919: }
920:
1.28 itojun 921: static void
1.1 markus 922: process_realpath(void)
923: {
924: char resolvedname[MAXPATHLEN];
925: u_int32_t id;
926: char *path;
927:
928: id = get_int();
929: path = get_string(NULL);
1.7 markus 930: if (path[0] == '\0') {
931: xfree(path);
932: path = xstrdup(".");
933: }
1.58 djm 934: debug3("request %u: realpath", id);
935: verbose("realpath \"%s\"", path);
1.1 markus 936: if (realpath(path, resolvedname) == NULL) {
937: send_status(id, errno_to_portable(errno));
938: } else {
939: Stat s;
940: attrib_clear(&s.attrib);
941: s.name = s.long_name = resolvedname;
942: send_names(id, 1, &s);
943: }
944: xfree(path);
945: }
946:
1.28 itojun 947: static void
1.1 markus 948: process_rename(void)
949: {
950: u_int32_t id;
951: char *oldpath, *newpath;
1.39 markus 952: int status;
1.41 deraadt 953: struct stat sb;
1.1 markus 954:
955: id = get_int();
956: oldpath = get_string(NULL);
957: newpath = get_string(NULL);
1.58 djm 958: debug3("request %u: rename", id);
959: logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
1.41 deraadt 960: status = SSH2_FX_FAILURE;
961: if (lstat(oldpath, &sb) == -1)
1.39 markus 962: status = errno_to_portable(errno);
1.41 deraadt 963: else if (S_ISREG(sb.st_mode)) {
964: /* Race-free rename of regular files */
1.47 dtucker 965: if (link(oldpath, newpath) == -1) {
966: if (errno == EOPNOTSUPP) {
967: struct stat st;
968:
969: /*
970: * fs doesn't support links, so fall back to
971: * stat+rename. This is racy.
972: */
973: if (stat(newpath, &st) == -1) {
974: if (rename(oldpath, newpath) == -1)
975: status =
976: errno_to_portable(errno);
977: else
978: status = SSH2_FX_OK;
979: }
980: } else {
981: status = errno_to_portable(errno);
982: }
983: } else if (unlink(oldpath) == -1) {
1.41 deraadt 984: status = errno_to_portable(errno);
985: /* clean spare link */
986: unlink(newpath);
987: } else
988: status = SSH2_FX_OK;
989: } else if (stat(newpath, &sb) == -1) {
990: if (rename(oldpath, newpath) == -1)
991: status = errno_to_portable(errno);
992: else
993: status = SSH2_FX_OK;
994: }
1.1 markus 995: send_status(id, status);
996: xfree(oldpath);
997: xfree(newpath);
998: }
999:
1.28 itojun 1000: static void
1.23 djm 1001: process_readlink(void)
1002: {
1003: u_int32_t id;
1.26 markus 1004: int len;
1.46 avsm 1005: char buf[MAXPATHLEN];
1.23 djm 1006: char *path;
1007:
1008: id = get_int();
1009: path = get_string(NULL);
1.58 djm 1010: debug3("request %u: readlink", id);
1011: verbose("readlink \"%s\"", path);
1.46 avsm 1012: if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
1.23 djm 1013: send_status(id, errno_to_portable(errno));
1014: else {
1015: Stat s;
1.31 deraadt 1016:
1.46 avsm 1017: buf[len] = '\0';
1.23 djm 1018: attrib_clear(&s.attrib);
1.46 avsm 1019: s.name = s.long_name = buf;
1.23 djm 1020: send_names(id, 1, &s);
1021: }
1022: xfree(path);
1023: }
1024:
1.28 itojun 1025: static void
1.23 djm 1026: process_symlink(void)
1027: {
1028: u_int32_t id;
1029: char *oldpath, *newpath;
1.39 markus 1030: int ret, status;
1.23 djm 1031:
1032: id = get_int();
1033: oldpath = get_string(NULL);
1034: newpath = get_string(NULL);
1.58 djm 1035: debug3("request %u: symlink", id);
1036: logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
1.39 markus 1037: /* this will fail if 'newpath' exists */
1038: ret = symlink(oldpath, newpath);
1039: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.23 djm 1040: send_status(id, status);
1041: xfree(oldpath);
1042: xfree(newpath);
1043: }
1044:
1.28 itojun 1045: static void
1.10 markus 1046: process_extended(void)
1047: {
1048: u_int32_t id;
1049: char *request;
1050:
1051: id = get_int();
1052: request = get_string(NULL);
1053: send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
1054: xfree(request);
1055: }
1.1 markus 1056:
1057: /* stolen from ssh-agent */
1058:
1.28 itojun 1059: static void
1.1 markus 1060: process(void)
1061: {
1.9 markus 1062: u_int msg_len;
1.34 markus 1063: u_int buf_len;
1064: u_int consumed;
1.9 markus 1065: u_int type;
1066: u_char *cp;
1.1 markus 1067:
1.34 markus 1068: buf_len = buffer_len(&iqueue);
1069: if (buf_len < 5)
1.1 markus 1070: return; /* Incomplete message. */
1.32 stevesk 1071: cp = buffer_ptr(&iqueue);
1.57 djm 1072: msg_len = get_u32(cp);
1.50 djm 1073: if (msg_len > SFTP_MAX_MSG_LENGTH) {
1.58 djm 1074: error("bad message from %s local user %s",
1075: client_addr, pw->pw_name);
1076: cleanup_exit(11);
1.1 markus 1077: }
1.34 markus 1078: if (buf_len < msg_len + 4)
1.1 markus 1079: return;
1080: buffer_consume(&iqueue, 4);
1.34 markus 1081: buf_len -= 4;
1.1 markus 1082: type = buffer_get_char(&iqueue);
1083: switch (type) {
1.10 markus 1084: case SSH2_FXP_INIT:
1.1 markus 1085: process_init();
1086: break;
1.10 markus 1087: case SSH2_FXP_OPEN:
1.1 markus 1088: process_open();
1089: break;
1.10 markus 1090: case SSH2_FXP_CLOSE:
1.1 markus 1091: process_close();
1092: break;
1.10 markus 1093: case SSH2_FXP_READ:
1.1 markus 1094: process_read();
1095: break;
1.10 markus 1096: case SSH2_FXP_WRITE:
1.1 markus 1097: process_write();
1098: break;
1.10 markus 1099: case SSH2_FXP_LSTAT:
1.1 markus 1100: process_lstat();
1101: break;
1.10 markus 1102: case SSH2_FXP_FSTAT:
1.1 markus 1103: process_fstat();
1104: break;
1.10 markus 1105: case SSH2_FXP_SETSTAT:
1.1 markus 1106: process_setstat();
1107: break;
1.10 markus 1108: case SSH2_FXP_FSETSTAT:
1.1 markus 1109: process_fsetstat();
1110: break;
1.10 markus 1111: case SSH2_FXP_OPENDIR:
1.1 markus 1112: process_opendir();
1113: break;
1.10 markus 1114: case SSH2_FXP_READDIR:
1.1 markus 1115: process_readdir();
1116: break;
1.10 markus 1117: case SSH2_FXP_REMOVE:
1.1 markus 1118: process_remove();
1119: break;
1.10 markus 1120: case SSH2_FXP_MKDIR:
1.1 markus 1121: process_mkdir();
1122: break;
1.10 markus 1123: case SSH2_FXP_RMDIR:
1.1 markus 1124: process_rmdir();
1125: break;
1.10 markus 1126: case SSH2_FXP_REALPATH:
1.1 markus 1127: process_realpath();
1128: break;
1.10 markus 1129: case SSH2_FXP_STAT:
1.1 markus 1130: process_stat();
1131: break;
1.10 markus 1132: case SSH2_FXP_RENAME:
1.1 markus 1133: process_rename();
1.23 djm 1134: break;
1135: case SSH2_FXP_READLINK:
1136: process_readlink();
1137: break;
1138: case SSH2_FXP_SYMLINK:
1139: process_symlink();
1.1 markus 1140: break;
1.10 markus 1141: case SSH2_FXP_EXTENDED:
1142: process_extended();
1143: break;
1.1 markus 1144: default:
1145: error("Unknown message %d", type);
1146: break;
1147: }
1.34 markus 1148: /* discard the remaining bytes from the current packet */
1149: if (buf_len < buffer_len(&iqueue))
1.58 djm 1150: fatal("iqueue grew unexpectedly");
1.34 markus 1151: consumed = buf_len - buffer_len(&iqueue);
1152: if (msg_len < consumed)
1153: fatal("msg_len %d < consumed %d", msg_len, consumed);
1154: if (msg_len > consumed)
1155: buffer_consume(&iqueue, msg_len - consumed);
1.1 markus 1156: }
1157:
1.58 djm 1158: /* Cleanup handler that logs active handles upon normal exit */
1159: void
1160: cleanup_exit(int i)
1161: {
1162: if (pw != NULL && client_addr != NULL) {
1163: handle_log_exit();
1164: logit("session closed for local user %s from [%s]",
1165: pw->pw_name, client_addr);
1166: }
1167: _exit(i);
1168: }
1169:
1170: static void
1171: usage(void)
1172: {
1173: extern char *__progname;
1174:
1175: fprintf(stderr,
1176: "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname);
1177: exit(1);
1178: }
1179:
1.1 markus 1180: int
1.58 djm 1181: main(int argc, char **argv)
1.1 markus 1182: {
1.21 millert 1183: fd_set *rset, *wset;
1.58 djm 1184: int in, out, max, ch, skipargs = 0, log_stderr = 0;
1.21 millert 1185: ssize_t len, olen, set_size;
1.58 djm 1186: SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1187: char *cp;
1188:
1189: extern char *optarg;
1190: extern char *__progname;
1.49 djm 1191:
1192: /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1193: sanitise_stdfd();
1.24 deraadt 1194:
1.58 djm 1195: log_init(__progname, log_level, log_facility, log_stderr);
1196:
1197: while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) {
1198: switch (ch) {
1199: case 'c':
1200: /*
1201: * Ignore all arguments if we are invoked as a
1202: * shell using "sftp-server -c command"
1203: */
1204: skipargs = 1;
1205: break;
1206: case 'e':
1207: log_stderr = 1;
1208: break;
1209: case 'l':
1210: log_level = log_level_number(optarg);
1211: if (log_level == SYSLOG_LEVEL_NOT_SET)
1212: error("Invalid log level \"%s\"", optarg);
1213: break;
1214: case 'f':
1215: log_facility = log_facility_number(optarg);
1216: if (log_level == SYSLOG_FACILITY_NOT_SET)
1217: error("Invalid log facility \"%s\"", optarg);
1218: break;
1219: case 'h':
1220: default:
1221: usage();
1222: }
1223: }
1224:
1225: log_init(__progname, log_level, log_facility, log_stderr);
1226:
1227: if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1228: client_addr = xstrdup(cp);
1229: if ((cp = strchr(client_addr, ' ')) == NULL)
1230: fatal("Malformed SSH_CONNECTION variable: \"%s\"",
1231: getenv("SSH_CONNECTION"));
1232: *cp = '\0';
1233: } else
1234: client_addr = xstrdup("UNKNOWN");
1235:
1236: if ((pw = getpwuid(getuid())) == NULL)
1237: fatal("No user found for uid %lu", (u_long)getuid());
1238: pw = pwcopy(pw);
1239:
1240: logit("session opened for local user %s from [%s]",
1241: pw->pw_name, client_addr);
1.1 markus 1242:
1243: handle_init();
1.10 markus 1244:
1.1 markus 1245: in = dup(STDIN_FILENO);
1246: out = dup(STDOUT_FILENO);
1247:
1248: max = 0;
1249: if (in > max)
1250: max = in;
1251: if (out > max)
1252: max = out;
1253:
1254: buffer_init(&iqueue);
1255: buffer_init(&oqueue);
1256:
1.21 millert 1257: set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1258: rset = (fd_set *)xmalloc(set_size);
1259: wset = (fd_set *)xmalloc(set_size);
1260:
1.1 markus 1261: for (;;) {
1.21 millert 1262: memset(rset, 0, set_size);
1263: memset(wset, 0, set_size);
1.1 markus 1264:
1.21 millert 1265: FD_SET(in, rset);
1.1 markus 1266: olen = buffer_len(&oqueue);
1267: if (olen > 0)
1.21 millert 1268: FD_SET(out, wset);
1.1 markus 1269:
1.21 millert 1270: if (select(max+1, rset, wset, NULL, NULL) < 0) {
1.1 markus 1271: if (errno == EINTR)
1272: continue;
1.58 djm 1273: error("select: %s", strerror(errno));
1274: cleanup_exit(2);
1.1 markus 1275: }
1276:
1277: /* copy stdin to iqueue */
1.21 millert 1278: if (FD_ISSET(in, rset)) {
1.1 markus 1279: char buf[4*4096];
1280: len = read(in, buf, sizeof buf);
1281: if (len == 0) {
1282: debug("read eof");
1.58 djm 1283: cleanup_exit(0);
1.1 markus 1284: } else if (len < 0) {
1.58 djm 1285: error("read: %s", strerror(errno));
1286: cleanup_exit(1);
1.1 markus 1287: } else {
1288: buffer_append(&iqueue, buf, len);
1289: }
1290: }
1291: /* send oqueue to stdout */
1.21 millert 1292: if (FD_ISSET(out, wset)) {
1.1 markus 1293: len = write(out, buffer_ptr(&oqueue), olen);
1294: if (len < 0) {
1.58 djm 1295: error("write: %s", strerror(errno));
1296: cleanup_exit(1);
1.1 markus 1297: } else {
1298: buffer_consume(&oqueue, len);
1299: }
1300: }
1301: /* process requests from client */
1302: process();
1303: }
1304: }