io_commands.c 11.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* io_commands.c
 *
 * $Id$
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "io_commands.h"

27
#include "command.h"
28
29
#include "connection.h"
#include "io.h"
30
#include "werror.h"
31
32
33
34
#include "xalloc.h"

#include <assert.h>

35
36
37
/* Needed only to get the error code from failed calls to io_connect() */
#include <errno.h>

38
39
40
/* For STDIN_FILENO */
#include <unistd.h>

41
42
43
44
45
#define GABA_DEFINE
#include "io_commands.h.x"
#undef GABA_DEFINE

#include "io_commands.c.x"
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/* GABA:
   (class
     (name backend_command)
     (super command)
     (vars
       (backend object io_backend)))
*/

/* (write file_info backend)
 *
 * Opens a file for write, and returns the corresponding write_buffer.
 * */
   
static void
do_io_write_file(struct command *s,
		 struct lsh_object *a,
		 struct command_continuation *c,
		 struct exception_handler *e)
{
  CAST(backend_command, self, s);
  CAST(io_write_file_info, info, a);

69
70
71
72
73
74
75
  struct lsh_fd *fd = io_write_file(self->backend,
				    info->name,
				    info->flags,
				    info->mode,
				    info->block_size,
				    NULL,
				    e);
76
77
78
79
80
81
  if (fd)
    COMMAND_RETURN(c, fd->write_buffer);
  else
    EXCEPTION_RAISE(e, make_io_exception(EXC_IO_OPEN_WRITE, NULL, errno, NULL));
}

82
83
84
85
86
87
88
89
90
91
92
COMMAND_SIMPLE(io_write_file_command)
{
  CAST(io_backend, backend, a);

  NEW(backend_command, self);
  self->super.call = do_io_write_file;
  self->backend = backend;

  return &self->super.super;
}

93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
struct io_write_file_info *
make_io_write_file_info(const char *name, int flags, int mode, UINT32 block_size)
{
  NEW(io_write_file_info, self);
  self->name = name;
  self->flags = flags;
  self->mode = mode;
  self->block_size = block_size;

  return self;
}

void do_io_read_fd(struct command *s,
		   struct lsh_object *a,
		   struct command_continuation *c,
		   struct exception_handler *e)
{
  CAST(io_read_fd, self, s);
  CAST(io_backend, backend, a);

113
  COMMAND_RETURN(c, make_lsh_fd(backend, self->fd, e));
114
115
116
117
118
}

struct io_read_fd io_read_stdin
= STATIC_IO_READ_FD(STDIN_FILENO);

119
120
#if 0
/* ;; GABA:
121
122
123
124
   (class
     (name listen_command_callback)
     (super fd_listen_callback)
     (vars
125
       (backend object io_backend)
126
127
       (c object command_continuation)
       (e object exception_handler)))
128
129
*/

Niels Möller's avatar
Niels Möller committed
130
131
132
static void
do_listen_continue(struct fd_listen_callback *s, int fd,
		   struct address_info *peer)
133
134
135
{
  CAST(listen_command_callback, self, s);
  NEW(listen_value, res);
136

137
  res->fd = make_lsh_fd(self->backend, fd, self->e);
138
139
  res->peer = peer;

Niels Möller's avatar
Niels Möller committed
140
  COMMAND_RETURN(self->c, res);
141
142
143
}

static struct fd_listen_callback *
144
make_listen_command_callback(struct io_backend *backend,
145
146
			     struct command_continuation *c,
			     struct exception_handler *e)
147
148
{
  NEW(listen_command_callback, closure);
149
  closure->backend = backend;
150
  closure->c = c;
151
  closure->e = e;
152
153
154
155
  closure->super.f = do_listen_continue;
  
  return &closure->super;
}
156
#endif
157

158
159
160
static struct exception resolve_exception =
STATIC_EXCEPTION(EXC_RESOLVE, "address could not be resolved");

Niels Möller's avatar
Niels Möller committed
161
162
163
164
165
166
static void
do_listen(struct io_backend *backend,
	  struct address_info *a,
	  struct resource_list *resources,
	  struct command_continuation *c,
	  struct exception_handler *e)
167
168
169
{
  /* FIXME: Add ipv6 support somewhere */
  struct sockaddr_in sin;
170
  struct lsh_fd *fd;
171
  
172
  if (!address_info2sockaddr_in(&sin, a))
Niels Möller's avatar
Niels Möller committed
173
174
175
176
177
    {
      EXCEPTION_RAISE(e, &resolve_exception);
      return;
    }
  
178
  fd = io_listen(backend, &sin,
179
		 make_listen_callback(backend, c, e),
180
		 e);
181
182

  if (!fd)
Niels Möller's avatar
Niels Möller committed
183
184
    {
      /* NOTE: Will never invoke the continuation. */
185
186
      EXCEPTION_RAISE(e, make_io_exception(EXC_IO_LISTEN,
					   NULL, errno, NULL));
Niels Möller's avatar
Niels Möller committed
187
188
189
      return;
    }
  
190
  if (resources)
191
    REMEMBER_RESOURCE(resources, &fd->super);
192
193
194
}

