📄 serverloop.c
字号:
/* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * Server main loop for handling the interactive session. * * 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 by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. 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: serverloop.c,v 1.102 2002/06/11 05:46:20 mpech Exp $");#include "xmalloc.h"#include "packet.h"#include "buffer.h"#include "log.h"#include "servconf.h"#include "sshpty.h"#include "channels.h"#include "compat.h"#include "ssh1.h"#include "ssh2.h"#include "auth.h"#include "session.h"#include "dispatch.h"#include "auth-options.h"#include "serverloop.h"#include "misc.h"#include "kex.h"extern ServerOptions options;/* XXX */extern Kex *xxx_kex;static Authctxt *xxx_authctxt;static Buffer stdin_buffer; /* Buffer for stdin data. */static Buffer stdout_buffer; /* Buffer for stdout data. */static Buffer stderr_buffer; /* Buffer for stderr data. */static int fdin; /* Descriptor for stdin (for writing) */static int fdout; /* Descriptor for stdout (for reading); May be same number as fdin. */static int fderr; /* Descriptor for stderr. May be -1. */static long stdin_bytes = 0; /* Number of bytes written to stdin. */static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */static long fdout_bytes = 0; /* Number of stdout bytes read from program. */static int stdin_eof = 0; /* EOF message received from client. */static int fdout_eof = 0; /* EOF encountered reading from fdout. */static int fderr_eof = 0; /* EOF encountered readung from fderr. */static int fdin_is_tty = 0; /* fdin points to a tty. */static int connection_in; /* Connection to client (input). */static int connection_out; /* Connection to client (output). */static int connection_closed = 0; /* Connection to client closed. */static u_int buffer_high; /* "Soft" max buffer size. */static int client_alive_timeouts = 0;/* * This SIGCHLD kludge is used to detect when the child exits. The server * will exit after that, as soon as forwarded connections have terminated. */static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. *//* prototypes */static void server_init_dispatch(void);/* * we write to this pipe if a SIGCHLD is caught in order to avoid * the race between select() and child_terminated */static int notify_pipe[2];static voidnotify_setup(void){ if (pipe(notify_pipe) < 0) { error("pipe(notify_pipe) failed %s", strerror(errno)); } else if ((fcntl(notify_pipe[0], F_SETFD, 1) == -1) || (fcntl(notify_pipe[1], F_SETFD, 1) == -1)) { error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); close(notify_pipe[0]); close(notify_pipe[1]); } else { set_nonblock(notify_pipe[0]); set_nonblock(notify_pipe[1]); return; } notify_pipe[0] = -1; /* read end */ notify_pipe[1] = -1; /* write end */}static voidnotify_parent(void){ if (notify_pipe[1] != -1) write(notify_pipe[1], "", 1);}static voidnotify_prepare(fd_set *readset){ if (notify_pipe[0] != -1) FD_SET(notify_pipe[0], readset);}static voidnotify_done(fd_set *readset){ char c; if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) while (read(notify_pipe[0], &c, 1) != -1) debug2("notify_done: reading");}static voidsigchld_handler(int sig){ int save_errno = errno; debug("Received SIGCHLD."); child_terminated = 1; mysignal(SIGCHLD, sigchld_handler); notify_parent(); errno = save_errno;}/* * Make packets from buffered stderr data, and buffer it for sending * to the client. */static voidmake_packets_from_stderr_data(void){ int len; /* Send buffered stderr data to the client. */ while (buffer_len(&stderr_buffer) > 0 && packet_not_very_much_data_to_write()) { len = buffer_len(&stderr_buffer); if (packet_is_interactive()) { if (len > 512) len = 512; } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()) len = packet_get_maxsize(); } packet_start(SSH_SMSG_STDERR_DATA); packet_put_string(buffer_ptr(&stderr_buffer), len); packet_send(); buffer_consume(&stderr_buffer, len); stderr_bytes += len; }}/* * Make packets from buffered stdout data, and buffer it for sending to the * client. */static voidmake_packets_from_stdout_data(void){ int len; /* Send buffered stdout data to the client. */ while (buffer_len(&stdout_buffer) > 0 && packet_not_very_much_data_to_write()) { len = buffer_len(&stdout_buffer); if (packet_is_interactive()) { if (len > 512) len = 512; } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()) len = packet_get_maxsize(); } packet_start(SSH_SMSG_STDOUT_DATA); packet_put_string(buffer_ptr(&stdout_buffer), len); packet_send(); buffer_consume(&stdout_buffer, len); stdout_bytes += len; }}static voidclient_alive_check(void){ static int had_channel = 0; int id; id = channel_find_open(); if (id == -1) { if (!had_channel) return; packet_disconnect("No open channels after timeout!"); } had_channel = 1; /* timeout, check to see how many we have had */ if (++client_alive_timeouts > options.client_alive_count_max) packet_disconnect("Timeout, your session not responding."); /* * send a bogus channel request with "wantreply", * we should get back a failure */ channel_request_start(id, "keepalive@openssh.com", 1); packet_send();}/* * Sleep in select() until we can do something. This will initialize the * select masks. Upon return, the masks will indicate which descriptors * have data or can accept data. Optionally, a maximum time can be specified * for the duration of the wait (0 = infinite). */static voidwait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, int *nallocp, u_int max_time_milliseconds){ struct timeval tv, *tvp; int ret; int client_alive_scheduled = 0; /* * if using client_alive, set the max timeout accordingly, * and indicate that this particular timeout was for client * alive by setting the client_alive_scheduled flag. * * this could be randomized somewhat to make traffic * analysis more difficult, but we're not doing it yet. */ if (compat20 && max_time_milliseconds == 0 && options.client_alive_interval) { client_alive_scheduled = 1; max_time_milliseconds = options.client_alive_interval * 1000; } /* Allocate and update select() masks for channel descriptors. */ channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0); if (compat20) {#if 0 /* wrong: bad condition XXX */ if (channel_not_very_much_buffered_data())#endif FD_SET(connection_in, *readsetp); } else { /* * Read packets from the client unless we have too much * buffered stdin or channel data. */ if (buffer_len(&stdin_buffer) < buffer_high && channel_not_very_much_buffered_data()) FD_SET(connection_in, *readsetp); /* * If there is not too much data already buffered going to * the client, try to get some more data from the program. */ if (packet_not_very_much_data_to_write()) { if (!fdout_eof) FD_SET(fdout, *readsetp); if (!fderr_eof) FD_SET(fderr, *readsetp); } /* * If we have buffered data, try to write some of that data * to the program. */ if (fdin != -1 && buffer_len(&stdin_buffer) > 0) FD_SET(fdin, *writesetp); } notify_prepare(*readsetp); /* * If we have buffered packet data going to the client, mark that * descriptor. */ if (packet_have_data_to_write()) FD_SET(connection_out, *writesetp); /* * If child has terminated and there is enough buffer space to read * from it, then read as much as is available and exit. */ if (child_terminated && packet_not_very_much_data_to_write()) if (max_time_milliseconds == 0 || client_alive_scheduled) max_time_milliseconds = 100; if (max_time_milliseconds == 0) tvp = NULL; else { tv.tv_sec = max_time_milliseconds / 1000; tv.tv_usec = 1000 * (max_time_milliseconds % 1000); tvp = &tv; } /* Wait for something to happen, or the timeout to expire. */ ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); if (ret == -1) { memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); if (errno != EINTR) error("select: %.100s", strerror(errno)); } else if (ret == 0 && client_alive_scheduled) client_alive_check(); notify_done(*readsetp);}/* * Processes input from the client and the program. Input data is stored * in buffers and processed later. */static voidprocess_input(fd_set * readset){ int len; char buf[16384]; /* Read and buffer any input data from the client. */ if (FD_ISSET(connection_in, readset)) { len = read(connection_in, buf, sizeof(buf)); if (len == 0) { verbose("Connection closed by remote host."); connection_closed = 1; if (compat20) return; fatal_cleanup(); } else if (len < 0) { if (errno != EINTR && errno != EAGAIN) { verbose("Read error from remote host: %.100s", strerror(errno)); fatal_cleanup(); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -