gateway_channel.c 9.3 KB
Newer Older
1 2 3 4 5 6
/* gateway_channel.c
 *
 */

/* lsh, an implementation of the ssh protocol
 *
Niels Möller's avatar
Niels Möller committed
7
 * Copyright (C) 2000 Niels Möller
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301  USA
22 23
 */

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

28
#include <assert.h>
29

30 31 32
#include "gateway.h"

#include "channel.h"
33 34 35 36 37 38 39 40 41 42 43 44
#include "format.h"
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

/* A pair of gateway_channel objects are chained together so that
 * requests and data received on one of the channels are directed to
 * the other.
 *
 * Chaining happens as follows:
 *
 * 1. First a CHANNEL_OPEN request is received on one connection, and
45
 *    a channel object is created. We refer to this object as the
46 47 48 49 50 51
 *    _originating_ channel.
 *
 * 2. Next, we send a similar CHANNEL_OPEN request on some other
 *    connection, and create a channel object referred to as the
 *    _target_ channel.
 *
52
 * 3. When we receive a reply to the CHANNEL_OPEN request sent in (2),
53 54 55
 *    we chain the two channel objects together, and reply to the
 *    CHANNEL_OPEN request we received in (1). */

56 57 58 59 60 61 62 63 64
static void
do_kill_gateway_channel(struct resource *s)
{
  CAST(gateway_channel, self, s);
  if (self->super.super.alive)
    {
      trace("do_kill_gateway_channel\n");
      self->super.super.alive = 0;

65 66 67
      if (self->x11)
	KILL_RESOURCE(&self->x11->super.super);

68 69 70 71
      if (self->chain->super.super.alive)
	{
	  /* When the kill method is called because a gateway client
	     disconnects, then to tear down the corrsponding channel
72
	     to the server, there are three possible states we need to
73 74 75 76 77 78 79 80 81 82
	     handle:

	     1. The channel is fully open. Then call channel_close to
	        close it.

	     2. The gateway client requested a CHANNEL_OPEN, which was
	        forwarded to the remote server, but then the gateway
	        client disappears before we get any reply from the
	        server. Then we can't send CHANNEL_CLOSE now. Instead,
	        do_gateway_channel_event checks for this case and
83
	        closes the channel as soon as a we get the response to
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	        CHANNEL_OPEN_CONFIRMATION.

	     3. The server requested a CHANNEL_OPEN, which was forwarded,
	        and then the gateway client disappeared before replying. Then
		we must send a CHANNEL_OPEN_FAILURE.
	  */
	  enum channel_alloc_state state =
	    self->super.connection->alloc_state[self->super.local_channel_number];
	  switch (state)
	    {
	    case CHANNEL_FREE:
	      fatal("Internal error.\n");
	    case CHANNEL_ALLOC_ACTIVE:
	      channel_close(&self->chain->super);
	      break;
	    case CHANNEL_ALLOC_RECEIVED_OPEN:
	      /* Case 2 above. Do nothing now. */
	      break;
	    case CHANNEL_ALLOC_SENT_OPEN:
103 104 105 106 107 108 109
	      /* Case 3 above. Send deny message on originating
		 channel, unless we already did that. We can also
		 enter this case after the "normal" handling in
		 CHANNEL_EVENT_DENY. */
	      if (self->info)
		channel_open_deny(self->info, SSH_OPEN_CONNECT_FAILED,
				  "Refused by server");	      	      
110 111 112
	      break;
	    }
	}
113 114 115
    }
}

116 117 118
static void
do_receive(struct ssh_channel *c,
	   int type,
119
	   uint32_t length, const uint8_t *data)
120
{
121 122
  CAST(gateway_channel, self, c);

123 124 125
  switch(type)
    {
    case CHANNEL_DATA:
126
      channel_transmit_data(&self->chain->super, length, data);
127
      
128 129
      break;
    case CHANNEL_STDERR_DATA:
130
      channel_transmit_extended(&self->chain->super, SSH_EXTENDED_DATA_STDERR,
131
				length, data);
132 133 134 135 136 137 138 139 140
      break;
    default:
      fatal("Internal error!\n");
    }
}

/* We may send more data */
static void
do_send_adjust(struct ssh_channel *s,
141
               uint32_t i)
142 143
{
  CAST(gateway_channel, self, s);
144
  if (i)
145
    channel_adjust_rec_window(&self->chain->super, i);
146 147 148
}

