channel.c 55.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
/* 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
struct exception *
48
make_channel_open_exception(uint32_t error_code, const char *msg)
Niels Möller's avatar
Niels Möller committed
49
50
{
  NEW(channel_open_exception, self);
Niels Möller's avatar
Niels Möller committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64

#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
65
  self->super.type = EXC_CHANNEL_OPEN;
Niels Möller's avatar
Niels Möller committed
66
  self->super.msg = msg ? msg : msgs[error_code];
Niels Möller's avatar
Niels Möller committed
67
68
69
70
71
  self->error_code = error_code;

  return &self->super;
}

72

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

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

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/* 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;
    }
}

108
109
struct lsh_string *
format_open_confirmation(struct ssh_channel *channel,
110
			 uint32_t channel_number,
111
			 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
119
#define CONFIRM_ARGS \
  SSH_MSG_CHANNEL_OPEN_CONFIRMATION, channel->channel_number, \
120
121
  channel_number, channel->rec_window_size, channel->rec_max_packet
    
122
123
  check_rec_max_packet(channel);

124
  debug("format_open_confirmation: rec_window_size = %i,\n"
125
	"                          rec_max_packet = %i,\n",
126
       channel->rec_window_size,
127
       channel->rec_max_packet);
128
129
130
131
132
133
134
135
  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);

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

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

  return packet;
#undef CONFIRM_FORMAT
#undef CONFIRM_ARGS
}

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

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

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

167
168
struct lsh_string *
prepare_window_adjust(struct ssh_channel *channel,
169
		      uint32_t add)
170
171
172
173
174
175
176
177
{
  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
/* GABA:
   (class
     (name exc_finish_channel_handler)
     (super exception_handler)
     (vars
183
       (table object channel_table)
184
185
       ; Non-zero if the channel has already been deallocated.
       (dead . int)
Niels Möller's avatar
Niels Möller committed
186
       ; Local channel number 
187
       (channel_number . uint32_t)))
Niels Möller's avatar
Niels Möller committed
188
189
*/

190
191
192
static void
do_exc_finish_channel_handler(struct exception_handler *s,
			      const struct exception *e)
Niels Möller's avatar
Niels Möller committed
193
194
195
196
197
{
  CAST(exc_finish_channel_handler, self, s);

  switch (e->type)
    {
Niels Möller's avatar
Niels Möller committed
198
    case EXC_FINISH_PENDING:
199
200
      if (self->dead)
	werror("channel.c: EXC_FINISH_PENDING on dead channel.\n");
Niels Möller's avatar
Niels Möller committed
201

202
      self->table->pending_close = 1;
203
204
205
206
207

      /* NOTE: We don't need to raise a EXC_FINISH_READ here. Only
       * code in a live channel is supposed to raise
       * EXC_FINISH_PENDING. The typical caller is a channel's
       * CHANNEL_CLOSE callback that is called below. */
Niels Möller's avatar
Niels Möller committed
208
209
      break;
      
Niels Möller's avatar
Niels Möller committed
210
211
    case EXC_FINISH_CHANNEL:
      /* NOTE: This type of exception must be handled only once.
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
       * However, there is at least one case where it is difficult to
       * ensure that the exception is raised only once.
       *
       * For instance, in do_channel_close, the CHANNEL_EOF callback
       * can decide to call close_channel, which might raise this
       * exception. When control gets back to do_channel_close, and
       * CHANNEL_SENT_CLOSE is true, it raises the exception again.
       *
       * To get this right, we set a flag when the channel is
       * deallocated. */
      if (self->dead)
	debug("EXC_FINISH_CHANNEL on dead channel.\n");
      else
	{
	  struct ssh_channel *channel
227
	    = self->table->channels[self->channel_number];
228
229
230
231
232
233
234
235

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

	  if (channel->close)
	    CHANNEL_CLOSE(channel);
	
	  KILL_RESOURCE_LIST(channel->resources);
Niels Möller's avatar
Niels Möller committed
236
	
237
	  dealloc_channel(self->table, self->channel_number);
238
239
	  self->dead = 1;

240
241
	  if (self->table->pending_close &&
	      !self->table->channel_count)
242
243
	    {
	      /* FIXME: Send a SSH_DISCONNECT_BY_APPLICATION message? */
244
	      EXCEPTION_RAISE(self->table->e, &finish_read_exception);
245
246
	    }
	}
Niels Möller's avatar
Niels Möller committed
247
248
249
250
251
252
253
      break;
    default:
      EXCEPTION_RAISE(self->super.parent, e);
    }
}

static struct exception_handler *
254
make_exc_finish_channel_handler(struct channel_table *table,
255
				uint32_t channel_number,
256
257
				struct exception_handler *e,
				const char *context)
Niels Möller's avatar
Niels Möller committed
258
259
260
261
{
  NEW(exc_finish_channel_handler, self);
  self->super.parent = e;
  self->super.raise = do_exc_finish_channel_handler;
262
  self->super.context = context;
Niels Möller's avatar
Niels Möller committed
263

264
  self->table = table;
265
  self->channel_number = channel_number;
266
  self->dead = 0;
267
  
Niels Möller's avatar
Niels Möller committed
268
  return &self->super;
Niels Möller's avatar
Niels Möller committed
269
270
271
}
				

272
/* Channel objects */
Niels Möller's avatar
Niels Möller committed
273
274
275
276
277

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

278
struct channel_table *
279
280
make_channel_table(struct abstract_write *write
		   /* , struct exception_handler *e */)
Niels Möller's avatar
Niels Möller committed
281
{
282
  NEW(channel_table, table);
283
284
  table->write = write;
  table->e = NULL; /* FIXME: XXX */
285
286
287
288
289
290

  /* FIXME: Really need this? */
  table->chain = NULL;

  /* FIXME: Use an argument for initialization? */
  table->resources = make_resource_list();
291
  
292
  table->channels = lsh_space_alloc(sizeof(struct ssh_channel *)
293
				      * INITIAL_CHANNELS);
Niels Möller's avatar
Niels Möller committed
294
295
  table->in_use = lsh_space_alloc(INITIAL_CHANNELS);
  
296
297
  table->allocated_channels = INITIAL_CHANNELS;
  table->used_channels = 0;
298
299
300
  table->next_channel = 0;
  table->channel_count = 0;
  
301
  table->max_channels = MAX_CHANNELS;
Niels Möller's avatar
Niels Möller committed
302

Niels Möller's avatar
Niels Möller committed
303
  table->pending_close = 0;
304

305
306
  table->global_requests = make_alist(0, -1);
  table->channel_types = make_alist(0, -1);
307
  table->open_fallback = NULL;
308
  
309
310
  object_queue_init(&table->local_ports);
  object_queue_init(&table->remote_ports);
311
  table->x11_display = NULL;
312
  
313
314
  object_queue_init(&table->active_global_requests);
  object_queue_init(&table->pending_global_requests);
Niels Möller's avatar
Niels Möller committed
315
  
316
  return table;
317
}
Niels Möller's avatar
Niels Möller committed
318
319

/* Returns -1 if allocation fails */
320
321
322
/* NOTE: This function returns locally chosen channel numbers, which
 * are always small integers. So there's no problem fitting them in
 * a signed int. */
323
324
int
alloc_channel(struct channel_table *table)
Niels Möller's avatar
Niels Möller committed
325
{
326
  uint32_t i;
327
  
328
  for(i = table->next_channel; i < table->used_channels; i++)
Niels Möller's avatar
Niels Möller committed
329
    {
330
      if (table->in_use[i] == CHANNEL_FREE)
Niels Möller's avatar
Niels Möller committed
331
	{
Niels Möller's avatar
Niels Möller committed
332
	  assert(!table->channels[i]);
333
	  table->in_use[i] = CHANNEL_RESERVED;
334
	  table->next_channel = i+1;
335

336
	  goto success;
Niels Möller's avatar
Niels Möller committed
337
338
	}
    }
339
  if (i == table->max_channels)
Niels Möller's avatar
Niels Möller committed
340
    return -1;
341

342
  if (i == table->allocated_channels) 
Niels Möller's avatar
Niels Möller committed
343
    {
344
      uint32_t new_size = table->allocated_channels * 2;
Niels Möller's avatar
Niels Möller committed
345
      struct ssh_channel **new_channels;
346
      uint8_t *new_in_use;
Niels Möller's avatar
Niels Möller committed
347

Niels Möller's avatar
Niels Möller committed
348
349
350
      new_channels = lsh_space_alloc(sizeof(struct ssh_channel *)
				     * new_size);
      memcpy(new_channels, table->channels,
351
	     sizeof(struct ssh_channel *) * table->used_channels);
Niels Möller's avatar
Niels Möller committed
352
353
354
      lsh_space_free(table->channels);
      table->channels = new_channels;

355
      /* FIXME: Use realloc(). */
Niels Möller's avatar
Niels Möller committed
356
357
358
359
360
      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;

361
      table->allocated_channels = new_size;
Niels Möller's avatar
Niels Möller committed
362
363
    }

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

366
  table->in_use[i] = CHANNEL_RESERVED;
367
368
  table->channels[i] = NULL;
  
369
370
 success:
  table->channel_count++;
371
  verbose("Allocated local channel number %i\n", i);
372

Niels Möller's avatar
Niels Möller committed
373
374
375
  return i;
}

376
377
void
dealloc_channel(struct channel_table *table, int i)
Niels Möller's avatar
Niels Möller committed
378
379
{
  assert(i >= 0);
380
  assert( (unsigned) i < table->used_channels);
381
382
  assert(table->channel_count);
  
383
  verbose("Deallocating local channel %i\n", i);
384
  table->channels[i] = NULL;
385
  table->in_use[i] = CHANNEL_FREE;
386
387

  table->channel_count--;
Niels Möller's avatar
Niels Möller committed
388
  
389
  if ( (unsigned) i < table->next_channel)
390
391
392
    table->next_channel = i;
}

393
void
394
use_channel(struct channel_table *table,
395
	    uint32_t local_channel_number)
396
397
398
399
400
401
402
403
404
405
{
  struct ssh_channel *channel = table->channels[local_channel_number];

  assert(channel);
  assert(table->in_use[local_channel_number] == CHANNEL_RESERVED);
  
  table->in_use[local_channel_number] = CHANNEL_IN_USE;
  verbose("Taking channel %i in use, (local %i).\n",
	  channel->channel_number, local_channel_number);
}
406

407
void
408
409
register_channel(struct channel_table *table,
		 uint32_t local_channel_number,
410
411
		 struct ssh_channel *channel,
		 int take_into_use)
412
{
413
  assert(table->in_use[local_channel_number] == CHANNEL_RESERVED);
Niels Möller's avatar
Niels Möller committed
414
  assert(!table->channels[local_channel_number]);
415

416
417
418
  verbose("Registering local channel %i.\n",
	  local_channel_number);
  
419
  /* NOTE: Is this the right place to install this exception handler? */
420
  channel->e =
421
    make_exc_finish_channel_handler(table,
422
				    local_channel_number,
423
				    (channel->e ? channel->e
424
				     : table->e),
425
426
427
				    HANDLER_CONTEXT);

  table->channels[local_channel_number] = channel;
428

429
  if (take_into_use)
430
    use_channel(table, local_channel_number);
431
  
432
  remember_resource(table->resources,
433
		    &channel->resources->super);
Niels Möller's avatar
Niels Möller committed
434
435
}

436
struct ssh_channel *
437
lookup_channel(struct channel_table *table, uint32_t i)
438
439
440
441
442
443
444
{
  return ( (i < table->used_channels)
	   && (table->in_use[i] == CHANNEL_IN_USE))
    ? table->channels[i] : NULL;
}

struct ssh_channel *
445
lookup_channel_reserved(struct channel_table *table, uint32_t i)
Niels Möller's avatar
Niels Möller committed
446
{
447
448
  return ( (i < table->used_channels)
	   && (table->in_use[i] == CHANNEL_RESERVED))
449
    ? table->channels[i] : NULL;
Niels Möller's avatar
Niels Möller committed
450
451
}

452
453
/* FIXME: It seems suboptimal to send a window adjust message for
 * *every* write that we do. A better scheme might be as follows:
454
455
456
457
458
459
460
 *
 * 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. */
461
static void
462
adjust_rec_window(struct flow_controlled *f, uint32_t written)
463
{
464
465
  CAST_SUBTYPE(ssh_channel, channel, f);

466
467
468
469
  /* 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)))
470
471
    A_WRITE(channel->table->write,
	    prepare_window_adjust(channel, written));
472
473
}

474
475
void
channel_start_receive(struct ssh_channel *channel,
476
		      uint32_t initial_window_size)
477
{
478
  if (channel->rec_window_size < initial_window_size)
479
    A_WRITE(channel->table->write,
480
481
		    prepare_window_adjust
		    (channel, initial_window_size - channel->rec_window_size));
482
483
}

Niels Möller's avatar
Niels Möller committed
484
/* Channel related messages */
485
486
487

/* GABA:
   (class
488
     (name request_status)
489
490
491
492
493
494
495
     (vars
       ; -1 for still active requests,
       ; 0 for failure,
       ; 1 for success
       (status . int)))
*/

496
497
static struct request_status *
make_request_status(void)
498
{
499
  NEW(request_status, self);
500
501
502
503
504
505
506
  self->status = -1;

  return self;
}

/* GABA:
   (class
507
508
     (name global_request_continuation)
     (super command_continuation)
509
     (vars
510
       (table object channel_table)
511
       (active object request_status)))
512
513
*/

514
static void 
515
send_global_request_responses(struct channel_table *table, 
516
			      struct object_queue *q)
517
{
518
519
520
521
522
523
   for (;;)
     {
       CAST(request_status, n, object_queue_peek_head(q));
       if (!n || (n->status < 0))
	 break;
 
524
      object_queue_remove_head(q);
Niels Möller's avatar
Niels Möller committed
525

526
527
528
529
      A_WRITE(table->write,
	      (n->status
	       ? format_global_success()
	       : format_global_failure()));
530
531
532
    }
}

533
534
535
static void
do_global_request_response(struct command_continuation *s,
			   struct lsh_object *x UNUSED)
536
{
537
  CAST(global_request_continuation, self, s);
538
  struct object_queue *q = &self->table->active_global_requests;
539

540
541
542
543
  assert(self->active->status == -1);
  assert(!object_queue_is_empty(q));
	  
  self->active->status = 1;
544

545
  send_global_request_responses(self->table, q);
546
}
547

548
static struct command_continuation *
549
make_global_request_response(struct channel_table *table,
550
551
552
553
554
			     struct request_status *active)
{
  NEW(global_request_continuation, self);

  self->super.c = do_global_request_response;
555
  self->table = table;
556
557
  self->active = active;
   
558
559
  return &self->super;
}
560
561
562
563
564
565
566


/* GABA:
   (class
     (name global_request_exception_handler)
     (super exception_handler)
     (vars
567
       (table object channel_table)
568
569
570
571
572
       (active object request_status)))
*/

/* NOTE: We handle *only* EXC_GLOBAL_REQUEST */
static void 
573
do_exc_global_request_handler(struct exception_handler *c,
Niels Möller's avatar
Niels Möller committed
574
			      const struct exception *e)
575
576
577
578
{
  CAST(global_request_exception_handler, self, c);
  if (e->type == EXC_GLOBAL_REQUEST)
    {
579
      struct object_queue *q = &self->table->active_global_requests;
580
581
582
583
584
585
      
      assert(self->active->status == -1);
      assert(!object_queue_is_empty(q));

      self->active->status = 0;
  
586
      send_global_request_responses(self->table, q);
587
588
589
590
591
592
    }
  else
    EXCEPTION_RAISE(c->parent, e);
}

static struct exception_handler *
593
make_global_request_exception_handler(struct channel_table *table,
594
595
596
				      struct request_status *active,
				      struct exception_handler *h,
				      const char *context)
Niels Möller's avatar
Niels Möller committed
597
{
598
599
  NEW(global_request_exception_handler, self);

600
  self->super.raise = do_exc_global_request_handler;
601
602
603
  self->super.context = context;
  self->super.parent = h;
  self->active = active;
604
  self->table = table;
605
606
  return &self->super;
}
Niels Möller's avatar
Niels Möller committed
607

608
609
610
void
handle_global_request(struct channel_table *table,
		      struct lsh_string *packet)
611
{
Niels Möller's avatar
Niels Möller committed
612
  struct simple_buffer buffer;
613
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
614
615
616
  int name;
  int want_reply;
  
617
  simple_buffer_init(&buffer, STRING_LD(packet));
Niels Möller's avatar
Niels Möller committed
618
619
620
621
622
623

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_GLOBAL_REQUEST)
      && parse_atom(&buffer, &name)
      && parse_boolean(&buffer, &want_reply))
    {
624
      struct global_request *req = NULL;
625
      struct command_continuation *c = &discard_continuation;
626
      struct exception_handler *e = table->e;
627

628
      if (name && table->global_requests)
629
630
	{
	  CAST_SUBTYPE(global_request, r,
631
		       ALIST_GET(table->global_requests,
632
633
634
635
				 name));
	  req = r;
	}
      if (!req)
636
	{
637
	  A_WRITE(table->write, format_global_failure());
Niels Möller's avatar
Niels Möller committed
638
	  return;
Niels Möller's avatar
Niels Möller committed
639
640
641
642
643
	}
      else
	{
	  if (want_reply)
	    {
644
	      struct request_status *a = make_request_status();
Niels Möller's avatar
Niels Möller committed
645
	      
646
	      object_queue_add_tail(&table->active_global_requests,
Niels Möller's avatar
Niels Möller committed
647
648
				    &a->super);
	      
649
650
	      c = make_global_request_response(table, a);
	      e = make_global_request_exception_handler(table, a, e, HANDLER_CONTEXT);
651
652
653
654
	    }
	  else
	    {
	      /* We should ignore failures. */
655
	      static const struct report_exception_info global_req_ignore =
656
657
658
659
660
		STATIC_REPORT_EXCEPTION_INFO(EXC_ALL, EXC_GLOBAL_REQUEST,
					     "Ignored:");
	      
	      e = make_report_exception_handler(&global_req_ignore,
						e, HANDLER_CONTEXT);
Niels Möller's avatar
Niels Möller committed
661
	    }
662
	  GLOBAL_REQUEST(req, table, name, want_reply, &buffer, c, e);
663
	}
Niels Möller's avatar
Niels Möller committed
664
    }
