channel.c 42.2 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
271
272
  /* NOTE: The channel object (referenced as a flow-control callback)
   * may live longer than the actual channel. */
  if (! (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
372
373
       (active object request_status)))
*/

/* NOTE: We handle *only* EXC_GLOBAL_REQUEST */
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
378
379
380
381
382
{
  CAST(global_request_exception_handler, self, c);
  if (e->type == EXC_GLOBAL_REQUEST)
    {
      assert(self->active->status == -1);
      self->active->status = 0;
  
383
      send_global_request_responses(self->connection);
384
385
386
387
388
389
    }
  else
    EXCEPTION_RAISE(c->parent, e);
}

static struct exception_handler *
390
make_global_request_exception_handler(struct ssh_connection *connection,
391
392
393
				      struct request_status *active,
				      struct exception_handler *h,
				      const char *context)
Niels Möller's avatar
Niels Möller committed
394
{
395
396
  NEW(global_request_exception_handler, self);

397
  self->super.raise = do_exc_global_request_handler;
398
399
400
  self->super.context = context;
  self->super.parent = h;
  self->active = active;
401
  self->connection = connection;
402
403
  return &self->super;
}
Niels Möller's avatar
Niels Möller committed
404

405
static void
406
handle_global_request(struct ssh_connection *connection,
407
		      struct simple_buffer *buffer)
408
{
Niels Möller's avatar
Niels Möller committed
409
410
411
  int name;
  int want_reply;
  
412
413
  if (parse_atom(buffer, &name)
      && parse_boolean(buffer, &want_reply))
Niels Möller's avatar
Niels Möller committed
414
    {
415
      struct global_request *req = NULL;
416
      struct command_continuation *c = &discard_continuation;
417

418
      if (name && connection->global_requests)
419
420
	{
	  CAST_SUBTYPE(global_request, r,
421
		       ALIST_GET(connection->global_requests,
422
423
424
425
				 name));
	  req = r;
	}
      if (!req)
426
	{
427
	  SSH_CONNECTION_WRITE(connection, format_global_failure());
Niels Möller's avatar
Niels Möller committed
428
	  return;
Niels Möller's avatar
Niels Möller committed
429
430
431
	}
      else
	{
432
	  struct exception_handler *e;
Niels Möller's avatar
Niels Möller committed
433
434
	  if (want_reply)
	    {
435
	      struct request_status *a = make_request_status();
Niels Möller's avatar
Niels Möller committed
436
	      
437
	      object_queue_add_tail(&connection->active_global_requests,
Niels Möller's avatar
Niels Möller committed
438
439
				    &a->super);
	      
440
441
	      c = make_global_request_response(connection, a);
	      e = make_global_request_exception_handler(connection, a, &default_exception_handler,
442
							HANDLER_CONTEXT);
443
444
445
446
	    }
	  else
	    {
	      /* We should ignore failures. */
447
	      static const struct report_exception_info global_req_ignore =
448
449
450
451
		STATIC_REPORT_EXCEPTION_INFO(EXC_ALL, EXC_GLOBAL_REQUEST,
					     "Ignored:");
	      
	      e = make_report_exception_handler(&global_req_ignore,
452
						&default_exception_handler, HANDLER_CONTEXT);
Niels Möller's avatar
Niels Möller committed
453
	    }
454
	  GLOBAL_REQUEST(req, connection, name, want_reply, buffer, c, e);
455
	}
Niels Möller's avatar
Niels Möller committed
456
    }
Niels Möller's avatar
Niels Möller committed
457
  else
458
    SSH_CONNECTION_ERROR(connection, "Invalid SSH_MSG_GLOBAL_REQUEST message.");
Niels Möller's avatar
Niels Möller committed
459
460
}

461
static void
462
handle_global_success(struct ssh_connection *connection,
463
		      struct simple_buffer *buffer)
