channel.c 45.3 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
21
/* channel.c
 *
 * $Id$
 */

/* 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
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
23
24
25
26
27
 */

#include "channel.h"

#include "format.h"
28
#include "io.h"
Niels Möller's avatar
Niels Möller committed
29
#include "read_data.h"
Niels Möller's avatar
Niels Möller committed
30
31
32
33
34
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

#include <assert.h>
35
#include <string.h>
Niels Möller's avatar
Niels Möller committed
36

37
#define GABA_DEFINE
38
#include "channel.h.x"
39
#undef GABA_DEFINE
40

41
42
#include "channel.c.x"

Niels Möller's avatar
Niels Möller committed
43
44
45
struct exception *make_channel_open_exception(UINT32 error_code, char *msg)
{
  NEW(channel_open_exception, self);
Niels Möller's avatar
Niels Möller committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59

#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
60
  self->super.type = EXC_CHANNEL_OPEN;
Niels Möller's avatar
Niels Möller committed
61
  self->super.msg = msg ? msg : msgs[error_code];
Niels Möller's avatar
Niels Möller committed
62
63
64
65
66
  self->error_code = error_code;

  return &self->super;
}

67
/* GABA:
68
69
   (class
     (name connection_service)
70
     (super command)
71
72
73
74
     (vars
       ; Supported global requests 
       (global_requests object alist)

75
       (channel_types object alist) ))
76
77
78
79

       ; Initialize connection (for instance, request channels to be 
       ; opened or services to be forwarded).

80
       ; (start object connection_startup)))
81
82
*/

83
/* ;; GABA:
84
85
   (class
     (name global_request_handler)
86
     (super packet_handler)
87
88
89
90
     (vars
       (global_requests object alist)))
*/

91
/* ;; GABA:
92
93
   (class
     (name channel_open_handler)
94
     (super packet_handler)
95
96
97
98
     (vars
       (channel_types object alist)))
*/

Niels Möller's avatar
Niels Möller committed
99
/* ;; GABA:
100
101
102
103
104
105
106
107
108
   (class
     (name channel_open_response)
     (super channel_open_callback)
     (vars
       (remote_channel_number simple UINT32)
       (window_size simple UINT32)
       (max_packet simple UINT32)))
*/

Niels Möller's avatar
Niels Möller committed
109
110
111
112
113
struct lsh_string *format_global_failure(void)
{
  return ssh_format("%c", SSH_MSG_REQUEST_FAILURE);
}

114
115
116
117
118
struct lsh_string *format_global_success(void)
{
  return ssh_format("%c", SSH_MSG_REQUEST_SUCCESS);
}

119
120
struct lsh_string *format_open_confirmation(struct ssh_channel *channel,
					    UINT32 channel_number,
121
					    const char *format, ...)
122
123
124
125
126
127
{
  va_list args;
  UINT32 l1, l2;
  struct lsh_string *packet;

#define CONFIRM_FORMAT "%c%i%i%i%i"
128
129
#define CONFIRM_ARGS \
  SSH_MSG_CHANNEL_OPEN_CONFIRMATION, channel->channel_number, \
130
131
132
133
134
135
136
137
138
139
140
141
142
  channel_number, channel->rec_window_size, channel->rec_max_packet
    
  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);

  ssh_format_write(CONFIRM_FORMAT, l1, packet->data, CONFIRM_ARGS);

  va_start(args, format);
143
  ssh_vformat_write(format, l2, packet->data+l1, args);
144
145
146
147
148
149
150
  va_end(args);

  return packet;
#undef CONFIRM_FORMAT
#undef CONFIRM_ARGS
}

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

158
159
160
161
162
struct lsh_string *format_channel_success(UINT32 channel)
{
  return ssh_format("%c%i", SSH_MSG_CHANNEL_SUCCESS, channel);
}

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

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

Niels Möller's avatar
Niels Möller committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* ;; GABA:
   (class
     (name channel_exception)
     (super exception)
     (vars
       (channel object ssh_channel)
       (pending_close . int)))
*/

/* GABA:
   (class
     (name exc_finish_channel_handler)
     (super exception_handler)
     (vars
       (table object channel_table)
       ; Local channel number 
       (channel_number . UINT32)))
*/

static void do_exc_finish_channel_handler(struct exception_handler *s,
Niels Möller's avatar
Niels Möller committed
198
					  const struct exception *e)
Niels Möller's avatar
Niels Möller committed
199
200
201
202
203
{
  CAST(exc_finish_channel_handler, self, s);

  switch (e->type)
    {
Niels Möller's avatar
Niels Möller committed
204
205
206
207
208
209
210
    case EXC_FINISH_PENDING:
      self->table->pending_close = 1;

      if (!self->table->next_channel)
	EXCEPTION_RAISE(self->super.parent, &finish_read_exception);
      break;
      
Niels Möller's avatar
Niels Möller committed
211
212
213
214
215
216
217
218
219
    case EXC_FINISH_CHANNEL:
      /* NOTE: This type of exception must be handled only once.
       * Perhaps we must add a liveness flag in the ssh_channel struct
       * to avoid deallocating dead channels? */
      {
	struct ssh_channel *channel
	  = self->table->channels[self->channel_number];

	assert(channel);
220
221
	assert(channel->resources->super.alive);

Niels Möller's avatar
Niels Möller committed
222
223
	if (channel->close)
	  CHANNEL_CLOSE(channel);
224
225

	KILL_RESOURCE_LIST(channel->resources);
Niels Möller's avatar
Niels Möller committed
226
227
228
229
	
	dealloc_channel(self->table, self->channel_number);

	if (self->table->pending_close && !self->table->next_channel)
230
231
232
233
	  {
	    /* FIXME: Send a SSH_DISCONNECT_BY_APPLICATION message? */
	    EXCEPTION_RAISE(self->super.parent, &finish_read_exception);
	  }
Niels Möller's avatar
Niels Möller committed
234
235
236
237
238
239
240
241
242
243
      }
      break;
    default:
      EXCEPTION_RAISE(self->super.parent, e);
    }
}

