| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 1 | /* | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 2 |  * Author: Tatu Ylonen <ylo@cs.hut.fi> | 
 | 3 |  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 
 | 4 |  *                    All rights reserved | 
 | 5 |  * Created: Sun Sep 10 00:30:37 1995 ylo | 
 | 6 |  * Server main loop for handling the interactive session. | 
 | 7 |  */ | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 8 | /* | 
 | 9 |  * SSH2 support by Markus Friedl. | 
 | 10 |  * Copyright (c) 2000 Markus Friedl. All rights reserved. | 
 | 11 |  */ | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 12 |  | 
 | 13 | #include "includes.h" | 
 | 14 | #include "xmalloc.h" | 
 | 15 | #include "ssh.h" | 
 | 16 | #include "packet.h" | 
 | 17 | #include "buffer.h" | 
 | 18 | #include "servconf.h" | 
 | 19 | #include "pty.h" | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 20 | #include "channels.h" | 
 | 21 |  | 
 | 22 | #include "compat.h" | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 23 | #include "ssh2.h" | 
 | 24 | #include "session.h" | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 25 | #include "dispatch.h" | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 26 |  | 
 | 27 | static Buffer stdin_buffer;	/* Buffer for stdin data. */ | 
 | 28 | static Buffer stdout_buffer;	/* Buffer for stdout data. */ | 
 | 29 | static Buffer stderr_buffer;	/* Buffer for stderr data. */ | 
 | 30 | static int fdin;		/* Descriptor for stdin (for writing) */ | 
 | 31 | static int fdout;		/* Descriptor for stdout (for reading); | 
 | 32 | 				   May be same number as fdin. */ | 
 | 33 | static int fderr;		/* Descriptor for stderr.  May be -1. */ | 
 | 34 | static long stdin_bytes = 0;	/* Number of bytes written to stdin. */ | 
 | 35 | static long stdout_bytes = 0;	/* Number of stdout bytes sent to client. */ | 
 | 36 | static long stderr_bytes = 0;	/* Number of stderr bytes sent to client. */ | 
 | 37 | static long fdout_bytes = 0;	/* Number of stdout bytes read from program. */ | 
 | 38 | static int stdin_eof = 0;	/* EOF message received from client. */ | 
 | 39 | static int fdout_eof = 0;	/* EOF encountered reading from fdout. */ | 
 | 40 | static int fderr_eof = 0;	/* EOF encountered readung from fderr. */ | 
 | 41 | static int connection_in;	/* Connection to client (input). */ | 
 | 42 | static int connection_out;	/* Connection to client (output). */ | 
 | 43 | static unsigned int buffer_high;/* "Soft" max buffer size. */ | 
 | 44 | static int max_fd;		/* Max file descriptor number for select(). */ | 
 | 45 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 46 | /* | 
 | 47 |  * This SIGCHLD kludge is used to detect when the child exits.  The server | 
 | 48 |  * will exit after that, as soon as forwarded connections have terminated. | 
| Damien Miller | b284b54 | 2000-01-17 20:55:18 +1100 | [diff] [blame] | 49 |  * | 
 | 50 |  * After SIGCHLD child_has_selected is set to 1 after the first pass | 
 | 51 |  * through the wait_until_can_do_something() select(). This ensures | 
 | 52 |  * that the child's output gets a chance to drain before it is yanked. | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 53 |  */ | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 54 |  | 
| Damien Miller | 166fca8 | 2000-04-20 07:42:21 +1000 | [diff] [blame] | 55 | static pid_t child_pid;			/* Pid of the child. */ | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 56 | static volatile int child_terminated;	/* The child has terminated. */ | 
| Damien Miller | b284b54 | 2000-01-17 20:55:18 +1100 | [diff] [blame] | 57 | static volatile int child_has_selected; /* Child has had chance to drain. */ | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 58 | static volatile int child_wait_status;	/* Status from wait(). */ | 
 | 59 |  | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 60 | void	server_init_dispatch(void); | 
 | 61 |  | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 62 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 63 | sigchld_handler(int sig) | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 64 | { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 65 | 	int save_errno = errno; | 
| Damien Miller | 166fca8 | 2000-04-20 07:42:21 +1000 | [diff] [blame] | 66 | 	pid_t wait_pid; | 
 | 67 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 68 | 	debug("Received SIGCHLD."); | 
 | 69 | 	wait_pid = wait((int *) &child_wait_status); | 
 | 70 | 	if (wait_pid != -1) { | 
 | 71 | 		if (wait_pid != child_pid) | 
 | 72 | 			error("Strange, got SIGCHLD and wait returned pid %d but child is %d", | 
 | 73 | 			      wait_pid, child_pid); | 
 | 74 | 		if (WIFEXITED(child_wait_status) || | 
 | 75 | 		    WIFSIGNALED(child_wait_status)) | 
 | 76 | 			child_terminated = 1; | 
| Damien Miller | b284b54 | 2000-01-17 20:55:18 +1100 | [diff] [blame] | 77 | 			child_has_selected = 0; | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 78 | 	} | 
 | 79 | 	signal(SIGCHLD, sigchld_handler); | 
 | 80 | 	errno = save_errno; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 81 | } | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 82 | void | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 83 | sigchld_handler2(int sig) | 
 | 84 | { | 
 | 85 | 	int save_errno = errno; | 
 | 86 | 	debug("Received SIGCHLD."); | 
 | 87 | 	child_terminated = 1; | 
 | 88 | 	signal(SIGCHLD, sigchld_handler2); | 
 | 89 | 	errno = save_errno; | 
 | 90 | } | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 91 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 92 | /* | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 93 |  * Make packets from buffered stderr data, and buffer it for sending | 
 | 94 |  * to the client. | 
 | 95 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 96 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 97 | make_packets_from_stderr_data() | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 98 | { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 99 | 	int len; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 100 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 101 | 	/* Send buffered stderr data to the client. */ | 
 | 102 | 	while (buffer_len(&stderr_buffer) > 0 && | 
| Damien Miller | 037a0dc | 1999-12-07 15:38:31 +1100 | [diff] [blame] | 103 | 	    packet_not_very_much_data_to_write()) { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 104 | 		len = buffer_len(&stderr_buffer); | 
 | 105 | 		if (packet_is_interactive()) { | 
 | 106 | 			if (len > 512) | 
 | 107 | 				len = 512; | 
 | 108 | 		} else { | 
 | 109 | 			/* Keep the packets at reasonable size. */ | 
 | 110 | 			if (len > packet_get_maxsize()) | 
 | 111 | 				len = packet_get_maxsize(); | 
 | 112 | 		} | 
 | 113 | 		packet_start(SSH_SMSG_STDERR_DATA); | 
 | 114 | 		packet_put_string(buffer_ptr(&stderr_buffer), len); | 
 | 115 | 		packet_send(); | 
 | 116 | 		buffer_consume(&stderr_buffer, len); | 
 | 117 | 		stderr_bytes += len; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 118 | 	} | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 119 | } | 
 | 120 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 121 | /* | 
 | 122 |  * Make packets from buffered stdout data, and buffer it for sending to the | 
 | 123 |  * client. | 
 | 124 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 125 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 126 | make_packets_from_stdout_data() | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 127 | { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 128 | 	int len; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 129 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 130 | 	/* Send buffered stdout data to the client. */ | 
 | 131 | 	while (buffer_len(&stdout_buffer) > 0 && | 
| Damien Miller | 037a0dc | 1999-12-07 15:38:31 +1100 | [diff] [blame] | 132 | 	    packet_not_very_much_data_to_write()) { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 133 | 		len = buffer_len(&stdout_buffer); | 
 | 134 | 		if (packet_is_interactive()) { | 
 | 135 | 			if (len > 512) | 
 | 136 | 				len = 512; | 
 | 137 | 		} else { | 
 | 138 | 			/* Keep the packets at reasonable size. */ | 
 | 139 | 			if (len > packet_get_maxsize()) | 
 | 140 | 				len = packet_get_maxsize();	 | 
 | 141 | 		} | 
 | 142 | 		packet_start(SSH_SMSG_STDOUT_DATA); | 
 | 143 | 		packet_put_string(buffer_ptr(&stdout_buffer), len); | 
 | 144 | 		packet_send(); | 
 | 145 | 		buffer_consume(&stdout_buffer, len); | 
 | 146 | 		stdout_bytes += len; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 147 | 	} | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 148 | } | 
 | 149 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 150 | /* | 
 | 151 |  * Sleep in select() until we can do something.  This will initialize the | 
 | 152 |  * select masks.  Upon return, the masks will indicate which descriptors | 
 | 153 |  * have data or can accept data.  Optionally, a maximum time can be specified | 
 | 154 |  * for the duration of the wait (0 = infinite). | 
 | 155 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 156 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 157 | wait_until_can_do_something(fd_set * readset, fd_set * writeset, | 
 | 158 | 			    unsigned int max_time_milliseconds) | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 159 | { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 160 | 	struct timeval tv, *tvp; | 
 | 161 | 	int ret; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 162 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 163 | 	/* When select fails we restart from here. */ | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 164 | retry_select: | 
 | 165 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 166 | 	/* Initialize select() masks. */ | 
 | 167 | 	FD_ZERO(readset); | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 168 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 169 | 	/* | 
 | 170 | 	 * Read packets from the client unless we have too much buffered | 
 | 171 | 	 * stdin or channel data. | 
 | 172 | 	 */ | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 173 | 	if (compat20) { | 
 | 174 | 		// wrong: bad conditionXXX | 
 | 175 | 		if (channel_not_very_much_buffered_data()) | 
 | 176 | 			FD_SET(connection_in, readset); | 
 | 177 | 	} else { | 
 | 178 | 		if (buffer_len(&stdin_buffer) < 4096 && | 
 | 179 | 		    channel_not_very_much_buffered_data()) | 
 | 180 | 			FD_SET(connection_in, readset); | 
 | 181 | 	} | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 182 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 183 | 	/* | 
 | 184 | 	 * If there is not too much data already buffered going to the | 
 | 185 | 	 * client, try to get some more data from the program. | 
 | 186 | 	 */ | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 187 | 	if (!compat20 && packet_not_very_much_data_to_write()) { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 188 | 		if (!fdout_eof) | 
 | 189 | 			FD_SET(fdout, readset); | 
 | 190 | 		if (!fderr_eof) | 
 | 191 | 			FD_SET(fderr, readset); | 
 | 192 | 	} | 
 | 193 | 	FD_ZERO(writeset); | 
 | 194 |  | 
 | 195 | 	/* Set masks for channel descriptors. */ | 
 | 196 | 	channel_prepare_select(readset, writeset); | 
 | 197 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 198 | 	/* | 
 | 199 | 	 * If we have buffered packet data going to the client, mark that | 
 | 200 | 	 * descriptor. | 
 | 201 | 	 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 202 | 	if (packet_have_data_to_write()) | 
 | 203 | 		FD_SET(connection_out, writeset); | 
 | 204 |  | 
 | 205 | 	/* If we have buffered data, try to write some of that data to the | 
 | 206 | 	   program. */ | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 207 | 	if (!compat20 && fdin != -1 && buffer_len(&stdin_buffer) > 0) | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 208 | 		FD_SET(fdin, writeset); | 
 | 209 |  | 
 | 210 | 	/* Update the maximum descriptor number if appropriate. */ | 
 | 211 | 	if (channel_max_fd() > max_fd) | 
 | 212 | 		max_fd = channel_max_fd(); | 
 | 213 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 214 | 	/* | 
 | 215 | 	 * If child has terminated and there is enough buffer space to read | 
 | 216 | 	 * from it, then read as much as is available and exit. | 
 | 217 | 	 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 218 | 	if (child_terminated && packet_not_very_much_data_to_write()) | 
 | 219 | 		if (max_time_milliseconds == 0) | 
 | 220 | 			max_time_milliseconds = 100; | 
 | 221 |  | 
 | 222 | 	if (max_time_milliseconds == 0) | 
 | 223 | 		tvp = NULL; | 
 | 224 | 	else { | 
 | 225 | 		tv.tv_sec = max_time_milliseconds / 1000; | 
 | 226 | 		tv.tv_usec = 1000 * (max_time_milliseconds % 1000); | 
 | 227 | 		tvp = &tv; | 
 | 228 | 	} | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 229 | 	if (tvp!=NULL) | 
 | 230 | 		debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds); | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 231 |  | 
 | 232 | 	/* Wait for something to happen, or the timeout to expire. */ | 
 | 233 | 	ret = select(max_fd + 1, readset, writeset, NULL, tvp); | 
 | 234 |  | 
 | 235 | 	if (ret < 0) { | 
 | 236 | 		if (errno != EINTR) | 
 | 237 | 			error("select: %.100s", strerror(errno)); | 
 | 238 | 		else | 
 | 239 | 			goto retry_select; | 
 | 240 | 	} | 
