📄 channels.c
字号:
/* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * This file contains functions for generic socket connection forwarding. * There is also code for initiating connection forwarding for X11 connections, * arbitrary tcp/ip connections, and the authentication agent connection. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 1999 Dug Song. All rights reserved. * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "includes.h"RCSID("$OpenBSD: channels.c,v 1.187 2003/03/05 22:33:43 markus Exp $");#include "ssh.h"#include "ssh1.h"#include "ssh2.h"#include "packet.h"#include "xmalloc.h"#include "log.h"#include "misc.h"#include "channels.h"#include "compat.h"#include "canohost.h"#include "key.h"#include "authfd.h"#include "pathnames.h"/* -- channel core *//* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. */static Channel **channels = NULL;/* * Size of the channel array. All slots of the array must always be * initialized (at least the type field); unused slots set to NULL */static int channels_alloc = 0;/* * Maximum file descriptor value used in any of the channels. This is * updated in channel_new. */static int channel_max_fd = 0;/* -- tcp forwarding *//* * Data structure for storing which hosts are permitted for forward requests. * The local sides of any remote forwards are stored in this array to prevent * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */typedef struct { char *host_to_connect; /* Connect to 'host'. */ u_short port_to_connect; /* Connect to 'port'. */ u_short listen_port; /* Remote side should listen port number. */} ForwardPermission;/* List of all permitted host/port pairs to connect. */static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];/* Number of permitted host/port pairs in the array. */static int num_permitted_opens = 0;/* * If this is true, all opens are permitted. This is the case on the server * on which we have to trust the client anyway, and the user could do * anything after logging in anyway. */static int all_opens_permitted = 0;/* -- X11 forwarding *//* Maximum number of fake X11 displays to try. */#define MAX_DISPLAYS 1000/* Saved X11 authentication protocol name. */static char *x11_saved_proto = NULL;/* Saved X11 authentication data. This is the real data. */static char *x11_saved_data = NULL;static u_int x11_saved_data_len = 0;/* * Fake X11 authentication data. This is what the server will be sending us; * we should replace any occurrences of this by the real data. */static char *x11_fake_data = NULL;static u_int x11_fake_data_len;/* -- agent forwarding */#define NUM_SOCKS 10/* AF_UNSPEC or AF_INET or AF_INET6 */static int IPv4or6 = AF_UNSPEC;/* helper */static void port_open_helper(Channel *c, char *rtype);/* -- channel core */Channel *channel_lookup(int id){ Channel *c; if (id < 0 || id >= channels_alloc) { log("channel_lookup: %d: bad id", id); return NULL; } c = channels[id]; if (c == NULL) { log("channel_lookup: %d: bad id: channel free", id); return NULL; } return c;}/* * Register filedescriptors for a channel, used when allocating a channel or * when the channel consumer/producer is ready, e.g. shell exec'd */static voidchannel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock){ /* Update the maximum file descriptor value. */ channel_max_fd = MAX(channel_max_fd, rfd); channel_max_fd = MAX(channel_max_fd, wfd); channel_max_fd = MAX(channel_max_fd, efd); /* XXX set close-on-exec -markus */ c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; c->efd = efd; c->extended_usage = extusage; /* XXX ugly hack: nonblock is only set by the server */ if (nonblock && isatty(c->rfd)) { debug("channel %d: rfd %d isatty", c->self, c->rfd); c->isatty = 1; if (!isatty(c->wfd)) { error("channel %d: wfd %d is not a tty?", c->self, c->wfd); } } else { c->isatty = 0; } /* enable nonblocking mode */ if (nonblock) { if (rfd != -1) set_nonblock(rfd); if (wfd != -1) set_nonblock(wfd); if (efd != -1) set_nonblock(efd); }}/* * Allocate a new channel object and set its type and socket. This will cause * remote_name to be freed. */Channel *channel_new(char *ctype, int type, int rfd, int wfd, int efd, u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock){ int i, found; Channel *c; /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { channels_alloc = 10; channels = xmalloc(channels_alloc * sizeof(Channel *)); for (i = 0; i < channels_alloc; i++) channels[i] = NULL; fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); } /* Try to find a free slot where to put the new channel. */ for (found = -1, i = 0; i < channels_alloc; i++) if (channels[i] == NULL) { /* Found a free slot. */ found = i; break; } if (found == -1) { /* There are no free slots. Take last+1 slot and expand the array. */ found = channels_alloc; channels_alloc += 10; if (channels_alloc > 10000) fatal("channel_new: internal error: channels_alloc %d " "too big.", channels_alloc); debug2("channel: expanding %d", channels_alloc); channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); for (i = found; i < channels_alloc; i++) channels[i] = NULL; } /* Initialize and return new channel. */ c = channels[found] = xmalloc(sizeof(Channel)); memset(c, 0, sizeof(Channel)); buffer_init(&c->input); buffer_init(&c->output); buffer_init(&c->extended); c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; c->flags = 0; channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); c->self = found; c->type = type; c->ctype = ctype; c->local_window = window; c->local_window_max = window; c->local_consumed = 0; c->local_maxpacket = maxpack; c->remote_id = -1; c->remote_name = remote_name; c->remote_window = 0; c->remote_maxpacket = 0; c->force_drain = 0; c->single_connection = 0; c->detach_user = NULL; c->confirm = NULL; c->input_filter = NULL; debug("channel %d: new [%s]", found, remote_name); return c;}static intchannel_find_maxfd(void){ int i, max = 0; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL) { max = MAX(max, c->rfd); max = MAX(max, c->wfd); max = MAX(max, c->efd); } } return max;}intchannel_close_fd(int *fdp){ int ret = 0, fd = *fdp; if (fd != -1) { ret = close(fd); *fdp = -1; if (fd == channel_max_fd) channel_max_fd = channel_find_maxfd(); } return ret;}/* Close all channel fd/socket. */static voidchannel_close_fds(Channel *c){ debug3("channel_close_fds: channel %d: r %d w %d e %d", c->self, c->rfd, c->wfd, c->efd); channel_close_fd(&c->sock); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd);}/* Free the channel and close its fd/socket. */voidchannel_free(Channel *c){ char *s; int i, n; for (n = 0, i = 0; i < channels_alloc; i++) if (channels[i]) n++; debug("channel_free: channel %d: %s, nchannels %d", c->self, c->remote_name ? c->remote_name : "???", n); s = channel_open_message(); debug3("channel_free: status: %s", s); xfree(s); if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); buffer_free(&c->extended); if (c->remote_name) { xfree(c->remote_name); c->remote_name = NULL; } channels[c->self] = NULL; xfree(c);}voidchannel_free_all(void){ int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) channel_free(channels[i]);}/* * Closes the sockets/fds of all channels. This is used to close extra file * descriptors after a fork. */voidchannel_close_all(void){ int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) channel_close_fds(channels[i]);}/* * Stop listening to channels. */voidchannel_stop_listening(void){ int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL) { switch (c->type) { case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: channel_close_fd(&c->sock); channel_free(c); break; } } }}/* * Returns true if no channel has too much buffered data, and false if one or * more channel is overfull. */intchannel_not_very_much_buffered_data(void){ u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL && c->type == SSH_CHANNEL_OPEN) {#if 0 if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { debug2("channel %d: big input buffer %d", c->self, buffer_len(&c->input)); return 0; }#endif if (buffer_len(&c->output) > packet_get_maxsize()) { debug2("channel %d: big output buffer %d > %d", c->self, buffer_len(&c->output), packet_get_maxsize()); return 0; } } } return 1;}/* Returns true if any channel is still open. */intchannel_still_open(void){ int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: continue; case SSH_CHANNEL_LARVAL: if (!compat20) fatal("cannot happen: SSH_CHANNEL_LARVAL"); continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: return 1; case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); return 1; default: fatal("channel_still_open: bad channel type %d", c->type); /* NOTREACHED */ } } return 0;}/* Returns the id of an open channel suitable for keepaliving */intchannel_find_open(void){ int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -