gateway_commands.c 5.16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* gateway_commands.c
 *
 * $Id$
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 2000 Niels Möller
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

25
#include "gateway_commands.h"
26 27 28

#include "channel.h"
#include "connection_commands.h"
29 30
#include "debug.h"
#include "format.h"
31
#include "gateway_channel.h"
32
#include "io_commands.h"
33 34
#include "read_packet.h"
#include "ssh.h"
35
#include "unpad.h"
36 37 38
#include "werror.h"
#include "xalloc.h"

39 40
#include <assert.h>

41 42
#include "gateway_commands.c.x"

43 44
/* A simplified version of do_pad() in pad.c. This one uses
 * a fixed block size and pads with zeros. */
45

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/* FIXME: It seems very unnecessary to pad at all; we could just use
 *
 *   uint32     packet_length
 *   byte[n]    payload; n = packet_length
 *
 * But then we can reuse the plain read_packet class. */

static void
do_gateway_pad(struct abstract_write *w,
	       struct lsh_string *packet)
{
  CAST(abstract_write_pipe, closure, w);

  struct lsh_string *new;
  
  UINT32 new_size;
  UINT8 padding;

  UINT8 *data;

  /* new_size is (packet->length + 9) rounded up to a multiple of
   * block_size. But the block_size if fixed to 8 octets. */
  new_size = 8 * (2 + packet->length / 8);

  padding = new_size - packet->length - 5;
  assert(padding >= 4);

  new = ssh_format("%i%c%lr", packet->length + padding + 1,
		   padding, packet->length + padding, &data);

  assert(new->length == new_size);

  memcpy(data, packet->data, packet->length);
  memset(data + packet->length, '\0', padding);
  
  lsh_string_free(packet);

  A_WRITE(closure->next, new);
}

static struct abstract_write *
make_gateway_pad(struct abstract_write *next)
{
  NEW(abstract_write_pipe, closure);

  closure->super.write = do_gateway_pad;
  closure->next = next;

  return &closure->super;
}
96

97

98 99 100 101 102 103
/* Buffer size when reading from the socket */
#define BUF_SIZE (1<<14)

/* Blocksize when writing */
#define BLOCK_SIZE 2000

104 105 106
static struct ssh_connection *
gateway_make_connection(struct listen_value *lv,
			struct exception_handler *e)
107
{
108
  /* NOTE: lv->peer is usually NULL here. */
109 110 111 112 113 114
  struct ssh_connection *connection
    = make_ssh_connection(0, /* flags */
			  lv->peer, "gateway",
			  NULL, /* established_continuation */
			  make_exc_finish_read_handler(lv->fd, e, HANDLER_CONTEXT));

115
  /* Instead of calling connection_init_io(). */
116 117
  connection->raw =
    &io_read_write(lv->fd,
118 119 120 121
		   make_buffered_read(
		     BUF_SIZE,
		     make_read_packet(
		       make_packet_unpad(
122
			 connection,
123
			 make_packet_debug(&connection->super,
124
				 	  ssh_format("%lz received", connection->debug_comment))),
125
		       connection)),
126 127 128
		   BLOCK_SIZE,
		   make_connection_close_handler(connection))->write_buffer->super;
  
129 130 131 132
  connection->write = make_packet_debug(
			make_gateway_pad(connection->raw),
			ssh_format("%lz sent", connection->debug_comment));

133 134 135 136
  init_connection_service(connection);

  connection->table->open_fallback = &gateway_channel_open_forward;

137 138 139
  connection->dispatch[SSH_MSG_DEBUG] = &connection_forward_handler;
  connection->dispatch[SSH_MSG_IGNORE] = &connection_forward_handler;

140
  return connection;
141
}
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

DEFINE_COMMAND(gateway_init, a, c, e)
{
  CAST(listen_value, lv, a);

  COMMAND_RETURN(c, gateway_make_connection(lv, e));
}


/* (gateway_accept main-connection gateway-connection) */

static void
do_gateway_accept(struct command *s,
		 struct lsh_object *x,
		 struct command_continuation *c,
		 struct exception_handler *e)
{
159
  CAST(connection_command, self, s);
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
  CAST(listen_value, lv, x);

  struct ssh_connection *gateway = gateway_make_connection(lv, e);
  
  /* Kill gateway connection if the main connection goes down. */
  REMEMBER_RESOURCE(self->connection->resources, &lv->fd->super);
  
  gateway->chain = self->connection;

  COMMAND_RETURN(c, gateway);
}

DEFINE_COMMAND_SIMPLE(gateway_accept, a)
{
  CAST(ssh_connection, connection, a);
175
  NEW(connection_command, self);
176 177 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
  self->connection = connection;
  self->super.call = do_gateway_accept;

  return &self->super.super;
}


/* GABA:
   (expr
     (name gateway_setup)
     (params
       (listen object command))
     (expr
       (lambda (connection)
         (connection_remember connection
	   (listen
	     (lambda (peer)
	       (gateway_accept connection peer)))))))
*/

struct command *
make_gateway_setup(struct command *listen)
{
  CAST_SUBTYPE(command, res,
	       gateway_setup(listen));

  trace("make_gateway_setup\n");

  return res;
}

DEFINE_COMMAND_SIMPLE(gateway_setup_command, a)
{
  CAST_SUBTYPE(command, listen, a);
  CAST_SUBTYPE(command, res,
	       gateway_setup(listen));

  return &res->super;
}