tcpforward_commands.c 15.1 KB
Newer Older
1
2
3
4
5
6
7
/* tcpforward_commands.c
 *
 * $Id$
 */

/* lsh, an implementation of the ssh protocol
 *
8
 * Copyright (C) 1998 Balzs Scheidler, Niels Mller
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *
 * 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 "tcpforward_commands.h"

Niels Möller's avatar
Niels Möller committed
27
28
#include "atoms.h"
#include "channel_commands.h"
29
#include "connection_commands.h"
Niels Möller's avatar
Niels Möller committed
30
31
32
33
34
35
36
37
38
#include "format.h"
#include "io_commands.h"
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

#include <assert.h>

/* Forward declarations */
39

40
extern struct collect_info_1 open_direct_tcpip;
Niels Möller's avatar
Niels Möller committed
41
extern struct collect_info_1 remote_listen_command;
42
extern struct collect_info_1 open_forwarded_tcpip;
Niels Möller's avatar
Niels Möller committed
43
44
45
extern struct command tcpip_start_io;
extern struct command tcpip_connect_io;

46
47
struct collect_info_1 install_direct_tcpip_handler;
struct collect_info_1 install_forwared_tcpip_handler;
Niels Möller's avatar
Niels Möller committed
48

49
50
51
52
static struct command make_direct_tcpip_handler;

struct collect_info_1 install_tcpip_forward_handler;
static struct command make_tcpip_forward_handler;
Niels Möller's avatar
Niels Möller committed
53

54
#define OPEN_DIRECT_TCPIP (&open_direct_tcpip.super.super.super)
Niels Möller's avatar
Niels Möller committed
55
56
57
#define REMOTE_LISTEN (&remote_listen_command.super.super.super)
#define TCPIP_START_IO (&tcpip_start_io.super)
#define TCPIP_CONNECT_IO (&tcpip_connect_io.super)
58
#define OPEN_FORWARDED_TCPIP (&open_forwarded_tcpip.super.super.super)
59
60
#define DIRECT_TCPIP_HANDLER (&make_direct_tcpip_handler.super)
#define INSTALL_DIRECT_TCPIP (&install_direct_tcpip_handler.super.super.super)
61

62
63
64
static struct catch_report_collect catch_channel_open;
#define CATCH_CHANNEL_OPEN (&catch_channel_open.super.super.super)

65
66
#include "tcpforward_commands.c.x"

67
68
69
70
71
static struct report_exception_info open_tcpip_report =
STATIC_REPORT_EXCEPTION_INFO(EXC_ALL, EXC_CHANNEL_OPEN, "Failed to open tcpip channel");

static struct catch_report_collect catch_channel_open
= STATIC_CATCH_REPORT(&open_tcpip_report);
72
73
74
75
76

/* Takes a socket as argument, and returns a tcpip channel. Used by
 * the party receiving a open-tcp request, when a channel to the
 * target has been opened. */

Niels Möller's avatar
Niels Möller committed
77
#define TCPIP_WINDOW_SIZE 10000
78

79
/* NOTE: make_tcpip_channel() adds the fd to the channel's resource list. */
Niels Möller's avatar
Niels Möller committed
80
81
82
83
84
static void
do_tcpip_connect_io(struct command *ignored UNUSED,
		    struct lsh_object *x,
		    struct command_continuation *c,
		    struct exception_handler *e UNUSED)
85
{
86
  CAST(listen_value, lv, x);
87

88
89
90
91
  assert(lv);
  assert(lv->fd);
  
  COMMAND_RETURN(c, make_tcpip_channel(lv->fd, TCPIP_WINDOW_SIZE));
92
93
}

Niels Möller's avatar
Niels Möller committed
94
struct command tcpip_connect_io = STATIC_COMMAND(do_tcpip_connect_io);
95
96
97
98
99
100

/* Used by the party requesting tcp forwarding, i.e. when a socket is
 * already open, and we have asked the other end to forward it. Takes
 * a channel as argument, and connects it to the socket. Returns the
 * channel. */

Niels Möller's avatar
Niels Möller committed
101
static void
Niels Möller's avatar
Niels Möller committed
102
103
do_tcpip_start_io(struct command *s UNUSED, 
		  struct lsh_object *x,
104
105
		  struct command_continuation *c,
		  struct exception_handler *e UNUSED)
106
{
Niels Möller's avatar
Niels Möller committed
107
  CAST_SUBTYPE(ssh_channel, channel, x);
108

109
110
  assert(channel);
  
Niels Möller's avatar
Niels Möller committed
111
  tcpip_channel_start_io(channel);
112

Niels Möller's avatar
Niels Möller committed
113
  COMMAND_RETURN(c, channel);
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
}

struct command tcpip_start_io =
{ STATIC_HEADER, do_tcpip_start_io };


/* Requesting the opening of a forwarded tcpip channel. */

/* Used for both forwarded-tcpip and direct-tcpip. Takes a listen
 * value as argument, and returns a channel connected to some tcpip
 * port at the other end. */

/* GABA:
   (class
     (name open_tcpip_command)
     (super channel_open_command)
     (vars
       ; ATOM_FORWARDED_TCPIP or ATOM_DIRECT_TCPIP
       (type . int)
Niels Möller's avatar
Niels Möller committed
133
134
135

       (initial_window . UINT32)

136
       ; For forwarded-tcpip, port is the port listened to.
137
       ; For direct-tcpip, port is the port to connect to.
138
139
140
141
142
143
144
145
       ; In both cases, it's a port used on the server end.
       (port object address_info)
       (peer object listen_value)))
*/

static struct ssh_channel *
new_tcpip_channel(struct channel_open_command *c,
		  struct ssh_connection *connection,
146
		  UINT32 local_channel_number,
147
148
149
150
151
152
		  struct lsh_string **request)
{
  CAST(open_tcpip_command, self, c);
  struct ssh_channel *channel;

  /* NOTE: All accepted fd:s must end up in this function, so it
153
154
   * should be ok to delay the REMEMBER() call until here. It is done
   * by make_tcpip_channel(). */
155
156
157
158

  debug("tcpforward_commands.c: new_tcpip_channel()\n");

  channel = make_tcpip_channel(self->peer->fd, TCPIP_WINDOW_SIZE);
159
  channel->write = connection->write;
160

161
162
163
164
165
  *request = format_channel_open(self->type, local_channel_number,
				 channel, 
				 "%S%i%S%i",
				 self->port->ip, self->port->port,
				 self->peer->peer->ip, self->peer->peer->port);
166
167
168
169
170
  
  return channel;
}

static struct command *
Niels Möller's avatar
Niels Möller committed
171
make_open_tcpip_command(int type, UINT32 initial_window,
Niels Möller's avatar
Niels Möller committed
172
			struct address_info *port,
173
174
			struct listen_value *peer)
{
Niels Möller's avatar
Niels Möller committed
175
  NEW(open_tcpip_command, self);
176
177
178

  debug("tcpforward_commands.c: make_open_tcpip_command()\n");

179
  self->super.super.call = do_channel_open_command;
Niels Möller's avatar
Niels Möller committed
180
  self->super.new_channel = new_tcpip_channel;
181
182

  self->type = type;
Niels Möller's avatar
Niels Möller committed
183
  self->initial_window = initial_window;
184
  
Niels Möller's avatar
Niels Möller committed
185
  self->port = port;
186
187
188
189
190
191
  self->peer = peer;
  
  return &self->super.super;
}

static struct lsh_object *
192
193
194
collect_open_forwarded_tcpip(struct collect_info_2 *info,
			     struct lsh_object *a,
			     struct lsh_object *b)
195
196
197
198
{
  CAST(address_info, local, a);
  CAST(listen_value, peer, b);

199
  assert(!info->next);
200

201
  return &make_open_tcpip_command(ATOM_FORWARDED_TCPIP, TCPIP_WINDOW_SIZE,
Niels Möller's avatar
Niels Möller committed
202
				  local, peer)->super;
203
204
}

205
206
static struct collect_info_2 collect_open_forwarded_tcpip_2 =
STATIC_COLLECT_2_FINAL(collect_open_forwarded_tcpip);
207

208
209
struct collect_info_1 open_forwarded_tcpip =
STATIC_COLLECT_1(&collect_open_forwarded_tcpip_2);
210
211

static struct lsh_object *
212
213
214
collect_open_direct_tcpip(struct collect_info_2 *info,
			  struct lsh_object *a,
			  struct lsh_object *b)
