⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 clientloop.c

📁 OpenSSL Source code for SFTP, SSH, and many others
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland *                    All rights reserved * The main loop for the interactive session (client side). * * 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". * * * 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. * * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 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: clientloop.c,v 1.101 2002/06/09 13:32:01 markus Exp $");#include "ssh.h"#include "ssh1.h"#include "ssh2.h"#include "xmalloc.h"#include "packet.h"#include "buffer.h"#include "compat.h"#include "channels.h"#include "dispatch.h"#include "buffer.h"#include "bufaux.h"#include "key.h"#include "kex.h"#include "log.h"#include "readconf.h"#include "clientloop.h"#include "authfd.h"#include "atomicio.h"#include "sshtty.h"#include "misc.h"#include "readpass.h"/* import options */extern Options options;/* Flag indicating that stdin should be redirected from /dev/null. */extern int stdin_null_flag;/* * Name of the host we are connecting to.  This is the name given on the * command line, or the HostName specified for the user-supplied name in a * configuration file. */extern char *host;/* * Flag to indicate that we have received a window change signal which has * not yet been processed.  This will cause a message indicating the new * window size to be sent to the server a little later.  This is volatile * because this is updated in a signal handler. */static volatile sig_atomic_t received_window_change_signal = 0;static volatile sig_atomic_t received_signal = 0;/* Flag indicating whether the user\'s terminal is in non-blocking mode. */static int in_non_blocking_mode = 0;/* Common data for the client loop code. */static int quit_pending;	/* Set to non-zero to quit the client loop. */static int escape_char;		/* Escape character. */static int escape_pending;	/* Last character was the escape character */static int last_was_cr;		/* Last character was a newline. */static int exit_status;		/* Used to store the exit status of the command. */static int stdin_eof;		/* EOF has been encountered on standard error. */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 u_long stdin_bytes, stdout_bytes, stderr_bytes;static u_int buffer_high;/* Soft max buffer size. */static int connection_in;	/* Connection to server (input). */static int connection_out;	/* Connection to server (output). */static int need_rekeying;	/* Set to non-zero if rekeying is requested. */static int session_closed = 0;	/* In SSH2: login session closed. */static void client_init_dispatch(void);int	session_ident = -1;/*XXX*/extern Kex *xxx_kex;/* Restores stdin to blocking mode. */static voidleave_non_blocking(void){	if (in_non_blocking_mode) {		(void) fcntl(fileno(stdin), F_SETFL, 0);		in_non_blocking_mode = 0;		fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL);	}}/* Puts stdin terminal in non-blocking mode. */static voidenter_non_blocking(void){	in_non_blocking_mode = 1;	(void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);	fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL);}/* * Signal handler for the window change signal (SIGWINCH).  This just sets a * flag indicating that the window has changed. */static voidwindow_change_handler(int sig){	received_window_change_signal = 1;	signal(SIGWINCH, window_change_handler);}/* * Signal handler for signals that cause the program to terminate.  These * signals must be trapped to restore terminal modes. */static voidsignal_handler(int sig){	received_signal = sig;	quit_pending = 1;}/* * Returns current time in seconds from Jan 1, 1970 with the maximum * available resolution. */static doubleget_current_time(void){	struct timeval tv;	gettimeofday(&tv, NULL);	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;}/* * This is called when the interactive is entered.  This checks if there is * an EOF coming on stdin.  We must check this explicitly, as select() does * not appear to wake up when redirecting from /dev/null. */static voidclient_check_initial_eof_on_stdin(void){	int len;	char buf[1];	/*	 * If standard input is to be "redirected from /dev/null", we simply	 * mark that we have seen an EOF and send an EOF message to the	 * server. Otherwise, we try to read a single character; it appears	 * that for some files, such /dev/null, select() never wakes up for	 * read for this descriptor, which means that we never get EOF.  This	 * way we will get the EOF if stdin comes from /dev/null or similar.	 */	if (stdin_null_flag) {		/* Fake EOF on stdin. */		debug("Sending eof.");		stdin_eof = 1;		packet_start(SSH_CMSG_EOF);		packet_send();	} else {		enter_non_blocking();		/* Check for immediate EOF on stdin. */		len = read(fileno(stdin), buf, 1);		if (len == 0) {			/* EOF.  Record that we have seen it and send EOF to server. */			debug("Sending eof.");			stdin_eof = 1;			packet_start(SSH_CMSG_EOF);			packet_send();		} else if (len > 0) {			/*			 * Got data.  We must store the data in the buffer,			 * and also process it as an escape character if			 * appropriate.			 */			if ((u_char) buf[0] == escape_char)				escape_pending = 1;			else				buffer_append(&stdin_buffer, buf, 1);		}		leave_non_blocking();	}}/* * Make packets from buffered stdin data, and buffer them for sending to the * connection. */static voidclient_make_packets_from_stdin_data(void){	u_int len;	/* Send buffered stdin data to the server. */	while (buffer_len(&stdin_buffer) > 0 &&	    packet_not_very_much_data_to_write()) {		len = buffer_len(&stdin_buffer);		/* Keep the packets at reasonable size. */		if (len > packet_get_maxsize())			len = packet_get_maxsize();		packet_start(SSH_CMSG_STDIN_DATA);		packet_put_string(buffer_ptr(&stdin_buffer), len);		packet_send();		buffer_consume(&stdin_buffer, len);		stdin_bytes += len;		/* If we have a pending EOF, send it now. */		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {			packet_start(SSH_CMSG_EOF);			packet_send();		}	}}/* * Checks if the client window has changed, and sends a packet about it to * the server if so.  The actual change is detected elsewhere (by a software * interrupt on Unix); this just checks the flag and sends a message if * appropriate. */static voidclient_check_window_change(void){	struct winsize ws;	if (! received_window_change_signal)		return;	/** XXX race */	received_window_change_signal = 0;	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)		return;	debug2("client_check_window_change: changed");	if (compat20) {		channel_request_start(session_ident, "window-change", 0);		packet_put_int(ws.ws_col);		packet_put_int(ws.ws_row);		packet_put_int(ws.ws_xpixel);		packet_put_int(ws.ws_ypixel);		packet_send();	} else {		packet_start(SSH_CMSG_WINDOW_SIZE);		packet_put_int(ws.ws_row);		packet_put_int(ws.ws_col);		packet_put_int(ws.ws_xpixel);		packet_put_int(ws.ws_ypixel);		packet_send();	}}/* * Waits until the client can do something (some data becomes available on * one of the file descriptors). */static voidclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,    int *maxfdp, int *nallocp, int rekeying){	/* Add any selections by the channel mechanism. */	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);	if (!compat20) {		/* Read from the connection, unless our buffers are full. */		if (buffer_len(&stdout_buffer) < buffer_high &&		    buffer_len(&stderr_buffer) < buffer_high &&		    channel_not_very_much_buffered_data())			FD_SET(connection_in, *readsetp);		/*		 * Read from stdin, unless we have seen EOF or have very much		 * buffered data to send to the server.		 */		if (!stdin_eof && packet_not_very_much_data_to_write())			FD_SET(fileno(stdin), *readsetp);		/* Select stdout/stderr if have data in buffer. */		if (buffer_len(&stdout_buffer) > 0)			FD_SET(fileno(stdout), *writesetp);		if (buffer_len(&stderr_buffer) > 0)			FD_SET(fileno(stderr), *writesetp);	} else {		/* channel_prepare_select could have closed the last channel */		if (session_closed && !channel_still_open() &&		    !packet_have_data_to_write()) {			/* clear mask since we did not call select() */			memset(*readsetp, 0, *nallocp);			memset(*writesetp, 0, *nallocp);			return;		} else {			FD_SET(connection_in, *readsetp);		}	}	/* Select server connection if have data to write to the server. */	if (packet_have_data_to_write())		FD_SET(connection_out, *writesetp);	/*	 * Wait for something to happen.  This will suspend the process until	 * some selected descriptor can be read, written, or has some other	 * event pending. Note: if you want to implement SSH_MSG_IGNORE	 * messages to fool traffic analysis, this might be the place to do	 * it: just have a random timeout for the select, and send a random	 * SSH_MSG_IGNORE packet when the timeout expires.	 */	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {		char buf[100];		/*		 * We have to clear the select masks, because we return.		 * We have to return, because the mainloop checks for the flags		 * set by the signal handlers.		 */		memset(*readsetp, 0, *nallocp);		memset(*writesetp, 0, *nallocp);		if (errno == EINTR)			return;		/* Note: we might still have data in the buffers. */		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));		buffer_append(&stderr_buffer, buf, strlen(buf));		quit_pending = 1;	}}static voidclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr){	struct winsize oldws, newws;	/* Flush stdout and stderr buffers. */	if (buffer_len(bout) > 0)		atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout));	if (buffer_len(berr) > 0)		atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr));	leave_raw_mode();	/*	 * Free (and clear) the buffer to reduce the amount of data that gets	 * written to swap.	 */	buffer_free(bin);	buffer_free(bout);	buffer_free(berr);	/* Save old window size. */	ioctl(fileno(stdin), TIOCGWINSZ, &oldws);	/* Send the suspend signal to the program itself. */	kill(getpid(), SIGTSTP);	/* Check if the window size has changed. */	if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&	    (oldws.ws_row != newws.ws_row ||	    oldws.ws_col != newws.ws_col ||	    oldws.ws_xpixel != newws.ws_xpixel ||	    oldws.ws_ypixel != newws.ws_ypixel))		received_window_change_signal = 1;	/* OK, we have been continued by the user. Reinitialize buffers. */	buffer_init(bin);	buffer_init(bout);	buffer_init(berr);	enter_raw_mode();}static voidclient_process_net_input(fd_set * readset){	int len;	char buf[8192];	/*	 * Read input from the server, and add any such data to the buffer of	 * the packet subsystem.	 */	if (FD_ISSET(connection_in, readset)) {		/* Read as much as possible. */		len = read(connection_in, buf, sizeof(buf));		if (len == 0) {			/* Received EOF.  The remote host has closed the connection. */			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",				 host);			buffer_append(&stderr_buffer, buf, strlen(buf));			quit_pending = 1;			return;		}		/*		 * There is a kernel bug on Solaris that causes select to		 * sometimes wake up even though there is no data available.		 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -