channel.c 33.7 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
22
23
24
25
26
27
/* 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
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#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
35
#include "service.h"
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

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

38
39
40
41
#define CLASS_DEFINE
#include "channel.h.x"
#undef CLASS_DEFINE

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "channel.c.x"

/* CLASS:
   (class
     (name connection_service)
     (super ssh_service)
     (vars
       ; Supported global requests 
       (global_requests object alist)

       (channel_types object alist)

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

       (start object connection_startup)))
*/

60
61
/* FIXME: Perhaps the channel table should be installed in the
 * connection object instead? */
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* CLASS:
   (class
     (name channel_handler)
     (super packet_handler)
     (vars
       (table object channel_table)))
*/

/* CLASS:
   (class
     (name global_request_handler)
     (super channel_handler super)
     (vars
       (global_requests object alist)))
*/

/* CLASS:
   (class
     (name channel_open_handler)
     (super channel_handler)
     (vars
       (channel_types object alist)))
*/

Niels Möller's avatar
Niels Möller committed
86
87
88
89
90
struct lsh_string *format_global_failure(void)
{
  return ssh_format("%c", SSH_MSG_REQUEST_FAILURE);
}

91
92
struct lsh_string *format_open_confirmation(struct ssh_channel *channel,
					    UINT32 channel_number,
93
					    const char *format, ...)
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
{
  va_list args;
  UINT32 l1, l2;
  struct lsh_string *packet;

#define CONFIRM_FORMAT "%c%i%i%i%i"
#define CONFIRM_ARGS SSH_MSG_CHANNEL_OPEN_CONFIRMATION, channel->channel_number, \
  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);
114
  ssh_vformat_write(format, l2, packet->data+l1, args);
115
116
117
118
119
120
121
  va_end(args);

  return packet;
#undef CONFIRM_FORMAT
#undef CONFIRM_ARGS
}

Niels Möller's avatar
Niels Möller committed
122
struct lsh_string *format_open_failure(UINT32 channel, UINT32 reason,
123
				       const char *msg, const char *language)
Niels Möller's avatar
Niels Möller committed
124
125
126
127
128
{
  return ssh_format("%c%i%i%z%z", SSH_MSG_CHANNEL_OPEN_FAILURE,
		    channel, reason, msg, language);
}

129
130
131
132
133
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
134
135
136
137
138
struct lsh_string *format_channel_failure(UINT32 channel)
{
  return ssh_format("%c%i", SSH_MSG_CHANNEL_FAILURE, channel);
}

139
140
141
142
143
144
145
146
147
148
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);
}

149
/* Channel objects */
Niels Möller's avatar
Niels Möller committed
150
151
152
153
154

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

Niels Möller's avatar
Niels Möller committed
155
struct channel_table *make_channel_table(void)
Niels Möller's avatar
Niels Möller committed
156
{
157
  NEW(channel_table, table);
Niels Möller's avatar
Niels Möller committed
158

159
  table->channels = lsh_space_alloc(sizeof(struct ssh_channel *)
160
				      * INITIAL_CHANNELS);
161
162
163
164
  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
165

Niels Möller's avatar
Niels Möller committed
166
167
  table->pending_close = 0;
  
168
  return table;
Niels Möller's avatar
Niels Möller committed
169
170
171
};

/* Returns -1 if allocation fails */
172
173
174
/* NOTE: This function returns locally chosen channel numbers, which
 * are always small integers. So there's no problem fitting them in
 * a signed int. */
175
int alloc_channel(struct channel_table *table)
Niels Möller's avatar
Niels Möller committed
176
{
177
  UINT32 i;
178
  
179
  for(i = table->next_channel; i < table->used_channels; i++)
Niels Möller's avatar
Niels Möller committed
180
    {
181
      if (!table->channels[i])
Niels Möller's avatar
Niels Möller committed
182
	{
183
	  table->next_channel = i+1;
Niels Möller's avatar
Niels Möller committed
184
185
186
	  return i;
	}
    }
187
  if (i == table->max_channels)
Niels Möller's avatar
Niels Möller committed
188
    return -1;
189

190
  if (i == table->allocated_channels) 
Niels Möller's avatar
Niels Möller committed
191
    {
192
      int new_size = table->allocated_channels * 2;
Niels Möller's avatar
Niels Möller committed
193
      struct ssh_channel **new
194
	= lsh_space_alloc(sizeof(struct ssh_channel *) * new_size);
Niels Möller's avatar
Niels Möller committed
195

196
197
      memcpy(new, table->channels,
	     sizeof(struct ssh_channel *) * table->used_channels);
Niels Möller's avatar
Niels Möller committed
198
      
199
200
      table->channels = new;
      table->allocated_channels = new_size;
Niels Möller's avatar
Niels Möller committed
201
202
    }

203
  table->next_channel = table->used_channels = i+1;
Niels Möller's avatar
Niels Möller committed
204
205
206
207

  return i;
}

208
void dealloc_channel(struct channel_table *table, int i)
Niels Möller's avatar
Niels Möller committed
209
210
{
  assert(i >= 0);
211
  assert( (unsigned) i < table->used_channels);
Niels Möller's avatar
Niels Möller committed
212
  
213
214
  table->channels[i] = NULL;

215
  if ( (unsigned) i < table->next_channel)
216
217
218
    table->next_channel = i;
}

219
220
/* Returns -1 if no channel number can be allocated. See also the note
 * for alloc_channel(). */
221
222
223
224
225
226
int register_channel(struct channel_table *table, struct ssh_channel *channel)
{
  int n = alloc_channel(table);

  if (n >= 0)
    table->channels[n] = channel;
Niels Möller's avatar
Niels Möller committed
227

228
  return n;
Niels Möller's avatar
Niels Möller committed
229
230
}

231
struct ssh_channel *lookup_channel(struct channel_table *table, UINT32 i)
Niels Möller's avatar
Niels Möller committed
232
{
233
234
  return (i < table->used_channels)
    ? table->channels[i] : NULL;
Niels Möller's avatar
Niels Möller committed
235
236
}

237
238
239
240
241
242
243
244
245
246
247
248
249
static int adjust_rec_window(struct ssh_channel *channel)
{
  if (channel->rec_window_size < channel->max_window / 2)
    {
      int increment = channel->max_window - channel->rec_window_size;
      channel->rec_window_size = channel->max_window;
      
      return A_WRITE(channel->write,
		     prepare_window_adjust(channel, increment));
    }
  return 0;
}

Niels Möller's avatar
Niels Möller committed
250
251
252
253
static int channel_process_status(struct channel_table *table,
				  int channel,
				  int status)
{
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  struct ssh_channel *c = table->channels[channel];
  
  while (!LSH_CLOSEDP(status))
    {
      if (status & LSH_CHANNEL_READY_SEND)
	{
	  status &= ~ LSH_CHANNEL_READY_SEND;
	  if (c->send_window_size)
	    status |= CHANNEL_SEND(c);
	}
      else if (status & LSH_CHANNEL_READY_REC)
	{
	  status &= ~ LSH_CHANNEL_READY_REC;
	  status |= adjust_rec_window(c);
	}
      else
	break;
    }
	
Niels Möller's avatar
Niels Möller committed
273
274
275
276
277
278
279
  if (status & LSH_CHANNEL_PENDING_CLOSE)
    table->pending_close = 1;
  
  if (status & LSH_CHANNEL_FINISHED)
    {
      /* Clear this bit */
      status &= ~LSH_CHANNEL_FINISHED;
280

281
282
      if (c->close)
	CHANNEL_CLOSE(c);
Niels Möller's avatar
Niels Möller committed
283
284
285
286
287
288
289
290
291
292
      
      dealloc_channel(table, channel);

      /* If this was the last channel, close connection */
      if (table->pending_close && !table->next_channel)
	status |= LSH_CLOSE;
    }

  return status;
}
Niels Möller's avatar
Niels Möller committed
293

Niels Möller's avatar
Niels Möller committed
294
/* Channel related messages */
Niels Möller's avatar
Niels Möller committed
295
296
297
298
static int do_global_request(struct packet_handler *c,
			     struct ssh_connection *connection,
			     struct lsh_string *packet)
{
299
  CAST(global_request_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

  struct simple_buffer buffer;
  int msg_number;
  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;

      lsh_string_free(packet);
      
      if (!name || !(req = ALIST_GET(closure->global_requests, name)))
	return A_WRITE(connection->write,
		       format_global_failure());

321
      return GLOBAL_REQUEST(req, connection, want_reply, &buffer);
Niels Möller's avatar
Niels Möller committed
322
323
324
325
326
327
328
329
330
331
    }
  lsh_string_free(packet);

  return LSH_FAIL | LSH_DIE;
}

static int do_channel_open(struct packet_handler *c,
			   struct ssh_connection *connection,
			   struct lsh_string *packet)
{
332
  CAST(channel_open_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
333
334
335
336

  struct simple_buffer buffer;
  int msg_number;
  int type;
337
338
339
  UINT32 remote_channel_number;
  UINT32 window_size;
  UINT32 max_packet;
Niels Möller's avatar
Niels Möller committed
340
341
342
343
344
345
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_OPEN)
      && parse_atom(&buffer, &type)
346
347
348
      && 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
349
350
    {
      struct channel_open *open;
351
352
353
354
355
356
357
      struct ssh_channel *channel;
      UINT32 error = 0;
      char *error_msg;
      struct lsh_string *args = NULL;
      
      int local_channel_number;
      
Niels Möller's avatar
Niels Möller committed
358
      lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
359
360
361
362
363
364
365

      if (closure->super.table->pending_close)
	/* We are waiting for channels to close. Don't open any new ones. */
	return A_WRITE(connection->write,
		       format_open_failure(remote_channel_number,
					   SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
					   "Waiting for channels to close.", ""));
Niels Möller's avatar
Niels Möller committed
366
367
368
      
      if (!type || !(open = ALIST_GET(closure->channel_types, type)))
	return A_WRITE(connection->write,
369
		       format_open_failure(remote_channel_number,
Niels Möller's avatar
Niels Möller committed
370
371
					   SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
					   "Unknown channel type", ""));
372
      
373
374
      channel = CHANNEL_OPEN(open, connection, &buffer,
			     &error, &error_msg, &args);
375
376
377
378
379
380
381
382
383
384
385
386
387
388
      
      if (!channel)
	{
	  if (error)
	    return A_WRITE(connection->write,
			   format_open_failure(remote_channel_number,
					       error, error_msg, ""));
	  /* The request was invalid */
	  return LSH_FAIL | LSH_DIE;
	}

      if ( (local_channel_number
	    = register_channel(closure->super.table, channel)) < 0)
	{
389
	  wwrite("Could not allocate a channel number for pened channel!\n");
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
	  return A_WRITE(connection->write,
			 format_open_failure(remote_channel_number,
					     SSH_OPEN_RESOURCE_SHORTAGE,
					     "Could not allocate a channel number "
					     "(shouldn't happen...)", ""));
	}
      
      channel->send_window_size = window_size;
      channel->send_max_packet = max_packet;
      channel->channel_number = remote_channel_number;

      channel->write = connection->write;

      return A_WRITE(connection->write,
		     args
		     ? format_open_confirmation(channel, local_channel_number,
						"%lfS", args)
Niels Möller's avatar
Niels Möller committed
407
408
		     : format_open_confirmation(channel, local_channel_number,
						""));
Niels Möller's avatar
Niels Möller committed
409
410
411
412
413
414
415
416
417
418
    }
  lsh_string_free(packet);

  return LSH_FAIL | LSH_DIE;
}     

static int do_channel_request(struct packet_handler *c,
			      struct ssh_connection *connection,
			      struct lsh_string *packet)
{
419
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

  struct simple_buffer buffer;
  int msg_number;
  UINT32 channel_number;
  int type;
  int want_reply;

  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))
    {
435
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
436
437
438
439
440
441
442
443
						   channel_number);

      lsh_string_free(packet);
      
      if (channel)
	{
	  struct channel_request *req;

444
445
	  if (type && channel->request_types 
	      && ( (req = ALIST_GET(channel->request_types, type)) ))
Niels Möller's avatar
Niels Möller committed
446
447
	    return channel_process_status
	      (closure->table, channel_number,
448
	       CHANNEL_REQUEST(req, channel, connection, want_reply, &buffer));
449
	  else
450
451
452
453
454
	    return want_reply
	      ? A_WRITE(connection->write,
			format_channel_failure(channel->channel_number))
	      : LSH_OK | LSH_GOON;
	  
Niels Möller's avatar
Niels Möller committed
455
456
457
458
459
460
461
462
463
464
465
	}
      werror("SSH_MSG_CHANNEL_REQUEST on nonexistant channel %d\n",
	     channel_number);
      return LSH_FAIL | LSH_DIE;
    }
  lsh_string_free(packet);

  return LSH_FAIL | LSH_DIE;
}
      
static int do_window_adjust(struct packet_handler *c,
466
			    struct ssh_connection *connection UNUSED,
467
			    struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
468
{
469
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
470
471
472
473
474
475
476
477
478
479

  struct simple_buffer buffer;
  int msg_number;
  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)
480
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
481
482
483
      && parse_uint32(&buffer, &size)
      && parse_eod(&buffer))
    {
484
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
485
486
487
488
						   channel_number);

      lsh_string_free(packet);
      
Niels Möller's avatar
Niels Möller committed
489
      if (channel
490
491
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF
				 | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
492
	{
Niels Möller's avatar
Niels Möller committed
493
494
495
496
	  if (! (channel->flags & CHANNEL_SENT_CLOSE))
	    {
	      channel->send_window_size += size;
	      if (channel->send_window_size && channel->send)
Niels Möller's avatar
Niels Möller committed
497
498
499
		return channel_process_status(closure->table,
					      channel_number,
					      CHANNEL_SEND(channel));
Niels Möller's avatar
Niels Möller committed
500
	    }
Niels Möller's avatar
Niels Möller committed
501
502
503
504
	  return LSH_OK | LSH_GOON;
	}
      /* FIXME: What to do now? Should unknown channel numbers be
       * ignored silently? */
Niels Möller's avatar
Niels Möller committed
505
      werror("SSH_MSG_CHANNEL_WINDOW_ADJUST on nonexistant or closed channel %d\n",
Niels Möller's avatar
Niels Möller committed
506
507
508
509
510
511
512
513
514
	     channel_number);
      return LSH_FAIL | LSH_DIE;
    }
  lsh_string_free(packet);

  return LSH_FAIL | LSH_DIE;
}

static int do_channel_data(struct packet_handler *c,
515
			   struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
516
517
			   struct lsh_string *packet)
{
518
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
519
520
521
522
523
524
525
526
527
528

  struct simple_buffer buffer;
  int msg_number;
  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)
529
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
530
531
532
      && ( (data = parse_string_copy(&buffer)) )
      && parse_eod(&buffer))
    {
533
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
534
535
536
537
						   channel_number);

      lsh_string_free(packet);
      
538
539
      if (channel && channel->receive
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
540
	{
Niels Möller's avatar
Niels Möller committed
541
	  if (channel->flags & CHANNEL_SENT_CLOSE)
Niels Möller's avatar
Niels Möller committed
542
	    {
543
	      wwrite("Ignoring data on channel which is closing\n");
Niels Möller's avatar
Niels Möller committed
544
	      return LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
545
	    }
Niels Möller's avatar
Niels Möller committed
546
547
	  else
	    {
548
549
	      int res = 0;
	      
Niels Möller's avatar
Niels Möller committed
550
551
552
	      if (data->length > channel->rec_window_size)
		{
		  /* Truncate data to fit window */
553
		  wwrite("Channel data overflow. Extra data ignored.\n"); 
Niels Möller's avatar
Niels Möller committed
554
555
		  data->length = channel->rec_window_size;
		}
556
557
558
559

	      if (!data->length)
		/* Ignore data packet */
		return 0;
560
561
	      channel->rec_window_size -= data->length;

562
	      /* FIXME: Unconditionally adjusting the receive window
563
	       * breaks flow control. We better let the channel's
564
	       * receive method decide whether or not to receive more
565
566
567
	       * data. */
	      res = adjust_rec_window(channel);
	      
568
569
570
571
572
573
574
575
576
	      if (channel->rec_window_size < channel->max_window / 2)
		{
		  res = A_WRITE(channel->write, prepare_window_adjust
				(channel,
				 channel->max_window - channel->rec_window_size));
		  if (LSH_CLOSEDP(res))
		    return res;
		}

Niels Möller's avatar
Niels Möller committed
577
578
	      return channel_process_status(
		closure->table, channel_number,
579
		res | CHANNEL_RECEIVE(channel, 
Niels Möller's avatar
Niels Möller committed
580
				      CHANNEL_DATA, data));
Niels Möller's avatar
Niels Möller committed
581
582
	    }
	  return LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
583
584
585
586
587
588
589
590
591
592
593
594
595
	}
	  
      werror("Data on closed or non-existant channel %d\n",
	     channel_number);
      lsh_string_free(data);
      return LSH_FAIL | LSH_DIE;
    }
  lsh_string_free(packet);
  
  return LSH_FAIL | LSH_DIE;
}

static int do_channel_extended_data(struct packet_handler *c,
596
				    struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
597
598
				    struct lsh_string *packet)
{
599
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
600
601
602
603
604
605
606
607
608
609
610

  struct simple_buffer buffer;
  int msg_number;
  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)
611
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
612
613
614
615
      && parse_uint32(&buffer, &type)
      && ( (data = parse_string_copy(&buffer)) )
      && parse_eod(&buffer))
    {
616
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
617
618
619
620
						   channel_number);

      lsh_string_free(packet);
      
621
622
      if (channel && channel->receive
	  && !(channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_RECEIVED_CLOSE)))
Niels Möller's avatar
Niels Möller committed
623
	{
Niels Möller's avatar
Niels Möller committed
624
	  if (channel->flags & CHANNEL_SENT_CLOSE)
Niels Möller's avatar
Niels Möller committed
625
	    {
626
	      wwrite("Ignoring extended data on channel which is closing\n");
Niels Möller's avatar
Niels Möller committed
627
	      return LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
628
	    }
Niels Möller's avatar
Niels Möller committed
629
	  else
Niels Möller's avatar
Niels Möller committed
630
	    {
631
632
	      int res = 0;
	      
Niels Möller's avatar
Niels Möller committed
633
634
635
	      if (data->length > channel->rec_window_size)
		{
		  /* Truncate data to fit window */
636
		  wwrite("Channel extended data overflow. "
Niels Möller's avatar
Niels Möller committed
637
638
639
640
641
			 "Extra data ignored.\n");
		  data->length = channel->rec_window_size;
		}
	      
	      channel->rec_window_size -= data->length;
642
643
644
645
646
647
648
649
650
651

	      if (channel->rec_window_size < channel->max_window / 2)
		{
		  res = A_WRITE(channel->write, prepare_window_adjust
				(channel,
				 channel->max_window - channel->rec_window_size));
		  if (LSH_CLOSEDP(res))
		    return res;
		}

Niels Möller's avatar
Niels Möller committed
652
653
654
	      switch(type)
		{
		case SSH_EXTENDED_DATA_STDERR:
Niels Möller's avatar
Niels Möller committed
655
656
		  return channel_process_status(
		    closure->table, channel_number,
657
		    res | CHANNEL_RECEIVE(channel, 
Niels Möller's avatar
Niels Möller committed
658
					  CHANNEL_STDERR_DATA, data));
Niels Möller's avatar
Niels Möller committed
659
660
661
662
663
664
		default:
		  werror("Unknown type %d of extended data.\n",
			 type);
		  lsh_string_free(data);
		  return LSH_FAIL | LSH_DIE;
		}
Niels Möller's avatar
Niels Möller committed
665
666
667
668
669
670
671
672
673
674
675
676
677
	    }
	}
      werror("Extended data on closed or non-existant channel %d\n",
	     channel_number);
      lsh_string_free(data);
      return LSH_FAIL | LSH_DIE;
    }
  lsh_string_free(packet);
  
  return LSH_FAIL | LSH_DIE;
}

static int do_channel_eof(struct packet_handler *c,
678
			  struct ssh_connection *connection UNUSED,
679
			  struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
680
{
681
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
682
683
684
685
686
687
688
689
690

  struct simple_buffer buffer;
  int msg_number;
  UINT32 channel_number;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_EOF)
691
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
692
693
      && parse_eod(&buffer))
    {
694
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
695
696
697
698
						   channel_number);

      lsh_string_free(packet);

Niels Möller's avatar
Niels Möller committed
699
700
      if (channel)
	{
701
702
	  int res = 0;
	  
703
	  if (channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_RECEIVED_CLOSE))
Niels Möller's avatar
Niels Möller committed
704
	    {
705
	      wwrite("Receiving EOF on channel on closed channel.\n");
Niels Möller's avatar
Niels Möller committed
706
707
708
	      return LSH_FAIL | LSH_DIE;
	    }

709
	  channel->flags |= CHANNEL_RECEIVED_EOF;
Niels Möller's avatar
Niels Möller committed
710

711
712
	  if (channel->eof)
	    res = CHANNEL_EOF(channel);
713
714
715
	  else
	    /* FIXME: What is a reasonable default behaviour?
	     * Closing the channel may be the right thing to do. */
716
717
718
	    if (! (channel->flags & CHANNEL_SENT_CLOSE))
	      res |= channel_close(channel);
#if 0
719
720
721
	  if (!LSH_CLOSEDP(res)
	      && ! (channel->flags & CHANNEL_SENT_CLOSE)
	      && (channel->flags & CHANNEL_SENT_EOF))
Niels Möller's avatar
Niels Möller committed
722
723
724
	    {
	      /* Both parties have sent EOF. Initiate close, if we
	       * havn't done that already. */
725
726
	      
	      res |= channel_close(channel);
Niels Möller's avatar
Niels Möller committed
727
	    }
728
#endif      
729
730
731
	  return channel_process_status(closure->table, channel_number,
					res);

Niels Möller's avatar
Niels Möller committed
732
733
734
735
	}
      werror("EOF on non-existant channel %d\n",
	     channel_number);
      return LSH_FAIL | LSH_DIE;
Niels Möller's avatar
Niels Möller committed
736
    }
Niels Möller's avatar
Niels Möller committed
737
      
Niels Möller's avatar
Niels Möller committed
738
739
740
741
742
  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

static int do_channel_close(struct packet_handler *c,
743
			    struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
744
745
			    struct lsh_string *packet)
{
746
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
747
748
749
750
751
752
753
754
755

  struct simple_buffer buffer;
  int msg_number;
  UINT32 channel_number;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_CLOSE)
756
      && parse_uint32(&buffer, &channel_number)
Niels Möller's avatar
Niels Möller committed
757
758
      && parse_eod(&buffer))
    {
759
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
760
761
762
763
						   channel_number);

      lsh_string_free(packet);
      
Niels Möller's avatar
Niels Möller committed
764
765
      if (channel)
	{
766
767
	  int res = 0;
	  
768
	  if (channel->flags & CHANNEL_RECEIVED_CLOSE)
Niels Möller's avatar
Niels Möller committed
769
	    {
770
	      wwrite("Receiving multiple CLOSE on channel.\n");
Niels Möller's avatar
Niels Möller committed
771
772
773
	      return LSH_FAIL | LSH_DIE;
	    }

774
	  channel->flags |= CHANNEL_RECEIVED_CLOSE;
Niels Möller's avatar
Niels Möller committed
775
	  
776
	  if (! (channel->flags & (CHANNEL_RECEIVED_EOF | CHANNEL_SENT_EOF)))
Niels Möller's avatar
Niels Möller committed
777
	    {
778
	      wwrite("Unexpected channel CLOSE.\n");
Niels Möller's avatar
Niels Möller committed
779
	    }
780

781
	  if (! (channel->flags & (CHANNEL_RECEIVED_EOF))
782
	      && channel->eof)
783
	    res = CHANNEL_EOF(channel);
Niels Möller's avatar
Niels Möller committed
784
	  
Niels Möller's avatar
Niels Möller committed
785
786
	  return channel_process_status(
	    closure->table, channel_number,
787
788
789
790
	    ( ( (channel->flags & (CHANNEL_SENT_CLOSE))
		? LSH_OK | LSH_CHANNEL_FINISHED
		: channel_close(channel))
	      | res));
Niels Möller's avatar
Niels Möller committed
791
792
793
794
	}
      werror("CLOSE on non-existant channel %d\n",
	     channel_number);
      return LSH_FAIL | LSH_DIE;
Niels Möller's avatar
Niels Möller committed
795
796
797
798
799
800
      
    }
  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

801
static int do_channel_open_confirm(struct packet_handler *c,
802
			      struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
803
804
			      struct lsh_string *packet)
{
805
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823

  struct simple_buffer buffer;
  int msg_number;
  UINT32 local_channel_number;
  UINT32 remote_channel_number;  
  UINT32 window_size;
  UINT32 max_packet;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_OPEN_CONFIRMATION)
      && parse_uint32(&buffer, &local_channel_number)
      && parse_uint32(&buffer, &remote_channel_number)
      && parse_uint32(&buffer, &window_size)
      && parse_uint32(&buffer, &max_packet)
      && parse_eod(&buffer))
    {
824
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
825
826
827
828
						   local_channel_number);

      lsh_string_free(packet);

829
      if (channel && channel->open_confirm)
Niels Möller's avatar
Niels Möller committed
830
831
832
833
834
	{
	  channel->channel_number = remote_channel_number;
	  channel->send_window_size = window_size;
	  channel->send_max_packet = max_packet;

Niels Möller's avatar
Niels Möller committed
835
836
	  return channel_process_status(closure->table, local_channel_number,
					CHANNEL_OPEN_CONFIRM(channel));
Niels Möller's avatar
Niels Möller committed
837
838
839
840
841
842
843
844
845
	}
      werror("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION on channel %d\n",
	     local_channel_number);
      return LSH_FAIL | LSH_DIE;
    }
  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

846
static int do_channel_open_failure(struct packet_handler *c,
847
			      struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
848
849
			      struct lsh_string *packet)
{
850
  CAST(channel_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872

  struct simple_buffer buffer;
  int msg_number;
  UINT32 channel_number;
  UINT32 reason;

  UINT8 *msg;
  UINT32 length;

  UINT8 *language;
  UINT32 language_length;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_OPEN_FAILURE)
      && parse_uint32(&buffer, &channel_number)
      && parse_uint32(&buffer, &reason)
      && parse_string(&buffer, &length, &msg)
      && parse_string(&buffer, &language_length, &language)
      && parse_eod(&buffer))
    {
873
      struct ssh_channel *channel = lookup_channel(closure->table,
Niels Möller's avatar
Niels Möller committed
874
875
876
877
						   channel_number);

      /* lsh_string_free(packet); */

878
      if (channel && channel->open_failure)
Niels Möller's avatar
Niels Möller committed
879
	{
880
	  int res = CHANNEL_OPEN_FAILURE(channel);
Niels Möller's avatar
Niels Möller committed
881
882

	  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
883
884
885

	  return channel_process_status(closure->table, channel_number,
					res | LSH_CHANNEL_FINISHED);
Niels Möller's avatar
Niels Möller committed
886
887
888
889
890
891
892
893
894
895
896
	}
      werror("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE on channel %d\n",
	     channel_number);
      lsh_string_free(packet);
      
      return LSH_FAIL | LSH_DIE;
    }
  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

897
static int do_channel_success(struct packet_handler *c,
898
			      struct ssh_connection *connection UNUSED,
899
900
			      struct lsh_string *packet)
{
901
  CAST(channel_handler, closure, c);
902
903
904
905
906
907
908
909
910

  struct simple_buffer buffer;
  int msg_number;
  UINT32 channel_number;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_SUCCESS)
911
      && parse_uint32(&buffer, &channel_number)
912
913
914
915
916
917
918
      && parse_eod(&buffer))
    {
      struct ssh_channel *channel = lookup_channel(closure->table,
						   channel_number);

      lsh_string_free(packet);
      
Niels Möller's avatar
Niels Möller committed
919
      if (channel && channel->channel_success)
Niels Möller's avatar
Niels Möller committed
920
921
	return channel_process_status(closure->table, channel_number,
				      CHANNEL_SUCCESS(channel));
922
923
924
925
926
927
    }
  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

static int do_channel_failure(struct packet_handler *c,
928
			      struct ssh_connection *connection UNUSED,
929
930
			      struct lsh_string *packet)
{
931
  CAST(channel_handler, closure, c);
932
933
934
935
936
937
938
939
940

  struct simple_buffer buffer;
  int msg_number;
  UINT32 channel_number;
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_CHANNEL_FAILURE)
941
      && parse_uint32(&buffer, &channel_number)
942
943
944
945
946
947
948
      && parse_eod(&buffer))
    {
      struct ssh_channel *channel = lookup_channel(closure->table,
						   channel_number);

      lsh_string_free(packet);
      
Niels Möller's avatar
Niels Möller committed
949
      if (channel && channel->channel_failure)
Niels Möller's avatar
Niels Möller committed
950
951
	return channel_process_status(closure->table, channel_number,
				      CHANNEL_FAILURE(channel));
952
953
954
955
956
    }
  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

957
static int init_connection_service(struct ssh_service *s,
Niels Möller's avatar
Niels Möller committed
958
				   struct ssh_connection *connection)