215
216
217
218
{
  CAST(address_info, local, a);
  CAST(listen_value, peer, b);

219
  assert(!info->next);
220

221
  return &make_open_tcpip_command(ATOM_DIRECT_TCPIP, TCPIP_WINDOW_SIZE,
Niels Möller's avatar
Niels Möller committed
222
				  local, peer)->super;
223
224
}

225
226
static struct collect_info_2 collect_open_direct_tcpip_2 =
STATIC_COLLECT_2_FINAL(collect_open_direct_tcpip);
227

228
229
struct collect_info_1 open_direct_tcpip =
STATIC_COLLECT_1(&collect_open_direct_tcpip_2);
230
231
232
233
234
235
236
237
238


/* Requesting remote forwarding of a port */

/* GABA:
   (class
     (name remote_port_install_continuation)
     (super command_frame)
     (vars
239
       (port object remote_port)
240
241
242
       (callback object command)))
*/

Niels Möller's avatar
Niels Möller committed
243
244
245
static void
do_remote_port_install_continuation(struct command_continuation *s,
				    struct lsh_object *x)
246
247
{
  CAST(remote_port_install_continuation, self, s);
248
249
  CAST(ssh_connection, connection, x);

250
251
252
253
  assert(connection);

  debug("tcpforward_commands.c: do_remote_port_install_continuation(), success.\n");
  self->port->callback = self->callback;
254

Niels Möller's avatar
Niels Möller committed
255
  COMMAND_RETURN(self->super.up, x);
256
257
258
}

static struct command_continuation *
259
260
make_remote_port_install_continuation(struct remote_port *port,
				      struct command *callback,
261
262
263
264
				      struct command_continuation *c)
{
  NEW(remote_port_install_continuation, self);

265
266
  debug("tcpforward_commands.c: make_remote_port_install_continuation()\n");

267
268
  self->super.super.c = do_remote_port_install_continuation;
  self->super.up = c;
269
270

  self->port = port;
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  self->callback = callback;

  return &self->super.super;
}

/* Listening on a remote port
 *
 * (remote_listen callback port connection)
 *
 * Returns a remote_port or NULL.
 * 
 * callback is invoked with a address_info peer as argument, and
 * should return a channel or NULL.
 */

/* GABA:
   (class
     (name request_tcpip_forward_command)
     (super global_request_command)
     (vars
       ; Invoked when a forwarded_tcpip request is received.
       ; Called with the struct address_info *peer as argument.
       (callback object command)
       (port object address_info))) */

static struct lsh_string *
do_format_request_tcpip_forward(struct global_request_command *s,
				struct ssh_connection *connection,
				struct command_continuation **c)
{
  CAST(request_tcpip_forward_command, self, s);
  struct remote_port *port;
  int want_reply;
304
305
306

  debug("tcpforward_commands.c: do_format_request_tcpip_forward()\n");

Niels Möller's avatar
Niels Möller committed
307
  if (CONTINUATION_USED_P(*c))
308
    {
309
310
      /* FIXME: Use some exception handler to remove the port from the
       * list if the request fails. */
311
      port = make_remote_port(self->port, NULL);
312
      *c = make_remote_port_install_continuation(port, self->callback, *c);
313
314
315
316
317
318
319
320
      want_reply = 1;
    }
  else
    {
      port = make_remote_port(self->port, self->callback);
      want_reply = 0;
    }
  
Niels Möller's avatar
Niels Möller committed
321
  object_queue_add_tail(&connection->table->remote_ports,
322
323
			&port->super.super);
  
Niels Möller's avatar
Niels Möller committed
324
325
  return format_global_request(ATOM_TCPIP_FORWARD, want_reply, "%S%i",
			       self->port->ip, self->port->port);
326
327
328
329
330
331
332
}
		    
static struct command *
make_request_tcpip_forward_command(struct command *callback,
				   struct address_info *listen)
{
  NEW(request_tcpip_forward_command, self);
333
334
335

  debug("tcpforward_commands.c: make_request_tcpip_forward_command()\n");
  
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  self->super.super.call = do_channel_global_command;
  self->super.format_request = do_format_request_tcpip_forward;

  self->callback = callback;
  self->port = listen;
  
  return &self->super.super;
}

static struct lsh_object *
collect_remote_listen(struct collect_info_2 *info,
		      struct lsh_object *a, struct lsh_object *b)
{
  CAST_SUBTYPE(command, callback, a);
  CAST(address_info, port, b);
351
  assert(!info->next);
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  
  return &make_request_tcpip_forward_command(callback, port)->super;
}

static struct collect_info_2 collect_info_remote_listen_2 =
STATIC_COLLECT_2_FINAL(collect_remote_listen);

static struct collect_info_1 remote_listen_command =
STATIC_COLLECT_1(&collect_info_remote_listen_2);


/* Cancel a remotely forwarded port.
 * FIXME: Not implemented */



/* GABA:
   (expr
370
     (name forward_local_port)
371
372
373
374
375
376
     (params
       (backend object io_backend)
       (local object address_info)
       (target object address_info))
     (expr
       (lambda (connection)
377
         (connection_remember connection
378
379
           (listen_callback
	     (lambda (peer)
380
381
	       ;; Remembering is done by open_direct_tcpip
	       ;; and new_tcpip_channel.
382
383
384
385
386
	       (tcpip_start_io
	         (catch_channel_open 
		   (open_direct_tcpip target peer) connection)))
	     backend
	     local)))))
387
388
*/

389
390
391
392
struct command *
make_forward_local_port(struct io_backend *backend,
			struct address_info *local,
			struct address_info *target)
393
{
394
  CAST_SUBTYPE(command, res,
395
396
	       forward_local_port(backend, local, target));

397
  trace("tcpforward_commands.c: forward_local_port()\n");
398
399
400
401
402
403

  return res;
}

/* GABA:
   (expr
404
     (name forward_remote_port)
405
406
     (params
       (connect object command)
407
       (remote object address_info))
408
409
410
     (expr
       (lambda (connection)
         (remote_listen (lambda (peer)
411
	                  (tcpip_connect_io 
412
413
414
415
416
			     ; NOTE: The use of prog1 is needed to
			     ; delay the connect call until the
			     ; (otherwise ignored) peer argument is
			     ; available.  
			     (connect (prog1 connection peer))))
417
418
419
420
	                remote
			connection))))
*/

421
422
423
424
struct command *
make_forward_remote_port(struct io_backend *backend,
			 struct address_info *remote,
			 struct address_info *target)
425
426
{
  CAST_SUBTYPE(command, res,
427
	       forward_remote_port(make_connect_port(backend, target), remote));
428

429
430
  debug("tcpforward_commands.c: forward_remote_port()\n");
  
431
432
433
  return res;
}

Niels Möller's avatar
Niels Möller committed
434
435
/* Takes a callback function and returns a channel_open
 * handler. */
Niels Möller's avatar
Niels Möller committed
436
static void
437
do_make_direct_tcpip_handler(struct command *s UNUSED,
438
439
440
			     struct lsh_object *x,
			     struct command_continuation *c,
			     struct exception_handler *e UNUSED)
Niels Möller's avatar
Niels Möller committed
441
442
443
{
  CAST_SUBTYPE(command, callback,  x);

444
445
  trace("tcpforward_commands.c: do_make_open_tcp_handler()\n");
  
Niels Möller's avatar
Niels Möller committed
446
447
  COMMAND_RETURN(c,
		 &make_channel_open_direct_tcpip(callback)->super);
Niels Möller's avatar
Niels Möller committed
448
449
450
}

static struct command
451
make_direct_tcpip_handler = STATIC_COMMAND(do_make_direct_tcpip_handler);
Niels Möller's avatar
Niels Möller committed
452
453

/* Takes a callback function and returns a global_request handler. */
Niels Möller's avatar
Niels Möller committed
454
static void
Niels Möller's avatar
Niels Möller committed
455
456
do_make_tcpip_forward_handler(struct command *s UNUSED,
			      struct lsh_object *x,
457
458
			      struct command_continuation *c,
			      struct exception_handler *e UNUSED)
Niels Möller's avatar
Niels Möller committed
459
460
461
{
  CAST_SUBTYPE(command, callback,  x);

462
463
  debug("tcpforward_commands.c: do_make_tcpip_forward_handler()\n");
  
Niels Möller's avatar
Niels Möller committed
464
465
  COMMAND_RETURN(c,
		 &make_tcpip_forward_request(callback)->super);
Niels Möller's avatar
Niels Möller committed
466
467
468
}

