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

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Balazs Scheidler, 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
23
 */
24
25
26

#include "tcpforward.h"

27
#include "channel_commands.h"
28
#include "format.h"
29
#include "io_commands.h"
30
31
32
#include "ssh.h"
#include "werror.h"

33
34
35
36
37
#if 0
#include "parse.h"
#include "read_data.h"
#endif

38
#include <assert.h>
39
40
41
#include <errno.h>
#include <string.h>

42
/* Forward declarations */
43
extern struct command_simple forward_start_io;
44
extern struct collect_info_1 open_forwarded_tcp;
45

46
47
48
#define FORWARD_START_IO (&forward_start_io.super.super)
#define OPEN_FORWARDED_TCP (&open_forwarded_tcp.super.super.super)

49
#define GABA_DEFINE
50
#include "tcpforward.h.x"
51
#undef GABA_DEFINE
52
53
54

#include "tcpforward.c.x"

55
#if 0
56
57
58
59
static struct fd_callback *
make_tcpip_connected(struct tcpip_channel *c,
		     struct channel_open_callback *response,
		     UINT32 block_size);
60
61
62
#endif

static struct forwarded_port *
63
make_forwarded_port(struct address_info *address, struct lsh_fd *socket)
64
65
66
67
{
  NEW(forwarded_port, self);

  self->local = address;  
68
  self->socket = socket;
69
70
  return self;
}
71

72
73
74
75
76
77
78
79
80
static struct forwarded_port *
lookup_forward(struct object_queue *q,
	       UINT32 length, UINT8 *ip, UINT32 port)
{
  FOR_OBJECT_QUEUE(q, n)
    {
      CAST(forwarded_port, f, n);
      
      if ( (port == f->local->port)
81
	   && (lsh_string_cmp_l(f->local->ip, length, ip) == 0) )
82
83
84
85
86
87
88
89
90
91
92
93
94
95
	return f;
    }
  return NULL;
}

static struct forwarded_port *
remove_forward(struct object_queue *q, int null_ok,
	       UINT32 length, UINT8 *ip, UINT32 port)
{
  FOR_OBJECT_QUEUE(q, n)
    {
      CAST(forwarded_port, f, n);
      
      if ( (port == f->local->port)
96
	   && (lsh_string_cmp_l(f->local->ip, length, ip) == 0) )
97
98
99
100
101
102
103
104
105
106
107
108
	{
	  if (null_ok || f->socket)
	    {
	      FOR_OBJECT_QUEUE_REMOVE(n);
	      return f;
	    }
	  else return NULL;
	}
    }
  return NULL;
}

109
/* TCP forwarding channel */
110
/* GABA:
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
   (class
     (name tcpip_channel)
     (super ssh_channel)
     (vars
       (socket object io_fd)))
*/

static int do_tcpip_receive(struct ssh_channel *c,
			    int type, struct lsh_string *data)
{
  CAST(tcpip_channel, closure, c);
  
  switch (type)
    {
    case CHANNEL_DATA:
      return A_WRITE(&closure->socket->buffer->super, data);
    case CHANNEL_STDERR_DATA:
      werror("Ignoring unexpected stderr data.\n");
      lsh_string_free(data);
      return LSH_OK | LSH_GOON;
    default:
      fatal("Internal error. do_tcpip_receive()");
    }
}

static int do_tcpip_send(struct ssh_channel *c)
{
  CAST(tcpip_channel, closure, c);
  
  closure->socket->super.want_read = 1;
  
  return LSH_OK | LSH_GOON;
}

static int do_tcpip_eof(struct ssh_channel *c)
{
  if ( (c->flags & CHANNEL_SENT_EOF)
       && (c->flags & CHANNEL_CLOSE_AT_EOF))
    return channel_close(c);
  else
    return LSH_OK | LSH_GOON;
}
                      
154
static struct tcpip_channel *make_tcpip_channel(struct io_fd *socket)
155
156
157
158
{
  NEW(tcpip_channel, self);
  
  init_channel(&self->super);
159
  self->socket = socket;
160
161
162
163
  
  return self;
}

164
165
/* GABA:
   (class
166
     (name direct_tcp_server_start_io)
167
168
169
170
171
172
173
     (super command)
     (vars
       (block_size . UINT32)
       (response object channel_open_callback)
       (channel object tcpip_channel)))
*/
  
174
175
176
static int do_direct_tcp_server_start_io(struct command *s, 
					 struct lsh_object *x, 
					 struct command_continuation *c)
177
{
178
  CAST(direct_tcp_server_start_io, self, s);
179
180
181
182
183
184
185
186
187
  CAST_SUBTYPE(io_fd, fd, x);
  int res;

  if (!fd)
    {
      verbose("Forward-request, error establishing connection.\n");
      return CHANNEL_OPEN_CALLBACK(self->response, &self->channel->super,
  				   SSH_OPEN_CONNECT_FAILED, STRERROR(errno), NULL);
    }
188

189
190
191
192
193
  self->channel->super.receive = do_tcpip_receive;
  self->channel->super.send = do_tcpip_send;
  self->channel->super.eof = do_tcpip_eof;
  
  self->channel->socket = 
194
     io_read_write(fd, 
195
196
197
198
		   make_channel_read_data(&self->channel->super), 
		   self->block_size,
		   make_channel_close(&self->channel->super));

199
200
201
202
203
204
205
  res = COMMAND_RETURN(c, (struct lsh_object *) self->channel);
  
  return res | (LSH_CLOSEDP(res)
		? CHANNEL_OPEN_CALLBACK(self->response, &self->channel->super,
					SSH_OPEN_RESOURCE_SHORTAGE, "Error creating channel.", NULL)
		: CHANNEL_OPEN_CALLBACK(self->response, &self->channel->super,
					0, NULL, NULL));
206
207
}

208
static struct command *
209
make_direct_tcp_server_start_io(struct channel_open_callback *response, 
210
211
			     struct tcpip_channel *channel,
			     UINT32 block_size)
212
{
213
  NEW(direct_tcp_server_start_io, self);
214

215
  self->super.call = do_direct_tcp_server_start_io;
216

217
218
  self->response = response;
  self->block_size = block_size;
219
  self->channel = channel;
220
221
222
  return &self->super;
}

223
224
/* GABA:
   (expr
225
     (name make_direct_tcp_connect)
226
227
228
229
230
231
232
     (params
       (connect object command)
       (start_io object command))
     (expr
       (lambda (port) (start_io (connect port)))))
*/
  
233
/* GABA:
234
235
236
237
238
239
240
241
242
243
244
245
246
   (class
     (name open_direct_tcpip)
     (super channel_open)
     (vars
       (backend object io_backend)))
*/

static int do_open_direct_tcpip(struct channel_open *c,
			        struct ssh_connection *connection,
			        struct simple_buffer *args,
			        struct channel_open_callback *response)
{
  CAST(open_direct_tcpip, closure, c);
247
248

  struct lsh_string *dest_host;
249
250
251
252
253
  UINT32 dest_port;
  UINT8 *orig_host;
  UINT32 orig_host_length;
  UINT32 orig_port;
  
254
255
256
257
258
  if ( (dest_host = parse_string_copy(args))
       && parse_uint32(args, &dest_port) 
       && parse_string(args, &orig_host_length, &orig_host)
       && parse_uint32(args, &orig_port) 
       && parse_eod(args))
259
    {
260
261
262
263
264
265
266
267
268
269
270
271
272
273
      struct address_info *a; 
      int res;

      /* FIXME: It might be more elegant to create this object only
       * once. I.e. have some command that is invoked when the
       * ssh-connection service is created, which installs a handler
       * for direct-tcp, and also creates a function which can be
       * invoked with a port to connect to appropriate. I'm not sure
       * how to get this right though; perhaps the start_io command
       * should be made a continuation instead. I.e. a connection should be created with
       *
       * COMMAND_CALL(closure->connect_command, port, make_start_io(make_tcpip_channel()))
       */
      struct lsh_object *o = 
274
275
276
277
278
279
280
281
	make_direct_tcp_connect(make_simple_connect(closure->backend, 
						   connection->resources),
				make_direct_tcp_server_start_io
				(response,
				 /* FIXME: Is it ok to pass NULL
				  * to make_tcpip_channel() ? */
				 make_tcpip_channel(NULL),
				 SSH_MAX_PACKET));
282
283
284
285
286
      
      verbose("direct-tcp connection attempt\n");
      
      /* FIXME: implement filtering on original host? */

287
288
289
290
291
292
293
294
295
296
297
298
      a = make_address_info(dest_host, dest_port);

      {
	CAST_SUBTYPE(command, forward_connect, o);      
	res = COMMAND_CALL(forward_connect, a, NULL);
	if (LSH_CLOSEDP(res))
	  return CHANNEL_OPEN_CALLBACK(response, NULL, 
				       SSH_OPEN_CONNECT_FAILED, 
				       "Error connecting to host", 
 				       NULL);
	return res;
      }
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
    }
  else
    {
      werror("do_open_direct_tcpip: Invalid message!\n");
      return LSH_FAIL | LSH_DIE;
    }
}

struct channel_open *make_open_direct_tcpip(struct io_backend *backend)
{
  NEW(open_direct_tcpip, self);
  
  self->super.handler = do_open_direct_tcpip;
  self->backend = backend;
  return &self->super;
}

316
317
318
319
/* Start i/o on a forwarded channel. Used by clients requesting
 * direct-tcp, and servers requesting tcp_forward. I.e. by the party
 * that accepted a connection for forwarding. */
static struct lsh_object *do_forward_start_io(struct command_simple *c UNUSED, 
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
                                               struct lsh_object *x)
{
  CAST_SUBTYPE(tcpip_channel, channel, x);
  if (!channel)
    {
      verbose("Error opening channel.\n");
      return NULL;
    }

  channel->super.receive = do_tcpip_receive;
  channel->super.send = do_tcpip_send;
  channel->super.eof = do_tcpip_eof;
  channel->socket = 
  	io_read_write(channel->socket,
  		      make_channel_read_data(&channel->super),
		      SSH_MAX_PACKET,
		      make_channel_close(&channel->super));

  return x;
}

341
342
static struct command_simple forward_start_io = 
STATIC_COMMAND_SIMPLE(do_forward_start_io);
343

344
/* GABA:
345
346
347
348
   (class
     (name open_tcpip_command)
     (super channel_open_command)
     (vars
349
350
       (local object address_info)
       (peer object listen_value)))
351
352
353
354
355
356
357
358
*/

static struct ssh_channel *new_tcpip_channel(struct channel_open_command *c,
					     struct ssh_connection *connection,
					     struct lsh_string **request)
{
  CAST(open_tcpip_command, self, c);
  struct tcpip_channel *channel;
359
360
361
362

  /* NOTE: All accepted fd:s must end up in this function, so it
   * should be ok to delay the REMEMBER() call until here. */
  
363
  REMEMBER_RESOURCE(connection->resources, &self->peer->fd->super.super);
364
  
365
  channel = make_tcpip_channel(self->peer->fd);
366
367
368
369
370
  channel->super.write = connection->write;

  *request = prepare_channel_open(connection->channels, ATOM_FORWARDED_TCPIP, 
  				  &channel->super, 
  				  "%S%i%S%i",
371
				  self->local->ip, self->local->port,
372
373
374
375
376
				  self->peer->peer->ip, self->peer->peer->port);
  
  return &channel->super;
}

377
static struct command *make_open_tcpip_command(struct address_info *local,
378
					       struct listen_value *peer)
379
380
381
{
  NEW(open_tcpip_command, self);
  
382
  self->super.super.call = do_channel_open_command;
383
  self->super.new_channel = new_tcpip_channel;
384
385
386

  self->local = local;
  self->peer = peer;
387
388
389
390
  
  return &self->super.super;
}

391
392
393
394
395
396
397
398
399
400
static struct lsh_object *
collect_open_forwarded_tcp(struct collect_info_2 *info,
			   struct lsh_object *a,
			   struct lsh_object *b)
{
  CAST(address_info, local, a);
  CAST(listen_value, peer, b);

  assert(!info);

401
  return &make_open_tcpip_command(local, peer)->super;
402
403
}

404
static struct collect_info_2 collect_open_forwarded_tcp_2 =
405
406
STATIC_COLLECT_2_FINAL(collect_open_forwarded_tcp);

407
static struct collect_info_1 open_forwarded_tcp =
408
409
410
411
412
413
STATIC_COLLECT_1(&collect_open_forwarded_tcp_2);

/* GABA:
   (expr
     (name make_forward_listen)
     (globals
414
415
416
       (start_io FORWARD_START_IO)
       (open_forwarded_tcp OPEN_FORWARDED_TCP)
       (listen LISTEN_COMMAND))
417
418
419
420
421
422
423
424
425
     (params
       (backend object io_backend)
       (connection object ssh_connection))
     (expr (lambda (port)
       (listen (lambda (peer)
                 (start_io (open_forwarded_tcp port peer connection)))
	       backend port))))
*/

426
427
/* GABA:
   (class
428
429
     (name tcp_forward_continuation)
     (super command_continuation)
430
     (vars
431
432
       (connection object ssh_connection)
       (forward object forwarded_port)
433
       (c object global_request_callback)))
434
435
*/

436
437
438
static int
do_tcp_forward_continuation(struct command_continuation *c,
			    struct lsh_object *x)
439
{
440
441
  CAST(tcp_forward_continuation, self, c);
  CAST_SUBTYPE(lsh_fd, fd, x);
442

443
  assert(self->forward);
444
  
445
  if (!fd)
446
    {
447
      struct forwarded_port *port
448
	= remove_forward(&self->connection->forwarded_ports,
449
			 1,
450
451
452
453
454
455
456
			 self->forward->local->ip->length,
			 self->forward->local->ip->data,
			 self->forward->local->port);
      assert(port);
      assert(port == self->forward);
      
      return GLOBAL_REQUEST_CALLBACK(self->c, 0);
457
458
    }
  
459
  REMEMBER_RESOURCE(self->connection->resources, &fd->super);
460

461
  self->forward->socket = fd;
462

463
  return GLOBAL_REQUEST_CALLBACK(self->c, 1);
464
465
}