Niels Möller's avatar
Niels Möller committed
959
{
960
961
  CAST(connection_service, self, s);

962
  struct channel_table *table;
Niels Möller's avatar
Niels Möller committed
963
  
964
965
966
  NEW(global_request_handler, globals);
  NEW(channel_open_handler, open);
  NEW(channel_handler, request);
Niels Möller's avatar
Niels Möller committed
967

968
969
970
  NEW(channel_handler, adjust);
  NEW(channel_handler, data);
  NEW(channel_handler, extended);
Niels Möller's avatar
Niels Möller committed
971

972
973
  NEW(channel_handler, eof);
  NEW(channel_handler, close);
974

975
976
  NEW(channel_handler, open_confirm);
  NEW(channel_handler, open_failure);
Niels Möller's avatar
Niels Möller committed
977

978
979
  NEW(channel_handler, channel_success);
  NEW(channel_handler, channel_failure);
Niels Möller's avatar
Niels Möller committed
980

981
  table = make_channel_table();
Niels Möller's avatar
Niels Möller committed
982
983
  
  globals->super.super.handler = do_global_request;
984
  globals->super.table = table;
Niels Möller's avatar
Niels Möller committed
985
986
987
988
  globals->global_requests = self->global_requests;
  connection->dispatch[SSH_MSG_GLOBAL_REQUEST] = &globals->super.super;
    
  open->super.super.handler = do_channel_open;
989
  open->super.table = table;
Niels Möller's avatar
Niels Möller committed
990
991
992
993
  open->channel_types = self->channel_types;
  connection->dispatch[SSH_MSG_CHANNEL_OPEN] = &open->super.super;

  request->super.handler = do_channel_request;
994
  request->table = table;
Niels Möller's avatar
Niels Möller committed
995
996
997
  connection->dispatch[SSH_MSG_CHANNEL_REQUEST] = &request->super;
  
  adjust->super.handler = do_window_adjust;
998
  adjust->table = table;
Niels Möller's avatar
Niels Möller committed
999
1000
1001
  connection->dispatch[SSH_MSG_CHANNEL_WINDOW_ADJUST] = &adjust->super;

  data->super.handler = do_channel_data;
1002
  data->table = table;
Niels Möller's avatar
Niels Möller committed
1003
1004
1005
  connection->dispatch[SSH_MSG_CHANNEL_DATA] = &data->super;

  extended->super.handler = do_channel_extended_data;
1006
  extended->table = table;
1007
  connection->dispatch[SSH_MSG_CHANNEL_EXTENDED_DATA] = &extended->super;
Niels Möller's avatar
Niels Möller committed
1008
1009

  eof->super.handler = do_channel_eof;
1010
  eof->table = table;
Niels Möller's avatar
Niels Möller committed
1011
1012
1013
  connection->dispatch[SSH_MSG_CHANNEL_EOF] = &eof->super;

  close->super.handler = do_channel_close;
1014
  close->table = table;
Niels Möller's avatar
Niels Möller committed
1015
1016
  connection->dispatch[SSH_MSG_CHANNEL_CLOSE] = &close->super;

1017
1018
1019
  open_confirm->super.handler = do_channel_open_confirm;
  open_confirm->table = table;
  connection->dispatch[SSH_MSG_CHANNEL_OPEN_CONFIRMATION] = &open_confirm->super;
Niels Möller's avatar
Niels Möller committed
1020

1021
1022
1023
  open_failure->super.handler = do_channel_open_failure;
  open_failure->table = table;
  connection->dispatch[SSH_MSG_CHANNEL_OPEN_FAILURE] = &open_failure->super;
Niels Möller's avatar
Niels Möller committed
1024
  
1025
1026
1027
1028
1029
1030
1031
1032
  channel_success->super.handler = do_channel_success;
  channel_success->table = table;
  connection->dispatch[SSH_MSG_CHANNEL_SUCCESS] = &channel_success->super;

  channel_failure->super.handler = do_channel_failure;
  channel_failure->table = table;
  connection->dispatch[SSH_MSG_CHANNEL_FAILURE] = &channel_failure->super;
    
Niels Möller's avatar
Niels Möller committed
1033
1034
  return self->start
    ? CONNECTION_START(self->start, table, connection->write)
1035
    : LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
1036
1037
}

1038
1039
1040
struct ssh_service *make_connection_service(struct alist *global_requests,
					    struct alist *channel_types,
					    struct connection_startup *start)
1041
{
1042
  NEW(connection_service, self);
1043

1044
1045
1046
1047
1048
1049
  self->super.init = init_connection_service;
  self->global_requests = global_requests;
  self->channel_types = channel_types;
  self->start = start;
  
  return &self->super;
1050
1051
}

Niels Möller's avatar
Niels Möller committed
1052
1053
1054
1055
1056
1057
1058
1059
1060
struct lsh_string *format_channel_close(struct ssh_channel *channel)
{
  return ssh_format("%c%i",
		    SSH_MSG_CHANNEL_CLOSE,
		    channel->channel_number);
}

int channel_close(struct ssh_channel *channel)
{
1061
1062
  assert(! (channel->flags & CHANNEL_SENT_CLOSE));
  
Niels Möller's avatar
Niels Möller committed
1063
1064
1065
  channel->flags |= CHANNEL_SENT_CLOSE;

  return A_WRITE(channel->write, format_channel_close(channel))
1066
    | ( (channel->flags & CHANNEL_RECEIVED_CLOSE)
Niels Möller's avatar
Niels Möller committed
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
	? LSH_CHANNEL_FINISHED : 0);
}

struct lsh_string *format_channel_eof(struct ssh_channel *channel)
{
  return ssh_format("%c%i",
		    SSH_MSG_CHANNEL_EOF,
		    channel->channel_number);
}

int channel_eof(struct ssh_channel *channel)
{
  int res ;
1080
1081

  assert(! (channel->flags & CHANNEL_SENT_EOF));
Niels Möller's avatar
Niels Möller committed
1082
  
1083
  channel->flags |= CHANNEL_SENT_EOF;
Niels Möller's avatar
Niels Möller committed
1084
  res = A_WRITE(channel->write, format_channel_eof(channel));
Niels Möller's avatar
Niels Möller committed
1085
1086
1087
1088

  if (LSH_CLOSEDP(res))
    return res;

1089
1090
  if ( (channel->flags & CHANNEL_CLOSE_AT_EOF)
       && (channel->flags & CHANNEL_RECEIVED_EOF))
Niels Möller's avatar
Niels Möller committed
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
    {
      /* Initiate close */
      res |= channel_close(channel);
    }

  return res;
}