static struct exception_handler *
make_exc_finish_channel_handler(struct channel_table *table,
				UINT32 channel_number,
244
245
				struct exception_handler *e,
				const char *context)
Niels Möller's avatar
Niels Möller committed
246
247
248
249
{
  NEW(exc_finish_channel_handler, self);
  self->super.parent = e;
  self->super.raise = do_exc_finish_channel_handler;
250
  self->super.context = context;
Niels Möller's avatar
Niels Möller committed
251

252
253
254
  self->table = table;
  self->channel_number = channel_number;
  
Niels Möller's avatar
Niels Möller committed
255
  return &self->super;
Niels Möller's avatar
Niels Möller committed
256
257
258
}
				

259
/* Channel objects */
Niels Möller's avatar
Niels Möller committed
260
261
262
263
264

#define INITIAL_CHANNELS 32
/* Arbitrary limit */
#define MAX_CHANNELS (1L<<17)

Niels Möller's avatar
Niels Möller committed
265
struct channel_table *make_channel_table(void)
Niels Möller's avatar
Niels Möller committed
266
{
267
  NEW(channel_table, table);
Niels Möller's avatar
Niels Möller committed
268

269
  table->channels = lsh_space_alloc(sizeof(struct ssh_channel *)
270
				      * INITIAL_CHANNELS);
Niels Möller's avatar
Niels Möller committed
271
272
  table->in_use = lsh_space_alloc(INITIAL_CHANNELS);
  
273
274
275
276
  table->allocated_channels = INITIAL_CHANNELS;
  table->next_channel = 0;
  table->used_channels = 0;
  table->max_channels = MAX_CHANNELS;
Niels Möller's avatar
Niels Möller committed
277

Niels Möller's avatar
Niels Möller committed
278
  table->pending_close = 0;
279

280
281
282
  table->global_requests = make_alist(0, -1);
  table->channel_types = make_alist(0, -1);
  
283
284
285
  object_queue_init(&table->local_ports);
  object_queue_init(&table->remote_ports);
  
286
287
  object_queue_init(&table->active_global_requests);
  object_queue_init(&table->pending_global_requests);
Niels Möller's avatar
Niels Möller committed
288
  
289
  return table;
Niels Möller's avatar
Niels Möller committed
290
291
292
};

/* Returns -1 if allocation fails */
293
294
295
/* NOTE: This function returns locally chosen channel numbers, which
 * are always small integers. So there's no problem fitting them in
 * a signed int. */
296
int alloc_channel(struct channel_table *table)
Niels Möller's avatar
Niels Möller committed
297
{
298
  UINT32 i;
299
  
300
  for(i = table->next_channel; i < table->used_channels; i++)
Niels Möller's avatar
Niels Möller committed
301
    {
Niels Möller's avatar
Niels Möller committed
302
      if (!table->in_use[i])
Niels Möller's avatar
Niels Möller committed
303
	{
Niels Möller's avatar
Niels Möller committed
304
305
	  assert(!table->channels[i]);
	  table->in_use[i] = 1;
306
	  table->next_channel = i+1;
307

308
	  verbose("Allocated local channel number %i\n", i);
Niels Möller's avatar
Niels Möller committed
309
310
311
	  return i;
	}
    }
312
  if (i == table->max_channels)
Niels Möller's avatar
Niels Möller committed
313
    return -1;
314

315
  if (i == table->allocated_channels) 
Niels Möller's avatar
Niels Möller committed
316
    {
317
      int new_size = table->allocated_channels * 2;
Niels Möller's avatar
Niels Möller committed
318
      struct ssh_channel **new_channels;
Niels Möller's avatar
Niels Möller committed
319
      UINT8 *new_in_use;
Niels Möller's avatar
Niels Möller committed
320

Niels Möller's avatar
Niels Möller committed
321
322
323
      new_channels = lsh_space_alloc(sizeof(struct ssh_channel *)
				     * new_size);
      memcpy(new_channels, table->channels,
324
	     sizeof(struct ssh_channel *) * table->used_channels);
Niels Möller's avatar
Niels Möller committed
325
326
327
328
329
330
331
332
      lsh_space_free(table->channels);
      table->channels = new_channels;

      new_in_use = lsh_space_alloc(new_size);
      memcpy(new_in_use, table->in_use, table->used_channels);
      lsh_space_free(table->in_use);
      table->in_use = new_in_use;

333
      table->allocated_channels = new_size;
Niels Möller's avatar
Niels Möller committed
334
335
    }

336
  table->next_channel = table->used_channels = i+1;
Niels Möller's avatar
Niels Möller committed
337

338
  table->in_use[i] = 1;
339
  verbose("Allocated local channel number %i\n", i);
340

Niels Möller's avatar
Niels Möller committed
341
342
343
  return i;
}

344
void dealloc_channel(struct channel_table *table, int i)
Niels Möller's avatar
Niels Möller committed
345
346
{
  assert(i >= 0);
347
  assert( (unsigned) i < table->used_channels);
348

349
  verbose("Deallocating local channel %i\n", i);
350
  table->channels[i] = NULL;
Niels Möller's avatar
Niels Möller committed
351
352
  table->in_use[i] = 0;
  
353
  if ( (unsigned) i < table->next_channel)
354
355
356
    table->next_channel = i;
}

357
358
359
360
void
register_channel(struct ssh_connection *connection,
		 UINT32 local_channel_number,
		 struct ssh_channel *channel)
361
{
362
363
  struct channel_table *table = connection->table;
  
Niels Möller's avatar
Niels Möller committed
364
365
  assert(table->in_use[local_channel_number]);
  assert(!table->channels[local_channel_number]);
366

367
368
  verbose("Taking channel %i in use, (local %i).\n",
	  channel->channel_number, local_channel_number);
Niels Möller's avatar
Niels Möller committed
369
370
371
372
373
  table->channels[local_channel_number] = channel;

  /* FIXME: Is this the right place to install this exception handler? */
  channel->e = make_exc_finish_channel_handler(table,
					       local_channel_number,
374
375
					       connection->e,
					       HANDLER_CONTEXT);
376
377

  REMEMBER_RESOURCE(connection->resources, &channel->resources->super);
Niels Möller's avatar
Niels Möller committed
378
379
}

380
struct ssh_channel *lookup_channel(struct channel_table *table, UINT32 i)
Niels Möller's avatar
Niels Möller committed
381
{
382
383
  return (i < table->used_channels)
    ? table->channels[i] : NULL;
Niels Möller's avatar
Niels Möller committed
384
385
}

386
387
388
389
390
391
392
393
394
395
/* FIXME: It seems suboptimal to send a window adjust message for *every* write that we do.
 * A better scheme might be as follows:
 *
 * 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. */
static void adjust_rec_window(struct flow_controlled *f, UINT32 written)
396
{
397
398
  CAST_SUBTYPE(ssh_channel, channel, f);

Niels Möller's avatar
Niels Möller committed
399
  A_WRITE(channel->write,
400
	  prepare_window_adjust(channel, written));
401
402
}

Niels Möller's avatar
Niels Möller committed
403
void channel_start_receive(struct ssh_channel *channel)
404
{
Niels Möller's avatar
Niels Möller committed
405
406
  A_WRITE(channel->write,
	  prepare_window_adjust
407
	  (channel, channel->max_window - channel->rec_window_size));
408
409
}

Niels Möller's avatar
Niels Möller committed
410

411
412
413
/* Ugly macros to make it a little simpler to free the input packet at
 * the right time. */

Niels Möller's avatar
Niels Möller committed
414
415
416
417
418
#define RETURN goto foo_finish
#define END(s) do { foo_finish: \
                    lsh_string_free((s)); \
                    return; } while(0)

419

Niels Möller's avatar
Niels Möller committed
420
/* Channel related messages */
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439

/* GABA:
   (class
     (name global_request_status)
     (vars
       ; -1 for still active requests,
       ; 0 for failure,
       ; 1 for success
       (status . int)))
*/

static struct global_request_status *make_global_request_status(void)
{
  NEW(global_request_status, self);
  self->status = -1;

  return self;
}

Niels Möller's avatar
Niels Möller committed
440
/* FIXME: Split into a continuation and an exception handler */
441
442
443
444
445
446
447
448
/* GABA:
   (class
     (name global_request_response)
     (super global_request_callback)
     (vars
       (active object global_request_status)))
*/

Niels Möller's avatar
Niels Möller committed
449
static void
450
451
452
453
do_global_request_response(struct global_request_callback *c,
			   int success)
{
  CAST(global_request_response, self, c);
Niels Möller's avatar
Niels Möller committed
454
  struct object_queue *q = &self->super.connection->table->active_global_requests;
455
456
457
458
459
460
461
462
463
464
465

  assert( self->active->status == -1);
  assert( (success == 0) || (success == 1) );
  assert( !object_queue_is_empty(q));
	  
  self->active->status = success;

  for (;;)
    {
      CAST(global_request_status, n, object_queue_peek_head(q));
      if (!n || (n->status < 0))
Niels Möller's avatar
Niels Möller committed
466
	break;
467
468

      object_queue_remove_head(q);
Niels Möller's avatar
Niels Möller committed
469
470
471

      /* FIXME: Perhaps install some exception handler that cancels
       * the queue as soon as a write failes. */
472
      C_WRITE(self->super.connection,
Niels Möller's avatar
Niels Möller committed
473
474
	      (n->status
	       ? format_global_success()
475
	       : format_global_failure()));
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
    }
}

static struct global_request_callback *
make_global_request_response(struct ssh_connection *connection,
			     struct global_request_status *active)
{
  NEW(global_request_response, self);

  self->super.connection = connection;
  self->super.response = do_global_request_response;

  self->active = active;

  return &self->super;
}
     
