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