464
{
465
  if (!parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
466
    {
467
      SSH_CONNECTION_ERROR(connection, "Invalid GLOBAL_REQUEST_SUCCESS message.");
468
      return;
Niels Möller's avatar
Niels Möller committed
469
    }
470

471
  if (object_queue_is_empty(&connection->pending_global_requests))
472
473
    {
      werror("do_global_request_success: Unexpected message, ignoring.\n");
Niels Möller's avatar
Niels Möller committed
474
      return;
475
476
    }
  {
477
    CAST_SUBTYPE(command_context, ctx,
478
479
		 object_queue_remove_head(&connection->pending_global_requests));
    COMMAND_RETURN(ctx->c, connection);
480
481
482
  }
}

483
484
485
struct exception global_request_exception =
STATIC_EXCEPTION(EXC_GLOBAL_REQUEST, "Global request failed");

486
static void
487
handle_global_failure(struct ssh_connection *connection,
488
		      struct simple_buffer *buffer)
489
{
490
  if (!parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
491
    {
492
      SSH_CONNECTION_ERROR(connection, "Invalid GLOBAL_REQUEST_FAILURE message.");
493
      return;
Niels Möller's avatar
Niels Möller committed
494
    }
495

496
  if (object_queue_is_empty(&connection->pending_global_requests))
497
498
499
    {
      werror("do_global_request_failure: Unexpected message, ignoring.\n");
    }
Niels Möller's avatar
Niels Möller committed
500
501
502
  else
    {
      CAST_SUBTYPE(command_context, ctx,
503
		   object_queue_remove_head(&connection->pending_global_requests));
Niels Möller's avatar
Niels Möller committed
504
505
      EXCEPTION_RAISE(ctx->e, &global_request_exception);
    }
506
507
}

508
509
/* FIXME: Don't store the channel here, instead have it passed as the
 * argument of the continuation. This might also allow some
510
511
512
513
514
 * 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. */
515
516
517
518
519
520
521
522
523
524
525

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

static void
526
send_channel_request_responses(struct ssh_channel *channel)
527
{
528
529
530
  struct object_queue *q = &channel->active_requests;
  assert(!object_queue_is_empty(q));

531
532
533
534
535
536
537
538
  for (;;)
    {
      CAST(request_status, n, object_queue_peek_head(q));
      if (!n || (n->status < 0))
	break;

      object_queue_remove_head(q);

539
      SSH_CONNECTION_WRITE(channel->connection,
540
541
542
		   (n->status
		    ? format_channel_success(channel->remote_channel_number)
		    : format_channel_failure(channel->remote_channel_number)));
543
544
545
546
547
548
549
550
551
    }
}

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

552
  trace("do_channel_request_response\n");
553
554
555
  assert(self->active->status == -1);
  self->active->status = 1;

556
  send_channel_request_responses(self->channel);
557
558
559
}

static struct command_continuation *
560
make_channel_request_response(struct ssh_channel *channel,
561
562
563
564
			      struct request_status *active)
{
  NEW(channel_request_continuation, self);

565
566
  trace("make_channel_request_response\n");

567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
  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)))
*/

/* NOTE: We handle *only* EXC_CHANNEL_REQUEST */
static void 
585
586
do_exc_channel_request_handler(struct exception_handler *c,
			       const struct exception *e)
587
588
589
590
591
592
593
{
  CAST(channel_request_exception_handler, self, c);
  if (e->type == EXC_CHANNEL_REQUEST)
    {
      assert(self->active->status == -1);
      self->active->status = 0;
      
594
      send_channel_request_responses(self->channel);
595
596
597
598
599
600
    }
  else
    EXCEPTION_RAISE(c->parent, e);
}

static struct exception_handler *
601
make_channel_request_exception_handler(struct ssh_channel *channel,
602
603
604
605
606
607
				       struct request_status *active,
				       struct exception_handler *h,
				       const char *context)
{
  NEW(channel_request_exception_handler, self);

608
  self->super.raise = do_exc_channel_request_handler;
609
610
  self->super.parent = h;
  self->super.context = context;
611

612
613
614
615
616
617
  self->channel = channel;
  self->active = active;

  return &self->super;
}

618
static void
619
handle_channel_request(struct ssh_connection *connection,
620
		       struct simple_buffer *buffer)
