tcpforward_commands.c 14 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 install_info install_direct_tcpip_handler;
struct install_info install_forwarded_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 install_info 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
static struct catch_report_collect catch_channel_open;
Niels Möller's avatar
Niels Möller committed
66
#define CATCH_CHANNEL_OPEN (&catch_channel_open.super.super)
67

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
DEFINE_COMMAND2(open_forwarded_tcpip)
195
196
     (struct command_2 *s UNUSED,
      struct lsh_object *a1,
197
198
199
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
200
{
201
202
203
204
205
206
207
  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));
208
209
}

210
DEFINE_COMMAND2(open_direct_tcpip)
211
212
     (struct command_2 *s UNUSED,
      struct lsh_object *a1,
213
214
215
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
216
{
217
218
219
220
221
222
223
  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));
224
225
226
227
228
229
230
231
232
233
}


/* Requesting remote forwarding of a port */

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

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

245
246
  assert(connection);

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

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

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

260
  debug("tcpforward_commands.c: make_remote_port_install_continuation\n");
261

262
263
  self->super.super.c = do_remote_port_install_continuation;
  self->super.up = c;
264
265

  self->port = port;
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
297
298
  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;
299

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

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


324
DEFINE_COMMAND2(remote_listen_command)
325
326
     (struct command_2 *s UNUSED,
      struct lsh_object *a1,
327
328
329
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
330
{
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
  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);
  }
347
348
349
350
351
352
353
354
355
356
}


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



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

376
377
378
379
struct command *
make_forward_local_port(struct io_backend *backend,
			struct address_info *local,
			struct address_info *target)
380
{
381
  CAST_SUBTYPE(command, res,
382
383
	       forward_local_port(backend, local, target));

384
  trace("tcpforward_commands.c: forward_local_port\n");
385
386
387
388
389
390

  return res;
}

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

408
409
410
411
struct command *
make_forward_remote_port(struct io_backend *backend,
			 struct address_info *remote,
			 struct address_info *target)
412
413
{
  CAST_SUBTYPE(command, res,
414
	       forward_remote_port(make_connect_port(backend, target), remote));
415

416
  debug("tcpforward_commands.c: forward_remote_port\n");
417
  
418
419
420
  return res;
}

Niels Möller's avatar
Niels Möller committed
421
422
/* Takes a callback function and returns a channel_open
 * handler. */
423
424
425
426
427
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
428
429
430
{
  CAST_SUBTYPE(command, callback,  x);

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

/* Takes a callback function and returns a global_request handler. */
438
439
440
441
442
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
443
444
445
{
  CAST_SUBTYPE(command, callback,  x);

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


453
/* Commands to install handlers */
454
struct install_info install_direct_tcpip_handler =
Niels Möller's avatar
Niels Möller committed
455
456
STATIC_INSTALL_OPEN_HANDLER(ATOM_DIRECT_TCPIP);

457
struct install_info install_forwarded_tcpip_handler =
Niels Möller's avatar
Niels Möller committed
458
459
460
461
462
463
464
465
466
467
468
469
470
STATIC_INSTALL_OPEN_HANDLER(ATOM_FORWARDED_TCPIP);


/* Server side callbacks */

/* Make this non-static? */
/* GABA:
   (expr
     (name direct_tcpip_hook)
     (params
       (connect object command))
     (expr
       (lambda (connection)
471
472
473
         (install_direct_tcpip connection
	   (direct_tcpip_handler (lambda (port)
	     (tcpip_connect_io (connect connection port))))))))
Niels Möller's avatar
Niels Möller committed
474
475
476
477
478
479
480
481
*/

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

482
  debug("tcpforward_commands.c: make_direct_tcpip_hook\n");
483

Niels Möller's avatar
Niels Möller committed
484
485
486
  return res;
}

487
struct install_info install_tcpip_forward_handler =
488
STATIC_INSTALL_GLOBAL_HANDLER(ATOM_TCPIP_FORWARD);
Niels Möller's avatar
Niels Möller committed
489
490
491
492


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

struct command *
521
make_tcpip_forward_hook(struct io_backend *backend)
Niels Möller's avatar
Niels Möller committed
522
{
523
  CAST_SUBTYPE(command, res, tcpip_forward_hook(backend));
Niels Möller's avatar
Niels Möller committed
524

525
  debug("tcpforward_commands.c: tcpip_forward_hook\n");
526
  
Niels Möller's avatar
Niels Möller committed
527
528
  return res;
}