channel.c 40.5 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* channel.c
 *
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 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
J.H.M. Dassen's avatar
J.H.M. Dassen committed
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
22
23
 */

24
25
26
27
28
29
30
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>
#include <string.h>

Niels Möller's avatar
Niels Möller committed
31
32
33
#include "channel.h"

#include "format.h"
34
#include "io.h"
35
#include "lsh_string.h"
Niels Möller's avatar
Niels Möller committed
36
#include "read_data.h"
Niels Möller's avatar
Niels Möller committed
37
38
39
40
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

41
#define GABA_DEFINE
42
#include "channel.h.x"
43
#undef GABA_DEFINE
44

45
46
#include "channel.c.x"

47

48
struct exception *
49
make_channel_open_exception(uint32_t error_code, const char *msg)
Niels Möller's avatar
Niels Möller committed
50
51
{
  NEW(channel_open_exception, self);
Niels Möller's avatar
Niels Möller committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#define MAX_ERROR 4
  static const char *msgs[MAX_ERROR + 1] = {
    "",
    "Administratively prohibited",
    "Connect failed",
    "Unknown channel type",
    "Resource shortage"
  };

  assert(error_code > 0);
  assert(error_code <= MAX_ERROR);
#undef MAX_ERROR
  
Niels Möller's avatar
Niels Möller committed
66
  self->super.type = EXC_CHANNEL_OPEN;
Niels Möller's avatar
Niels Möller committed
67
  self->super.msg = msg ? msg : msgs[error_code];
Niels Möller's avatar
Niels Möller committed
68
69
70
71
72
  self->error_code = error_code;

  return &self->super;
}

73

74
static struct lsh_string *
75
format_global_failure(void)
Niels Möller's avatar
Niels Möller committed
76
77
78
79
{
  return ssh_format("%c", SSH_MSG_REQUEST_FAILURE);
}

80
static struct lsh_string *
81
format_global_success(void)
82
83
84
85
{
  return ssh_format("%c", SSH_MSG_REQUEST_SUCCESS);
}

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* The advertised rec_max_size must be a little smaller than SSH_MAX_PACKET,
 * to make sure that our peer won't send us packets exceeding our limit for
 * the connection. */

/* NOTE: It would make some sense to use the connection's
 * rec_max_packet instead of the SSH_MAX_PACKET constant. */

#define SSH_MAX_DATA_SIZE (SSH_MAX_PACKET - SSH_CHANNEL_MAX_PACKET_FUZZ)

static void
check_rec_max_packet(struct ssh_channel *channel)
{
  /* Never advertise a larger rec_max_packet than we're willing to
   * handle. */

  if (channel->rec_max_packet > SSH_MAX_DATA_SIZE)
    {
      debug("check_rec_max_packet: Reduced rec_max_packet from %i to %i.\n",
	    channel->rec_max_packet, SSH_MAX_DATA_SIZE);
      channel->rec_max_packet = SSH_MAX_DATA_SIZE;
    }
}

109
static struct lsh_string *
110
111
format_open_confirmation(struct ssh_channel *channel,
			 const char *format, ...)
112
113
{
  va_list args;
114
  uint32_t l1, l2;
115
  struct lsh_string *packet;
116
  
117
#define CONFIRM_FORMAT "%c%i%i%i%i"
118
#define CONFIRM_ARGS \
119
120
121
  SSH_MSG_CHANNEL_OPEN_CONFIRMATION, \
  channel->remote_channel_number, channel->local_channel_number, \
  channel->rec_window_size, channel->rec_max_packet
122
    
123
124
  check_rec_max_packet(channel);

125
  debug("format_open_confirmation: rec_window_size = %i,\n"
126
	"                          rec_max_packet = %i,\n",
127
       channel->rec_window_size,
128
       channel->rec_max_packet);
129
130
131
132
133
134
135
136
  l1 = ssh_format_length(CONFIRM_FORMAT, CONFIRM_ARGS);

  va_start(args, format);
  l2 = ssh_vformat_length(format, args);
  va_end(args);

  packet = lsh_string_alloc(l1 + l2);

137
  ssh_format_write(CONFIRM_FORMAT, packet, 0, CONFIRM_ARGS);
138
139

  va_start(args, format);
140
  ssh_vformat_write(format, packet, l1, args);
141
142
143
144
145
146
147
  va_end(args);

  return packet;
#undef CONFIRM_FORMAT
#undef CONFIRM_ARGS
}

148
static struct lsh_string *
149
format_open_failure(uint32_t channel, uint32_t reason,
150
		    const char *msg, const char *language)
Niels Möller's avatar
Niels Möller committed
151
152
153
154
155
{
  return ssh_format("%c%i%i%z%z", SSH_MSG_CHANNEL_OPEN_FAILURE,
		    channel, reason, msg, language);
}

156
static struct lsh_string *
157
format_channel_success(uint32_t channel)
158
159
160
161
{
  return ssh_format("%c%i", SSH_MSG_CHANNEL_SUCCESS, channel);
}

162
static struct lsh_string *
163
format_channel_failure(uint32_t channel)
Niels Möller's avatar
Niels Möller committed
164
165
166
167
{
  return ssh_format("%c%i", SSH_MSG_CHANNEL_FAILURE, channel);
}

168
static struct lsh_string *
169
prepare_window_adjust(struct ssh_channel *channel,
170
		      uint32_t add)
171
172
173
174
175
{
  channel->rec_window_size += add;
  
  return ssh_format("%c%i%i",
		    SSH_MSG_CHANNEL_WINDOW_ADJUST,
176
		    channel->remote_channel_number, add);
177
178
}

179
static void
180
channel_finished(struct ssh_channel *channel)
Niels Möller's avatar
Niels Möller committed
181
{
182
183
184
  if (!channel->super.alive)
    werror("channel_finished called on a dead channel.\n");
  else
Niels Möller's avatar
Niels Möller committed
185
    {
186
      struct ssh_connection *connection = channel->connection;
Niels Möller's avatar
Niels Möller committed
187

188
189
      trace("channel_finished: Deallocating channel %i\n", channel->local_channel_number);
      KILL_RESOURCE(&channel->super);
190

191
      /* Disassociate from the connection. */
192
      channel->connection = NULL;
Niels Möller's avatar
Niels Möller committed
193
      
194
      ssh_connection_dealloc_channel(connection, channel->local_channel_number);
195

196
197
      trace("connection->pending_close = %i, connection->channel_count = %i\n",
	    connection->pending_close, connection->channel_count);
198

199
200
      if (connection->pending_close && !connection->channel_count)
	KILL_RESOURCE(&connection->super);
Niels Möller's avatar
Niels Möller committed
201
202
203
204
    }
}


205
/* Channel objects */
Niels Möller's avatar
Niels Möller committed
206
207


208
void
209
register_channel(struct ssh_connection *connection,
210
		 uint32_t local_channel_number,
211
212
		 struct ssh_channel *channel,
		 int take_into_use)
213
{
214
215
  assert(connection->in_use[local_channel_number] == CHANNEL_RESERVED);
  assert(!connection->channels[local_channel_number]);
216

217
218
219
  verbose("Registering local channel %i.\n",
	  local_channel_number);

220
  connection->channels[local_channel_number] = channel;
221
  channel->local_channel_number = local_channel_number;
222

223
  if (take_into_use)
224
225
226
    ssh_connection_use_channel(connection, local_channel_number);

  remember_resource(connection->resources, &channel->super);  
Niels Möller's avatar
Niels Möller committed
227
228
}

229
struct ssh_channel *
230
lookup_channel(struct ssh_connection *connection, uint32_t i)
231
{
232
  if ( (i < connection->used_channels) && (connection->in_use[i] == CHANNEL_IN_USE))
233
    {
234
      struct ssh_channel *channel = connection->channels[i];
235
236
237
238
239
240
      assert(channel);
      assert(channel->local_channel_number == i);

      return channel;
    }
  return NULL;
241
242
243
}

struct ssh_channel *
244
lookup_channel_reserved(struct ssh_connection *connection, uint32_t i)
Niels Möller's avatar
Niels Möller committed
245
{
246
  if ( (i < connection->used_channels) && (connection->in_use[i] == CHANNEL_RESERVED))
247
    {
248
      struct ssh_channel *channel = connection->channels[i];
249
250
251
252
253
254
      assert(channel);
      assert(channel->local_channel_number == i);

      return channel;
    }
  return NULL;
Niels Möller's avatar
Niels Möller committed
255
256
}

257
258
/* FIXME: It seems suboptimal to send a window adjust message for
 * *every* write that we do. A better scheme might be as follows:
259
260
261
262
263
264
265
 *
 * Delay window adjust messages, keeping track of both the locally
 * maintained window size, which is updated after each write, and the
 * size that has been reported to the remote end. When the difference
 * between these two values gets large enough (say, larger than one
 * half or one third of the maximum window size), we send a
 * window_adjust message to sync them. */
266
267
void
channel_adjust_rec_window(struct ssh_channel *channel, uint32_t written)
268
{
269
270
  /* NOTE: The channel object (referenced as a flow-control callback)
   * may live longer than the actual channel. */
271
272
  if (written && ! (channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_RECEIVED_CLOSE
				      | CHANNEL_SENT_CLOSE)))
273
    SSH_CONNECTION_WRITE(channel->connection, prepare_window_adjust(channel, written));
274
275
}

276
277
void
channel_start_receive(struct ssh_channel *channel,
278
		      uint32_t initial_window_size)
279
{
280
  if (channel->rec_window_size < initial_window_size)
281
    SSH_CONNECTION_WRITE(channel->connection,
282
283
		 prepare_window_adjust
		 (channel, initial_window_size - channel->rec_window_size));
284
285
}

Niels Möller's avatar
Niels Möller committed
286
/* Channel related messages */
287
288
289

/* GABA:
   (class
290
     (name request_status)
291
292
293
294
295
296
297
     (vars
       ; -1 for still active requests,
       ; 0 for failure,
       ; 1 for success
       (status . int)))
*/

298
299
static struct request_status *
make_request_status(void)
300
{
301
  NEW(request_status, self);
302
303
304
305
306
307
308
  self->status = -1;

  return self;
}

/* GABA:
   (class
309
310
     (name global_request_continuation)
     (super command_continuation)
311
     (vars
312
       (connection object ssh_connection)
313
       (active object request_status)))
314
315
*/

316
static void 
317
send_global_request_responses(struct ssh_connection *connection)
318
{
319
  struct object_queue *q = &connection->active_global_requests;
320
321
322
323
324
325
326
327

  assert(!object_queue_is_empty(q));

  for (;;)
    {
      CAST(request_status, n, object_queue_peek_head(q));
      if (!n || (n->status < 0))
	break;
328
 
329
      object_queue_remove_head(q);
Niels Möller's avatar
Niels Möller committed
330

331
      SSH_CONNECTION_WRITE(connection, (n->status
332
333
				  ? format_global_success()
				  : format_global_failure()));
334
335
336
    }
}

337
338
339
static void
do_global_request_response(struct command_continuation *s,
			   struct lsh_object *x UNUSED)
340
{
341
  CAST(global_request_continuation, self, s);
342

343
344
  assert(self->active->status == -1);
  self->active->status = 1;
345

346
  send_global_request_responses(self->connection);
347
}
348

349
static struct command_continuation *
350
make_global_request_response(struct ssh_connection *connection,
351
352
353
354
355
			     struct request_status *active)
{
  NEW(global_request_continuation, self);

  self->super.c = do_global_request_response;
356
  self->connection = connection;
357
358
  self->active = active;
   
359
360
  return &self->super;
}
361
362
363
364
365
366
367


/* GABA:
   (class
     (name global_request_exception_handler)
     (super exception_handler)
     (vars
368
       (connection object ssh_connection)
369
370
371
       (active object request_status)))
*/

372
/* All exceptions are treated as a failure. */
373
static void 
374
do_exc_global_request_handler(struct exception_handler *c,
Niels Möller's avatar
Niels Möller committed
375
			      const struct exception *e)
376
377
{
  CAST(global_request_exception_handler, self, c);
378
379
380
381
382
383

  assert(self->active->status == -1);
  self->active->status = 0;

  werror("Denying global request: %z\n", e->msg);
  send_global_request_responses(self->connection);
384
385
386
}

static struct exception_handler *
387
make_global_request_exception_handler(struct ssh_connection *connection,
388
389
				      struct request_status *active,
				      const char *context)
Niels Möller's avatar
Niels Möller committed
390
{
391
392
  NEW(global_request_exception_handler, self);

393
  self->super.raise = do_exc_global_request_handler;
394
395
  self->super.context = context;
  self->active = active;
396
  self->connection = connection;
397
398
  return &self->super;
}
Niels Möller's avatar
Niels Möller committed
399