621
{
622
  struct channel_request_info info;
623
  uint32_t channel_number;
624
  
625
626
627
628
  if (parse_uint32(buffer, &channel_number)
      && parse_string(buffer,
		      &info.type_length, &info.type_data)
      && parse_boolean(buffer, &info.want_reply))
629
    {
630
      struct ssh_channel *channel;
631
632
633
      trace("handle_channel_request: Request type `%ps' on channel %i\n",
	    info.type_length, info.type_data, channel_number);

634
      info.type = lookup_atom(info.type_length, info.type_data);
635

636
      channel = lookup_channel(connection, channel_number);
637
      
638
639
      if (channel)
	{
640
	  struct channel_request *req = NULL;
641
642
643
	  struct command_continuation *c = &discard_continuation;
	  struct exception_handler *e = channel->e;

644
	  if (info.type && channel->request_types)
645
646
	    {
	      CAST_SUBTYPE(channel_request, r,
647
			   ALIST_GET(channel->request_types, info.type));
648
649
	      req = r;
	    }
650
651
652
	  if (!req)
	    req = channel->request_fallback;
	  
653
	  if (req)
654
	    {
655
	      if (info.want_reply)
656
657
		{
		  struct request_status *a = make_request_status();
658

659
660
		  object_queue_add_tail(&channel->active_requests,
					&a->super);
661

662
663
		  c = make_channel_request_response(channel, a);
		  e = make_channel_request_exception_handler(channel, a, e, HANDLER_CONTEXT);
664
665
666
667
		}
	      else
		{
		  /* We should ignore failures. */
668
669
		  static const struct report_exception_info
		    channel_req_ignore =
670
671
672
673
674
675
676
		    STATIC_REPORT_EXCEPTION_INFO(EXC_ALL, EXC_CHANNEL_REQUEST,
						 "Ignored:");
		  
		  e = make_report_exception_handler(&channel_req_ignore,
						    e, HANDLER_CONTEXT);
		}
	      
677
	      CHANNEL_REQUEST(req, channel, &info, buffer, c, e);
678
679
680
	    }
	  else
	    {
681
	      if (info.want_reply)
682
		SSH_CONNECTION_WRITE(connection,
683
				    format_channel_failure(channel->remote_channel_number));
684
685
686
687
	    }
	}
      else
	{
688
689
	  werror("SSH_MSG_CHANNEL_REQUEST on nonexistant channel %i: %xs\n",
		 channel_number, buffer->capacity, buffer->pos);
690
691
692
	}
    }
  else
693
    SSH_CONNECTION_ERROR(connection, "Invalid SSH_MSG_CHANNEL_REQUEST message.");
694
695
696
}


Niels Möller's avatar
Niels Möller committed
697
/* GABA:
698
699
700
701
   (class
     (name channel_open_continuation)
     (super command_continuation)
     (vars
702
       (connection object ssh_connection)
703
704
705
706
       (local_channel_number . uint32_t)
       (remote_channel_number . uint32_t)
       (send_window_size . uint32_t)
       (send_max_packet . uint32_t)))
707
*/
708

Niels Möller's avatar
Niels Möller committed
709
710
711
static void
do_channel_open_continue(struct command_continuation *c,
			 struct lsh_object *value)
712
713
{
  CAST(channel_open_continuation, self, c);
Niels Möller's avatar
Niels Möller committed
714
715
716
717
718
719
720
  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. */
721
722
  channel->send_window_size = self->send_window_size;
  channel->send_max_packet = self->send_max_packet;
723
  channel->remote_channel_number = self->remote_channel_number;
Niels Möller's avatar
Niels Möller committed
724

725
  channel->connection = self->connection;
726
  
727
  register_channel(self->connection, self->local_channel_number,
728
		   channel,
729
		   1);
Niels Möller's avatar
Niels Möller committed
730
731
732
733

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

734
  SSH_CONNECTION_WRITE(self->connection,
735
	       format_open_confirmation(channel, ""));
736
}
Niels Möller's avatar
Niels Möller committed
737

