/* tcpforward_commands.c * * $Id$ */ /* lsh, an implementation of the ssh protocol * * Copyright (C) 1998 Balazs Scheidler, 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 */ #include "tcpforward_commands.h" #include "atoms.h" #include "channel_commands.h" #include "format.h" #include "io_commands.h" #include "ssh.h" #include "werror.h" #include "xalloc.h" #include /* Forward declarations */ extern struct collect_info_1 open_direct_tcpip; extern struct collect_info_1 remote_listen_command; extern struct collect_info_1 open_forwarded_tcpip; extern struct command tcpip_start_io; extern struct command tcpip_connect_io; struct collect_info_1 install_direct_tcpip_handler; struct collect_info_1 install_forwared_tcpip_handler; static struct command make_direct_tcpip_handler; struct collect_info_1 install_tcpip_forward_handler; static struct command make_tcpip_forward_handler; #define OPEN_DIRECT_TCPIP (&open_direct_tcpip.super.super.super) #define REMOTE_LISTEN (&remote_listen_command.super.super.super) #define TCPIP_START_IO (&tcpip_start_io.super) #define TCPIP_CONNECT_IO (&tcpip_connect_io.super) #define OPEN_FORWARDED_TCPIP (&open_forwarded_tcpip.super.super.super) #include "tcpforward_commands.c.x" /* Takes a socket as argument, and returns a tcpip channel. Used by * the party receiving a open-tcp request, when a channel to the * target has been opened. */ #define TCPIP_WINDOW_SIZE (SSH_MAX_PACKET << 3) /* NOTE: This command does not do any remembering. */ static void do_tcpip_connect_io(struct command *ignored UNUSED, struct lsh_object *x, struct command_continuation *c, struct exception_handler *e UNUSED) { CAST(io_fd, socket, x); assert(socket); #if 0 debug("tcpforward_commands.c: do_tcpip_connect_io, socket is %z\n", socket ? "valid" : "invalid"); if (!socket) COMMAND_RETURN(c, NULL); #endif COMMAND_RETURN(c, make_tcpip_channel(socket, TCPIP_WINDOW_SIZE)); } struct command tcpip_connect_io = STATIC_COMMAND(do_tcpip_connect_io); /* Used by the party requesting tcp forwarding, i.e. when a socket is * already open, and we have asked the other end to forward it. Takes * a channel as argument, and connects it to the socket. Returns the * channel. */ static void do_tcpip_start_io(struct command *s UNUSED, struct lsh_object *x, struct command_continuation *c, struct exception_handler *e UNUSED) { CAST_SUBTYPE(ssh_channel, channel, x); assert(channel); #if 0 debug("tcpforward_commands.c: do_tcpip_start_io, channel is %z\n", channel ? "valid" : "invalid"); if (!channel) { verbose("Error opening channel.\n"); COMMAND_RETURN(c, NULL); } #endif tcpip_channel_start_io(channel); COMMAND_RETURN(c, channel); } struct command tcpip_start_io = { STATIC_HEADER, do_tcpip_start_io }; /* Requesting the opening of a forwarded tcpip channel. */ /* Used for both forwarded-tcpip and direct-tcpip. Takes a listen * value as argument, and returns a channel connected to some tcpip * port at the other end. */ /* GABA: (class (name open_tcpip_command) (super channel_open_command) (vars ; ATOM_FORWARDED_TCPIP or ATOM_DIRECT_TCPIP (type . int) (max_window . UINT32) ; For forwarded-tcpip, port is the port listened to. ; For direct-tcpip, poprt is the port to conect to. ; In both cases, it's a port used on the server end. (port object address_info) (peer object listen_value))) */ static struct ssh_channel * new_tcpip_channel(struct channel_open_command *c, struct ssh_connection *connection, struct lsh_string **request) { CAST(open_tcpip_command, self, c); struct ssh_channel *channel; /* NOTE: All accepted fd:s must end up in this function, so it * should be ok to delay the REMEMBER() call until here. */ debug("tcpforward_commands.c: new_tcpip_channel()\n"); REMEMBER_RESOURCE(connection->resources, &self->peer->fd->super.super); channel = make_tcpip_channel(self->peer->fd, TCPIP_WINDOW_SIZE); channel->write = connection->write; *request = prepare_channel_open(connection, self->type, channel, "%S%i%S%i", self->port->ip, self->port->port, self->peer->peer->ip, self->peer->peer->port); return channel; } static struct command * make_open_tcpip_command(int type, UINT32 max_window, struct address_info *port, struct listen_value *peer) { NEW(open_tcpip_command, self); debug("tcpforward_commands.c: make_open_tcpip_command()\n"); self->super.super.call = do_channel_open_command; self->super.new_channel = new_tcpip_channel; self->type = type; self->max_window = max_window; self->port = port; self->peer = peer; return &self->super.super; } static struct lsh_object * collect_open_forwarded_tcpip(struct collect_info_2 *info, struct lsh_object *a, struct lsh_object *b) { CAST(address_info, local, a); CAST(listen_value, peer, b); assert(!info->next); return &make_open_tcpip_command(ATOM_FORWARDED_TCPIP, TCPIP_WINDOW_SIZE, local, peer)->super; } static struct collect_info_2 collect_open_forwarded_tcpip_2 = STATIC_COLLECT_2_FINAL(collect_open_forwarded_tcpip); struct collect_info_1 open_forwarded_tcpip = STATIC_COLLECT_1(&collect_open_forwarded_tcpip_2); static struct lsh_object * collect_open_direct_tcpip(struct collect_info_2 *info, struct lsh_object *a, struct lsh_object *b) { CAST(address_info, local, a); CAST(listen_value, peer, b); assert(!info->next); return &make_open_tcpip_command(ATOM_DIRECT_TCPIP, TCPIP_WINDOW_SIZE, local, peer)->super; } static struct collect_info_2 collect_open_direct_tcpip_2 = STATIC_COLLECT_2_FINAL(collect_open_direct_tcpip); struct collect_info_1 open_direct_tcpip = STATIC_COLLECT_1(&collect_open_direct_tcpip_2); /* Requesting remote forwarding of a port */ /* GABA: (class (name remote_port_install_continuation) (super command_frame) (vars (port object remote_port) (callback object command))) */ static void do_remote_port_install_continuation(struct command_continuation *s, struct lsh_object *x) { CAST(remote_port_install_continuation, self, s); CAST(ssh_connection, connection, x); if (connection) { debug("tcpforward_commands.c: do_remote_port_install_continuation(), success.\n"); self->port->callback = self->callback; } else { debug("tcpforward_commands.c: do_remote_port_install_continuation(), failed.\n"); /* FIXME: Cleanup, and delete the port from the remote_ports list. */ } COMMAND_RETURN(self->super.up, x); } static struct command_continuation * make_remote_port_install_continuation(struct remote_port *port, struct command *callback, struct command_continuation *c) { NEW(remote_port_install_continuation, self); debug("tcpforward_commands.c: make_remote_port_install_continuation()\n"); self->super.super.c = do_remote_port_install_continuation; self->super.up = c; self->port = port; self->callback = callback; return &self->super.super; } /* Listening on a remote port * * (remote_listen callback port connection) * * Returns a remote_port or NULL. * * callback is invoked with a address_info peer as argument, and * should return a channel or NULL. */ /* GABA: (class (name request_tcpip_forward_command) (super global_request_command) (vars ; Invoked when a forwarded_tcpip request is received. ; Called with the struct address_info *peer as argument. (callback object command) (port object address_info))) */ static struct lsh_string * do_format_request_tcpip_forward(struct global_request_command *s, struct ssh_connection *connection, struct command_continuation **c) { CAST(request_tcpip_forward_command, self, s); struct remote_port *port; int want_reply; debug("tcpforward_commands.c: do_format_request_tcpip_forward()\n"); if (CONTINUATION_USED_P(*c)) { port = make_remote_port(self->port, NULL); *c = make_remote_port_install_continuation(port, self->callback, *c); want_reply = 1; } else { port = make_remote_port(self->port, self->callback); want_reply = 0; } object_queue_add_tail(&connection->table->remote_ports, &port->super.super); return ssh_format("%c%a%c%S%i", SSH_MSG_GLOBAL_REQUEST, ATOM_TCPIP_FORWARD, want_reply, self->port->ip, self->port->port); } static struct command * make_request_tcpip_forward_command(struct command *callback, struct address_info *listen) { NEW(request_tcpip_forward_command, self); debug("tcpforward_commands.c: make_request_tcpip_forward_command()\n"); self->super.super.call = do_channel_global_command; self->super.format_request = do_format_request_tcpip_forward; self->callback = callback; self->port = listen; return &self->super.super; } static struct lsh_object * collect_remote_listen(struct collect_info_2 *info, struct lsh_object *a, struct lsh_object *b) { CAST_SUBTYPE(command, callback, a); CAST(address_info, port, b); assert(!info->next); return &make_request_tcpip_forward_command(callback, port)->super; } static struct collect_info_2 collect_info_remote_listen_2 = STATIC_COLLECT_2_FINAL(collect_remote_listen); static struct collect_info_1 remote_listen_command = STATIC_COLLECT_1(&collect_info_remote_listen_2); /* Cancel a remotely forwarded port. * FIXME: Not implemented */ /* GABA: (expr (name forward_local_port) (globals (listen LISTEN_COMMAND) (start_io TCPIP_START_IO) (open_direct_tcpip OPEN_DIRECT_TCPIP)) (params (backend object io_backend) (local object address_info) (target object address_info)) (expr (lambda (connection) (listen (lambda (peer) (start_io (open_direct_tcpip target peer connection))) backend local)))) */ struct command * make_forward_local_port(struct io_backend *backend, struct address_info *local, struct address_info *target) { CAST_SUBTYPE(command, res, forward_local_port(backend, local, target)); debug("tcpforward_commands.c: forward_local_port()\n"); return res; } /* GABA: (expr (name forward_remote_port) (globals (remote_listen REMOTE_LISTEN) ;; (connection_remember CONNECTION_REMEMBER) (connect_io TCPIP_CONNECT_IO)) (params (connect object command) (remote object address_info)) (expr (lambda (connection) (remote_listen (lambda (peer) (connect_io ; peer ;; FIXME: This calls connect too early ;; Let it take peer as argument ;; Replacing connection with (K connection peer) ;; might work, except that it is optimized away. (connect connection))) remote connection)))) */ struct command * make_forward_remote_port(struct io_backend *backend, struct address_info *remote, struct address_info *target) { CAST_SUBTYPE(command, res, forward_remote_port(make_connect_port(backend, target), remote)); debug("tcpforward_commands.c: forward_remote_port()\n"); return res; } /* Takes a callback function and returns a channel_open * handler. */ static void do_make_direct_tcpip_handler(struct command *s UNUSED, struct lsh_object *x, struct command_continuation *c, struct exception_handler *e UNUSED) { CAST_SUBTYPE(command, callback, x); trace("tcpforward_commands.c: do_make_open_tcp_handler()\n"); COMMAND_RETURN(c, &make_channel_open_direct_tcpip(callback)->super); } static struct command make_direct_tcpip_handler = STATIC_COMMAND(do_make_direct_tcpip_handler); /* Takes a callback function and returns a global_request handler. */ static void do_make_tcpip_forward_handler(struct command *s UNUSED, struct lsh_object *x, struct command_continuation *c, struct exception_handler *e UNUSED) { CAST_SUBTYPE(command, callback, x); debug("tcpforward_commands.c: do_make_tcpip_forward_handler()\n"); COMMAND_RETURN(c, &make_tcpip_forward_request(callback)->super); } static struct command make_tcpip_forward_handler = STATIC_COMMAND(do_make_tcpip_forward_handler); /* Commands to install handlers */ struct install_info install_direct_tcpip_info_2 = STATIC_INSTALL_OPEN_HANDLER(ATOM_DIRECT_TCPIP); struct collect_info_1 install_direct_tcpip_handler = STATIC_COLLECT_1(&install_direct_tcpip_info_2.super); struct install_info install_forwarded_tcpip_info_2 = STATIC_INSTALL_OPEN_HANDLER(ATOM_FORWARDED_TCPIP); struct collect_info_1 install_forwarded_tcpip_handler = STATIC_COLLECT_1(&install_forwarded_tcpip_info_2.super); /* Server side callbacks */ /* Make this non-static? */ /* GABA: (expr (name direct_tcpip_hook) (globals (install "&install_direct_tcpip_handler.super.super.super") (handler "&make_direct_tcpip_handler.super") (connect_io TCPIP_CONNECT_IO)) (params (connect object command)) (expr (lambda (connection) (install connection (handler (lambda (port) (connect_io (connect connection port)))))))) */ struct command * make_direct_tcpip_hook(struct io_backend *backend) { CAST_SUBTYPE(command, res, direct_tcpip_hook(make_connect_connection(backend))); debug("tcpforward_commands.c: make_direct_tcpip_hook()\n"); return res; } struct install_info install_tcpip_forward_info_2 = STATIC_INSTALL_GLOBAL_HANDLER(ATOM_TCPIP_FORWARD); struct collect_info_1 install_tcpip_forward_handler = STATIC_COLLECT_1(&install_tcpip_forward_info_2.super); /* GABA: (expr (name tcpip_forward_hook) (globals (install "&install_tcpip_forward_handler.super.super.super") (handler "&make_tcpip_forward_handler.super") (start_io TCPIP_START_IO) (open_forwarded_tcpip OPEN_FORWARDED_TCPIP) (listen LISTEN_COMMAND)) (params (backend object io_backend)) (expr (lambda (connection) ;; Called when the ssh-connection is established (install connection (handler (lambda (port) ;; Called when the client requests remote forwarding (listen (lambda (peer) ;; Called when someone connects to the ;; forwarded port. (start_io (open_forwarded_tcpip port peer connection))) backend port))))))) */ struct command * make_tcpip_forward_hook(struct io_backend *backend) { CAST_SUBTYPE(command, res, tcpip_forward_hook(backend)); debug("tcpforward_commands.c: tcpip_forward_hook()\n"); return res; }