tcpforward_commands.c 14.3 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 Balázs Scheidler, Niels Möller
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
41
42
extern struct command_2 open_direct_tcpip;
extern struct command_2 remote_listen_command;
extern struct command_2 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
/* FIXME: Should be static? */
struct command make_direct_tcpip_handler;
51
52

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

54
55
56
57
58
/* FIXME: Should be static? */
struct command make_tcpip_forward_handler;

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

65
66
67
static struct catch_report_collect catch_channel_open;
#define CATCH_CHANNEL_OPEN (&catch_channel_open.super.super.super)

68
69
#include "tcpforward_commands.c.x"

70
71
72
73
74
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);
75
76
77
78
79

/* 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
80
#define TCPIP_WINDOW_SIZE 10000
81

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

91
92
93
94
  assert(lv);
  assert(lv->fd);
  
  COMMAND_RETURN(c, make_tcpip_channel(lv->fd, TCPIP_WINDOW_SIZE));
95
96
}

Niels Möller's avatar
Niels Möller committed
97
struct command tcpip_connect_io = STATIC_COMMAND(do_tcpip_connect_io);
98
99
100
101
102
103

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

112
113
  assert(channel);
  
Niels Möller's avatar
Niels Möller committed
114
  tcpip_channel_start_io(channel);
115

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

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
136
137
138

       (initial_window . UINT32)

139
       ; For forwarded-tcpip, port is the port listened to.
140
       ; For direct-tcpip, port is the port to connect to.
141
142
143
144
145
146
147
148
       ; 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,
149
		  UINT32 local_channel_number,
150
151
152
153
154
155
		  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
156
157
   * should be ok to delay the REMEMBER call until here. It is done
   * by make_tcpip_channel. */
158

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

  channel = make_tcpip_channel(self->peer->fd, TCPIP_WINDOW_SIZE);
162
  channel->write = connection->write;
163

164
165
166
167
168
  *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);
169
170
171
172
173
  
  return channel;
}

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

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

182
  self->super.super.call = do_channel_open_command;
Niels Möller's avatar
Niels Möller committed
183
  self->super.new_channel = new_tcpip_channel;
184
185

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

194
195
196
197
198
DEFINE_COMMAND2(open_forwarded_tcpip)
     (struct lsh_object *a1,
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
199
{
200
201
202
203
204
205
206
  CAST(address_info, local, a1);
  CAST(listen_value, peer, a2);
  
  COMMAND_RETURN(c,
		 make_open_tcpip_command(ATOM_FORWARDED_TCPIP,
					 TCPIP_WINDOW_SIZE,
					 local, peer));
207
208
}

209
210
211
212
213
DEFINE_COMMAND2(open_direct_tcpip)
     (struct lsh_object *a1,
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
214
{
215
216
217
218
219
220
221
  CAST(address_info, local, a1);
  CAST(listen_value, peer, a2);
  
  COMMAND_RETURN(c,
		 make_open_tcpip_command(ATOM_DIRECT_TCPIP,
					 TCPIP_WINDOW_SIZE,
					 local, peer));
222
223
224
225
226
227
228
229
230
231
}


/* Requesting remote forwarding of a port */

/* GABA:
   (class
     (name remote_port_install_continuation)
     (super command_frame)
     (vars
232
       (port object remote_port)
233
234
235
       (callback object command)))
*/

Niels Möller's avatar
Niels Möller committed
236
237
238
static void
do_remote_port_install_continuation(struct command_continuation *s,
				    struct lsh_object *x)
239
240
{
  CAST(remote_port_install_continuation, self, s);
241
242
  CAST(ssh_connection, connection, x);

243
244
  assert(connection);

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

Niels Möller's avatar
Niels Möller committed
248
  COMMAND_RETURN(self->super.up, x);
249
250
251
}

static struct command_continuation *
252
253
make_remote_port_install_continuation(struct remote_port *port,
				      struct command *callback,
254
255
256
257
				      struct command_continuation *c)
{
  NEW(remote_port_install_continuation, self);

258
  debug("tcpforward_commands.c: make_remote_port_install_continuation\n");
259

260
261
  self->super.super.c = do_remote_port_install_continuation;
  self->super.up = c;
262
263

  self->port = port;
264
265
266
267
268
269
270
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
  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;
297

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

Niels Möller's avatar
Niels Möller committed
300
  if (CONTINUATION_USED_P(*c))
301
    {
302
303
      /* FIXME: Use some exception handler to remove the port from the
       * list if the request fails. */
304
      port = make_remote_port(self->port, NULL);
305
      *c = make_remote_port_install_continuation(port, self->callback, *c);
306
307
308
309
310
311
312
313
      want_reply = 1;
    }
  else
    {
      port = make_remote_port(self->port, self->callback);
      want_reply = 0;
    }
  
Niels Möller's avatar
Niels Möller committed
314
  object_queue_add_tail(&connection->table->remote_ports,
315
316
			&port->super.super);
  
Niels Möller's avatar
Niels Möller committed
317
318
  return format_global_request(ATOM_TCPIP_FORWARD, want_reply, "%S%i",
			       self->port->ip, self->port->port);
319
320
321
}


322
323
324
325
326
DEFINE_COMMAND2(remote_listen_command)
     (struct lsh_object *a1,
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
327
{
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  trace("remote_listen_command\n");

  {
    CAST_SUBTYPE(command, callback, a1);
    CAST(address_info, port, a2);
    
    NEW(request_tcpip_forward_command, self);
    
    self->super.super.call = do_channel_global_command;
    self->super.format_request = do_format_request_tcpip_forward;

    self->callback = callback;
    self->port = port;
    
    COMMAND_RETURN(c, self);
  }
344
345
346
347
348
349
350
351
352
353
}


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



/* GABA:
   (expr
354
     (name forward_local_port)
355
356
357
358
359
360
     (params
       (backend object io_backend)
       (local object address_info)
       (target object address_info))
     (expr
       (lambda (connection)
361
         (connection_remember connection
362
363
           (listen_callback
	     (lambda (peer)
364
365
	       ;; Remembering is done by open_direct_tcpip
	       ;; and new_tcpip_channel.
366
367
368
369
370
	       (tcpip_start_io
	         (catch_channel_open 
		   (open_direct_tcpip target peer) connection)))
	     backend
	     local)))))
371
372
*/

373
374
375
376
struct command *
make_forward_local_port(struct io_backend *backend,
			struct address_info *local,
			struct address_info *target)
377
{
378
  CAST_SUBTYPE(command, res,
379
380
	       forward_local_port(backend, local, target));

381
  trace("tcpforward_commands.c: forward_local_port\n");
382
383
384
385
386
387

  return res;
}

/* GABA:
   (expr
388
     (name forward_remote_port)
389
390
     (params
       (connect object command)
391
       (remote object address_info))
392
393
394
     (expr
       (lambda (connection)
         (remote_listen (lambda (peer)
395
	                  (tcpip_connect_io 
396
397
398
399
400
			     ; NOTE: The use of prog1 is needed to
			     ; delay the connect call until the
			     ; (otherwise ignored) peer argument is
			     ; available.  
			     (connect (prog1 connection peer))))
401
402
403
404
	                remote
			connection))))
*/

405
406
407
408
struct command *
make_forward_remote_port(struct io_backend *backend,
			 struct address_info *remote,
			 struct address_info *target)
409
410
{
  CAST_SUBTYPE(command, res,
411
	       forward_remote_port(make_connect_port(backend, target), remote));
412

413
  debug("tcpforward_commands.c: forward_remote_port\n");
414
  
415
416
417
  return res;
}

Niels Möller's avatar
Niels Möller committed
418
419
/* Takes a callback function and returns a channel_open
 * handler. */
420
421
422
423
424
DEFINE_COMMAND(make_direct_tcpip_handler)
     (struct command *s UNUSED,
      struct lsh_object *x,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
Niels Möller's avatar
Niels Möller committed
425
426
427
{
  CAST_SUBTYPE(command, callback,  x);

428
  trace("tcpforward_commands.c: do_make_open_tcp_handler\n");
429
  
Niels Möller's avatar
Niels Möller committed
430
431
  COMMAND_RETURN(c,
		 &make_channel_open_direct_tcpip(callback)->super);
Niels Möller's avatar
Niels Möller committed
432
433
434
}

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

443
  debug("tcpforward_commands.c: make_tcpip_forward_handler\n");
444
  
Niels Möller's avatar
Niels Möller committed
445
446
  COMMAND_RETURN(c,
		 &make_tcpip_forward_request(callback)->super);
Niels Möller's avatar
Niels Möller committed
447
448
}

449
#if 0
Niels Möller's avatar
Niels Möller committed
450
static struct command
451
make_tcpip_forward_handler
Niels Möller's avatar
Niels Möller committed
452
= STATIC_COMMAND(do_make_tcpip_forward_handler);
453
#endif
Niels Möller's avatar
Niels Möller committed
454
455


456
457
/* Commands to install handlers */
struct install_info install_direct_tcpip_info_2 =
Niels Möller's avatar
Niels Möller committed
458
459
STATIC_INSTALL_OPEN_HANDLER(ATOM_DIRECT_TCPIP);

460
461
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
462

463
struct install_info install_forwarded_tcpip_info_2 =
Niels Möller's avatar
Niels Möller committed
464
465
STATIC_INSTALL_OPEN_HANDLER(ATOM_FORWARDED_TCPIP);

466
467
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
468
469
470
471
472
473
474
475
476
477
478

/* Server side callbacks */

/* Make this non-static? */
/* GABA:
   (expr
     (name direct_tcpip_hook)
     (params
       (connect object command))
     (expr
       (lambda (connection)
479
480
481
         (install_direct_tcpip connection
	   (direct_tcpip_handler (lambda (port)
	     (tcpip_connect_io (connect connection port))))))))
Niels Möller's avatar
Niels Möller committed
482
483
484
485
486
487
488
489
*/

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

490
  debug("tcpforward_commands.c: make_direct_tcpip_hook\n");
491

Niels Möller's avatar
Niels Möller committed
492
493
494
495
  return res;
}


496
497
struct install_info install_tcpip_forward_info_2 =
STATIC_INSTALL_GLOBAL_HANDLER(ATOM_TCPIP_FORWARD);
Niels Möller's avatar
Niels Möller committed
498

499
500
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
501
502
503

/* GABA:
   (expr
504
     (name tcpip_forward_hook)
505
     ; FIXME: Don't use globals.
Niels Möller's avatar
Niels Möller committed
506
     (globals
507
       (install "&install_tcpip_forward_handler.super.super.super")
508
       (handler "&make_tcpip_forward_handler.super"))
Niels Möller's avatar
Niels Möller committed
509
510
511
512
     (params
       (backend object io_backend))
     (expr
       (lambda (connection)
513
         ;; Called when the ssh-connection is established
Niels Möller's avatar
Niels Möller committed
514
515
         (install connection
	   (handler (lambda (port)
Niels Möller's avatar
Niels Möller committed
516
	     ;; Called when the client requests remote forwarding.
517
	     ;; It should return the fd associated with the port.
Niels Möller's avatar
Niels Möller committed
518
	     ;; NOTE: The caller, do_tcpip_forward_request, is responsible
519
	     ;; for handling I/O exceptions, and for remembering the port.
520
521
522
	     (listen_callback (lambda (peer)
  		  		;; Called when someone connects to the
		  		;; forwarded port.
523
524
525
				;; Remembering is done by open_direct_tcpip
				;; and new_tcpip_channel.
				(tcpip_start_io
526
527
528
		  		  (catch_channel_open 
		  		    (open_forwarded_tcpip port peer) connection)))
		  	      backend port))))))))
Niels Möller's avatar
Niels Möller committed
529
530
531
*/

struct command *
532
make_tcpip_forward_hook(struct io_backend *backend)
Niels Möller's avatar
Niels Möller committed
533
{
534
  CAST_SUBTYPE(command, res, tcpip_forward_hook(backend));
Niels Möller's avatar
Niels Möller committed
535

536
  debug("tcpforward_commands.c: tcpip_forward_hook\n");
537
  
Niels Möller's avatar
Niels Möller committed
538
539
  return res;
}