Niels Möller's avatar
Niels Möller committed
493
494
495
static void do_global_request(struct packet_handler *s UNUSED,
			      struct ssh_connection *connection,
			      struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
496
{
497
  /* CAST(global_request_handler, closure, c); */
Niels Möller's avatar
Niels Möller committed
498
499

  struct simple_buffer buffer;
500
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
501
502
503
504
505
506
507
508
509
510
511
  int name;
  int want_reply;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_GLOBAL_REQUEST)
      && parse_atom(&buffer, &name)
      && parse_boolean(&buffer, &want_reply))
    {
      struct global_request *req;
512
513
      struct global_request_callback *c = NULL;
      
Niels Möller's avatar
Niels Möller committed
514
      if (!name || !(req = ALIST_GET(connection->table->global_requests,
515
				     name)))
516
	{
Niels Möller's avatar
Niels Möller committed
517
	  lsh_string_free(packet);
518

Niels Möller's avatar
Niels Möller committed
519
520
	  C_WRITE(connection, format_global_failure());
	  return;
Niels Möller's avatar
Niels Möller committed
521
522
523
524
525
526
527
	}
      else
	{
	  if (want_reply)
	    {
	      struct global_request_status *a = make_global_request_status();
	      
Niels Möller's avatar
Niels Möller committed
528
	      object_queue_add_tail(&connection->table->active_global_requests,
Niels Möller's avatar
Niels Möller committed
529
530
531
532
533
				    &a->super);
	      
	      c = make_global_request_response(connection, a);
	    }
	  GLOBAL_REQUEST(req, connection, &buffer, c);
534
	}
Niels Möller's avatar
Niels Möller committed
535
    }
Niels Möller's avatar
Niels Möller committed
536
537
  else
    {
538
      PROTOCOL_ERROR(connection->e, "Invalid SSH_MSG_GLOBAL_REQUEST message.");
Niels Möller's avatar
Niels Möller committed
539
540
    }
  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
541
542
}

Niels Möller's avatar
Niels Möller committed
543
544
545
546
static void
do_global_request_success(struct packet_handler *s UNUSED,
			  struct ssh_connection *connection,
			  struct lsh_string *packet)
547
548
{
  if (packet->length != 1)
Niels Möller's avatar
Niels Möller committed
549
    {
550
      PROTOCOL_ERROR(connection->e, "Invalid GLOBAL_REQUEST_SUCCESS message.");
Niels Möller's avatar
Niels Möller committed
551
552
      RETURN;
    }
553
554
555

  assert(packet->data[0] == SSH_MSG_REQUEST_SUCCESS);

Niels Möller's avatar
Niels Möller committed
556
  if (object_queue_is_empty(&connection->table->pending_global_requests))
557
558
    {
      werror("do_global_request_success: Unexpected message, ignoring.\n");
Niels Möller's avatar
Niels Möller committed
559
      return;
560
561
    }
  {
562
    CAST_SUBTYPE(command_context, ctx,
Niels Möller's avatar
Niels Möller committed
563
564
		 object_queue_remove_head(&connection->table->pending_global_requests));
    COMMAND_RETURN(ctx->c, connection);
565
  }
Niels Möller's avatar
Niels Möller committed
566
  END(packet);
567
568
}

569
570
571
struct exception global_request_exception =
STATIC_EXCEPTION(EXC_GLOBAL_REQUEST, "Global request failed");

Niels Möller's avatar
Niels Möller committed
572
573
574
575
static void
do_global_request_failure(struct packet_handler *s UNUSED,
			  struct ssh_connection *connection,
			  struct lsh_string *packet)
576
577
{
  if (packet->length != 1)
Niels Möller's avatar
Niels Möller committed
578
    {
579
      PROTOCOL_ERROR(connection->e, "Invalid GLOBAL_REQUEST_FAILURE message.");
Niels Möller's avatar
Niels Möller committed
580
581
      RETURN;
    }
582
583
584

  assert(packet->data[0] == SSH_MSG_REQUEST_FAILURE);

Niels Möller's avatar
Niels Möller committed
585
  if (object_queue_is_empty(&connection->table->pending_global_requests))
586
587
588
    {
      werror("do_global_request_failure: Unexpected message, ignoring.\n");
    }
Niels Möller's avatar
Niels Möller committed
589
590
591
592
593
594
595
  else
    {
      CAST_SUBTYPE(command_context, ctx,
		   object_queue_remove_head(&connection->table->pending_global_requests));
      EXCEPTION_RAISE(ctx->e, &global_request_exception);
    }
  END(packet);
596
597
}

598

Niels Möller's avatar
Niels Möller committed
599
/* GABA:
600
601
602
603
604
   (class
     (name channel_open_continuation)
     (super command_continuation)
     (vars
       (connection object ssh_connection)
Niels Möller's avatar
Niels Möller committed
605
606
607
608
       (local_channel_number . UINT32)
       (remote_channel_number . UINT32)
       (window_size . UINT32)
       (max_packet . UINT32)))
609
*/
610

Niels Möller's avatar
Niels Möller committed
611
612
613
static void
do_channel_open_continue(struct command_continuation *c,
			 struct lsh_object *value)
614
615
{
  CAST(channel_open_continuation, self, c);
Niels Möller's avatar
Niels Möller committed
616
617
618
619
620
621
622
623
624
625
626
627
  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. */
  channel->send_window_size = self->window_size;
  channel->send_max_packet = self->max_packet;
  channel->channel_number = self->remote_channel_number;

  /* FIXME: Is the channel->write field really needed? */
Niels Möller's avatar
Niels Möller committed
628
  channel->write = self->connection->write;
Niels Möller's avatar
Niels Möller committed
629

630
  register_channel(self->connection,
Niels Möller's avatar
Niels Möller committed
631
		   self->local_channel_number, channel);
Niels Möller's avatar
Niels Möller committed
632
633
634
635
636

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

  C_WRITE(self->connection,
Niels Möller's avatar
Niels Möller committed
637
	  format_open_confirmation(channel, self->local_channel_number, ""));
638
}
Niels Möller's avatar
Niels Möller committed
639