400
static void
401
handle_global_request(struct ssh_connection *connection,
402
		      struct simple_buffer *buffer)
403
{
Niels Möller's avatar
Niels Möller committed
404
405
406
  int name;
  int want_reply;
  
407
408
  if (parse_atom(buffer, &name)
      && parse_boolean(buffer, &want_reply))
Niels Möller's avatar
Niels Möller committed
409
    {
410
411
      struct global_request *req = NULL;

412
      if (name && connection->global_requests)
413
414
	{
	  CAST_SUBTYPE(global_request, r,
415
		       ALIST_GET(connection->global_requests,
416
417
418
419
				 name));
	  req = r;
	}
      if (!req)
420
	{
421
	  SSH_CONNECTION_WRITE(connection, format_global_failure());
Niels Möller's avatar
Niels Möller committed
422
	  return;
Niels Möller's avatar
Niels Möller committed
423
424
425
	}
      else
	{
426
	  struct command_continuation *c;
427
	  struct exception_handler *e;
Niels Möller's avatar
Niels Möller committed
428
429
	  if (want_reply)
	    {
430
	      struct request_status *a = make_request_status();
Niels Möller's avatar
Niels Möller committed
431
	      
432
	      object_queue_add_tail(&connection->active_global_requests,
Niels Möller's avatar
Niels Möller committed
433
434
				    &a->super);
	      
435
	      c = make_global_request_response(connection, a);
436
	      e = make_global_request_exception_handler(connection, a,
437
							HANDLER_CONTEXT);
438
439
440
441
	    }
	  else
	    {
	      /* We should ignore failures. */
442
443
	      c = &discard_continuation;
	      e = &ignore_exception_handler;
Niels Möller's avatar
Niels Möller committed
444
	    }
445
	  GLOBAL_REQUEST(req, connection, name, want_reply, buffer, c, e);
446
	}
Niels Möller's avatar
Niels Möller committed
447
    }
Niels Möller's avatar
Niels Möller committed
448
  else
449
    SSH_CONNECTION_ERROR(connection, "Invalid SSH_MSG_GLOBAL_REQUEST message.");
Niels Möller's avatar
Niels Möller committed
450
451
}

452
static void
453
handle_global_success(struct ssh_connection *connection,
454
		      struct simple_buffer *buffer)