| Damien Miller | b284b54 | 2000-01-17 20:55:18 +1100 | [diff] [blame] | 241 | 	 | 
 | 242 | 	if (child_terminated) | 
 | 243 | 		child_has_selected = 1; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 244 | } | 
 | 245 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 246 | /* | 
 | 247 |  * Processes input from the client and the program.  Input data is stored | 
 | 248 |  * in buffers and processed later. | 
 | 249 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 250 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 251 | process_input(fd_set * readset) | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 252 | { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 253 | 	int len; | 
 | 254 | 	char buf[16384]; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 255 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 256 | 	/* Read and buffer any input data from the client. */ | 
 | 257 | 	if (FD_ISSET(connection_in, readset)) { | 
 | 258 | 		len = read(connection_in, buf, sizeof(buf)); | 
 | 259 | 		if (len == 0) { | 
 | 260 | 			verbose("Connection closed by remote host."); | 
 | 261 | 			fatal_cleanup(); | 
 | 262 | 		} | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 263 | 		/* | 
 | 264 | 		 * There is a kernel bug on Solaris that causes select to | 
 | 265 | 		 * sometimes wake up even though there is no data available. | 
 | 266 | 		 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 267 | 		if (len < 0 && errno == EAGAIN) | 
 | 268 | 			len = 0; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 269 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 270 | 		if (len < 0) { | 
 | 271 | 			verbose("Read error from remote host: %.100s", strerror(errno)); | 
 | 272 | 			fatal_cleanup(); | 
 | 273 | 		} | 
 | 274 | 		/* Buffer any received data. */ | 
 | 275 | 		packet_process_incoming(buf, len); | 
 | 276 | 	} | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 277 | 	if (compat20) | 
 | 278 | 		return; | 
 | 279 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 280 | 	/* Read and buffer any available stdout data from the program. */ | 
 | 281 | 	if (!fdout_eof && FD_ISSET(fdout, readset)) { | 
 | 282 | 		len = read(fdout, buf, sizeof(buf)); | 
 | 283 | 		if (len <= 0) | 
 | 284 | 			fdout_eof = 1; | 
 | 285 | 		else { | 
 | 286 | 			buffer_append(&stdout_buffer, buf, len); | 
 | 287 | 			fdout_bytes += len; | 
 | 288 | 		} | 
 | 289 | 	} | 
 | 290 | 	/* Read and buffer any available stderr data from the program. */ | 
 | 291 | 	if (!fderr_eof && FD_ISSET(fderr, readset)) { | 
 | 292 | 		len = read(fderr, buf, sizeof(buf)); | 
 | 293 | 		if (len <= 0) | 
 | 294 | 			fderr_eof = 1; | 
 | 295 | 		else | 
 | 296 | 			buffer_append(&stderr_buffer, buf, len); | 
 | 297 | 	} | 
 | 298 | } | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 299 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 300 | /* | 
 | 301 |  * Sends data from internal buffers to client program stdin. | 
 | 302 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 303 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 304 | process_output(fd_set * writeset) | 
 | 305 | { | 
 | 306 | 	int len; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 307 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 308 | 	/* Write buffered data to program stdin. */ | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 309 | 	if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 310 | 		len = write(fdin, buffer_ptr(&stdin_buffer), | 
| Damien Miller | 037a0dc | 1999-12-07 15:38:31 +1100 | [diff] [blame] | 311 | 		    buffer_len(&stdin_buffer)); | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 312 | 		if (len <= 0) { | 
 | 313 | #ifdef USE_PIPES | 
 | 314 | 			close(fdin); | 
 | 315 | #else | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 316 | 			if (fdin != fdout) | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 317 | 				close(fdin); | 
 | 318 | 			else | 
 | 319 | 				shutdown(fdin, SHUT_WR); /* We will no longer send. */ | 
 | 320 | #endif | 
 | 321 | 			fdin = -1; | 
 | 322 | 		} else { | 
 | 323 | 			/* Successful write.  Consume the data from the buffer. */ | 
 | 324 | 			buffer_consume(&stdin_buffer, len); | 
 | 325 | 			/* Update the count of bytes written to the program. */ | 
 | 326 | 			stdin_bytes += len; | 
 | 327 | 		} | 
 | 328 | 	} | 
 | 329 | 	/* Send any buffered packet data to the client. */ | 
 | 330 | 	if (FD_ISSET(connection_out, writeset)) | 
 | 331 | 		packet_write_poll(); | 
 | 332 | } | 
 | 333 |  | 
 | 334 | /* | 
 | 335 |  * Wait until all buffered output has been sent to the client. | 
 | 336 |  * This is used when the program terminates. | 
 | 337 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 338 | void | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 339 | drain_output() | 
 | 340 | { | 
 | 341 | 	/* Send any buffered stdout data to the client. */ | 
 | 342 | 	if (buffer_len(&stdout_buffer) > 0) { | 
 | 343 | 		packet_start(SSH_SMSG_STDOUT_DATA); | 
 | 344 | 		packet_put_string(buffer_ptr(&stdout_buffer), | 
 | 345 | 				  buffer_len(&stdout_buffer)); | 
 | 346 | 		packet_send(); | 
 | 347 | 		/* Update the count of sent bytes. */ | 
 | 348 | 		stdout_bytes += buffer_len(&stdout_buffer); | 
 | 349 | 	} | 
 | 350 | 	/* Send any buffered stderr data to the client. */ | 
 | 351 | 	if (buffer_len(&stderr_buffer) > 0) { | 
 | 352 | 		packet_start(SSH_SMSG_STDERR_DATA); | 
 | 353 | 		packet_put_string(buffer_ptr(&stderr_buffer), | 
 | 354 | 				  buffer_len(&stderr_buffer)); | 
 | 355 | 		packet_send(); | 
 | 356 | 		/* Update the count of sent bytes. */ | 
 | 357 | 		stderr_bytes += buffer_len(&stderr_buffer); | 
 | 358 | 	} | 
 | 359 | 	/* Wait until all buffered data has been written to the client. */ | 
 | 360 | 	packet_write_wait(); | 
 | 361 | } | 
 | 362 |  | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 363 | void | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 364 | process_buffered_input_packets() | 
 | 365 | { | 
 | 366 | 	dispatch_run(DISPATCH_NONBLOCK, NULL); | 
 | 367 | } | 
 | 368 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 369 | /* | 
 | 370 |  * Performs the interactive session.  This handles data transmission between | 
 | 371 |  * the client and the program.  Note that the notion of stdin, stdout, and | 
 | 372 |  * stderr in this function is sort of reversed: this function writes to | 
 | 373 |  * stdin (of the child program), and reads from stdout and stderr (of the | 
 | 374 |  * child program). | 
 | 375 |  */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 376 | void | 
| Damien Miller | 166fca8 | 2000-04-20 07:42:21 +1000 | [diff] [blame] | 377 | server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 378 | { | 
| Damien Miller | 166fca8 | 2000-04-20 07:42:21 +1000 | [diff] [blame] | 379 | 	int wait_status;	/* Status returned by wait(). */ | 
 | 380 | 	pid_t wait_pid;		/* pid returned by wait(). */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 381 | 	int waiting_termination = 0;	/* Have displayed waiting close message. */ | 
 | 382 | 	unsigned int max_time_milliseconds; | 
 | 383 | 	unsigned int previous_stdout_buffer_bytes; | 
 | 384 | 	unsigned int stdout_buffer_bytes; | 
 | 385 | 	int type; | 
 | 386 |  | 
 | 387 | 	debug("Entering interactive session."); | 
 | 388 |  | 
 | 389 | 	/* Initialize the SIGCHLD kludge. */ | 
 | 390 | 	child_pid = pid; | 
 | 391 | 	child_terminated = 0; | 
| Damien Miller | b284b54 | 2000-01-17 20:55:18 +1100 | [diff] [blame] | 392 | 	child_has_selected = 0; | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 393 | 	signal(SIGCHLD, sigchld_handler); | 
 | 394 |  | 
 | 395 | 	/* Initialize our global variables. */ | 
 | 396 | 	fdin = fdin_arg; | 
 | 397 | 	fdout = fdout_arg; | 
 | 398 | 	fderr = fderr_arg; | 
 | 399 | 	connection_in = packet_get_connection_in(); | 
 | 400 | 	connection_out = packet_get_connection_out(); | 
 | 401 |  | 
 | 402 | 	previous_stdout_buffer_bytes = 0; | 
 | 403 |  | 
 | 404 | 	/* Set approximate I/O buffer size. */ | 
 | 405 | 	if (packet_is_interactive()) | 
 | 406 | 		buffer_high = 4096; | 
 | 407 | 	else | 
 | 408 | 		buffer_high = 64 * 1024; | 
 | 409 |  | 
 | 410 | 	/* Initialize max_fd to the maximum of the known file descriptors. */ | 
 | 411 | 	max_fd = fdin; | 
 | 412 | 	if (fdout > max_fd) | 
 | 413 | 		max_fd = fdout; | 
 | 414 | 	if (fderr != -1 && fderr > max_fd) | 
 | 415 | 		max_fd = fderr; | 
 | 416 | 	if (connection_in > max_fd) | 
 | 417 | 		max_fd = connection_in; | 
 | 418 | 	if (connection_out > max_fd) | 
 | 419 | 		max_fd = connection_out; | 
 | 420 |  | 
 | 421 | 	/* Initialize Initialize buffers. */ | 
 | 422 | 	buffer_init(&stdin_buffer); | 
 | 423 | 	buffer_init(&stdout_buffer); | 
 | 424 | 	buffer_init(&stderr_buffer); | 
 | 425 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 426 | 	/* | 
 | 427 | 	 * If we have no separate fderr (which is the case when we have a pty | 
 | 428 | 	 * - there we cannot make difference between data sent to stdout and | 
 | 429 | 	 * stderr), indicate that we have seen an EOF from stderr.  This way | 
 | 430 | 	 * we don\'t need to check the descriptor everywhere. | 
 | 431 | 	 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 432 | 	if (fderr == -1) | 
 | 433 | 		fderr_eof = 1; | 
 | 434 |  | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 435 | 	server_init_dispatch(); | 
 | 436 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 437 | 	/* Main loop of the server for the interactive session mode. */ | 
 | 438 | 	for (;;) { | 
 | 439 | 		fd_set readset, writeset; | 
 | 440 |  | 
 | 441 | 		/* Process buffered packets from the client. */ | 
 | 442 | 		process_buffered_input_packets(); | 
 | 443 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 444 | 		/* | 
 | 445 | 		 * If we have received eof, and there is no more pending | 
 | 446 | 		 * input data, cause a real eof by closing fdin. | 
 | 447 | 		 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 448 | 		if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { | 
 | 449 | #ifdef USE_PIPES | 
 | 450 | 			close(fdin); | 
 | 451 | #else | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 452 | 			if (fdin != fdout) | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 453 | 				close(fdin); | 
 | 454 | 			else | 
 | 455 | 				shutdown(fdin, SHUT_WR); /* We will no longer send. */ | 
 | 456 | #endif | 
 | 457 | 			fdin = -1; | 
 | 458 | 		} | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 459 | 		/* Make packets from buffered stderr data to send to the client. */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 460 | 		make_packets_from_stderr_data(); | 
 | 461 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 462 | 		/* | 
 | 463 | 		 * Make packets from buffered stdout data to send to the | 
 | 464 | 		 * client. If there is very little to send, this arranges to | 
 | 465 | 		 * not send them now, but to wait a short while to see if we | 
 | 466 | 		 * are getting more data. This is necessary, as some systems | 
 | 467 | 		 * wake up readers from a pty after each separate character. | 
 | 468 | 		 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 469 | 		max_time_milliseconds = 0; | 
 | 470 | 		stdout_buffer_bytes = buffer_len(&stdout_buffer); | 
 | 471 | 		if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && | 
 | 472 | 		    stdout_buffer_bytes != previous_stdout_buffer_bytes) { | 
 | 473 | 			/* try again after a while */ | 
 | 474 | 			max_time_milliseconds = 10; | 
 | 475 | 		} else { | 
 | 476 | 			/* Send it now. */ | 
 | 477 | 			make_packets_from_stdout_data(); | 
 | 478 | 		} | 
 | 479 | 		previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); | 
 | 480 |  | 
 | 481 | 		/* Send channel data to the client. */ | 
 | 482 | 		if (packet_not_very_much_data_to_write()) | 
 | 483 | 			channel_output_poll(); | 
 | 484 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 485 | 		/* | 
 | 486 | 		 * Bail out of the loop if the program has closed its output | 
 | 487 | 		 * descriptors, and we have no more data to send to the | 
 | 488 | 		 * client, and there is no pending buffered data. | 
 | 489 | 		 */ | 