Niels Möller's avatar
Niels Möller committed
640
static struct command_continuation *
Niels Möller's avatar
Niels Möller committed
641
642
643
644
645
646
647
make_channel_open_continuation(struct ssh_connection *connection,
			       UINT32 local_channel_number,
			       UINT32 remote_channel_number,
			       UINT32 window_size,
			       UINT32 max_packet)
{
  NEW(channel_open_continuation, self);
Niels Möller's avatar
Niels Möller committed
648

Niels Möller's avatar
Niels Möller committed
649
650
651
652
653
654
655
  self->super.c = do_channel_open_continue;
  self->connection = connection;
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;
  self->window_size = window_size;
  self->max_packet = max_packet;

Niels Möller's avatar
Niels Möller committed
656
  return &self->super;
Niels Möller's avatar
Niels Möller committed
657
658
659
660
661
662
663
664
665
}
			       
/* GABA:
   (class
     (name exc_channel_open_handler)
     (super exception_handler)
     (vars
       (connection object ssh_connection)
       (local_channel_number . UINT32)
Niels Möller's avatar
Niels Möller committed
666
       (remote_channel_number . UINT32)))
Niels Möller's avatar
Niels Möller committed
667
668
669
*/

static void do_exc_channel_open_handler(struct exception_handler *s,
Niels Möller's avatar
Niels Möller committed
670
					const struct exception *e)
Niels Möller's avatar
Niels Möller committed
671
672
673
674
675
676
677
678
{
  CAST(exc_channel_open_handler, self, s);

  switch (e->type)
    {
    case EXC_CHANNEL_OPEN:
      {
	CAST_SUBTYPE(channel_open_exception, exc, e);
Niels Möller's avatar
Niels Möller committed
679
	struct channel_table *table = self->connection->table;
Niels Möller's avatar
Niels Möller committed
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
	
	assert(table->in_use[self->local_channel_number]);
	assert(!table->channels[self->local_channel_number]);

	dealloc_channel(table, self->local_channel_number);
	
        C_WRITE(self->connection,
		format_open_failure(self->remote_channel_number,
				    exc->error_code, e->msg, ""));
	break;
      }
    default:
      EXCEPTION_RAISE(self->super.parent, e);
    }      
}

static struct exception_handler *
make_exc_channel_open_handler(struct ssh_connection *connection,
			      UINT32 local_channel_number,
			      UINT32 remote_channel_number,
			      struct exception_handler *parent)
{
Niels Möller's avatar
Niels Möller committed
702
  NEW(exc_channel_open_handler, self);
Niels Möller's avatar
Niels Möller committed
703
  self->super.parent = parent;
Niels Möller's avatar
Niels Möller committed
704
705
  self->super.raise = do_exc_channel_open_handler;
  self->connection = connection;
Niels Möller's avatar
Niels Möller committed
706
707
708
709
710
711
712
713
714
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;

  return &self->super;
}

static void do_channel_open(struct packet_handler *c UNUSED,
			    struct ssh_connection *connection,
			    struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
715
{
716
  /* CAST(channel_open_handler, closure, c); */
Niels Möller's avatar
Niels Möller committed
717
718

  struct simple_buffer buffer;
719
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
720
  int type;
721
722
723
  UINT32 remote_channel_number;
  UINT32 window_size;
  UINT32 max_packet;
Niels Möller's avatar
Niels Möller committed
724
725
726
727
728
729
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_OPEN)
      && parse_atom(&buffer, &type)
730
731
732
      && parse_uint32(&buffer, &remote_channel_number)
      && parse_uint32(&buffer, &window_size)
      && parse_uint32(&buffer, &max_packet))
Niels Möller's avatar
Niels Möller committed
733
734
    {
      struct channel_open *open;
Niels Möller's avatar
Niels Möller committed
735

Niels Möller's avatar
Niels Möller committed
736
737
738
739
      /* NOTE: We can't free the packet yet, as the buffer is passed
       * to the CHANNEL_OPEN method later. */

      if (connection->table->pending_close)
Niels Möller's avatar
Niels Möller committed
740
741
742
743
744
745
746
747
	{
	  /* We are waiting for channels to close. Don't open any new ones. */

	  C_WRITE(connection,
		  format_open_failure(remote_channel_number,
				      SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
				      "Waiting for channels to close.", ""));
	}
Niels Möller's avatar
Niels Möller committed
748
      else if (!type || !(open = ALIST_GET(connection->table->channel_types,
749
				      type)))
Niels Möller's avatar
Niels Möller committed
750
751
752
753
754
755
756
757
	{
	  C_WRITE(connection,
		  format_open_failure(remote_channel_number,
				      SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
				      "Unknown channel type", ""));
	}
      else
	{
Niels Möller's avatar
Niels Möller committed
758
      	  int local_number = alloc_channel(connection->table);
Niels Möller's avatar
Niels Möller committed
759
760
761
762
763
764
765
766

	  if (local_number < 0)
	    C_WRITE(connection,
		    format_open_failure(remote_channel_number,
					SSH_OPEN_RESOURCE_SHORTAGE,
					"Unknown channel type", ""));

	  
Niels Möller's avatar
Niels Möller committed
767
	  
768
	  CHANNEL_OPEN(open, connection, type, &buffer,
Niels Möller's avatar
Niels Möller committed
769
770
771
772
773
774
775
776
777
		       make_channel_open_continuation(connection,
						      local_number,
						      remote_channel_number,
						      window_size,
						      max_packet),
		       make_exc_channel_open_handler(connection,
						     local_number,
						     remote_channel_number,
						     connection->e));
Niels Möller's avatar
Niels Möller committed
778
	}
Niels Möller's avatar
Niels Möller committed
779
    }