static void
149
do_gateway_channel_event(struct ssh_channel *c, enum channel_event event)
150
{
151
  CAST(gateway_channel, self, c);
152

153
  if (!self->chain->super.super.alive)
154
    {
155 156 157 158 159 160 161 162 163 164 165 166 167 168
      /* The other channel has disappeared. The only event we need to
	 handle is CONFIRM. */
      if (event == CHANNEL_EVENT_CONFIRM)
	channel_close(&self->super);
    }
  else
    switch(event)
      {
      case CHANNEL_EVENT_CONFIRM:
	/* This method is invoked on the target channel. Propagate
	   the target channel's send variables to the originating
	   channel's receive variables. */
	self->chain->super.rec_window_size = self->super.send_window_size;
	self->chain->super.rec_max_packet = self->super.send_max_packet;
169

170 171
	self->super.receive = do_receive;
	self->super.send_adjust = do_send_adjust;
172

173 174
	self->chain->super.receive = do_receive;
	self->chain->super.send_adjust = do_send_adjust;
175

176 177
	channel_open_confirm(self->info, &self->chain->super);
	self->info = NULL;
178

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	break;
      case CHANNEL_EVENT_DENY:
	/* This method is invoked on the target channel. We need to tear
	   down the originating channel. */

	/* FIXME: We should propagate the error code and message
	   over the gateway. */
	channel_open_deny(self->info, SSH_OPEN_CONNECT_FAILED,
			  "Refused by server");

	self->info = NULL;
	break;
      case CHANNEL_EVENT_EOF:
	channel_eof(&self->chain->super);
	break;

      case CHANNEL_EVENT_CLOSE:
	/* FIXME: Can we arrange so that the gateway connection is
	   closed if pending_close on the shared connection is set,
	   and the last channel is closed? */
	channel_close(&self->chain->super);
	break;

      case CHANNEL_EVENT_SUCCESS:
	if (self->x11 && self->x11->pending)
	  {
	    if (!--self->x11->pending)
	      {
		CAST(client_connection, connection, self->super.connection);
		client_add_x11_handler(connection, &self->x11->super);
	      }
	  }

	SSH_CONNECTION_WRITE(self->chain->super.connection,
			     format_channel_success(self->chain->super.remote_channel_number));
	break;

      case CHANNEL_EVENT_FAILURE:
	if (self->x11 && self->x11->pending)
	  {
	    if (!--self->x11->pending)
	      {
		KILL_RESOURCE(&self->x11->super.super);
		self->x11 = NULL;
	      }
	  }
225
      
226 227 228
	SSH_CONNECTION_WRITE(self->chain->super.connection,
			     format_channel_failure(self->chain->super.remote_channel_number));
	break;
229
      
230 231 232 233 234 235
      case CHANNEL_EVENT_STOP:
      case CHANNEL_EVENT_START:
	/* Ignore. The channel doesn't do any i/o of its own, so flow
	   control must be handled elsewhere. */
	break;
      }      
236
}  
237

238 239 240
DEFINE_CHANNEL_REQUEST(gateway_forward_channel_request)
	(struct channel_request *s UNUSED,
	 struct ssh_channel *c,
241
	 const struct request_info *info,
242
	 struct simple_buffer *buffer)
243
{
244 245 246 247 248 249
  CAST(gateway_channel, self, c);
  uint32_t arg_length;
  const uint8_t *arg;

  parse_rest(buffer, &arg_length, &arg);

250
  channel_send_request(&self->chain->super,
251 252 253
		       info->type_length, info->type_data,
		       info->want_reply,
		       "%ls", arg_length, arg);
254 255
}

256
int
257 258 259
gateway_forward_channel_open(struct ssh_connection *target_connection,
			     const struct channel_open_info *info,
			     uint32_t arg_length, const uint8_t *arg)
260
{
261 262
  NEW(gateway_channel, origin);
  NEW(gateway_channel, target);
263

264 265 266 267
  init_channel(&origin->super,
	       do_kill_gateway_channel, do_gateway_channel_event);
  init_channel(&target->super,
	       do_kill_gateway_channel, do_gateway_channel_event);
268

269 270 271 272 273
#if WITH_X11_FORWARD
  origin->super.request_types = make_alist(1,
					   ATOM_X11_REQ, &gateway_x11_request_handler,
					   -1);
#endif
274 275
  origin->super.request_fallback = &gateway_forward_channel_request;
  target->super.request_fallback = &gateway_forward_channel_request;
276

277 278
  origin->chain = target;
  target->chain = origin;
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

  origin->info = NULL;
  target->info = info;

  target->super.rec_max_packet = info->send_max_packet;
  target->super.rec_window_size = info->send_window_size;

  /* Prevents the processing when sending and receiving CHANNEL_EOF from
     closing the channel. */
  origin->super.sinks++;
  target->super.sinks++;

  return channel_open_new_type(target_connection,
			       &target->super,
			       info->type_length, info->type_data,
294
			       "%ls", arg_length, arg);
295 296
}

297
/* Used for all channel open requests sent to the gateway. I.e., the
298
   target connection is the shared one. */
299 300 301 302
DEFINE_CHANNEL_OPEN(gateway_channel_open)
	(struct channel_open *s UNUSED,
	 const struct channel_open_info *info,
	 struct simple_buffer *args)
303
{
304 305
  CAST(gateway_connection, connection, info->connection);
  trace("gateway_channel_open\n");
306

307 308 309
  trace("gateway_channel_open: send_window_size = %i\n",
	info->send_window_size);

310
  if (connection->shared->super.pending_close)
311 312 313
    /* We are waiting for channels to close. Don't open any new ones. */
    channel_open_deny(info, SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
		      "Waiting for channels to close.");
314 315
  else
    {
316 317 318
      uint32_t arg_length;
      const uint8_t *arg;

319
      parse_rest(args, &arg_length, &arg);
320

321 322
      if (!gateway_forward_channel_open(&connection->shared->super,
					info, arg_length, arg))
323 324 325
	channel_open_deny(info, SSH_OPEN_RESOURCE_SHORTAGE,
			  "Too many channels.");
    }
326
}