455
{
456
  if (!parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
457
    {
458
      SSH_CONNECTION_ERROR(connection, "Invalid GLOBAL_REQUEST_SUCCESS message.");
459
      return;
Niels Möller's avatar
Niels Möller committed
460
    }
461

462
  if (object_queue_is_empty(&connection->pending_global_requests))
463
464
    {
      werror("do_global_request_success: Unexpected message, ignoring.\n");
Niels Möller's avatar
Niels Möller committed
465
      return;
466
467
    }
  {
468
    CAST_SUBTYPE(command_context, ctx,
469
470
		 object_queue_remove_head(&connection->pending_global_requests));
    COMMAND_RETURN(ctx->c, connection);
471
472
473
  }
}

474
struct exception global_request_exception =
475
STATIC_EXCEPTION(EXC_GLOBAL_REQUEST, 0, "Global request failed");
476

477
static void
478
handle_global_failure(struct ssh_connection *connection,
479
		      struct simple_buffer *buffer)
480
{
481
  if (!parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
482
    {
483
      SSH_CONNECTION_ERROR(connection, "Invalid GLOBAL_REQUEST_FAILURE message.");
484
      return;
Niels Möller's avatar
Niels Möller committed
485
    }
486

487
  if (object_queue_is_empty(&connection->pending_global_requests))
488
489
490
    {
      werror("do_global_request_failure: Unexpected message, ignoring.\n");
    }
Niels Möller's avatar
Niels Möller committed
491
492
493
  else
    {
      CAST_SUBTYPE(command_context, ctx,
494
		   object_queue_remove_head(&connection->pending_global_requests));
Niels Möller's avatar
Niels Möller committed
495
496
      EXCEPTION_RAISE(ctx->e, &global_request_exception);
    }
497
498
}

499
500
/* FIXME: Don't store the channel here, instead have it passed as the
 * argument of the continuation. This might also allow some
501
502
503
504
505
 * unification with the handling of global_requests.
 *
 * This won't quite work yet, because not all channel request
 * handlers, in particular gateway_channel_request and
 * x11_req_handler, return the channel in question. */
506
507
508
509
510
511
512
513
514
515
516

/* GABA:
   (class
     (name channel_request_continuation)
     (super command_continuation)
     (vars
       (channel object ssh_channel)
       (active object request_status)))
*/

static void
517
send_channel_request_responses(struct ssh_channel *channel)
518
{
519
520
521
  struct object_queue *q = &channel->active_requests;
  assert(!object_queue_is_empty(q));

522
523
524
525
526
527
528
529
  for (;;)
    {
      CAST(request_status, n, object_queue_peek_head(q));
      if (!n || (n->status < 0))
	break;

      object_queue_remove_head(q);

530
      SSH_CONNECTION_WRITE(channel->connection,
531
532
533
		   (n->status
		    ? format_channel_success(channel->remote_channel_number)
		    : format_channel_failure(channel->remote_channel_number)));
534
535
536
537
538
539
540
541
542
    }
}

static void
do_channel_request_response(struct command_continuation *s,
			    struct lsh_object *x UNUSED)
{
  CAST(channel_request_continuation, self, s);

543
  trace("do_channel_request_response\n");
544
545
546
  assert(self->active->status == -1);
  self->active->status = 1;

547
  send_channel_request_responses(self->channel);
548
549
550
}

static struct command_continuation *
551
make_channel_request_response(struct ssh_channel *channel,
552
553
554
555
			      struct request_status *active)
{
  NEW(channel_request_continuation, self);

556
557
  trace("make_channel_request_response\n");

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  self->super.c = do_channel_request_response;
  self->channel = channel;
  self->active = active;

  return &self->super;
}

/* GABA:
   (class
     (name channel_request_exception_handler)
     (super exception_handler)
     (vars
       (channel object ssh_channel)
       (active object request_status)))
*/

574
/* All exceptions are treated as a failure. */
575
static void 
576
577
do_exc_channel_request_handler(struct exception_handler *c,
			       const struct exception *e)
578
579
{
  CAST(channel_request_exception_handler, self, c);
580
581
582
583
584
585

  assert(self->active->status == -1);
  self->active->status = 0;

  werror("Denying channel request: %z\n", e->msg);
  send_channel_request_responses(self->channel);
586
587
588
}

static struct exception_handler *
589
make_channel_request_exception_handler(struct ssh_channel *channel,
590
591
592
593
594
				       struct request_status *active,
				       const char *context)
{
  NEW(channel_request_exception_handler, self);

595
  self->super.raise = do_exc_channel_request_handler;
596
  self->super.context = context;
597

598
599
600
601
602
603
  self->channel = channel;
  self->active = active;

  return &self->super;
}

604
static void
605
handle_channel_request(struct ssh_connection *connection,
606
		       struct simple_buffer *buffer)
607
{
608
  struct channel_request_info info;
609
  uint32_t channel_number;
610
  
611
612
613
614
  if (parse_uint32(buffer, &channel_number)
      && parse_string(buffer,
		      &info.type_length, &info.type_data)
      && parse_boolean(buffer, &info.want_reply))
615
    {
616
      struct ssh_channel *channel;
617
618
619
      trace("handle_channel_request: Request type `%ps' on channel %i\n",
	    info.type_length, info.type_data, channel_number);

620
      info.type = lookup_atom(info.type_length, info.type_data);
621

622
      channel = lookup_channel(connection, channel_number);
623
      
624
625
      if (channel)
	{
626
	  struct channel_request *req = NULL;
627

628
	  if (info.type && channel->request_types)
629
630
	    {
	      CAST_SUBTYPE(channel_request, r,
631
			   ALIST_GET(channel->request_types, info.type));
632
633
	      req = r;
	    }
634
635
636
	  if (!req)
	    req = channel->request_fallback;
	  
637
	  if (req)
638
	    {
639
640
	      struct command_continuation *c;
	      struct exception_handler *e;
641
	      if (info.want_reply)
642
643
		{
		  struct request_status *a = make_request_status();
644

645
646
		  object_queue_add_tail(&channel->active_requests,
					&a->super);
647

648
		  c = make_channel_request_response(channel, a);
649
650
		  e = make_channel_request_exception_handler(channel, a,
							     HANDLER_CONTEXT);
651
652
653
654
		}
	      else
		{
		  /* We should ignore failures. */
655
656
		  c = &discard_continuation;
		  e = &ignore_exception_handler;
657
658
		}
	      
659
	      CHANNEL_REQUEST(req, channel, &info, buffer, c, e);
660
661
662
	    }
	  else
	    {
663
	      if (info.want_reply)
664
		SSH_CONNECTION_WRITE(connection,
665
				    format_channel_failure(channel->remote_channel_number));
666
667
668
669
	    }
	}
      else
	{
670
671
	  werror("SSH_MSG_CHANNEL_REQUEST on nonexistant channel %i: %xs\n",
		 channel_number, buffer->capacity, buffer->pos);
672
673
674
	}
    }
  else
675
    SSH_CONNECTION_ERROR(connection, "Invalid SSH_MSG_CHANNEL_REQUEST message.");
676
677
678
}


Niels Möller's avatar
Niels Möller committed
679
/* GABA:
680
681
682
683
   (class
     (name channel_open_continuation)
     (super command_continuation)
     (vars
684
       (connection object ssh_connection)
685
686
687
688
       (local_channel_number . uint32_t)
       (remote_channel_number . uint32_t)
       (send_window_size . uint32_t)
       (send_max_packet . uint32_t)))
689
*/
690

Niels Möller's avatar
Niels Möller committed
691
692
693
static void
do_channel_open_continue(struct command_continuation *c,
			 struct lsh_object *value)
694
695
{
  CAST(channel_open_continuation, self, c);
Niels Möller's avatar
Niels Möller committed
696
697
698
699
700
701
702
  CAST_SUBTYPE(ssh_channel, channel, value);

  assert(channel);

  /* FIXME: This copying could just as well be done by the
   * CHANNEL_OPEN handler? Then we can remove the corresponding fields
   * from the closure as well. */
703
704
  channel->send_window_size = self->send_window_size;
  channel->send_max_packet = self->send_max_packet;
705
  channel->remote_channel_number = self->remote_channel_number;
Niels Möller's avatar
Niels Möller committed
706

707
  channel->connection = self->connection;
708
  
709
  register_channel(self->connection, self->local_channel_number,
710
		   channel,
711
		   1);
Niels Möller's avatar
Niels Möller committed
712
713
714
715

  /* FIXME: Doesn't support sending extra arguments with the
   * confirmation message. */

716
  SSH_CONNECTION_WRITE(self->connection,
717
	       format_open_confirmation(channel, ""));
718
}
Niels Möller's avatar
Niels Möller committed
719

Niels Möller's avatar
Niels Möller committed
720
static struct command_continuation *
721
make_channel_open_continuation(struct ssh_connection *connection,
722
723
724
725
			       uint32_t local_channel_number,
			       uint32_t remote_channel_number,
			       uint32_t send_window_size,
			       uint32_t send_max_packet)
Niels Möller's avatar
Niels Möller committed
726
727
{
  NEW(channel_open_continuation, self);
Niels Möller's avatar
Niels Möller committed
728

Niels Möller's avatar
Niels Möller committed
729
  self->super.c = do_channel_open_continue;
730
  self->connection = connection;
Niels Möller's avatar
Niels Möller committed
731
732
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;
733
734
  self->send_window_size = send_window_size;
  self->send_max_packet = send_max_packet;
Niels Möller's avatar
Niels Möller committed
735

Niels Möller's avatar
Niels Möller committed
736
  return &self->super;
Niels Möller's avatar
Niels Möller committed
737
738
739
740
741
742
743
}
			       
/* GABA:
   (class
     (name exc_channel_open_handler)
     (super exception_handler)
     (vars
744
       (connection object ssh_connection)
745
746
       (local_channel_number . uint32_t)
       (remote_channel_number . uint32_t)))
Niels Möller's avatar
Niels Möller committed
747
748
*/

749
750
751
static void
do_exc_channel_open_handler(struct exception_handler *s,
			    const struct exception *e)
Niels Möller's avatar
Niels Möller committed
752
753
{
  CAST(exc_channel_open_handler, self, s);
754
755
756
  struct ssh_connection *connection = self->connection;
  uint32_t error_code = (e->type == EXC_CHANNEL_OPEN)
    ? e->subtype : SSH_OPEN_RESOURCE_SHORTAGE;
Niels Möller's avatar
Niels Möller committed
757

758
759
760
761
762
763
764
765
766
767
  assert(connection->in_use[self->local_channel_number]);
  assert(!connection->channels[self->local_channel_number]);

  ssh_connection_dealloc_channel(connection, self->local_channel_number);

  werror("Denying channel open: %z\n", e->msg);
  
  SSH_CONNECTION_WRITE(connection,
		       format_open_failure(self->remote_channel_number,
					   error_code, e->msg, ""));
Niels Möller's avatar
Niels Möller committed
768
769
770
}

static struct exception_handler *
771
make_exc_channel_open_handler(struct ssh_connection *connection,
772
773
			      uint32_t local_channel_number,
			      uint32_t remote_channel_number,
774
			      const char *context)
Niels Möller's avatar
Niels Möller committed
775
{
Niels Möller's avatar
Niels Möller committed
776
777
  NEW(exc_channel_open_handler, self);
  self->super.raise = do_exc_channel_open_handler;
778
779
  self->super.context = context;
  
780
  self->connection = connection;
Niels Möller's avatar
Niels Möller committed
781
782
783
784
785
786
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;

  return &self->super;
}

787
static void
788
handle_channel_open(struct ssh_connection *connection,
789
		    struct simple_buffer *buffer)
Niels Möller's avatar
Niels Möller committed
790
{
791
  struct channel_open_info info;
792
793

  trace("handle_channel_open\n");
794
  
795
796
797
798
799
  if (parse_string(buffer, &info.type_length, &info.type_data)
      && parse_uint32(buffer, &info.remote_channel_number)
      && parse_uint32(buffer, &info.send_window_size)
      && parse_uint32(buffer, &info.send_max_packet))
  {
800
      struct channel_open *open = NULL;
Niels Möller's avatar
Niels Möller committed
801

802
      info.type = lookup_atom(info.type_length, info.type_data);
Niels Möller's avatar
Niels Möller committed
803

804
805
806
807
808
809
810
811
      /* We don't support larger packets than the default,
       * SSH_MAX_PACKET. */
      if (info.send_max_packet > SSH_MAX_PACKET)
	{
	  werror("handle_channel_open: The remote end asked for really large packets.\n");
	  info.send_max_packet = SSH_MAX_PACKET;
	}
      
812
      if (connection->pending_close)
Niels Möller's avatar
Niels Möller committed
813
814
815
	{
	  /* We are waiting for channels to close. Don't open any new ones. */

816
	  SSH_CONNECTION_WRITE(connection,
817
818
819
820
		       format_open_failure(
			 info.remote_channel_number,
			 SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
			 "Waiting for channels to close.", ""));
Niels Möller's avatar
Niels Möller committed
821
822
823
	}
      else
	{
824
	  if (info.type)
825
	    {
826
	      CAST_SUBTYPE(channel_open, o,
827
			   ALIST_GET(connection->channel_types,
828
829
830
831
832
				     info.type));
	      open = o;
	    }

	  if (!open)
833
	    open = connection->open_fallback;
834
835
836
	  
	  if (!open)
	    {
837
838
	      werror("handle_channel_open: Unknown channel type `%ps'\n",
		     info.type_length, info.type_data);
839
	      SSH_CONNECTION_WRITE(connection,
840
841
842
843
			   format_open_failure(
			     info.remote_channel_number,
			     SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
			     "Unknown channel type", ""));
844
845
846
	    }
	  else
	    {
847
	      int local_number = ssh_connection_alloc_channel(connection);
848
849

	      if (local_number < 0)
850
		{
851
		  SSH_CONNECTION_WRITE(connection,
852
853
854
855
			       format_open_failure(
				 info.remote_channel_number,
				 SSH_OPEN_RESOURCE_SHORTAGE,
				 "Channel limit exceeded.", ""));
856
857
858
		  return;
		}
	      
859
	      CHANNEL_OPEN(open, connection,
860
			   &info,
861
			   buffer,
862
			   make_channel_open_continuation(connection,
863
864
865
866
							  local_number,
							  info.remote_channel_number,
							  info.send_window_size,
							  info.send_max_packet),
867
			   make_exc_channel_open_handler(connection,
868
869
870
871
							 local_number,
							 info.remote_channel_number,
							 HANDLER_CONTEXT));

872
	    }
Niels Möller's avatar
Niels Möller committed
873
	}
Niels Möller's avatar
Niels Möller committed
874
    }
Niels Möller's avatar
Niels Möller committed
875
  else
876
    SSH_CONNECTION_ERROR(connection, "Invalid SSH_MSG_CHANNEL_OPEN message.");
Niels Möller's avatar
Niels Möller committed
877
878
}     

879
static void
880
handle_adjust_window(struct ssh_connection *connection,
881
		     struct simple_buffer *buffer)
Niels Möller's avatar
Niels Möller committed
882
{
883
884
  uint32_t channel_number;
  uint32_t size;
Niels Möller's avatar
Niels Möller committed
885

886
887
888
  if (parse_uint32(buffer, &channel_number)
      && parse_uint32(buffer, &size)
      && parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
889
    {
890
      struct ssh_channel *channel = lookup_channel(connection, channel_number);
Niels Möller's avatar
Niels Möller committed
891

Niels Möller's avatar
Niels Möller committed
892
      if (channel
893
	  && !(channel->flags & CHANNEL_RECEIVED_CLOSE))
Niels Möller's avatar
Niels Möller committed
894
	{
895
	  if (! (channel->flags & (CHANNEL_SENT_CLOSE | CHANNEL_SENT_EOF)))
Niels Möller's avatar
Niels Möller committed
896
897
	    {
	      channel->send_window_size += size;
898

899
	      if (channel->send_window_size && channel->send_adjust)
900
		CHANNEL_SEND_ADJUST(channel, size);
Niels Möller's avatar
Niels Möller committed
901
	    }
Niels Möller's avatar
Niels Möller committed
902
	}
Niels Möller's avatar
Niels Möller committed
903
904
905
906
      else
	{
	  werror("SSH_MSG_CHANNEL_WINDOW_ADJUST on nonexistant or closed "
		 "channel %i\n", channel_number);
907
	  SSH_CONNECTION_ERROR(connection, "Unexpected CHANNEL_WINDOW_ADJUST");
Niels Möller's avatar
Niels Möller committed
908
909
910
	}
    }
  else
911
    SSH_CONNECTION_ERROR(connection, "Invalid CHANNEL_WINDOW_ADJUST message.");
Niels Möller's avatar
Niels Möller committed
912
913
}

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
/* Common processing for ordinary and "extended" data. */
static int
receive_data_common(struct ssh_channel *channel,
		    int type, uint32_t length, const uint8_t *data)
{
  if (channel->receive
      && !(channel->flags & (CHANNEL_RECEIVED_EOF
			     | CHANNEL_RECEIVED_CLOSE)))
    {
      if (channel->flags & CHANNEL_SENT_CLOSE)
	{
	  werror("Ignoring data on channel which is closing\n");
	  return 1;
	}
      else
	{
	  if (length > channel->rec_max_packet)
	    {
	      werror("Channel data larger than rec_max_packet. Extra data ignored.\n");
	      length = channel->rec_max_packet;
	    }

	  if (length > channel->rec_window_size)
	    {
	      /* Truncate data to fit window */
	      werror("Channel data overflow. Extra data ignored.\n");
	      debug("   (type = %i, data->length=%i, rec_window_size=%i).\n",
		    type, length, channel->rec_window_size);

	      length = channel->rec_window_size;
	    }

	  if (!length)
	    {
	      /* Ignore data packet */
	      return 1;
	    }
	  channel->rec_window_size -= length;

	  CHANNEL_RECEIVE(channel, type, length, data);
	}
      return 1;
    }
  else
    return 0;
}

961
static void
962
handle_channel_data(struct ssh_connection *connection,
963
		    struct simple_buffer *buffer)
Niels Möller's avatar
Niels Möller committed
964
{
965
  uint32_t channel_number;
966
967
  uint32_t length;
  const uint8_t *data;
Niels Möller's avatar
Niels Möller committed
968
  
969
  if (parse_uint32(buffer, &channel_number)
970
      && parse_string(buffer, &length, &data)
971
      && parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
972
    {
973
974
      struct ssh_channel *channel
	= lookup_channel(connection, channel_number);
Niels Möller's avatar
Niels Möller committed