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