blob: abc9539e40911b63abd4c8cd38e2ae19366c9ad6 [file] [log] [blame]
Damien Millere7a1e5c2006-08-05 11:34:19 +10001/* $OpenBSD: sftp-server.c,v 1.68 2006/07/26 13:57:17 stevesk Exp $ */
Damien Miller7b28dc52000-09-05 13:34:53 +11002/*
Darren Tucker37bd3662004-02-24 09:19:15 +11003 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
Damien Miller7b28dc52000-09-05 13:34:53 +11004 *
Darren Tucker37bd3662004-02-24 09:19:15 +11005 * 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.
Damien Miller7b28dc52000-09-05 13:34:53 +11008 *
Darren Tucker37bd3662004-02-24 09:19:15 +11009 * 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.
Damien Miller7b28dc52000-09-05 13:34:53 +110016 */
17#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110018
19#include <sys/types.h>
Damien Miller8dbffe72006-08-05 11:02:17 +100020#include <sys/param.h>
Damien Millerf17883e2006-03-15 11:45:54 +110021#include <sys/stat.h>
Damien Miller9aec9192006-08-05 10:57:45 +100022#ifdef HAVE_SYS_TIME_H
23# include <sys/time.h>
24#endif
Damien Miller88f254b2006-03-15 11:25:13 +110025
26#include <dirent.h>
Darren Tucker39972492006-07-12 22:22:46 +100027#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100028#include <fcntl.h>
Damien Miller9f2abc42006-07-10 20:53:08 +100029#include <pwd.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100030#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100031#include <string.h>
Damien Miller5598b4f2006-07-24 14:09:40 +100032#include <time.h>
Damien Millere3476ed2006-07-24 14:13:33 +100033#include <unistd.h>
Damien Miller7b28dc52000-09-05 13:34:53 +110034
Damien Miller7b28dc52000-09-05 13:34:53 +110035#include "buffer.h"
36#include "bufaux.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000037#include "log.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110038#include "xmalloc.h"
Darren Tuckerce321d82005-10-03 18:11:24 +100039#include "misc.h"
Damien Millerfef95ad2006-07-10 20:46:55 +100040#include "uidswap.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110041
Ben Lindstrom2f959b42001-01-11 06:20:23 +000042#include "sftp.h"
Damien Miller33804262001-02-04 23:20:18 +110043#include "sftp-common.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110044
45/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000046#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110047#define get_int() buffer_get_int(&iqueue);
48#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Damien Miller7b28dc52000-09-05 13:34:53 +110049
Damien Millerfef95ad2006-07-10 20:46:55 +100050/* Our verbosity */
51LogLevel log_level = SYSLOG_LEVEL_ERROR;
52
53/* Our client */
54struct passwd *pw = NULL;
55char *client_addr = NULL;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000056
Damien Miller7b28dc52000-09-05 13:34:53 +110057/* input and output queue */
58Buffer iqueue;
59Buffer oqueue;
60
Damien Miller058316f2001-03-08 10:08:49 +110061/* Version of client */
62int version;
63
Darren Tuckera6612d42003-06-28 12:39:03 +100064/* portable attributes, etc. */
Damien Miller7b28dc52000-09-05 13:34:53 +110065
Damien Miller7b28dc52000-09-05 13:34:53 +110066typedef struct Stat Stat;
67
Damien Miller33804262001-02-04 23:20:18 +110068struct Stat {
Damien Miller7b28dc52000-09-05 13:34:53 +110069 char *name;
70 char *long_name;
71 Attrib attrib;
72};
73
Ben Lindstrombba81212001-06-25 05:01:22 +000074static int
Damien Miller7b28dc52000-09-05 13:34:53 +110075errno_to_portable(int unixerrno)
76{
77 int ret = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000078
Damien Miller7b28dc52000-09-05 13:34:53 +110079 switch (unixerrno) {
80 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000081 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110082 break;
83 case ENOENT:
84 case ENOTDIR:
85 case EBADF:
86 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000087 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110088 break;
89 case EPERM:
90 case EACCES:
91 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000092 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110093 break;
94 case ENAMETOOLONG:
95 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000096 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110097 break;
98 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000099 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100100 break;
101 }
102 return ret;
103}
104
Ben Lindstrombba81212001-06-25 05:01:22 +0000105static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100106flags_from_portable(int pflags)
107{
108 int flags = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000109
Ben Lindstrom36592512001-03-05 05:02:08 +0000110 if ((pflags & SSH2_FXF_READ) &&
111 (pflags & SSH2_FXF_WRITE)) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100112 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000113 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100114 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000115 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100116 flags = O_WRONLY;
117 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000118 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100119 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000120 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100121 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000122 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100123 flags |= O_EXCL;
124 return flags;
125}
126
Damien Millerfef95ad2006-07-10 20:46:55 +1000127static const char *
128string_from_portable(int pflags)
129{
130 static char ret[128];
131
132 *ret = '\0';
133
134#define PAPPEND(str) { \
135 if (*ret != '\0') \
136 strlcat(ret, ",", sizeof(ret)); \
137 strlcat(ret, str, sizeof(ret)); \
138 }
139
140 if (pflags & SSH2_FXF_READ)
141 PAPPEND("READ")
142 if (pflags & SSH2_FXF_WRITE)
143 PAPPEND("WRITE")
144 if (pflags & SSH2_FXF_CREAT)
145 PAPPEND("CREATE")
146 if (pflags & SSH2_FXF_TRUNC)
147 PAPPEND("TRUNCATE")
148 if (pflags & SSH2_FXF_EXCL)
149 PAPPEND("EXCL")
150
151 return ret;
152}
153
Ben Lindstrombba81212001-06-25 05:01:22 +0000154static Attrib *
Damien Miller7b28dc52000-09-05 13:34:53 +1100155get_attrib(void)
156{
157 return decode_attrib(&iqueue);
158}
159
160/* handle handles */
161
162typedef struct Handle Handle;
163struct Handle {
164 int use;
165 DIR *dirp;
166 int fd;
167 char *name;
Damien Millerfef95ad2006-07-10 20:46:55 +1000168 u_int64_t bytes_read, bytes_write;
Damien Miller7b28dc52000-09-05 13:34:53 +1100169};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000170
Damien Miller7b28dc52000-09-05 13:34:53 +1100171enum {
172 HANDLE_UNUSED,
173 HANDLE_DIR,
174 HANDLE_FILE
175};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000176
Damien Miller7b28dc52000-09-05 13:34:53 +1100177Handle handles[100];
178
Ben Lindstrombba81212001-06-25 05:01:22 +0000179static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100180handle_init(void)
181{
Damien Millereccb9de2005-06-17 12:59:34 +1000182 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000183
Damien Miller9f0f5c62001-12-21 14:45:46 +1100184 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
Damien Miller7b28dc52000-09-05 13:34:53 +1100185 handles[i].use = HANDLE_UNUSED;
186}
187
Ben Lindstrombba81212001-06-25 05:01:22 +0000188static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100189handle_new(int use, const char *name, int fd, DIR *dirp)
Damien Miller7b28dc52000-09-05 13:34:53 +1100190{
Damien Millereccb9de2005-06-17 12:59:34 +1000191 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000192
Damien Miller9f0f5c62001-12-21 14:45:46 +1100193 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100194 if (handles[i].use == HANDLE_UNUSED) {
195 handles[i].use = use;
196 handles[i].dirp = dirp;
197 handles[i].fd = fd;
Damien Miller00111382003-03-10 11:21:17 +1100198 handles[i].name = xstrdup(name);
Damien Millerfef95ad2006-07-10 20:46:55 +1000199 handles[i].bytes_read = handles[i].bytes_write = 0;
Damien Miller7b28dc52000-09-05 13:34:53 +1100200 return i;
201 }
202 }
203 return -1;
204}
205
Ben Lindstrombba81212001-06-25 05:01:22 +0000206static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100207handle_is_ok(int i, int type)
208{
Damien Millereccb9de2005-06-17 12:59:34 +1000209 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000210 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100211}
212
Ben Lindstrombba81212001-06-25 05:01:22 +0000213static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100214handle_to_string(int handle, char **stringp, int *hlenp)
215{
Damien Miller7b28dc52000-09-05 13:34:53 +1100216 if (stringp == NULL || hlenp == NULL)
217 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000218 *stringp = xmalloc(sizeof(int32_t));
Damien Miller3f941882006-03-31 23:13:02 +1100219 put_u32(*stringp, handle);
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000220 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100221 return 0;
222}
223
Ben Lindstrombba81212001-06-25 05:01:22 +0000224static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100225handle_from_string(const char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100226{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000227 int val;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000228
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000229 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100230 return -1;
Damien Miller3f941882006-03-31 23:13:02 +1100231 val = get_u32(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100232 if (handle_is_ok(val, HANDLE_FILE) ||
233 handle_is_ok(val, HANDLE_DIR))
234 return val;
235 return -1;
236}
237
Ben Lindstrombba81212001-06-25 05:01:22 +0000238static char *
Damien Miller7b28dc52000-09-05 13:34:53 +1100239handle_to_name(int handle)
240{
241 if (handle_is_ok(handle, HANDLE_DIR)||
242 handle_is_ok(handle, HANDLE_FILE))
243 return handles[handle].name;
244 return NULL;
245}
246
Ben Lindstrombba81212001-06-25 05:01:22 +0000247static DIR *
Damien Miller7b28dc52000-09-05 13:34:53 +1100248handle_to_dir(int handle)
249{
250 if (handle_is_ok(handle, HANDLE_DIR))
251 return handles[handle].dirp;
252 return NULL;
253}
254
Ben Lindstrombba81212001-06-25 05:01:22 +0000255static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100256handle_to_fd(int handle)
257{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000258 if (handle_is_ok(handle, HANDLE_FILE))
Damien Miller7b28dc52000-09-05 13:34:53 +1100259 return handles[handle].fd;
260 return -1;
261}
262
Damien Millerfef95ad2006-07-10 20:46:55 +1000263static void
264handle_update_read(int handle, ssize_t bytes)
265{
266 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
267 handles[handle].bytes_read += bytes;
268}
269
270static void
271handle_update_write(int handle, ssize_t bytes)
272{
273 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
274 handles[handle].bytes_write += bytes;
275}
276
277static u_int64_t
278handle_bytes_read(int handle)
279{
280 if (handle_is_ok(handle, HANDLE_FILE))
281 return (handles[handle].bytes_read);
282 return 0;
283}
284
285static u_int64_t
286handle_bytes_write(int handle)
287{
288 if (handle_is_ok(handle, HANDLE_FILE))
289 return (handles[handle].bytes_write);
290 return 0;
291}
292
Ben Lindstrombba81212001-06-25 05:01:22 +0000293static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100294handle_close(int handle)
295{
296 int ret = -1;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000297
Damien Miller7b28dc52000-09-05 13:34:53 +1100298 if (handle_is_ok(handle, HANDLE_FILE)) {
299 ret = close(handles[handle].fd);
300 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100301 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100302 } else if (handle_is_ok(handle, HANDLE_DIR)) {
303 ret = closedir(handles[handle].dirp);
304 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100305 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100306 } else {
307 errno = ENOENT;
308 }
309 return ret;
310}
311
Damien Millerfef95ad2006-07-10 20:46:55 +1000312static void
313handle_log_close(int handle, char *emsg)
314{
315 if (handle_is_ok(handle, HANDLE_FILE)) {
316 logit("%s%sclose \"%s\" bytes read %llu written %llu",
317 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
318 handle_to_name(handle),
319 handle_bytes_read(handle), handle_bytes_write(handle));
320 } else {
321 logit("%s%sclosedir \"%s\"",
322 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
323 handle_to_name(handle));
324 }
325}
326
327static void
328handle_log_exit(void)
329{
330 u_int i;
331
332 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
333 if (handles[i].use != HANDLE_UNUSED)
334 handle_log_close(i, "forced");
335}
336
Ben Lindstrombba81212001-06-25 05:01:22 +0000337static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100338get_handle(void)
339{
340 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000341 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100342 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000343
Damien Miller7b28dc52000-09-05 13:34:53 +1100344 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000345 if (hlen < 256)
346 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100347 xfree(handle);
348 return val;
349}
350
351/* send replies */
352
Ben Lindstrombba81212001-06-25 05:01:22 +0000353static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100354send_msg(Buffer *m)
355{
356 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000357
Damien Miller7b28dc52000-09-05 13:34:53 +1100358 buffer_put_int(&oqueue, mlen);
359 buffer_append(&oqueue, buffer_ptr(m), mlen);
360 buffer_consume(m, mlen);
361}
362
Damien Millerfef95ad2006-07-10 20:46:55 +1000363static const char *
364status_to_message(u_int32_t status)
Damien Miller7b28dc52000-09-05 13:34:53 +1100365{
Damien Miller058316f2001-03-08 10:08:49 +1100366 const char *status_messages[] = {
367 "Success", /* SSH_FX_OK */
368 "End of file", /* SSH_FX_EOF */
369 "No such file", /* SSH_FX_NO_SUCH_FILE */
370 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
371 "Failure", /* SSH_FX_FAILURE */
372 "Bad message", /* SSH_FX_BAD_MESSAGE */
373 "No connection", /* SSH_FX_NO_CONNECTION */
374 "Connection lost", /* SSH_FX_CONNECTION_LOST */
375 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
376 "Unknown error" /* Others */
377 };
Damien Millerfef95ad2006-07-10 20:46:55 +1000378 return (status_messages[MIN(status,SSH2_FX_MAX)]);
379}
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000380
Damien Millerfef95ad2006-07-10 20:46:55 +1000381static void
382send_status(u_int32_t id, u_int32_t status)
383{
384 Buffer msg;
385
386 debug3("request %u: sent status %u", id, status);
387 if (log_level > SYSLOG_LEVEL_VERBOSE ||
388 (status != SSH2_FX_OK && status != SSH2_FX_EOF))
389 logit("sent status %s", status_to_message(status));
Damien Miller7b28dc52000-09-05 13:34:53 +1100390 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000391 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100392 buffer_put_int(&msg, id);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000393 buffer_put_int(&msg, status);
Damien Miller058316f2001-03-08 10:08:49 +1100394 if (version >= 3) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000395 buffer_put_cstring(&msg, status_to_message(status));
Damien Miller058316f2001-03-08 10:08:49 +1100396 buffer_put_cstring(&msg, "");
397 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100398 send_msg(&msg);
399 buffer_free(&msg);
400}
Ben Lindstrombba81212001-06-25 05:01:22 +0000401static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100402send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100403{
404 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000405
Damien Miller7b28dc52000-09-05 13:34:53 +1100406 buffer_init(&msg);
407 buffer_put_char(&msg, type);
408 buffer_put_int(&msg, id);
409 buffer_put_string(&msg, data, dlen);
410 send_msg(&msg);
411 buffer_free(&msg);
412}
413
Ben Lindstrombba81212001-06-25 05:01:22 +0000414static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100415send_data(u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100416{
Damien Millerfef95ad2006-07-10 20:46:55 +1000417 debug("request %u: sent data len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000418 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100419}
420
Ben Lindstrombba81212001-06-25 05:01:22 +0000421static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100422send_handle(u_int32_t id, int handle)
423{
424 char *string;
425 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000426
Damien Miller7b28dc52000-09-05 13:34:53 +1100427 handle_to_string(handle, &string, &hlen);
Damien Millerfef95ad2006-07-10 20:46:55 +1000428 debug("request %u: sent handle handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000429 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100430 xfree(string);
431}
432
Ben Lindstrombba81212001-06-25 05:01:22 +0000433static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100434send_names(u_int32_t id, int count, const Stat *stats)
Damien Miller7b28dc52000-09-05 13:34:53 +1100435{
436 Buffer msg;
437 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000438
Damien Miller7b28dc52000-09-05 13:34:53 +1100439 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000440 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100441 buffer_put_int(&msg, id);
442 buffer_put_int(&msg, count);
Damien Millerfef95ad2006-07-10 20:46:55 +1000443 debug("request %u: sent names count %d", id, count);
Damien Miller7b28dc52000-09-05 13:34:53 +1100444 for (i = 0; i < count; i++) {
445 buffer_put_cstring(&msg, stats[i].name);
446 buffer_put_cstring(&msg, stats[i].long_name);
447 encode_attrib(&msg, &stats[i].attrib);
448 }
449 send_msg(&msg);
450 buffer_free(&msg);
451}
452
Ben Lindstrombba81212001-06-25 05:01:22 +0000453static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100454send_attrib(u_int32_t id, const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100455{
456 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000457
Damien Millerfef95ad2006-07-10 20:46:55 +1000458 debug("request %u: sent attrib have 0x%x", id, a->flags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100459 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000460 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100461 buffer_put_int(&msg, id);
462 encode_attrib(&msg, a);
463 send_msg(&msg);
464 buffer_free(&msg);
465}
466
467/* parse incoming */
468
Ben Lindstrombba81212001-06-25 05:01:22 +0000469static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100470process_init(void)
471{
472 Buffer msg;
Damien Miller7b28dc52000-09-05 13:34:53 +1100473
Ben Lindstrom937df1d2002-06-06 21:58:35 +0000474 version = get_int();
Damien Millerfef95ad2006-07-10 20:46:55 +1000475 verbose("received client version %d", version);
Damien Miller7b28dc52000-09-05 13:34:53 +1100476 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000477 buffer_put_char(&msg, SSH2_FXP_VERSION);
478 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100479 send_msg(&msg);
480 buffer_free(&msg);
481}
482
Ben Lindstrombba81212001-06-25 05:01:22 +0000483static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100484process_open(void)
485{
486 u_int32_t id, pflags;
487 Attrib *a;
488 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000489 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100490
491 id = get_int();
492 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000493 pflags = get_int(); /* portable flags */
Damien Miller6444fe92006-07-10 21:31:27 +1000494 debug3("request %u: open flags %d", id, pflags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100495 a = get_attrib();
496 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000497 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Damien Millerfef95ad2006-07-10 20:46:55 +1000498 logit("open \"%s\" flags %s mode 0%o",
499 name, string_from_portable(pflags), mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100500 fd = open(name, flags, mode);
501 if (fd < 0) {
502 status = errno_to_portable(errno);
503 } else {
Damien Miller00111382003-03-10 11:21:17 +1100504 handle = handle_new(HANDLE_FILE, name, fd, NULL);
Damien Miller7b28dc52000-09-05 13:34:53 +1100505 if (handle < 0) {
506 close(fd);
507 } else {
508 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000509 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100510 }
511 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000512 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100513 send_status(id, status);
514 xfree(name);
515}
516
Ben Lindstrombba81212001-06-25 05:01:22 +0000517static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100518process_close(void)
519{
520 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000521 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100522
523 id = get_int();
524 handle = get_handle();
Damien Millerfef95ad2006-07-10 20:46:55 +1000525 debug3("request %u: close handle %u", id, handle);
526 handle_log_close(handle, NULL);
Damien Miller7b28dc52000-09-05 13:34:53 +1100527 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000528 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100529 send_status(id, status);
530}
531
Ben Lindstrombba81212001-06-25 05:01:22 +0000532static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100533process_read(void)
534{
535 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000536 u_int32_t id, len;
537 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100538 u_int64_t off;
539
540 id = get_int();
541 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000542 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100543 len = get_int();
544
Damien Millerfef95ad2006-07-10 20:46:55 +1000545 debug("request %u: read \"%s\" (handle %d) off %llu len %d",
546 id, handle_to_name(handle), handle, (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100547 if (len > sizeof buf) {
548 len = sizeof buf;
Damien Millerfef95ad2006-07-10 20:46:55 +1000549 debug2("read change len %d", len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100550 }
551 fd = handle_to_fd(handle);
552 if (fd >= 0) {
553 if (lseek(fd, off, SEEK_SET) < 0) {
554 error("process_read: seek failed");
555 status = errno_to_portable(errno);
556 } else {
557 ret = read(fd, buf, len);
558 if (ret < 0) {
559 status = errno_to_portable(errno);
560 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000561 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100562 } else {
563 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000564 status = SSH2_FX_OK;
Damien Millerfef95ad2006-07-10 20:46:55 +1000565 handle_update_read(handle, ret);
Damien Miller7b28dc52000-09-05 13:34:53 +1100566 }
567 }
568 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000569 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100570 send_status(id, status);
571}
572
Ben Lindstrombba81212001-06-25 05:01:22 +0000573static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100574process_write(void)
575{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000576 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100577 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100578 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000579 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100580 char *data;
581
582 id = get_int();
583 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000584 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100585 data = get_string(&len);
586
Damien Millerfef95ad2006-07-10 20:46:55 +1000587 debug("request %u: write \"%s\" (handle %d) off %llu len %d",
588 id, handle_to_name(handle), handle, (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100589 fd = handle_to_fd(handle);
590 if (fd >= 0) {
591 if (lseek(fd, off, SEEK_SET) < 0) {
592 status = errno_to_portable(errno);
593 error("process_write: seek failed");
594 } else {
595/* XXX ATOMICIO ? */
596 ret = write(fd, data, len);
Damien Millereccb9de2005-06-17 12:59:34 +1000597 if (ret < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100598 error("process_write: write failed");
599 status = errno_to_portable(errno);
Damien Millereccb9de2005-06-17 12:59:34 +1000600 } else if ((size_t)ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000601 status = SSH2_FX_OK;
Damien Millerfef95ad2006-07-10 20:46:55 +1000602 handle_update_write(handle, ret);
Damien Miller7b28dc52000-09-05 13:34:53 +1100603 } else {
Damien Millerfef95ad2006-07-10 20:46:55 +1000604 debug2("nothing at all written");
Damien Miller7b28dc52000-09-05 13:34:53 +1100605 }
606 }
607 }
608 send_status(id, status);
609 xfree(data);
610}
611
Ben Lindstrombba81212001-06-25 05:01:22 +0000612static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100613process_do_stat(int do_lstat)
614{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000615 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100616 struct stat st;
617 u_int32_t id;
618 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000619 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100620
621 id = get_int();
622 name = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +1000623 debug3("request %u: %sstat", id, do_lstat ? "l" : "");
624 verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100625 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
626 if (ret < 0) {
627 status = errno_to_portable(errno);
628 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000629 stat_to_attrib(&st, &a);
630 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000631 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100632 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000633 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100634 send_status(id, status);
635 xfree(name);
636}
637
Ben Lindstrombba81212001-06-25 05:01:22 +0000638static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100639process_stat(void)
640{
641 process_do_stat(0);
642}
643
Ben Lindstrombba81212001-06-25 05:01:22 +0000644static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100645process_lstat(void)
646{
647 process_do_stat(1);
648}
649
Ben Lindstrombba81212001-06-25 05:01:22 +0000650static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100651process_fstat(void)
652{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000653 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100654 struct stat st;
655 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000656 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100657
658 id = get_int();
659 handle = get_handle();
Damien Millerfef95ad2006-07-10 20:46:55 +1000660 debug("request %u: fstat \"%s\" (handle %u)",
661 id, handle_to_name(handle), handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100662 fd = handle_to_fd(handle);
663 if (fd >= 0) {
664 ret = fstat(fd, &st);
665 if (ret < 0) {
666 status = errno_to_portable(errno);
667 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000668 stat_to_attrib(&st, &a);
669 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000670 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100671 }
672 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000673 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100674 send_status(id, status);
675}
676
Ben Lindstrombba81212001-06-25 05:01:22 +0000677static struct timeval *
Damien Millerf58b58c2003-11-17 21:18:23 +1100678attrib_to_tv(const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100679{
680 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000681
Damien Miller7b28dc52000-09-05 13:34:53 +1100682 tv[0].tv_sec = a->atime;
683 tv[0].tv_usec = 0;
684 tv[1].tv_sec = a->mtime;
685 tv[1].tv_usec = 0;
686 return tv;
687}
688
Ben Lindstrombba81212001-06-25 05:01:22 +0000689static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100690process_setstat(void)
691{
692 Attrib *a;
693 u_int32_t id;
694 char *name;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000695 int status = SSH2_FX_OK, ret;
Damien Miller7b28dc52000-09-05 13:34:53 +1100696
697 id = get_int();
698 name = get_string(NULL);
699 a = get_attrib();
Damien Millerfef95ad2006-07-10 20:46:55 +1000700 debug("request %u: setstat name \"%s\"", id, name);
Damien Miller00c92172002-02-13 14:05:00 +1100701 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000702 logit("set \"%s\" size %llu", name, a->size);
Damien Miller00c92172002-02-13 14:05:00 +1100703 ret = truncate(name, a->size);
704 if (ret == -1)
705 status = errno_to_portable(errno);
706 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000707 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000708 logit("set \"%s\" mode %04o", name, a->perm);
Damien Miller7b28dc52000-09-05 13:34:53 +1100709 ret = chmod(name, a->perm & 0777);
710 if (ret == -1)
711 status = errno_to_portable(errno);
712 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000713 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000714 char buf[64];
715 time_t t = a->mtime;
716
717 strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
718 localtime(&t));
719 logit("set \"%s\" modtime %s", name, buf);
Damien Miller7b28dc52000-09-05 13:34:53 +1100720 ret = utimes(name, attrib_to_tv(a));
721 if (ret == -1)
722 status = errno_to_portable(errno);
723 }
Kevin Steves8e743932001-02-05 13:24:35 +0000724 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000725 logit("set \"%s\" owner %lu group %lu", name,
726 (u_long)a->uid, (u_long)a->gid);
Kevin Steves8e743932001-02-05 13:24:35 +0000727 ret = chown(name, a->uid, a->gid);
728 if (ret == -1)
729 status = errno_to_portable(errno);
730 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100731 send_status(id, status);
732 xfree(name);
733}
734
Ben Lindstrombba81212001-06-25 05:01:22 +0000735static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100736process_fsetstat(void)
737{
738 Attrib *a;
739 u_int32_t id;
740 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000741 int status = SSH2_FX_OK;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000742
Damien Miller7b28dc52000-09-05 13:34:53 +1100743 id = get_int();
744 handle = get_handle();
745 a = get_attrib();
Damien Millerfef95ad2006-07-10 20:46:55 +1000746 debug("request %u: fsetstat handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100747 fd = handle_to_fd(handle);
Damien Millerfef95ad2006-07-10 20:46:55 +1000748 if (fd < 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000749 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100750 } else {
Damien Millerfef95ad2006-07-10 20:46:55 +1000751 char *name = handle_to_name(handle);
752
Damien Miller00c92172002-02-13 14:05:00 +1100753 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000754 logit("set \"%s\" size %llu", name, a->size);
Damien Miller00c92172002-02-13 14:05:00 +1100755 ret = ftruncate(fd, a->size);
756 if (ret == -1)
757 status = errno_to_portable(errno);
758 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000759 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000760 logit("set \"%s\" mode %04o", name, a->perm);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000761#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100762 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000763#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000764 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000765#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100766 if (ret == -1)
767 status = errno_to_portable(errno);
768 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000769 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000770 char buf[64];
771 time_t t = a->mtime;
772
773 strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
774 localtime(&t));
775 logit("set \"%s\" modtime %s", name, buf);
Damien Miller7b28dc52000-09-05 13:34:53 +1100776#ifdef HAVE_FUTIMES
777 ret = futimes(fd, attrib_to_tv(a));
778#else
779 ret = utimes(name, attrib_to_tv(a));
780#endif
781 if (ret == -1)
782 status = errno_to_portable(errno);
783 }
Kevin Steves8e743932001-02-05 13:24:35 +0000784 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Damien Millerfef95ad2006-07-10 20:46:55 +1000785 logit("set \"%s\" owner %lu group %lu", name,
786 (u_long)a->uid, (u_long)a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000787#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000788 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000789#else
790 ret = chown(name, a->uid, a->gid);
791#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000792 if (ret == -1)
793 status = errno_to_portable(errno);
794 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100795 }
796 send_status(id, status);
797}
798
Ben Lindstrombba81212001-06-25 05:01:22 +0000799static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100800process_opendir(void)
801{
802 DIR *dirp = NULL;
803 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000804 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100805 u_int32_t id;
806
807 id = get_int();
808 path = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +1000809 debug3("request %u: opendir", id);
810 logit("opendir \"%s\"", path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000811 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100812 if (dirp == NULL) {
813 status = errno_to_portable(errno);
814 } else {
Damien Miller00111382003-03-10 11:21:17 +1100815 handle = handle_new(HANDLE_DIR, path, 0, dirp);
Damien Miller7b28dc52000-09-05 13:34:53 +1100816 if (handle < 0) {
817 closedir(dirp);
818 } else {
819 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000820 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100821 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000822
Damien Miller7b28dc52000-09-05 13:34:53 +1100823 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000824 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100825 send_status(id, status);
826 xfree(path);
827}
828
Ben Lindstrombba81212001-06-25 05:01:22 +0000829static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100830process_readdir(void)
831{
832 DIR *dirp;
833 struct dirent *dp;
834 char *path;
835 int handle;
836 u_int32_t id;
837
838 id = get_int();
839 handle = get_handle();
Damien Millerfef95ad2006-07-10 20:46:55 +1000840 debug("request %u: readdir \"%s\" (handle %d)", id,
841 handle_to_name(handle), handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100842 dirp = handle_to_dir(handle);
843 path = handle_to_name(handle);
844 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000845 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100846 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100847 struct stat st;
Damien Millerfef95ad2006-07-10 20:46:55 +1000848 char pathname[MAXPATHLEN];
Damien Miller7b28dc52000-09-05 13:34:53 +1100849 Stat *stats;
850 int nstats = 10, count = 0, i;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000851
Damien Miller07d86be2006-03-26 14:19:21 +1100852 stats = xcalloc(nstats, sizeof(Stat));
Damien Miller7b28dc52000-09-05 13:34:53 +1100853 while ((dp = readdir(dirp)) != NULL) {
854 if (count >= nstats) {
855 nstats *= 2;
Damien Miller36812092006-03-26 14:22:47 +1100856 stats = xrealloc(stats, nstats, sizeof(Stat));
Damien Miller7b28dc52000-09-05 13:34:53 +1100857 }
858/* XXX OVERFLOW ? */
Ben Lindstrom95148e32001-08-06 21:30:53 +0000859 snprintf(pathname, sizeof pathname, "%s%s%s", path,
860 strcmp(path, "/") ? "/" : "", dp->d_name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100861 if (lstat(pathname, &st) < 0)
862 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000863 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100864 stats[count].name = xstrdup(dp->d_name);
Damien Millere1a49812002-09-12 09:54:25 +1000865 stats[count].long_name = ls_file(dp->d_name, &st, 0);
Damien Miller7b28dc52000-09-05 13:34:53 +1100866 count++;
867 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000868 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100869 if (count == 100)
870 break;
871 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000872 if (count > 0) {
873 send_names(id, count, stats);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100874 for (i = 0; i < count; i++) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000875 xfree(stats[i].name);
876 xfree(stats[i].long_name);
877 }
878 } else {
879 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100880 }
881 xfree(stats);
882 }
883}
884
Ben Lindstrombba81212001-06-25 05:01:22 +0000885static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100886process_remove(void)
887{
888 char *name;
889 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000890 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100891 int ret;
892
893 id = get_int();
894 name = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +1000895 debug3("request %u: remove", id);
896 logit("remove name \"%s\"", name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000897 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000898 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100899 send_status(id, status);
900 xfree(name);
901}
902
Ben Lindstrombba81212001-06-25 05:01:22 +0000903static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100904process_mkdir(void)
905{
906 Attrib *a;
907 u_int32_t id;
908 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000909 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100910
911 id = get_int();
912 name = get_string(NULL);
913 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000914 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
915 a->perm & 0777 : 0777;
Damien Millerfef95ad2006-07-10 20:46:55 +1000916 debug3("request %u: mkdir", id);
917 logit("mkdir name \"%s\" mode 0%o", name, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100918 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000919 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100920 send_status(id, status);
921 xfree(name);
922}
923
Ben Lindstrombba81212001-06-25 05:01:22 +0000924static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100925process_rmdir(void)
926{
927 u_int32_t id;
928 char *name;
929 int ret, status;
930
931 id = get_int();
932 name = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +1000933 debug3("request %u: rmdir", id);
934 logit("rmdir name \"%s\"", name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100935 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000936 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100937 send_status(id, status);
938 xfree(name);
939}
940
Ben Lindstrombba81212001-06-25 05:01:22 +0000941static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100942process_realpath(void)
943{
944 char resolvedname[MAXPATHLEN];
945 u_int32_t id;
946 char *path;
947
948 id = get_int();
949 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000950 if (path[0] == '\0') {
951 xfree(path);
952 path = xstrdup(".");
953 }
Damien Millerfef95ad2006-07-10 20:46:55 +1000954 debug3("request %u: realpath", id);
955 verbose("realpath \"%s\"", path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100956 if (realpath(path, resolvedname) == NULL) {
957 send_status(id, errno_to_portable(errno));
958 } else {
959 Stat s;
960 attrib_clear(&s.attrib);
961 s.name = s.long_name = resolvedname;
962 send_names(id, 1, &s);
963 }
964 xfree(path);
965}
966
Ben Lindstrombba81212001-06-25 05:01:22 +0000967static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100968process_rename(void)
969{
970 u_int32_t id;
971 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100972 int status;
Damien Millerb3207e82003-03-26 16:01:11 +1100973 struct stat sb;
Damien Miller7b28dc52000-09-05 13:34:53 +1100974
975 id = get_int();
976 oldpath = get_string(NULL);
977 newpath = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +1000978 debug3("request %u: rename", id);
979 logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
Damien Millerb3207e82003-03-26 16:01:11 +1100980 status = SSH2_FX_FAILURE;
981 if (lstat(oldpath, &sb) == -1)
Damien Miller9e51a732003-02-24 11:58:44 +1100982 status = errno_to_portable(errno);
Damien Millerb3207e82003-03-26 16:01:11 +1100983 else if (S_ISREG(sb.st_mode)) {
984 /* Race-free rename of regular files */
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000985 if (link(oldpath, newpath) == -1) {
Darren Tuckere59b5082004-06-28 16:01:19 +1000986 if (errno == EOPNOTSUPP
987#ifdef LINK_OPNOTSUPP_ERRNO
988 || errno == LINK_OPNOTSUPP_ERRNO
989#endif
990 ) {
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000991 struct stat st;
992
993 /*
994 * fs doesn't support links, so fall back to
995 * stat+rename. This is racy.
996 */
997 if (stat(newpath, &st) == -1) {
998 if (rename(oldpath, newpath) == -1)
999 status =
1000 errno_to_portable(errno);
1001 else
1002 status = SSH2_FX_OK;
1003 }
1004 } else {
1005 status = errno_to_portable(errno);
1006 }
1007 } else if (unlink(oldpath) == -1) {
Damien Millerb3207e82003-03-26 16:01:11 +11001008 status = errno_to_portable(errno);
1009 /* clean spare link */
1010 unlink(newpath);
1011 } else
1012 status = SSH2_FX_OK;
1013 } else if (stat(newpath, &sb) == -1) {
1014 if (rename(oldpath, newpath) == -1)
1015 status = errno_to_portable(errno);
1016 else
1017 status = SSH2_FX_OK;
1018 }
Damien Miller7b28dc52000-09-05 13:34:53 +11001019 send_status(id, status);
1020 xfree(oldpath);
1021 xfree(newpath);
1022}
1023
Ben Lindstrombba81212001-06-25 05:01:22 +00001024static void
Damien Miller058316f2001-03-08 10:08:49 +11001025process_readlink(void)
1026{
1027 u_int32_t id;
Ben Lindstromabbb73d2001-05-17 03:14:57 +00001028 int len;
Darren Tucker3f9fdc72004-06-22 12:56:01 +10001029 char buf[MAXPATHLEN];
Damien Miller058316f2001-03-08 10:08:49 +11001030 char *path;
1031
1032 id = get_int();
1033 path = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +10001034 debug3("request %u: readlink", id);
1035 verbose("readlink \"%s\"", path);
Darren Tucker3f9fdc72004-06-22 12:56:01 +10001036 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
Damien Miller058316f2001-03-08 10:08:49 +11001037 send_status(id, errno_to_portable(errno));
1038 else {
1039 Stat s;
Damien Miller9f0f5c62001-12-21 14:45:46 +11001040
Darren Tucker3f9fdc72004-06-22 12:56:01 +10001041 buf[len] = '\0';
Damien Miller058316f2001-03-08 10:08:49 +11001042 attrib_clear(&s.attrib);
Darren Tucker3f9fdc72004-06-22 12:56:01 +10001043 s.name = s.long_name = buf;
Damien Miller058316f2001-03-08 10:08:49 +11001044 send_names(id, 1, &s);
1045 }
1046 xfree(path);
1047}
1048
Ben Lindstrombba81212001-06-25 05:01:22 +00001049static void
Damien Miller058316f2001-03-08 10:08:49 +11001050process_symlink(void)
1051{
1052 u_int32_t id;
Damien Miller058316f2001-03-08 10:08:49 +11001053 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +11001054 int ret, status;
Damien Miller058316f2001-03-08 10:08:49 +11001055
1056 id = get_int();
1057 oldpath = get_string(NULL);
1058 newpath = get_string(NULL);
Damien Millerfef95ad2006-07-10 20:46:55 +10001059 debug3("request %u: symlink", id);
1060 logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
Damien Miller9e51a732003-02-24 11:58:44 +11001061 /* this will fail if 'newpath' exists */
1062 ret = symlink(oldpath, newpath);
1063 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller058316f2001-03-08 10:08:49 +11001064 send_status(id, status);
1065 xfree(oldpath);
1066 xfree(newpath);
1067}
1068
Ben Lindstrombba81212001-06-25 05:01:22 +00001069static void
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001070process_extended(void)
1071{
1072 u_int32_t id;
1073 char *request;
1074
1075 id = get_int();
1076 request = get_string(NULL);
1077 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
1078 xfree(request);
1079}
Damien Miller7b28dc52000-09-05 13:34:53 +11001080
1081/* stolen from ssh-agent */
1082
Ben Lindstrombba81212001-06-25 05:01:22 +00001083static void
Damien Miller7b28dc52000-09-05 13:34:53 +11001084process(void)
1085{
Ben Lindstrom46c16222000-12-22 01:43:59 +00001086 u_int msg_len;
Ben Lindstrom2c140472002-06-06 21:57:54 +00001087 u_int buf_len;
1088 u_int consumed;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001089 u_int type;
1090 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +11001091
Ben Lindstrom2c140472002-06-06 21:57:54 +00001092 buf_len = buffer_len(&iqueue);
1093 if (buf_len < 5)
Damien Miller7b28dc52000-09-05 13:34:53 +11001094 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +11001095 cp = buffer_ptr(&iqueue);
Damien Miller3f941882006-03-31 23:13:02 +11001096 msg_len = get_u32(cp);
Damien Miller54446182006-01-02 23:40:50 +11001097 if (msg_len > SFTP_MAX_MSG_LENGTH) {
Damien Millerfef95ad2006-07-10 20:46:55 +10001098 error("bad message from %s local user %s",
1099 client_addr, pw->pw_name);
1100 cleanup_exit(11);
Damien Miller7b28dc52000-09-05 13:34:53 +11001101 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001102 if (buf_len < msg_len + 4)
Damien Miller7b28dc52000-09-05 13:34:53 +11001103 return;
1104 buffer_consume(&iqueue, 4);
Ben Lindstrom2c140472002-06-06 21:57:54 +00001105 buf_len -= 4;
Damien Miller7b28dc52000-09-05 13:34:53 +11001106 type = buffer_get_char(&iqueue);
1107 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001108 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001109 process_init();
1110 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001111 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +11001112 process_open();
1113 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001114 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +11001115 process_close();
1116 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001117 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +11001118 process_read();
1119 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001120 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +11001121 process_write();
1122 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001123 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001124 process_lstat();
1125 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001126 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001127 process_fstat();
1128 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001129 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001130 process_setstat();
1131 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001132 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001133 process_fsetstat();
1134 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001135 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001136 process_opendir();
1137 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001138 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001139 process_readdir();
1140 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001141 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +11001142 process_remove();
1143 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001144 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001145 process_mkdir();
1146 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001147 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001148 process_rmdir();
1149 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001150 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +11001151 process_realpath();
1152 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001153 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001154 process_stat();
1155 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001156 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +11001157 process_rename();
1158 break;
Damien Miller058316f2001-03-08 10:08:49 +11001159 case SSH2_FXP_READLINK:
1160 process_readlink();
1161 break;
1162 case SSH2_FXP_SYMLINK:
1163 process_symlink();
1164 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001165 case SSH2_FXP_EXTENDED:
1166 process_extended();
1167 break;
Damien Miller7b28dc52000-09-05 13:34:53 +11001168 default:
1169 error("Unknown message %d", type);
1170 break;
1171 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001172 /* discard the remaining bytes from the current packet */
1173 if (buf_len < buffer_len(&iqueue))
Damien Millerfef95ad2006-07-10 20:46:55 +10001174 fatal("iqueue grew unexpectedly");
Ben Lindstrom2c140472002-06-06 21:57:54 +00001175 consumed = buf_len - buffer_len(&iqueue);
1176 if (msg_len < consumed)
1177 fatal("msg_len %d < consumed %d", msg_len, consumed);
1178 if (msg_len > consumed)
1179 buffer_consume(&iqueue, msg_len - consumed);
Damien Miller7b28dc52000-09-05 13:34:53 +11001180}
1181
Damien Millerfef95ad2006-07-10 20:46:55 +10001182/* Cleanup handler that logs active handles upon normal exit */
1183void
1184cleanup_exit(int i)
1185{
1186 if (pw != NULL && client_addr != NULL) {
1187 handle_log_exit();
1188 logit("session closed for local user %s from [%s]",
1189 pw->pw_name, client_addr);
1190 }
1191 _exit(i);
1192}
1193
1194static void
1195usage(void)
1196{
1197 extern char *__progname;
1198
1199 fprintf(stderr,
1200 "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname);
1201 exit(1);
1202}
1203
Damien Miller7b28dc52000-09-05 13:34:53 +11001204int
Damien Millerfef95ad2006-07-10 20:46:55 +10001205main(int argc, char **argv)
Damien Miller7b28dc52000-09-05 13:34:53 +11001206{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001207 fd_set *rset, *wset;
Damien Millerfef95ad2006-07-10 20:46:55 +10001208 int in, out, max, ch, skipargs = 0, log_stderr = 0;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001209 ssize_t len, olen, set_size;
Damien Millerfef95ad2006-07-10 20:46:55 +10001210 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1211 char *cp;
1212
Damien Millerfef95ad2006-07-10 20:46:55 +10001213 extern char *optarg;
1214 extern char *__progname;
Damien Miller7b28dc52000-09-05 13:34:53 +11001215
Darren Tuckerce321d82005-10-03 18:11:24 +10001216 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1217 sanitise_stdfd();
1218
Damien Millerfef95ad2006-07-10 20:46:55 +10001219 __progname = ssh_get_progname(argv[0]);
1220 log_init(__progname, log_level, log_facility, log_stderr);
Ben Lindstromc7f4ccd2001-03-15 00:09:15 +00001221
Damien Millerfef95ad2006-07-10 20:46:55 +10001222 while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) {
1223 switch (ch) {
1224 case 'c':
1225 /*
1226 * Ignore all arguments if we are invoked as a
1227 * shell using "sftp-server -c command"
1228 */
1229 skipargs = 1;
1230 break;
1231 case 'e':
1232 log_stderr = 1;
1233 break;
1234 case 'l':
1235 log_level = log_level_number(optarg);
1236 if (log_level == SYSLOG_LEVEL_NOT_SET)
1237 error("Invalid log level \"%s\"", optarg);
1238 break;
1239 case 'f':
1240 log_facility = log_facility_number(optarg);
1241 if (log_level == SYSLOG_FACILITY_NOT_SET)
1242 error("Invalid log facility \"%s\"", optarg);
1243 break;
1244 case 'h':
1245 default:
1246 usage();
1247 }
1248 }
1249
1250 log_init(__progname, log_level, log_facility, log_stderr);
1251
1252 if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1253 client_addr = xstrdup(cp);
1254 if ((cp = strchr(client_addr, ' ')) == NULL)
1255 fatal("Malformed SSH_CONNECTION variable: \"%s\"",
1256 getenv("SSH_CONNECTION"));
1257 *cp = '\0';
1258 } else
1259 client_addr = xstrdup("UNKNOWN");
1260
1261 if ((pw = getpwuid(getuid())) == NULL)
1262 fatal("No user found for uid %lu", (u_long)getuid());
1263 pw = pwcopy(pw);
1264
1265 logit("session opened for local user %s from [%s]",
1266 pw->pw_name, client_addr);
1267
Damien Miller7b28dc52000-09-05 13:34:53 +11001268 handle_init();
1269
Damien Miller7b28dc52000-09-05 13:34:53 +11001270 in = dup(STDIN_FILENO);
1271 out = dup(STDOUT_FILENO);
1272
Damien Miller402b3312001-04-14 00:28:42 +10001273#ifdef HAVE_CYGWIN
1274 setmode(in, O_BINARY);
1275 setmode(out, O_BINARY);
1276#endif
1277
Damien Miller7b28dc52000-09-05 13:34:53 +11001278 max = 0;
1279 if (in > max)
1280 max = in;
1281 if (out > max)
1282 max = out;
1283
1284 buffer_init(&iqueue);
1285 buffer_init(&oqueue);
1286
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001287 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1288 rset = (fd_set *)xmalloc(set_size);
1289 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +11001290
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001291 for (;;) {
1292 memset(rset, 0, set_size);
1293 memset(wset, 0, set_size);
1294
1295 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001296 olen = buffer_len(&oqueue);
1297 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001298 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001299
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001300 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001301 if (errno == EINTR)
1302 continue;
Damien Millerfef95ad2006-07-10 20:46:55 +10001303 error("select: %s", strerror(errno));
1304 cleanup_exit(2);
Damien Miller7b28dc52000-09-05 13:34:53 +11001305 }
1306
1307 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001308 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001309 char buf[4*4096];
1310 len = read(in, buf, sizeof buf);
1311 if (len == 0) {
1312 debug("read eof");
Damien Millerfef95ad2006-07-10 20:46:55 +10001313 cleanup_exit(0);
Damien Miller7b28dc52000-09-05 13:34:53 +11001314 } else if (len < 0) {
Damien Millerfef95ad2006-07-10 20:46:55 +10001315 error("read: %s", strerror(errno));
1316 cleanup_exit(1);
Damien Miller7b28dc52000-09-05 13:34:53 +11001317 } else {
1318 buffer_append(&iqueue, buf, len);
1319 }
1320 }
1321 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001322 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001323 len = write(out, buffer_ptr(&oqueue), olen);
1324 if (len < 0) {
Damien Millerfef95ad2006-07-10 20:46:55 +10001325 error("write: %s", strerror(errno));
1326 cleanup_exit(1);
Damien Miller7b28dc52000-09-05 13:34:53 +11001327 } else {
1328 buffer_consume(&oqueue, len);
1329 }
1330 }
1331 /* process requests from client */
1332 process();
1333 }
1334}