Niels Möller's avatar
Niels Möller committed
665
  else
666
    PROTOCOL_ERROR(table->e, "Invalid SSH_MSG_GLOBAL_REQUEST message.");
Niels Möller's avatar
Niels Möller committed
667
668
}

669
670
671
void
handle_global_success(struct channel_table *table,
		      struct lsh_string *packet)
672
{
673
  if (lsh_string_length(packet) != 1)
Niels Möller's avatar
Niels Möller committed
674
    {
675
      PROTOCOL_ERROR(table->e, "Invalid GLOBAL_REQUEST_SUCCESS message.");
676
      return;
Niels Möller's avatar
Niels Möller committed
677
    }
678

679
  assert(lsh_string_data(packet)[0] == SSH_MSG_REQUEST_SUCCESS);
680

681
  if (object_queue_is_empty(&table->pending_global_requests))
682
683
    {
      werror("do_global_request_success: Unexpected message, ignoring.\n");
Niels Möller's avatar
Niels Möller committed
684
      return;
685
686
    }
  {
687
    CAST_SUBTYPE(command_context, ctx,
688
689
		 object_queue_remove_head(&table->pending_global_requests));
    COMMAND_RETURN(ctx->c, table);
690
691
692
  }
}

693
694
695
struct exception global_request_exception =
STATIC_EXCEPTION(EXC_GLOBAL_REQUEST, "Global request failed");

696
697
698
void
handle_global_failure(struct channel_table *table,
		      struct lsh_string *packet)
699
{
700
  if (lsh_string_length(packet) != 1)
Niels Möller's avatar
Niels Möller committed
701
    {
702
      PROTOCOL_ERROR(table->e, "Invalid GLOBAL_REQUEST_FAILURE message.");
703
      return;
Niels Möller's avatar
Niels Möller committed
704
    }
705

706
  assert(lsh_string_data(packet)[0] == SSH_MSG_REQUEST_FAILURE);
707

708
  if (object_queue_is_empty(&table->pending_global_requests))
709
710
711
    {
      werror("do_global_request_failure: Unexpected message, ignoring.\n");
    }
Niels Möller's avatar
Niels Möller committed
712
713
714
  else
    {
      CAST_SUBTYPE(command_context, ctx,
715
		   object_queue_remove_head(&table->pending_global_requests));
Niels Möller's avatar
Niels Möller committed
716
717
      EXCEPTION_RAISE(ctx->e, &global_request_exception);
    }
718
719
}

720
721
/* FIXME: Don't store the channel here, instead have it passed as the
 * argument of the continuation. This might also allow some
722
723
724
725
726
 * 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. */
727
728
729
730
731
732
733
734
735
736
737

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

static void
738
send_channel_request_responses(struct ssh_channel *channel,
739
740
741
742
743
744
745
746
747
748
			       struct object_queue *q)
{
  for (;;)
    {
      CAST(request_status, n, object_queue_peek_head(q));
      if (!n || (n->status < 0))
	break;

      object_queue_remove_head(q);

749
750
751
752
      A_WRITE(channel->table->write,
	      (n->status
	       ? format_channel_success(channel->channel_number)
	       : format_channel_failure(channel->channel_number)));
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
    }
}

static void
do_channel_request_response(struct command_continuation *s,
			    struct lsh_object *x UNUSED)
{
  CAST(channel_request_continuation, self, s);
  struct object_queue *q = &self->channel->active_requests;

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

768
  send_channel_request_responses(self->channel, q);
769
770
771
}

static struct command_continuation *
772
make_channel_request_response(struct ssh_channel *channel,
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
			      struct request_status *active)
{
  NEW(channel_request_continuation, self);

  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 
795
796
do_exc_channel_request_handler(struct exception_handler *c,
			       const struct exception *e)
797
798
799
800
801
802
803
804
805
806
807
{
  CAST(channel_request_exception_handler, self, c);
  if (e->type == EXC_CHANNEL_REQUEST)
    {
      struct object_queue *q = &self->channel->active_requests;

      assert(self->active->status == -1);
      assert(!object_queue_is_empty(q));
      
      self->active->status = 0;
      
808
      send_channel_request_responses(self->channel, q);
809
810
811
812
813
814
    }
  else
    EXCEPTION_RAISE(c->parent, e);
}

static struct exception_handler *
815
make_channel_request_exception_handler(struct ssh_channel *channel,
816
817
818
819
820
821
				       struct request_status *active,
				       struct exception_handler *h,
				       const char *context)
{
  NEW(channel_request_exception_handler, self);

822
  self->super.raise = do_exc_channel_request_handler;
823
824
  self->super.parent = h;
  self->super.context = context;
825

826
827
828
829
830
831
  self->channel = channel;
  self->active = active;

  return &self->super;
}

832
833
static int
parse_channel_request(struct simple_buffer *buffer,
834
		      uint32_t *channel_number,
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
		      struct channel_request_info *info)
{
  unsigned msg_number;