Niels Möller's avatar
Niels Möller committed
780
  else
781
    PROTOCOL_ERROR(connection->e, "Invalid SSH_MSG_CHANNEL_OPEN message.");
Niels Möller's avatar
Niels Möller committed
782

Niels Möller's avatar
Niels Möller committed
783
  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
784
785
}     

Niels Möller's avatar
Niels Möller committed
786
787
788
789
static void
do_channel_request(struct packet_handler *closure UNUSED,
		   struct ssh_connection *connection,
		   struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
790
791
{
  struct simple_buffer buffer;
792
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
793
794
795
  UINT32 channel_number;
  int type;
  int want_reply;
796
  
Niels Möller's avatar
Niels Möller committed
797
798
799
800
801
802
803
804
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_REQUEST)
      && parse_uint32(&buffer, &channel_number)
      && parse_atom(&buffer, &type)
      && parse_boolean(&buffer, &want_reply))
    {
Niels Möller's avatar
Niels Möller committed
805
      struct ssh_channel *channel = lookup_channel(connection->table,
Niels Möller's avatar
Niels Möller committed
806
807
						   channel_number);

808
809
810
811
      /* NOTE: We can't free packet yet, because it is not yet fully
       * parsed. There may be some more arguments, which are parsed by
       * the CHANNEL_REQUEST method below. */

Niels Möller's avatar
Niels Möller committed
812
813
814
815
      if (channel)
	{
	  struct channel_request *req;

816
817
	  if (type && channel->request_types 
	      && ( (req = ALIST_GET(channel->request_types, type)) ))
Niels Möller's avatar
Niels Möller committed
818
	    CHANNEL_REQUEST(req, channel, connection, want_reply, &buffer);
819
	  else
Niels Möller's avatar
Niels Möller committed
820
821
	    {
	      if (want_reply)
Niels Möller's avatar
Niels Möller committed
822
		C_WRITE(connection,
Niels Möller's avatar
Niels Möller committed
823
824
825
826
827
828
829
			format_channel_failure(channel->channel_number));
	    }
	}
      else
	{
	  werror("SSH_MSG_CHANNEL_REQUEST on nonexistant channel %i\n",
		 channel_number);
Niels Möller's avatar
Niels Möller committed
830
	}
831
    }
Niels Möller's avatar
Niels Möller committed
832
  else
833
    PROTOCOL_ERROR(connection->e, "Invalid SSH_MSG_CHANNEL_REQUEST message.");
Niels Möller's avatar
Niels Möller committed
834
  
Niels Möller's avatar
Niels Möller committed
835
  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
836
837
}
      
Niels Möller's avatar
Niels Möller committed
838
839
840
841
static void
do_window_adjust(struct packet_handler *closure UNUSED,
		 struct ssh_connection *connection,
		 struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
842
843
{
  struct simple_buffer buffer;
844
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
845
846
847
848
849
850
851
  UINT32 channel_number;
  UINT32 size;

  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_WINDOW_ADJUST)
852
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
853
854
855
      && parse_uint32(&buffer, &size)
      && parse_eod(&buffer))
    {
Niels Möller's avatar
Niels Möller committed
856
      struct ssh_channel *channel = lookup_channel(connection->table,
Niels Möller's avatar
Niels Möller committed
857
858
859
860
						   channel_number);

      lsh_string_free(packet);
      
Niels Möller's avatar
Niels Möller committed
861
      if (channel
862
863
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF
				 | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
864
	{
Niels Möller's avatar
Niels Möller committed
865
866
867
868
	  if (! (channel->flags & CHANNEL_SENT_CLOSE))
	    {
	      channel->send_window_size += size;
	      if (channel->send_window_size && channel->send)
869
		CHANNEL_SEND(channel, connection);
Niels Möller's avatar
Niels Möller committed
870
	    }
Niels Möller's avatar
Niels Möller committed
871
	}
Niels Möller's avatar
Niels Möller committed
872
873
874
875
876
877
      else
	{
	  /* FIXME: What to do now? Should unknown channel numbers be
	   * ignored silently? */
	  werror("SSH_MSG_CHANNEL_WINDOW_ADJUST on nonexistant or closed "
		 "channel %i\n", channel_number);
878
	  PROTOCOL_ERROR(connection->e, "Unexpected CHANNEL_WINDOW_ADJUST");
Niels Möller's avatar
Niels Möller committed
879
880
881
882
883
	}
    }
  else
    {
      lsh_string_free(packet);
884
      PROTOCOL_ERROR(connection->e, "Invalid CHANNEL_WINDOW_ADJUST message.");
Niels Möller's avatar
Niels Möller committed
885
886
887
    }
}

