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

Niels Möller's avatar
Niels Möller committed
79
/* NOTE: This command does not do any remembering. */
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(lsh_fd, socket, x);
87

88
89
  assert(socket);

Niels Möller's avatar
Niels Möller committed
90
  COMMAND_RETURN(c, make_tcpip_channel(socket, TCPIP_WINDOW_SIZE));
91
92
}

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

/* 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
100
static void
Niels Möller's avatar
Niels Möller committed
101
102
do_tcpip_start_io(struct command *s UNUSED, 
		  struct lsh_object *x,
103
104
		  struct command_continuation *c,
		  struct exception_handler *e UNUSED)
105
{
Niels Möller's avatar
Niels Möller committed
106
  CAST_SUBTYPE(ssh_channel, channel, x);
107

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

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

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)
132
       (max_window . UINT32)
133
       ; For forwarded-tcpip, port is the port listened to.
134
       ; For direct-tcpip, port is the port to connect to.
135
136
137
138
139
140
141
142
       ; 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,
143
		  UINT32 local_channel_number,
144
145
146
147
148
149
150
		  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
   * should be ok to delay the REMEMBER() call until here. */
151
152
153
154

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

  channel = make_tcpip_channel(self->peer->fd, TCPIP_WINDOW_SIZE);
155
  channel->write = connection->write;
156

157
  REMEMBER_RESOURCE(channel->resources, &self->peer->fd->super);
158

159
160
161
162
163
  *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);
164
165
166
167
168
  
  return channel;
}

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

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

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

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

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

197
  assert(!info->next);
198

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

203
204
static struct collect_info_2 collect_open_forwarded_tcpip_2 =
STATIC_COLLECT_2_FINAL(collect_open_forwarded_tcpip);
205

206
207
struct collect_info_1 open_forwarded_tcpip =
STATIC_COLLECT_1(&collect_open_forwarded_tcpip_2);
208
209

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

217
  assert(!info->next);
218

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

223
224
static struct collect_info_2 collect_open_direct_tcpip_2 =
STATIC_COLLECT_2_FINAL(collect_open_direct_tcpip);
225

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


/* Requesting remote forwarding of a port */

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

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

  if (connection)
    {
      debug("tcpforward_commands.c: do_remote_port_install_continuation(), success.\n");
251
  
252
253
254
255
256
      self->port->callback = self->callback;
    }
  else
    {
      debug("tcpforward_commands.c: do_remote_port_install_continuation(), failed.\n");
257

258
259
260
      /* FIXME: Cleanup, and delete the port from the remote_ports list. */
      
    }
Niels Möller's avatar
Niels Möller committed
261
  COMMAND_RETURN(self->super.up, x);
262
263
264
}

static struct command_continuation *
265
266
make_remote_port_install_continuation(struct remote_port *port,
				      struct command *callback,
267
268
269
270
				      struct command_continuation *c)
{
  NEW(remote_port_install_continuation, self);

271
272
  debug("tcpforward_commands.c: make_remote_port_install_continuation()\n");

273
274
  self->super.super.c = do_remote_port_install_continuation;
  self->super.up = c;
275
276

  self->port = port;
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
304
305
306
307
308
309
  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;
310
311
312

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

Niels Möller's avatar
Niels Möller committed
313
  if (CONTINUATION_USED_P(*c))
314
315
    {
      port = make_remote_port(self->port, NULL);
316
      *c = make_remote_port_install_continuation(port, self->callback, *c);
317
318
319
320
321
322
323
324
      want_reply = 1;
    }
  else
    {
      port = make_remote_port(self->port, self->callback);
      want_reply = 0;
    }
  
Niels Möller's avatar
Niels Möller committed
325
  object_queue_add_tail(&connection->table->remote_ports,
326
327
328
329
330
331
332
333
334
335
336
			&port->super.super);
  
  return ssh_format("%c%a%c%S%i", SSH_MSG_GLOBAL_REQUEST, ATOM_TCPIP_FORWARD,
		    want_reply, self->port->ip, self->port->port);
}
		    
static struct command *
make_request_tcpip_forward_command(struct command *callback,
				   struct address_info *listen)
{
  NEW(request_tcpip_forward_command, self);
337
338
339

  debug("tcpforward_commands.c: make_request_tcpip_forward_command()\n");
  
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
  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);
355
  assert(!info->next);
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  
  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
374
     (name forward_local_port)
375
     (globals
376
       (listen LISTEN_COMMAND))
377
378
379
380
381
382
     (params
       (backend object io_backend)
       (local object address_info)
       (target object address_info))
     (expr
       (lambda (connection)
383
384
385
386
387
388
389
         (connection_remember connection
           (listen (lambda (peer)
	             (tcpip_start_io
		       (catch_channel_open 
		         (open_direct_tcpip target peer) connection)))
		   backend
	           local)))))
390
391
*/

392
393
394
395
struct command *
make_forward_local_port(struct io_backend *backend,
			struct address_info *local,
			struct address_info *target)
396
{
397
  CAST_SUBTYPE(command, res,
398
399
400
	       forward_local_port(backend, local, target));

  debug("tcpforward_commands.c: forward_local_port()\n");
401
402
403
404
405
406

  return res;
}

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

424
425
426
427
struct command *
make_forward_remote_port(struct io_backend *backend,
			 struct address_info *remote,
			 struct address_info *target)
428
429
{
  CAST_SUBTYPE(command, res,
430
	       forward_remote_port(make_connect_port(backend, target), remote));
431

432
433
  debug("tcpforward_commands.c: forward_remote_port()\n");
  
434
435
436
  return res;
}

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

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

static struct command
454
make_direct_tcpip_handler = STATIC_COMMAND(do_make_direct_tcpip_handler);
Niels Möller's avatar
Niels Möller committed
455
456

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

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

static struct command
472
make_tcpip_forward_handler
Niels Möller's avatar
Niels Möller committed
473
474
475
= STATIC_COMMAND(do_make_tcpip_forward_handler);


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

480
481
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
482

483
struct install_info install_forwarded_tcpip_info_2 =
Niels Möller's avatar
Niels Möller committed
484
485
STATIC_INSTALL_OPEN_HANDLER(ATOM_FORWARDED_TCPIP);

486
487
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
488
489
490
491
492
493
494
495
496
497
498

/* Server side callbacks */

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

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

510
511
  debug("tcpforward_commands.c: make_direct_tcpip_hook()\n");

Niels Möller's avatar
Niels Möller committed
512
513
514
515
  return res;
}


516
517
struct install_info install_tcpip_forward_info_2 =
STATIC_INSTALL_GLOBAL_HANDLER(ATOM_TCPIP_FORWARD);
Niels Möller's avatar
Niels Möller committed
518

519
520
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
521
522
523

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

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

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