void init_channel(struct ssh_channel *channel)
{
  /* channel->super.handler = do_read_channel; */
  channel->write = NULL;

  channel->flags = 0;
1105
  channel->sources = 0;
Niels Möller's avatar
Niels Möller committed
1106
1107
  
  channel->request_types = NULL;
1108
  channel->receive = NULL;
Niels Möller's avatar
Niels Möller committed
1109
  channel->send = NULL;
1110

Niels Möller's avatar
Niels Möller committed
1111
1112
  channel->close = NULL;
  channel->eof = NULL;
1113

Niels Möller's avatar
Niels Möller committed
1114
1115
1116
1117
1118
1119
  channel->open_confirm = NULL;
  channel->open_failure = NULL;
  channel->channel_success = NULL;
  channel->channel_failure = NULL;
}

1120
1121
struct lsh_string *channel_transmit_data(struct ssh_channel *channel,
					 struct lsh_string *data)
1122
{
1123
1124
1125
1126
1127
1128
1129
  assert(data->length <= channel->send_window_size);
  assert(data->length <= channel->send_max_packet);
  
  return ssh_format("%c%i%fS",
		    SSH_MSG_CHANNEL_DATA,
		    channel->channel_number,
		    data);
1130
1131
}

1132
1133
1134
struct lsh_string *channel_transmit_extended(struct ssh_channel *channel,
					     UINT32 type,
					     struct lsh_string *data)
1135
{
1136
1137
  assert(data->length <= channel->send_window_size);
  assert(data->length <= channel->send_max_packet);
1138
  
1139
1140
1141
  return ssh_format("%c%i%i%fS",
		    SSH_MSG_CHANNEL_EXTENDED_DATA,
		    channel->channel_number,
1142
		    type,
1143
		    data);
1144
1145
}

Niels Möller's avatar
Niels Möller committed
1146
/* Writing data to a channel */
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
/* CLASS:
   (class
     (name channel_write)
     (super abstract_write)
     (vars
       (channel object ssh_channel)))
*/

/* CLASS:
   (class
     (name channel_write_extended)
     (super channel_write)
     (vars
       (type simple UINT32)))
*/
Niels Möller's avatar
Niels Möller committed
1162

1163
static int do_channel_write(struct abstract_write *w,
Niels Möller's avatar
Niels Möller committed
1164
1165
			    struct lsh_string *packet)
{
1166
  struct channel_write *closure = (struct channel_write *) w;
Niels Möller's avatar
Niels Möller committed
1167

Niels Möller's avatar
Niels Möller committed
1168
1169
1170
1171
  return A_WRITE(closure->channel->write,
		 channel_transmit_data(closure->channel, packet));
}

1172
1173
static int do_channel_write_extended(struct abstract_write *w,
				     struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
1174
{
1175
  CAST(channel_write_extended, closure, w);
Niels Möller's avatar
Niels Möller committed
1176
1177
1178
1179
1180

  return A_WRITE(closure->super.channel->write,
		 channel_transmit_extended(closure->super.channel,
					   closure->type,
					   packet));
1181
1182
}

Niels Möller's avatar
Niels Möller committed
1183
struct abstract_write *make_channel_write(struct ssh_channel *channel)
1184
{
1185
  NEW(channel_write, closure);
Niels Möller's avatar
Niels Möller committed
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195

  closure->super.write = do_channel_write;
  closure->channel = channel;

  return &closure->super;
}

struct abstract_write *make_channel_write_extended(struct ssh_channel *channel,
						   UINT32 type)
{
1196
  NEW(channel_write_extended, closure);
Niels Möller's avatar
Niels Möller committed
1197
1198
1199
1200

  closure->super.super.write = do_channel_write_extended;
  closure->super.channel = channel;
  closure->type = type;
1201
  
Niels Möller's avatar
Niels Möller committed
1202
  return &closure->super.super;
Niels Möller's avatar
Niels Möller committed
1203
}
1204

Niels Möller's avatar
Niels Möller committed
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
struct read_handler *make_channel_read_data(struct ssh_channel *channel)
{
  return make_read_data(channel, make_channel_write(channel));
}

struct read_handler *make_channel_read_stderr(struct ssh_channel *channel)
{
  return make_read_data(channel,
			make_channel_write_extended(channel,
						    SSH_EXTENDED_DATA_STDERR));
}    

1217
1218
1219
1220
1221
1222
1223
1224
/* CLASS:
   (class
     (name channel_close_callback)
     (super close_callback)
     (vars
       (channel object ssh_channel)))
*/

1225
/* Close callback for files we are writing to. */
1226
1227
static int channel_close_callback(struct close_callback *c, int reason)
{
1228
  CAST(channel_close_callback, closure, c);
1229

1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
  switch (reason)
    {
    case CLOSE_EOF:
      /* Expected close: Do nothing */
      debug("channel_close_callback: Closing after EOF.\n");
      break;
    case CLOSE_WRITE_FAILED:
    case CLOSE_BROKEN_PIPE:
      channel_close(closure->channel);
      break;
    default:
      fatal("channel_close_callback: Unexpected close reason %d!\n",
	    reason);
    }
1244
1245
1246
1247
1248
1249
  /* FIXME: So far, the returned value is ignored. */
  return 17;
}
  
struct close_callback *make_channel_close(struct ssh_channel *channel)
{
1250
1251
  NEW(channel_close_callback, closure);