Niels Möller's avatar
Niels Möller committed
888
889
890
891
static void
do_channel_data(struct packet_handler *closure UNUSED,
		struct ssh_connection *connection,
		struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
892
893
{
  struct simple_buffer buffer;
894
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
895
896
897
898
899
900
901
  UINT32 channel_number;
  struct lsh_string *data;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_DATA)
902
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
903
904
905
      && ( (data = parse_string_copy(&buffer)) )
      && parse_eod(&buffer))
    {
Niels Möller's avatar
Niels Möller committed
906
      struct ssh_channel *channel = lookup_channel(connection->table,
Niels Möller's avatar
Niels Möller committed
907
908
909
910
						   channel_number);

      lsh_string_free(packet);
      
911
      if (channel && channel->receive
912
913
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF
				 | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
914
	{
Niels Möller's avatar
Niels Möller committed
915
	  if (channel->flags & CHANNEL_SENT_CLOSE)
Niels Möller's avatar
Niels Möller committed
916
	    {
Niels Möller's avatar
Niels Möller committed
917
	      lsh_string_free(data);
918
	      werror("Ignoring data on channel which is closing\n");
Niels Möller's avatar
Niels Möller committed
919
	      return;
Niels Möller's avatar
Niels Möller committed
920
	    }
Niels Möller's avatar
Niels Möller committed
921
922
923
924
925
	  else
	    {
	      if (data->length > channel->rec_window_size)
		{
		  /* Truncate data to fit window */
926
		  werror("Channel data overflow. Extra data ignored.\n"); 
Niels Möller's avatar
Niels Möller committed
927
928
		  data->length = channel->rec_window_size;
		}
929
930

	      if (!data->length)
Niels Möller's avatar
Niels Möller committed
931
932
933
934
935
		{
		  /* Ignore data packet */
		  lsh_string_free(data);
		  return;
		}
936
937
	      channel->rec_window_size -= data->length;

Niels Möller's avatar
Niels Möller committed
938
	      CHANNEL_RECEIVE(channel, CHANNEL_DATA, data);
Niels Möller's avatar
Niels Möller committed
939
	    }
Niels Möller's avatar
Niels Möller committed
940
	}
Niels Möller's avatar
Niels Möller committed
941
942
943
944
945
946
947
948
949
950
      else
	{
	  werror("Data on closed or non-existant channel %i\n",
		 channel_number);
	  lsh_string_free(data);
	}
    }
  else
    {
      lsh_string_free(packet);
951
      PROTOCOL_ERROR(connection->e, "Invalid CHANNEL_DATA message.");
Niels Möller's avatar
Niels Möller committed
952
953
954
    }
}

Niels Möller's avatar
Niels Möller committed
955
956
957
958
static void
do_channel_extended_data(struct packet_handler *closure UNUSED,
			 struct ssh_connection *connection,
			 struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
959
960
{
  struct simple_buffer buffer;
961
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
962
963
964
965
966
967
968
969
  UINT32 channel_number;
  UINT32 type;
  struct lsh_string *data;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_EXTENDED_DATA)
970
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
971
972
973
974
      && parse_uint32(&buffer, &type)
      && ( (data = parse_string_copy(&buffer)) )
      && parse_eod(&buffer))
    {
Niels Möller's avatar
Niels Möller committed
975
      struct ssh_channel *channel = lookup_channel(connection->table,
Niels Möller's avatar
Niels Möller committed
976
977
978
979
						   channel_number);

      lsh_string_free(packet);
      
980
      if (channel && channel->receive
981
982
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF
				 | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
983
	{
Niels Möller's avatar
Niels Möller committed
984
	  if (channel->flags & CHANNEL_SENT_CLOSE)
Niels Möller's avatar
Niels Möller committed
985
	    {
Niels Möller's avatar
Niels Möller committed
986
	      lsh_string_free(data);
987
	      werror("Ignoring extended data on channel which is closing\n");
Niels Möller's avatar
Niels Möller committed
988
	      return;
Niels Möller's avatar
Niels Möller committed
989
	    }
Niels Möller's avatar
Niels Möller committed
990
	  else
Niels Möller's avatar
Niels Möller committed
991
	    {
Niels Möller's avatar
Niels Möller committed
992
993
994
	      if (data->length > channel->rec_window_size)
		{
		  /* Truncate data to fit window */
995
		  werror("Channel extended data overflow. "
Niels Möller's avatar
Niels Möller committed
996
997
998
999
			 "Extra data ignored.\n");
		  data->length = channel->rec_window_size;
		}
	      
Niels Möller's avatar
Niels Möller committed
1000
1001
1002
1003
1004
1005
	      if (!data->length)
		{
		  /* Ignore data packet */
		  lsh_string_free(data);
		  return;
		}
1006

Niels Möller's avatar
Niels Möller committed
1007
	      channel->rec_window_size -= data->length;
1008

Niels Möller's avatar
Niels Möller committed
1009
1010
1011
	      switch(type)
		{
		case SSH_EXTENDED_DATA_STDERR:
Niels Möller's avatar
Niels Möller committed
1012
1013
		  CHANNEL_RECEIVE(channel, CHANNEL_STDERR_DATA, data);
		  break;
Niels Möller's avatar
Niels Möller committed
1014
		default:
1015
		  werror("Unknown type %i of extended data.\n",
Niels Möller's avatar
Niels Möller committed
1016
1017
1018
			 type);
		  lsh_string_free(data);
		}
Niels Möller's avatar
Niels Möller committed
1019
1020
	    }
	}
Niels Möller's avatar
Niels Möller committed
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
      else
	{
	  werror("Extended data on closed or non-existant channel %i\n",
		 channel_number);
	  lsh_string_free(data);
	}
    }
  else
    {
      lsh_string_free(packet);
1031
      PROTOCOL_ERROR(connection->e, "Invalid CHANNEL_EXTENDED_DATA message.");
Niels Möller's avatar
Niels Möller committed
1032
1033
1034
    }
}

Niels Möller's avatar
Niels Möller committed
1035
1036
1037
1038
static void
do_channel_eof(struct packet_handler *closure UNUSED,
	       struct ssh_connection *connection,
	       struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
1039
1040
{
  struct simple_buffer buffer;
1041
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
1042
1043
1044
1045
1046
1047
  UINT32 channel_number;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_EOF)
1048
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
1049
1050
      && parse_eod(&buffer))
    {
Niels Möller's avatar
Niels Möller committed
1051
      struct ssh_channel *channel = lookup_channel(connection->table,
Niels Möller's avatar
Niels Möller committed
1052
1053
1054
1055
						   channel_number);

      lsh_string_free(packet);

Niels Möller's avatar
Niels Möller committed
1056
1057
      if (channel)
	{
1058
	  if (channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_RECEIVED_CLOSE))
Niels Möller's avatar
Niels Möller committed
1059
	    {
1060
	      werror("Receiving EOF on channel on closed channel.\n");
1061
1062
	      PROTOCOL_ERROR(connection->e,
			     "Received EOF on channel on closed channel.");
Niels Möller's avatar
Niels Möller committed
1063
	    }
1064
	  else
Niels Möller's avatar
Niels Möller committed
1065
	    {
1066
1067
	      verbose("Receiving EOF on channel %i (local %i)\n",
		      channel->channel_number, channel_number);
1068
	      
Niels Möller's avatar
Niels Möller committed
1069
	      channel->flags |= CHANNEL_RECEIVED_EOF;
1070
	      
Niels Möller's avatar
Niels Möller committed
1071
1072
1073
1074
1075
	      if (channel->eof)
		CHANNEL_EOF(channel);
	      else
		/* FIXME: What is a reasonable default behaviour?
		 * Closing the channel may be the right thing to do. */
1076
		channel_close(channel);
Niels Möller's avatar
Niels Möller committed
1077
1078
	    }
	}
Niels Möller's avatar
Niels Möller committed
1079
1080
1081
1082
      else
	{
	  werror("EOF on non-existant channel %i\n",
		 channel_number);
1083
	  PROTOCOL_ERROR(connection->e, "EOF on non-existant channel");
Niels Möller's avatar
Niels Möller committed
1084
1085
1086
1087
1088
	}
    }
  else
    {
      lsh_string_free(packet);
1089
      PROTOCOL_ERROR(connection->e, "Invalid CHANNEL_EOF message");
Niels Möller's avatar
Niels Möller committed
1090
1091
1092
    }
}

Niels Möller's avatar
Niels Möller committed
1093
1094
1095
1096
static void
do_channel_close(struct packet_handler *closure UNUSED,
		 struct ssh_connection *connection,
		 struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
1097
1098
{
  struct simple_buffer buffer;
1099
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
1100
1101
1102
1103
1104
1105
  UINT32 channel_number;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_CLOSE)
1106
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
1107
1108
      && parse_eod(&buffer))
    {
Niels Möller's avatar
Niels Möller committed
1109
      struct ssh_channel *channel = lookup_channel(connection->table,
Niels Möller's avatar
Niels Möller committed
1110
1111
1112
1113
						   channel_number);

      lsh_string_free(packet);
      
Niels Möller's avatar
Niels Möller committed
1114
1115
      if (channel)
	{
1116
1117
	  verbose("Receiving CLOSE on channel %i (local %i)\n",
		  channel->channel_number, channel_number);
1118
	      
1119
	  if (channel->flags & CHANNEL_RECEIVED_CLOSE)
Niels Möller's avatar
Niels Möller committed
1120
	    {
1121
	      werror("Receiving multiple CLOSE on channel.\n");
1122
	      PROTOCOL_ERROR(connection->e, "Receiving multiple CLOSE on channel.");
Niels Möller's avatar
Niels Möller committed
1123
	    }
Niels Möller's avatar
Niels Möller committed
1124
	  else
Niels Möller's avatar
Niels Möller committed
1125
	    {
Niels Möller's avatar
Niels Möller committed
1126
	      channel->flags |= CHANNEL_RECEIVED_CLOSE;
Niels Möller's avatar
Niels Möller committed
1127
	  
Niels Möller's avatar
Niels Möller committed
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
	      if (! (channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_SENT_EOF
				       | CHANNEL_SENT_CLOSE)))
		{
		  werror("Unexpected channel CLOSE.\n");
		}

	      if (! (channel->flags & (CHANNEL_RECEIVED_EOF))
		  && channel->eof)
		CHANNEL_EOF(channel);

1138
	      if (channel->flags & CHANNEL_SENT_CLOSE)
Niels Möller's avatar
Niels Möller committed
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
		{
		  static const struct exception finish_exception
		    = STATIC_EXCEPTION(EXC_FINISH_CHANNEL, "Received CLOSE message.");
	      
		  EXCEPTION_RAISE(channel->e,
				  &finish_exception);
		}
	      else
		channel_close(channel);
	    }
	}
      else
	{
	  werror("CLOSE on non-existant channel %i\n",
		 channel_number);
1154
	  PROTOCOL_ERROR(connection->e, "CLOSE on non-existant channel");
Niels Möller's avatar
Niels Möller committed
1155
	}
Niels Möller's avatar
Niels Möller committed
1156
    }
Niels Möller's avatar
Niels Möller committed
1157
1158
1159
  else
    {
      lsh_string_free(packet);
1160
      PROTOCOL_ERROR(connection->e, "Invalid CHANNEL_CLOSE message");
Niels Möller's avatar
Niels Möller committed
1161
    }
Niels Möller's avatar
Niels Möller committed
1162
1163
}

Niels Möller's avatar
Niels Möller committed
1164
1165
1166