466
static struct command_continuation *
467
make_tcpforward_continuation(struct ssh_connection *connection,
468
469
			      struct forwarded_port *forward,
			      struct global_request_callback *c)
470
{
471
  NEW(tcp_forward_continuation, self);
472

473
474
475
  self->connection = connection;
  self->forward = forward;
  self->c = c;
476
  
477
  self->super.c = do_tcp_forward_continuation;
478
479
480
481

  return &self->super;
}
  
482
/* GABA:
483
484
485
486
487
488
489
   (class
     (name tcpip_forward_request)
     (super global_request)
     (vars
       (backend object io_backend)))
*/

490
static int do_tcpip_forward_request(struct global_request *s, 
Niels Möller's avatar
Niels Möller committed
491
				    struct ssh_connection *connection,
492
493
				    struct simple_buffer *args,
				    struct global_request_callback *c)
494
{
495
496
  CAST(tcpip_forward_request, self, s);
  struct lsh_string *bind_host;
497
498
  UINT32 bind_port;
  
499
  if ((bind_host = parse_string_copy(args)) 
500
501
502
      && parse_uint32(args, &bind_port) 
      && parse_eod(args))
    {
503
504
505
506
507
508
509
510
      struct address_info *a = make_address_info(bind_host, bind_port);
      struct forwarded_port *forward;

      if (bind_port < 1024)
	{
	  werror("Denying forwarding of privileged port %i.\n", bind_port);
	  return GLOBAL_REQUEST_CALLBACK(c, 0);
	}
511

512
      if (lookup_forward(&connection->forwarded_ports,
513
514
515
516
517
			 bind_host->length, bind_host->data, bind_port))
	{
	  verbose("An already requested tcp-forward requested again\n");
	  return GLOBAL_REQUEST_CALLBACK(c, 0);
	}
518
      
519
520
      verbose("Adding forward-tcpip\n");
      forward = make_forwarded_port(a, NULL);
521
      object_queue_add_head(&connection->forwarded_ports, &forward->super.super);
522
523
524
525
526

      {
	struct lsh_object *o = make_forward_listen(self->backend, connection);
	
	CAST_SUBTYPE(command, listen, o);
527
      
528
529
530
531
	return COMMAND_CALL(listen,
			    a,
			    make_tcpforward_continuation(connection, forward, c));
      }
532
533
534
    }
  else
    {
535
      werror("Incorrectly formatted tcpip-forward request\n");
536
537
538
539
540
541
542
543
544
545
      return LSH_FAIL | LSH_CLOSE;
    }
}

struct global_request *make_tcpip_forward_request(struct io_backend *backend)
{
  NEW(tcpip_forward_request, self);
  
  self->super.handler = do_tcpip_forward_request;
  self->backend = backend;
546
  
547
548
549
  return &self->super;
}

550
static int do_cancel_tcpip_forward(struct global_request *s UNUSED, 
551
				   struct ssh_connection *connection,
552
553
				   struct simple_buffer *args,
				   struct global_request_callback *c)
554
{
555
556
  UINT32 bind_host_length;
  UINT8 *bind_host;
557
558
  UINT32 bind_port;
  
559
  if (parse_string(args, &bind_host_length, &bind_host) &&
560
561
562
      parse_uint32(args, &bind_port) &&
      parse_eod(args))
    {
563
      struct forwarded_port *port
564
	= remove_forward(&connection->forwarded_ports, 0,
565
566
			 bind_host_length,
			 bind_host,
567
568
569
			 bind_port);

      if (port)
570
        {
571
	  assert(port->socket);
572
573
	  verbose("Cancelling a requested tcpip-forward.\n");

574
575
	  close_fd(port->socket, 0);
	  port->socket = NULL;
576

577
	  return GLOBAL_REQUEST_CALLBACK(c, 1);
578
	}
579
      else
580
581
	{      
	  verbose("Could not find tcpip-forward to cancel\n");
582
583

	  return GLOBAL_REQUEST_CALLBACK(c, 0);
584
	}
585
586
587
    }
  else
    {
588
      werror("Incorrectly formatted cancel-tcpip-forward request\n");
589
590
591
592
593
594
595
596
597
598
599
      return LSH_FAIL | LSH_CLOSE;
    }
}

struct global_request *make_cancel_tcpip_forward_request(void)
{
  NEW(global_request, self);
  
  self->handler = do_cancel_tcpip_forward;
  return self;
}
Niels Möller's avatar
Niels Möller committed
600