Niels Möller's avatar
Niels Möller committed
738
static struct command_continuation *
739
make_channel_open_continuation(struct ssh_connection *connection,
740
741
742
743
			       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
744
745
{
  NEW(channel_open_continuation, self);
Niels Möller's avatar
Niels Möller committed
746

Niels Möller's avatar
Niels Möller committed
747
  self->super.c = do_channel_open_continue;
748
  self->connection = connection;
Niels Möller's avatar
Niels Möller committed
749
750
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;
751
752
  self->send_window_size = send_window_size;
  self->send_max_packet = send_max_packet;
Niels Möller's avatar
Niels Möller committed
753

Niels Möller's avatar
Niels Möller committed
754
  return &self->super;
Niels Möller's avatar
Niels Möller committed
755
756
757
758
759
760
761
}
			       
/* GABA:
   (class
     (name exc_channel_open_handler)
     (super exception_handler)
     (vars
762
       (connection object ssh_connection)
763
764
       (local_channel_number . uint32_t)
       (remote_channel_number . uint32_t)))
Niels Möller's avatar
Niels Möller committed
765
766
*/

767
768
769
static void
do_exc_channel_open_handler(struct exception_handler *s,
			    const struct exception *e)
Niels Möller's avatar
Niels Möller committed
770
771
772
773
774
775
776
777
{
  CAST(exc_channel_open_handler, self, s);

  switch (e->type)
    {
    case EXC_CHANNEL_OPEN:
      {
	CAST_SUBTYPE(channel_open_exception, exc, e);
778
	struct ssh_connection *connection = self->connection;
Niels Möller's avatar
Niels Möller committed
779
	
780
781
	assert(connection->in_use[self->local_channel_number]);
	assert(!connection->channels[self->local_channel_number]);
Niels Möller's avatar
Niels Möller committed
782

783
	ssh_connection_dealloc_channel(connection, self->local_channel_number);
Niels Möller's avatar
Niels Möller committed
784
	
785
        SSH_CONNECTION_WRITE(connection,
786
787
		     format_open_failure(self->remote_channel_number,
					 exc->error_code, e->msg, ""));
Niels Möller's avatar
Niels Möller committed
788
789
790
791
792
793
794
795
	break;
      }
    default:
      EXCEPTION_RAISE(self->super.parent, e);
    }      
}

static struct exception_handler *
796
make_exc_channel_open_handler(struct ssh_connection *connection,
797
798
			      uint32_t local_channel_number,
			      uint32_t remote_channel_number,
799
800
			      struct exception_handler *parent,
			      const char *context)
Niels Möller's avatar
Niels Möller committed
801
{
Niels Möller's avatar
Niels Möller committed
802
  NEW(exc_channel_open_handler, self);
Niels Möller's avatar
Niels Möller committed
803
  self->super.parent = parent;
Niels Möller's avatar
Niels Möller committed
804
  self->super.raise = do_exc_channel_open_handler;
805
806
  self->super.context = context;
  
807
  self->connection = connection;
Niels Möller's avatar
Niels Möller committed
808
809
810
811
812
813
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;

  return &self->super;
}

814
static void
815
handle_channel_open(struct ssh_connection *connection,
816
		    struct simple_buffer *buffer)
Niels Möller's avatar
Niels Möller committed
817
{
818
  struct channel_open_info info;
819
820

  trace("handle_channel_open\n");
821
  
822
823
824
825
826
  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))
  {
827
      struct channel_open *open = NULL;
Niels Möller's avatar
Niels Möller committed
828

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

831
832
833
834
835
836
837
838
      /* 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;
	}
      
839
      if (connection->pending_close)
Niels Möller's avatar
Niels Möller committed
840
841
842
	{
	  /* We are waiting for channels to close. Don't open any new ones. */

843
	  SSH_CONNECTION_WRITE(connection,
844
845
846
847
		       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
848
849
850
	}
      else
	{
851
	  if (info.type)
852
	    {
853
	      CAST_SUBTYPE(channel_open, o,
854
			   ALIST_GET(connection->channel_types,
855
856
857
858
859
				     info.type));
	      open = o;
	    }

	  if (!open)
860
	    open = connection->open_fallback;
861
862
863
	  
	  if (!open)
	    {
864
865
	      werror("handle_channel_open: Unknown channel type `%ps'\n",
		     info.type_length, info.type_data);
866
	      SSH_CONNECTION_WRITE(connection,
867
868
869
870
			   format_open_failure(
			     info.remote_channel_number,
			     SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
			     "Unknown channel type", ""));
871
872
873
	    }
	  else
	    {
874
	      int local_number = ssh_connection_alloc_channel(connection);
875
876

	      if (local_number < 0)
877
		{
878
		  SSH_CONNECTION_WRITE(connection,
879
880
881
882
			       format_open_failure(
				 info.remote_channel_number,
				 SSH_OPEN_RESOURCE_SHORTAGE,
				 "Channel limit exceeded.", ""));
883
884
885
		  return;
		}
	      
886
	      CHANNEL_OPEN(open, connection,
887
			   &info,
888
			   buffer,
889
			   make_channel_open_continuation(connection,
890
891
892
893
							  local_number,
							  info.remote_channel_number,
							  info.send_window_size,
							  info.send_max_packet),
894
			   make_exc_channel_open_handler(connection,
895
896
							 local_number,
							 info.remote_channel_number,
897
							 &default_exception_handler,
898
899
							 HANDLER_CONTEXT));

900
	    }
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
  else
904
    SSH_CONNECTION_ERROR(connection, "Invalid SSH_MSG_CHANNEL_OPEN message.");
Niels Möller's avatar
Niels Möller committed
905
906
}     

907
static void
908
handle_adjust_window(struct ssh_connection *connection,
909
		     struct simple_buffer *buffer)
Niels Möller's avatar
Niels Möller committed
910
{
911
912
  uint32_t channel_number;
  uint32_t size;
Niels Möller's avatar
Niels Möller committed
913

914
915
916
  if (parse_uint32(buffer, &channel_number)
      && parse_uint32(buffer, &size)
      && parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
917
    {
918
      struct ssh_channel *channel = lookup_channel(connection, channel_number);
Niels Möller's avatar
Niels Möller committed
919

Niels Möller's avatar
Niels Möller committed
920
      if (channel
921
	  && !(channel->flags & CHANNEL_RECEIVED_CLOSE))
Niels Möller's avatar
Niels Möller committed
922
	{
923
	  if (! (channel->flags & (CHANNEL_SENT_CLOSE | CHANNEL_SENT_EOF)))
Niels Möller's avatar
Niels Möller committed
924
925
	    {
	      channel->send_window_size += size;
926

927
	      if (channel->send_window_size && channel->send_adjust)
928
		CHANNEL_SEND_ADJUST(channel, size);
Niels Möller's avatar
Niels Möller committed
929
	    }
Niels Möller's avatar
Niels Möller committed
930
	}
Niels Möller's avatar
Niels Möller committed
931
932
933
934
      else
	{
	  werror("SSH_MSG_CHANNEL_WINDOW_ADJUST on nonexistant or closed "
		 "channel %i\n", channel_number);
935
	  SSH_CONNECTION_ERROR(connection, "Unexpected CHANNEL_WINDOW_ADJUST");
Niels Möller's avatar
Niels Möller committed
936
937
938
	}
    }
  else
939
    SSH_CONNECTION_ERROR(connection, "Invalid CHANNEL_WINDOW_ADJUST message.");
Niels Möller's avatar
Niels Möller committed
940
941
}

942
943
/* FIXME: Pass length, pointer to receive, to avoid unnecessary
   allocation and copying. */
944
static void
945
handle_channel_data(struct ssh_connection *connection,
946
		    struct simple_buffer *buffer)
Niels Möller's avatar
Niels Möller committed
947
{
948
  uint32_t channel_number;
Niels Möller's avatar
Niels Möller committed
949
950
  struct lsh_string *data;
  
951
952
953
  if (parse_uint32(buffer, &channel_number)
      && ( (data = parse_string_copy(buffer)) )
      && parse_eod(buffer))
Niels Möller's avatar
Niels Möller committed
954
    {
955
      struct ssh_channel *channel = lookup_channel(connection, channel_number);
Niels Möller's avatar
Niels Möller committed
956

957
      if (channel && channel->receive
958
959
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF
				 | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
960
	{