  if (parse_uint8(buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_REQUEST)
      && parse_uint32(buffer, channel_number)
      && parse_string(buffer,
		      &info->type_length, &info->type_data)
      && parse_boolean(buffer, &info->want_reply))
    {
      info->type = lookup_atom(info->type_length, info->type_data);
      return 1;
    }
  else
    return 0;
}

853
854
855
void
handle_channel_request(struct channel_table *table,
		       struct lsh_string *packet)
856
857
{
  struct simple_buffer buffer;
858
  struct channel_request_info info;
859
  uint32_t channel_number;
860
  
861
  simple_buffer_init(&buffer, STRING_LD(packet));
862

863
  if (parse_channel_request(&buffer, &channel_number, &info))
864
    {
865
      struct ssh_channel *channel = lookup_channel(table,
866
867
868
869
870
871
872
873
						   channel_number);

      /* 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. */

      if (channel)
	{
874
	  struct channel_request *req = NULL;
875
876
877
	  struct command_continuation *c = &discard_continuation;
	  struct exception_handler *e = channel->e;

878
	  if (info.type && channel->request_types)
879
880
	    {
	      CAST_SUBTYPE(channel_request, r,
881
			   ALIST_GET(channel->request_types, info.type));
882
883
	      req = r;
	    }
884
885
886
	  if (!req)
	    req = channel->request_fallback;
	  
887
	  if (req)
888
	    {
889
	      if (info.want_reply)
890
891
892
893
894
895
		{
		  struct request_status *a = make_request_status();
		  
		  object_queue_add_tail(&channel->active_requests,
					&a->super);
		  
896
897
		  c = make_channel_request_response(channel, a);
		  e = make_channel_request_exception_handler(channel, a, e, HANDLER_CONTEXT);
898
899
900
901
		}
	      else
		{
		  /* We should ignore failures. */
902
903
		  static const struct report_exception_info
		    channel_req_ignore =
904
905
906
907
908
909
910
		    STATIC_REPORT_EXCEPTION_INFO(EXC_ALL, EXC_CHANNEL_REQUEST,
						 "Ignored:");
		  
		  e = make_report_exception_handler(&channel_req_ignore,
						    e, HANDLER_CONTEXT);
		}
	      
911
	      CHANNEL_REQUEST(req, channel, &info, &buffer, c, e);
912
913
914
	    }
	  else
	    {
915
	      if (info.want_reply)
916
917
		A_WRITE(table->write,
			format_channel_failure(channel->channel_number));
918
919
920
921
	    }
	}
      else
	{
922
923
	  werror("SSH_MSG_CHANNEL_REQUEST on nonexistant channel %i: %xS\n",
		 channel_number, packet);
924
925
926
	}
    }
  else
927
    PROTOCOL_ERROR(table->e, "Invalid SSH_MSG_CHANNEL_REQUEST message.");
928
929
930
}


Niels Möller's avatar
Niels Möller committed
931
/* GABA:
932
933
934
935
   (class
     (name channel_open_continuation)
     (super command_continuation)
     (vars
936
       (table object channel_table)
937
938
939
940
       (local_channel_number . uint32_t)
       (remote_channel_number . uint32_t)
       (send_window_size . uint32_t)
       (send_max_packet . uint32_t)))
941
*/
942

Niels Möller's avatar
Niels Möller committed
943
944
945
static void
do_channel_open_continue(struct command_continuation *c,
			 struct lsh_object *value)
946
947
{
  CAST(channel_open_continuation, self, c);
Niels Möller's avatar
Niels Möller committed
948
949
950
951
952
953
954
  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. */
955
956
  channel->send_window_size = self->send_window_size;
  channel->send_max_packet = self->send_max_packet;
Niels Möller's avatar
Niels Möller committed
957
958
  channel->channel_number = self->remote_channel_number;

959
  channel->table = self->table;
960
  
961
  register_channel(self->table, self->local_channel_number,
962
		   channel,
963
		   1);
Niels Möller's avatar
Niels Möller committed
964
965
966
967

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

968
969
970
  A_WRITE(self->table->write,
	  format_open_confirmation(channel,
				   self->local_channel_number, ""));
971
}
Niels Möller's avatar
Niels Möller committed
972

Niels Möller's avatar
Niels Möller committed
973
static struct command_continuation *
974
make_channel_open_continuation(struct channel_table *table,
975
976
977
978
			       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
979
980
{
  NEW(channel_open_continuation, self);
Niels Möller's avatar
Niels Möller committed
981

Niels Möller's avatar
Niels Möller committed
982
  self->super.c = do_channel_open_continue;
983
  self->table = table;
Niels Möller's avatar
Niels Möller committed
984
985
  self->local_channel_number = local_channel_number;
  self->remote_channel_number = remote_channel_number;
986
987
  self->send_window_size = send_window_size;
  self->send_max_packet = send_max_packet;
Niels Möller's avatar
Niels Möller committed
988

Niels Möller's avatar
Niels Möller committed
989
  return &self->super;
Niels Möller's avatar
Niels Möller committed
990
991
992
993
994
995
996
}
			       
/* GABA:
   (class
     (name exc_channel_open_handler)
     (super exception_handler)
     (vars
997
       (table object channel_table)
998
999
       (local_channel_number . uint32_t)
       (remote_channel_number . uint32_t)))
Niels Möller's avatar
Niels Möller committed
1000
1001
*/

1002
1003
1004
static void
do_exc_channel_open_handler(struct exception_handler *s,
			    const struct exception *e)
Niels Möller's avatar
Niels Möller committed
1005
1006
1007
1008
1009
1010
1011
1012
{
  CAST(exc_channel_open_handler, self, s);

  switch (e->type)
    {
    case EXC_CHANNEL_OPEN:
      {
	CAST_SUBTYPE(channel_open_exception, exc, e);
1013
	struct channel_table *table = self->table;
Niels Möller's avatar
Niels Möller committed
1014
1015
1016
1017
1018
1019
	
	assert(table->in_use[self->local_channel_number]);
	assert(!table->channels[self->local_channel_number]);

	dealloc_channel(table, self->local_channel_number);
	
1020
1021
1022
        A_WRITE(table->write,
		format_open_failure(self->remote_channel_number,
				    exc->error_code, e->msg, ""));
Niels Möller's avatar
Niels Möller committed
1023
1024
1025
1026
1027
1028
1029
1030
	break;
      }
    default:
      EXCEPTION_RAISE(self->super.parent, e);
    }      
}

static struct exception_handler *
1031
make_exc_channel_open_handler(struct channel_table *table,
1032