/* A listen function taking three arguments:
195
 * (listen callback backend port).
196
197
198
199
200
201
202
203
 *
 * Suitable for handling forwarding requests. */

/* GABA:
   (class
     (name listen_connection)
     (super command)
     (vars
204
205
206
       (callback object command)
       (backend object io_backend)))
       ;; (connection object ssh_connection)))
207
208
*/

Niels Möller's avatar
Niels Möller committed
209
static void
210
211
do_listen_connection(struct command *s,
		     struct lsh_object *x,
212
213
		     struct command_continuation *c,
		     struct exception_handler *e)
214
215
216
{
  CAST(listen_connection, self, s);
  CAST(address_info, address, x);
217
218
219
220
221

  /* FIXME: Add ipv6 support somewhere */
  struct sockaddr_in sin;
  
  if (!address_info2sockaddr_in(&sin, address))
Niels Möller's avatar
Niels Möller committed
222
223
224
225
    {
      EXCEPTION_RAISE(e, &resolve_exception);
      return;
    }
226

227
  /* FIXME: Asyncronous dns lookups should go here */
Niels Möller's avatar
Niels Möller committed
228
229
  COMMAND_RETURN(c, io_listen
		 (self->backend, &sin,
230
		  make_listen_callback
Niels Möller's avatar
Niels Möller committed
231
232
		  (self->backend,
		   make_apply(self->callback,
233
			      &discard_continuation, e), e), e));
234
235
}

236
237
struct command *make_listen_command(struct command *callback,
				    struct io_backend *backend)
238
{
239
  NEW(listen_connection, self);
240
  self->callback = callback;
241
242
243
244
245
246
247
248
  self->backend = backend;

  self->super.call = do_listen_connection;

  return &self->super;
}

static struct lsh_object *
249
collect_listen(struct collect_info_2 *info,
250
251
	       struct lsh_object *a,
	       struct lsh_object *b)
252
{
253
254
  CAST_SUBTYPE(command, callback, a);
  CAST(io_backend, backend, b);
255
  assert(!info->next);
256

257
  return &make_listen_command(callback, backend)->super;
258
259
}

260
261
262
263
264
265
static struct collect_info_2 collect_info_listen_2 =
STATIC_COLLECT_2_FINAL(collect_listen);

struct collect_info_1 listen_command =
STATIC_COLLECT_1(&collect_info_listen_2);

266
#if 0
267
/* FIXME: This could perhaps be merged with io_connect in io.c? */ 
268
/* ;; GABA:
269
270
   (class
     (name connect_command_callback)
271
     (super fd_callback)
272
273
     (vars
       (backend object io_backend)
274
275
       (c object command_continuation)
       (e object exception_handler)))
276
277
*/

Niels Möller's avatar
Niels Möller committed
278
/* FIXME: The new fd object should be added to the same resource list
279
 * as the old one. Perhaps the connection code in io.c should reuse
Niels Möller's avatar
Niels Möller committed
280
 * the fd object in some way? */
Niels Möller's avatar
Niels Möller committed
281
282
static void
do_connect_continue(struct fd_callback **s, int fd)
283
{
284
  CAST(connect_command_callback, self, *s);
285

Niels Möller's avatar
Niels Möller committed
286
  assert(fd >= 0);
Niels Möller's avatar
Niels Möller committed
287

288
  COMMAND_RETURN(self->c, make_lsh_fd(self->backend, fd, self->e));
289
290
}

291
static struct fd_callback *
292
make_connect_command_callback(struct io_backend *backend,
293
294
			      struct command_continuation *c,
			      struct exception_handler *e)
295
{
296
  NEW(connect_command_callback, closure);
297
298
  closure->backend = backend;
  closure->c = c;
299
  closure->e = e;
300
301
302
303
  closure->super.f = do_connect_continue;
  
  return &closure->super;
}
304
#endif
305

Niels Möller's avatar
Niels Möller committed
306
307
308
309
310
311
static void
do_connect(struct io_backend *backend,
	   struct address_info *a,
	   struct resource_list *resources,
	   struct command_continuation *c,
	   struct exception_handler *e)
312
313
314
{
  /* FIXME: Add ipv6 support somewhere */
  struct sockaddr_in sin;
315
  struct lsh_fd *fd;
316
317

  /* Address must specify a host */
Niels Möller's avatar
Niels Möller committed
318
  assert(a->ip);
319
  
320
  if (!address_info2sockaddr_in(&sin, a))
Niels Möller's avatar
Niels Möller committed
321
322
323
324
    {
      EXCEPTION_RAISE(e, &resolve_exception);
      return;
    }
325

326
  fd = io_connect(backend, &sin, NULL,
327
		  c, e);
328
329

  if (!fd)
Niels Möller's avatar
Niels Möller committed
330
    {
331
      EXCEPTION_RAISE(e, make_io_exception(EXC_IO_CONNECT, NULL, errno, NULL));
Niels Möller's avatar
Niels Möller committed
332
333
      return;
    }
334
335

  if (resources)
336
    REMEMBER_RESOURCE(resources,
337
		      &fd->super);
338
339
}

340
341

/* Connect variant, taking a connection object as argument (used for
342
343
344
 * rememembering the connected fd).
 *
 * (connect backend port connection) -> fd */
345
346
347

/* GABA:
   (class
348
     (name connect_port)
349
350
351
352
353
354
     (super command)
     (vars
       (backend object io_backend)
       (target object address_info)))
*/

Niels Möller's avatar
Niels Möller committed
355
356
357
358
359
static void
do_connect_port(struct command *s,
		struct lsh_object *x,
		struct command_continuation *c,
		struct exception_handler *e)
360
{
361
  CAST(connect_port, self, s);
362
363
  CAST(ssh_connection, connection, x);
  
Niels Möller's avatar
Niels Möller committed
364
  do_connect(self->backend, self->target, connection->resources, c, e);
365
366
367
}


368
369
struct command *make_connect_port(struct io_backend *backend,
				  struct address_info *target)
370
{
371
372
  NEW(connect_port, self);
  self->super.call = do_connect_port;
373
374
375
376
377
378
379
  self->backend = backend;
  self->target = target;

  return &self->super;
}

static struct lsh_object *
380
collect_connect_port(struct collect_info_2 *info,
381
382
		     struct lsh_object *a,
		     struct lsh_object *b)
383
384
385
{
  CAST(io_backend, backend, a);
  CAST(address_info, target, b);
386
  assert(!info->next);
387
388
389
  assert(backend);
  assert(target);
  
390
  return &make_connect_port(backend, target)->super;
391
392
}

393
static struct collect_info_2 collect_info_connect_port_2 =
394
STATIC_COLLECT_2_FINAL(collect_connect_port);
395

396
397
struct collect_info_1 connect_with_port =
STATIC_COLLECT_1(&collect_info_connect_port_2);
398

399

400
401
/* GABA:
   (class
402
     (name simple_io_command)
403
404
405
406
407
408
     (super command)
     (vars
       (backend object io_backend)
       (resources object resource_list)))
*/

409
410
411
412
413
/* Simple connect function taking port only as argument. Also used for
 * listen.
 *
 * (connect address) */

Niels Möller's avatar
Niels Möller committed
414
415
416
417
418
static void
do_simple_connect(struct command *s,
		  struct lsh_object *a,
		  struct command_continuation *c,
		  struct exception_handler *e)
419
{
420
  CAST(simple_io_command, self, s);
421
  CAST(address_info, address, a);
422

Niels Möller's avatar
Niels Möller committed
423
  do_connect(self->backend, address, self->resources, c, e);
424
}
425

426
427
428
429
struct command *
make_simple_connect(struct io_backend *backend,
		    struct resource_list *resources)
{
430
  NEW(simple_io_command, self);
431
432
433
434
435
436
437
  self->backend = backend;
  self->resources = resources;

  self->super.call = do_simple_connect;

  return &self->super;
}
438

439

440
/* (connect port connection) */
441
442
443
444
445
446
447
448
/* GABA:
   (class
     (name connect_connection)
     (super command)
     (vars
       (backend object io_backend)))
*/

Niels Möller's avatar
Niels Möller committed
449
450
451
452
453
static void
do_connect_connection(struct command *s,
		      struct lsh_object *x,
		      struct command_continuation *c,
		      struct exception_handler *e UNUSED)
454
455
456
457
{
  CAST(connect_connection, self, s);
  CAST(ssh_connection, connection, x);

Niels Möller's avatar
Niels Möller committed
458
459
  COMMAND_RETURN(c,
		 make_simple_connect(self->backend, connection->resources));
460
461
462
463
464
465
466
467
468
469
470
}

struct command *make_connect_connection(struct io_backend *backend)
{
  NEW(connect_connection, self);
  self->super.call = do_connect_connection;
  self->backend = backend;

  return &self->super;
}

471

Niels Möller's avatar
Niels Möller committed
472
473
474
475
476
static void
do_simple_listen(struct command *s,
		 struct lsh_object *a,
		 struct command_continuation *c,
		 struct exception_handler *e)
477
478
479
480
{
  CAST(simple_io_command, self, s);
  CAST(address_info, address, a);

Niels Möller's avatar
Niels Möller committed
481
  do_listen(self->backend, address, self->resources, c, e);
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
}

struct command *
make_simple_listen(struct io_backend *backend,
		   struct resource_list *resources)
{
  NEW(simple_io_command, self);
  self->backend = backend;
  self->resources = resources;

  self->super.call = do_simple_listen;

  return &self->super;
}


/* Takes a listen_value as argument, logs the peer address, and
 * returns the fd object. */

501
502
503
504
505
506
507
COMMAND_SIMPLE(io_log_peer_command)
{
  CAST(listen_value, lv, a);

  verbose("Accepting connection from %S, port %i\n",
	  lv->peer->ip, lv->peer->port);

508
  return &lv->fd->super.super;
509
510
511
}


512
513
514
515
516
517
518
/* ***
 *
 * (lambda (backend connection port)
     (listen backend connection port
             (lambda (peer)
                (start-io peer (request-forwarded-tcpip connection peer)))))
 */