| Damien Miller | b284b54 | 2000-01-17 20:55:18 +1100 | [diff] [blame] | 490 | 		if (((fdout_eof && fderr_eof) ||  | 
 | 491 | 		    (child_terminated && child_has_selected)) &&  | 
 | 492 | 		    !packet_have_data_to_write() && | 
 | 493 | 		    (buffer_len(&stdout_buffer) == 0) &&  | 
 | 494 | 			 (buffer_len(&stderr_buffer) == 0)) { | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 495 | 			if (!channel_still_open()) | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 496 | 				break; | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 497 | 			if (!waiting_termination) { | 
 | 498 | 				const char *s = "Waiting for forwarded connections to terminate...\r\n"; | 
 | 499 | 				char *cp; | 
 | 500 | 				waiting_termination = 1; | 
 | 501 | 				buffer_append(&stderr_buffer, s, strlen(s)); | 
 | 502 |  | 
 | 503 | 				/* Display list of open channels. */ | 
 | 504 | 				cp = channel_open_message(); | 
 | 505 | 				buffer_append(&stderr_buffer, cp, strlen(cp)); | 
 | 506 | 				xfree(cp); | 
 | 507 | 			} | 
 | 508 | 		} | 
 | 509 | 		/* Sleep in select() until we can do something. */ | 
 | 510 | 		wait_until_can_do_something(&readset, &writeset, | 
 | 511 | 					    max_time_milliseconds); | 
 | 512 |  | 
 | 513 | 		/* Process any channel events. */ | 
 | 514 | 		channel_after_select(&readset, &writeset); | 
 | 515 |  | 
 | 516 | 		/* Process input from the client and from program stdout/stderr. */ | 
 | 517 | 		process_input(&readset); | 
 | 518 |  | 
 | 519 | 		/* Process output to the client and to program stdin. */ | 
 | 520 | 		process_output(&writeset); | 
 | 521 | 	} | 
 | 522 |  | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 523 | 	/* Cleanup and termination code. */ | 
 | 524 |  | 
 | 525 | 	/* Wait until all output has been sent to the client. */ | 
 | 526 | 	drain_output(); | 
 | 527 |  | 
 | 528 | 	debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", | 
 | 529 | 	      stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); | 
 | 530 |  | 
 | 531 | 	/* Free and clear the buffers. */ | 
 | 532 | 	buffer_free(&stdin_buffer); | 
 | 533 | 	buffer_free(&stdout_buffer); | 
 | 534 | 	buffer_free(&stderr_buffer); | 
 | 535 |  | 
 | 536 | 	/* Close the file descriptors. */ | 
 | 537 | 	if (fdout != -1) | 
 | 538 | 		close(fdout); | 
 | 539 | 	fdout = -1; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 540 | 	fdout_eof = 1; | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 541 | 	if (fderr != -1) | 
 | 542 | 		close(fderr); | 
 | 543 | 	fderr = -1; | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 544 | 	fderr_eof = 1; | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 545 | 	if (fdin != -1) | 
 | 546 | 		close(fdin); | 
 | 547 | 	fdin = -1; | 
 | 548 |  | 
 | 549 | 	/* Stop listening for channels; this removes unix domain sockets. */ | 
 | 550 | 	channel_stop_listening(); | 
 | 551 |  | 
 | 552 | 	/* Wait for the child to exit.  Get its exit status. */ | 
 | 553 | 	wait_pid = wait(&wait_status); | 
 | 554 | 	if (wait_pid < 0) { | 
 | 555 | 		/* | 
 | 556 | 		 * It is possible that the wait was handled by SIGCHLD | 
 | 557 | 		 * handler.  This may result in either: this call | 
 | 558 | 		 * returning with EINTR, or: this call returning ECHILD. | 
 | 559 | 		 */ | 
 | 560 | 		if (child_terminated) | 
 | 561 | 			wait_status = child_wait_status; | 
 | 562 | 		else | 
 | 563 | 			packet_disconnect("wait: %.100s", strerror(errno)); | 
 | 564 | 	} else { | 
 | 565 | 		/* Check if it matches the process we forked. */ | 
 | 566 | 		if (wait_pid != pid) | 
 | 567 | 			error("Strange, wait returned pid %d, expected %d", | 
| Damien Miller | aae6c61 | 1999-12-06 11:47:28 +1100 | [diff] [blame] | 568 | 			       wait_pid, pid); | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 569 | 	} | 
 | 570 |  | 
 | 571 | 	/* We no longer want our SIGCHLD handler to be called. */ | 
 | 572 | 	signal(SIGCHLD, SIG_DFL); | 
 | 573 |  | 
 | 574 | 	/* Check if it exited normally. */ | 
 | 575 | 	if (WIFEXITED(wait_status)) { | 
 | 576 | 		/* Yes, normal exit.  Get exit status and send it to the client. */ | 
 | 577 | 		debug("Command exited with status %d.", WEXITSTATUS(wait_status)); | 
 | 578 | 		packet_start(SSH_SMSG_EXITSTATUS); | 
 | 579 | 		packet_put_int(WEXITSTATUS(wait_status)); | 
 | 580 | 		packet_send(); | 
 | 581 | 		packet_write_wait(); | 
 | 582 |  | 
| Damien Miller | 5428f64 | 1999-11-25 11:54:57 +1100 | [diff] [blame] | 583 | 		/* | 
 | 584 | 		 * Wait for exit confirmation.  Note that there might be | 
 | 585 | 		 * other packets coming before it; however, the program has | 
 | 586 | 		 * already died so we just ignore them.  The client is | 
 | 587 | 		 * supposed to respond with the confirmation when it receives | 
 | 588 | 		 * the exit status. | 
 | 589 | 		 */ | 
| Damien Miller | 95def09 | 1999-11-25 00:26:21 +1100 | [diff] [blame] | 590 | 		do { | 
 | 591 | 			int plen; | 
 | 592 | 			type = packet_read(&plen); | 
 | 593 | 		} | 
 | 594 | 		while (type != SSH_CMSG_EXIT_CONFIRMATION); | 
 | 595 |  | 
 | 596 | 		debug("Received exit confirmation."); | 
 | 597 | 		return; | 
 | 598 | 	} | 
 | 599 | 	/* Check if the program terminated due to a signal. */ | 
 | 600 | 	if (WIFSIGNALED(wait_status)) | 
 | 601 | 		packet_disconnect("Command terminated on signal %d.", | 
 | 602 | 				  WTERMSIG(wait_status)); | 
 | 603 |  | 
 | 604 | 	/* Some weird exit cause.  Just exit. */ | 
 | 605 | 	packet_disconnect("wait returned status %04x.", wait_status); | 
 | 606 | 	/* NOTREACHED */ | 