static struct command
469
make_tcpip_forward_handler
Niels Möller's avatar
Niels Möller committed
470
471
472
= STATIC_COMMAND(do_make_tcpip_forward_handler);


473
474
/* Commands to install handlers */
struct install_info install_direct_tcpip_info_2 =
Niels Möller's avatar
Niels Möller committed
475
476
STATIC_INSTALL_OPEN_HANDLER(ATOM_DIRECT_TCPIP);

477
478
struct collect_info_1 install_direct_tcpip_handler =
STATIC_COLLECT_1(&install_direct_tcpip_info_2.super);
Niels Möller's avatar
Niels Möller committed
479

480
struct install_info install_forwarded_tcpip_info_2 =
Niels Möller's avatar
Niels Möller committed
481
482
STATIC_INSTALL_OPEN_HANDLER(ATOM_FORWARDED_TCPIP);

483
484
struct collect_info_1 install_forwarded_tcpip_handler =
STATIC_COLLECT_1(&install_forwarded_tcpip_info_2.super);
Niels Möller's avatar
Niels Möller committed
485
486
487
488
489
490
491
492
493
494
495

/* Server side callbacks */

/* Make this non-static? */
/* GABA:
   (expr
     (name direct_tcpip_hook)
     (params
       (connect object command))
     (expr
       (lambda (connection)
496
497
498
         (install_direct_tcpip connection
	   (direct_tcpip_handler (lambda (port)
	     (tcpip_connect_io (connect connection port))))))))
Niels Möller's avatar
Niels Möller committed
499
500
501
502
503
504
505
506
*/

struct command *
make_direct_tcpip_hook(struct io_backend *backend)
{
  CAST_SUBTYPE(command, res,
	       direct_tcpip_hook(make_connect_connection(backend)));

507
508
  debug("tcpforward_commands.c: make_direct_tcpip_hook()\n");

Niels Möller's avatar
Niels Möller committed
509
510
511
512
  return res;
}


513
514
struct install_info install_tcpip_forward_info_2 =
STATIC_INSTALL_GLOBAL_HANDLER(ATOM_TCPIP_FORWARD);
Niels Möller's avatar
Niels Möller committed
515

516
517
struct collect_info_1 install_tcpip_forward_handler =
STATIC_COLLECT_1(&install_tcpip_forward_info_2.super);
Niels Möller's avatar
Niels Möller committed
518
519
520

/* GABA:
   (expr
521
     (name tcpip_forward_hook)
Niels Möller's avatar
Niels Möller committed
522
     (globals
523
       (install "&install_tcpip_forward_handler.super.super.super")
524
       (handler "&make_tcpip_forward_handler.super"))
Niels Möller's avatar
Niels Möller committed
525
526
527
528
     (params
       (backend object io_backend))
     (expr
       (lambda (connection)
529
         ;; Called when the ssh-connection is established
Niels Möller's avatar
Niels Möller committed
530
531
         (install connection
	   (handler (lambda (port)
Niels Möller's avatar
Niels Möller committed
532
	     ;; Called when the client requests remote forwarding.
533
	     ;; It should return the fd associated with the port.
Niels Möller's avatar
Niels Möller committed
534
	     ;; NOTE: The caller, do_tcpip_forward_request, is responsible
535
	     ;; for handling I/O exceptions, and for remembering the port.
536
537
538
	     (listen_callback (lambda (peer)
  		  		;; Called when someone connects to the
		  		;; forwarded port.
539
540
541
				;; Remembering is done by open_direct_tcpip
				;; and new_tcpip_channel.
				(tcpip_start_io
542
543
544
		  		  (catch_channel_open 
		  		    (open_forwarded_tcpip port peer) connection)))
		  	      backend port))))))))
Niels Möller's avatar
Niels Möller committed
545
546
547
*/

struct command *
548
make_tcpip_forward_hook(struct io_backend *backend)
Niels Möller's avatar
Niels Möller committed
549
{
550
  CAST_SUBTYPE(command, res, tcpip_forward_hook(backend));
Niels Möller's avatar
Niels Möller committed
551

552
553
  debug("tcpforward_commands.c: tcpip_forward_hook()\n");
  
Niels Möller's avatar
Niels Möller committed
554
555
  return res;
}