| Damien Miller | d4a8b7e | 1999-10-27 13:42:43 +1000 | [diff] [blame] | 607 | } | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 608 |  | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 609 | void | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 610 | server_loop2(void) | 
 | 611 | { | 
 | 612 | 	fd_set readset, writeset; | 
 | 613 | 	int had_channel = 0; | 
 | 614 | 	int status; | 
 | 615 | 	pid_t pid; | 
 | 616 |  | 
 | 617 | 	debug("Entering interactive session for SSH2."); | 
 | 618 |  | 
 | 619 | 	signal(SIGCHLD, sigchld_handler2); | 
 | 620 | 	child_terminated = 0; | 
 | 621 | 	connection_in = packet_get_connection_in(); | 
 | 622 | 	connection_out = packet_get_connection_out(); | 
 | 623 | 	max_fd = connection_in; | 
 | 624 | 	if (connection_out > max_fd) | 
 | 625 | 		max_fd = connection_out; | 
 | 626 | 	server_init_dispatch(); | 
 | 627 |  | 
 | 628 | 	for (;;) { | 
 | 629 | 		process_buffered_input_packets(); | 
 | 630 | 		if (!had_channel && channel_still_open()) | 
 | 631 | 			had_channel = 1; | 
 | 632 | 		if (had_channel && !channel_still_open()) { | 
 | 633 | 			debug("!channel_still_open."); | 
 | 634 | 			break; | 
 | 635 | 		} | 
 | 636 | 		if (packet_not_very_much_data_to_write()) | 
 | 637 | 			channel_output_poll(); | 
 | 638 | 		wait_until_can_do_something(&readset, &writeset, 0); | 
 | 639 | 		if (child_terminated) { | 
 | 640 | 			while ((pid = waitpid(-1, &status, WNOHANG)) > 0) | 
 | 641 | 				session_close_by_pid(pid, status); | 
 | 642 | 			child_terminated = 0; | 
 | 643 | 		} | 
 | 644 | 		channel_after_select(&readset, &writeset); | 
 | 645 | 		process_input(&readset); | 
 | 646 | 		process_output(&writeset); | 
 | 647 | 	} | 
 | 648 | 	signal(SIGCHLD, SIG_DFL); | 
 | 649 | 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) | 
 | 650 | 		session_close_by_pid(pid, status); | 
 | 651 | 	channel_stop_listening(); | 
 | 652 | } | 
 | 653 |  | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 654 | void | 
 | 655 | server_input_stdin_data(int type, int plen) | 
 | 656 | { | 
 | 657 | 	char *data; | 
 | 658 | 	unsigned int data_len; | 
 | 659 |  | 
 | 660 | 	/* Stdin data from the client.  Append it to the buffer. */ | 
 | 661 | 	/* Ignore any data if the client has closed stdin. */ | 
 | 662 | 	if (fdin == -1) | 
 | 663 | 		return; | 
 | 664 | 	data = packet_get_string(&data_len); | 
 | 665 | 	packet_integrity_check(plen, (4 + data_len), type); | 
 | 666 | 	buffer_append(&stdin_buffer, data, data_len); | 
 | 667 | 	memset(data, 0, data_len); | 
 | 668 | 	xfree(data); | 
 | 669 | } | 
 | 670 |  | 
 | 671 | void | 
 | 672 | server_input_eof(int type, int plen) | 
 | 673 | { | 
 | 674 | 	/* | 
 | 675 | 	 * Eof from the client.  The stdin descriptor to the | 
 | 676 | 	 * program will be closed when all buffered data has | 
 | 677 | 	 * drained. | 
 | 678 | 	 */ | 
 | 679 | 	debug("EOF received for stdin."); | 
 | 680 | 	packet_integrity_check(plen, 0, type); | 
 | 681 | 	stdin_eof = 1; | 
 | 682 | } | 
 | 683 |  | 
 | 684 | void | 
 | 685 | server_input_window_size(int type, int plen) | 
 | 686 | { | 
 | 687 | 	int row = packet_get_int(); | 
 | 688 | 	int col = packet_get_int(); | 
 | 689 | 	int xpixel = packet_get_int(); | 
 | 690 | 	int ypixel = packet_get_int(); | 
 | 691 |  | 
 | 692 | 	debug("Window change received."); | 
 | 693 | 	packet_integrity_check(plen, 4 * 4, type); | 
 | 694 | 	if (fdin != -1) | 
 | 695 | 		pty_change_window_size(fdin, row, col, xpixel, ypixel); | 
 | 696 | } | 
 | 697 |  | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 698 | int | 
 | 699 | input_direct_tcpip(void) | 
 | 700 | { | 
 | 701 | 	int sock; | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 702 | 	char *target, *originator; | 
 | 703 | 	int target_port, originator_port; | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 704 |  | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 705 | 	target = packet_get_string(NULL); | 
 | 706 | 	target_port = packet_get_int(); | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 707 | 	originator = packet_get_string(NULL); | 
 | 708 | 	originator_port = packet_get_int(); | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 709 | 	packet_done(); | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 710 | 	/* XXX check permission */ | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 711 | 	sock = channel_connect_to(target, target_port); | 
 | 712 | 	xfree(target); | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 713 | 	xfree(originator); | 
 | 714 | 	if (sock < 0) | 
 | 715 | 		return -1; | 
 | 716 | 	return channel_new("direct-tcpip", SSH_CHANNEL_OPEN, | 
 | 717 | 	    sock, sock, -1, 4*1024, 32*1024, 0, xstrdup("direct-tcpip")); | 
 | 718 | } | 
 | 719 |  | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 720 | void | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 721 | server_input_channel_open(int type, int plen) | 
 | 722 | { | 
 | 723 | 	Channel *c = NULL; | 
 | 724 | 	char *ctype; | 
 | 725 | 	int id; | 
 | 726 | 	unsigned int len; | 
 | 727 | 	int rchan; | 
 | 728 | 	int rmaxpack; | 
 | 729 | 	int rwindow; | 
 | 730 |  | 
 | 731 | 	ctype = packet_get_string(&len); | 
 | 732 | 	rchan = packet_get_int(); | 
 | 733 | 	rwindow = packet_get_int(); | 
 | 734 | 	rmaxpack = packet_get_int(); | 
 | 735 |  | 
| Damien Miller | eba71ba | 2000-04-29 23:57:08 +1000 | [diff] [blame] | 736 | 	debug("channel_input_open: ctype %s rchan %d win %d max %d", | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 737 | 	    ctype, rchan, rwindow, rmaxpack); | 
 | 738 |  | 
 | 739 | 	if (strcmp(ctype, "session") == 0) { | 
 | 740 | 		debug("open session"); | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 741 | 		packet_done(); | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 742 | 		/* | 
 | 743 | 		 * A server session has no fd to read or write | 
 | 744 | 		 * until a CHANNEL_REQUEST for a shell is made, | 
 | 745 | 		 * so we set the type to SSH_CHANNEL_LARVAL. | 
 | 746 | 		 * Additionally, a callback for handling all | 
 | 747 | 		 * CHANNEL_REQUEST messages is registered. | 
 | 748 | 		 */ | 
 | 749 | 		id = channel_new(ctype, SSH_CHANNEL_LARVAL, | 
 | 750 | 		    -1, -1, -1, 0, 32*1024, 0, xstrdup("server-session")); | 
 | 751 | 		if (session_open(id) == 1) { | 
 | 752 | 			channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, | 
 | 753 | 			    session_input_channel_req, (void *)0); | 
 | 754 | 			channel_register_cleanup(id, session_close_by_channel); | 
 | 755 | 			c = channel_lookup(id); | 
 | 756 | 		} else { | 
 | 757 | 			debug("session open failed, free channel %d", id); | 
 | 758 | 			channel_free(id); | 
 | 759 | 		} | 
 | 760 | 	} else if (strcmp(ctype, "direct-tcpip") == 0) { | 
 | 761 | 		debug("open direct-tcpip"); | 
 | 762 | 		id = input_direct_tcpip(); | 
 | 763 | 		if (id >= 0) | 
 | 764 | 			c = channel_lookup(id); | 
 | 765 | 	} | 
 | 766 | 	if (c != NULL) { | 
 | 767 | 		debug("confirm %s", ctype); | 
 | 768 | 		c->remote_id = rchan; | 
 | 769 | 		c->remote_window = rwindow; | 
 | 770 | 		c->remote_maxpacket = rmaxpack; | 
 | 771 |  | 
 | 772 | 		packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); | 
 | 773 | 		packet_put_int(c->remote_id); | 
 | 774 | 		packet_put_int(c->self); | 
 | 775 | 		packet_put_int(c->local_window); | 
 | 776 | 		packet_put_int(c->local_maxpacket); | 
 | 777 | 		packet_send(); | 
 | 778 | 	} else { | 
 | 779 | 		debug("failure %s", ctype); | 
 | 780 | 		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); | 
 | 781 | 		packet_put_int(rchan); | 
 | 782 | 		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); | 
 | 783 | 		packet_put_cstring("bla bla"); | 
 | 784 | 		packet_put_cstring(""); | 
 | 785 | 		packet_send(); | 
 | 786 | 	} | 
 | 787 | 	xfree(ctype); | 
 | 788 | } | 
 | 789 |  | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 790 | void | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 791 | server_init_dispatch_20() | 
 | 792 | { | 
 | 793 | 	debug("server_init_dispatch_20"); | 
 | 794 | 	dispatch_init(&dispatch_protocol_error); | 
 | 795 | 	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); | 
 | 796 | 	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); | 
 | 797 | 	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); | 
 | 798 | 	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); | 
 | 799 | 	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); | 
 | 800 | 	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); | 
 | 801 | 	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); | 
 | 802 | 	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); | 
 | 803 | 	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); | 
 | 804 | } | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 805 | void | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 806 | server_init_dispatch_13() | 
 | 807 | { | 
 | 808 | 	debug("server_init_dispatch_13"); | 
 | 809 | 	dispatch_init(NULL); | 
 | 810 | 	dispatch_set(SSH_CMSG_EOF, &server_input_eof); | 
 | 811 | 	dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); | 
 | 812 | 	dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); | 
 | 813 | 	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); | 
 | 814 | 	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); | 
 | 815 | 	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); | 
 | 816 | 	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); | 
 | 817 | 	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); | 
 | 818 | 	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); | 
 | 819 | } | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 820 | void | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 821 | server_init_dispatch_15() | 
 | 822 | { | 
 | 823 | 	server_init_dispatch_13(); | 
 | 824 | 	debug("server_init_dispatch_15"); | 
 | 825 | 	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); | 
 | 826 | 	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); | 
 | 827 | } | 
| Damien Miller | 4af5130 | 2000-04-16 11:18:38 +1000 | [diff] [blame] | 828 | void | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 829 | server_init_dispatch() | 
 | 830 | { | 
| Damien Miller | efb4afe | 2000-04-12 18:45:05 +1000 | [diff] [blame] | 831 | 	if (compat20) | 
 | 832 | 		server_init_dispatch_20(); | 
 | 833 | 	else if (compat13) | 
| Damien Miller | b38eff8 | 2000-04-01 11:09:21 +1000 | [diff] [blame] | 834 | 		server_init_dispatch_13(); | 
 | 835 | 	else | 
 | 836 | 		server_init_dispatch_15(); | 
 | 837 | } |