diff options
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 1524 |
1 files changed, 747 insertions, 777 deletions
diff --git a/clientloop.c b/clientloop.c index 8e8d7627d..c49346c2c 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | clientloop.c | 3 | * clientloop.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | 10 | * | |
11 | Created: Sat Sep 23 12:23:57 1995 ylo | 11 | * Created: Sat Sep 23 12:23:57 1995 ylo |
12 | 12 | * | |
13 | The main loop for the interactive session (client side). | 13 | * The main loop for the interactive session (client side). |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: clientloop.c,v 1.4 1999/11/21 02:23:53 damien Exp $"); | 18 | RCSID("$Id: clientloop.c,v 1.5 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #include "xmalloc.h" | 20 | #include "xmalloc.h" |
21 | #include "ssh.h" | 21 | #include "ssh.h" |
@@ -49,292 +49,294 @@ static int in_raw_mode = 0; | |||
49 | static int in_non_blocking_mode = 0; | 49 | static int in_non_blocking_mode = 0; |
50 | 50 | ||
51 | /* Common data for the client loop code. */ | 51 | /* Common data for the client loop code. */ |
52 | static int escape_pending; /* Last character was the escape character */ | 52 | static int escape_pending; /* Last character was the escape character */ |
53 | static int last_was_cr; /* Last character was a newline. */ | 53 | static int last_was_cr; /* Last character was a newline. */ |
54 | static int exit_status; /* Used to store the exit status of the command. */ | 54 | static int exit_status; /* Used to store the exit status of the command. */ |
55 | static int stdin_eof; /* EOF has been encountered on standard error. */ | 55 | static int stdin_eof; /* EOF has been encountered on standard error. */ |
56 | static Buffer stdin_buffer; /* Buffer for stdin data. */ | 56 | static Buffer stdin_buffer; /* Buffer for stdin data. */ |
57 | static Buffer stdout_buffer; /* Buffer for stdout data. */ | 57 | static Buffer stdout_buffer; /* Buffer for stdout data. */ |
58 | static Buffer stderr_buffer; /* Buffer for stderr data. */ | 58 | static Buffer stderr_buffer; /* Buffer for stderr data. */ |
59 | static unsigned int buffer_high; /* Soft max buffer size. */ | 59 | static unsigned int buffer_high;/* Soft max buffer size. */ |
60 | static int max_fd; /* Maximum file descriptor number in select(). */ | 60 | static int max_fd; /* Maximum file descriptor number in select(). */ |
61 | static int connection_in; /* Connection to server (input). */ | 61 | static int connection_in; /* Connection to server (input). */ |
62 | static int connection_out; /* Connection to server (output). */ | 62 | static int connection_out; /* Connection to server (output). */ |
63 | static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; | 63 | static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; |
64 | static int quit_pending; /* Set to non-zero to quit the client loop. */ | 64 | static int quit_pending; /* Set to non-zero to quit the client loop. */ |
65 | static int escape_char; /* Escape character. */ | 65 | static int escape_char; /* Escape character. */ |
66 | 66 | ||
67 | /* Returns the user\'s terminal to normal mode if it had been put in raw | 67 | /* Returns the user\'s terminal to normal mode if it had been put in raw |
68 | mode. */ | 68 | mode. */ |
69 | 69 | ||
70 | void leave_raw_mode() | 70 | void |
71 | leave_raw_mode() | ||
71 | { | 72 | { |
72 | if (!in_raw_mode) | 73 | if (!in_raw_mode) |
73 | return; | 74 | return; |
74 | in_raw_mode = 0; | 75 | in_raw_mode = 0; |
75 | if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) | 76 | if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) |
76 | perror("tcsetattr"); | 77 | perror("tcsetattr"); |
77 | 78 | ||
78 | fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL); | 79 | fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); |
79 | } | 80 | } |
80 | 81 | ||
81 | /* Puts the user\'s terminal in raw mode. */ | 82 | /* Puts the user\'s terminal in raw mode. */ |
82 | 83 | ||
83 | void enter_raw_mode() | 84 | void |
85 | enter_raw_mode() | ||
84 | { | 86 | { |
85 | struct termios tio; | 87 | struct termios tio; |
86 | 88 | ||
87 | if (tcgetattr(fileno(stdin), &tio) < 0) | 89 | if (tcgetattr(fileno(stdin), &tio) < 0) |
88 | perror("tcgetattr"); | 90 | perror("tcgetattr"); |
89 | saved_tio = tio; | 91 | saved_tio = tio; |
90 | tio.c_iflag |= IGNPAR; | 92 | tio.c_iflag |= IGNPAR; |
91 | tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF); | 93 | tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); |
92 | tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL); | 94 | tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); |
93 | #ifdef IEXTEN | 95 | #ifdef IEXTEN |
94 | tio.c_lflag &= ~IEXTEN; | 96 | tio.c_lflag &= ~IEXTEN; |
95 | #endif /* IEXTEN */ | 97 | #endif /* IEXTEN */ |
96 | tio.c_oflag &= ~OPOST; | 98 | tio.c_oflag &= ~OPOST; |
97 | tio.c_cc[VMIN] = 1; | 99 | tio.c_cc[VMIN] = 1; |
98 | tio.c_cc[VTIME] = 0; | 100 | tio.c_cc[VTIME] = 0; |
99 | if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) | 101 | if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) |
100 | perror("tcsetattr"); | 102 | perror("tcsetattr"); |
101 | in_raw_mode = 1; | 103 | in_raw_mode = 1; |
102 | 104 | ||
103 | fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL); | 105 | fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); |
104 | } | 106 | } |
105 | |||
106 | /* Puts stdin terminal in non-blocking mode. */ | ||
107 | 107 | ||
108 | /* Restores stdin to blocking mode. */ | 108 | /* Restores stdin to blocking mode. */ |
109 | 109 | ||
110 | void leave_non_blocking() | 110 | void |
111 | leave_non_blocking() | ||
111 | { | 112 | { |
112 | if (in_non_blocking_mode) | 113 | if (in_non_blocking_mode) { |
113 | { | 114 | (void) fcntl(fileno(stdin), F_SETFL, 0); |
114 | (void)fcntl(fileno(stdin), F_SETFL, 0); | 115 | in_non_blocking_mode = 0; |
115 | in_non_blocking_mode = 0; | 116 | fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL); |
116 | fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL); | 117 | } |
117 | } | ||
118 | } | 118 | } |
119 | 119 | ||
120 | void enter_non_blocking() | 120 | /* Puts stdin terminal in non-blocking mode. */ |
121 | |||
122 | void | ||
123 | enter_non_blocking() | ||
121 | { | 124 | { |
122 | in_non_blocking_mode = 1; | 125 | in_non_blocking_mode = 1; |
123 | (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); | 126 | (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); |
124 | fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL); | 127 | fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL); |
125 | } | 128 | } |
126 | 129 | ||
127 | /* Signal handler for the window change signal (SIGWINCH). This just | 130 | /* Signal handler for the window change signal (SIGWINCH). This just |
128 | sets a flag indicating that the window has changed. */ | 131 | sets a flag indicating that the window has changed. */ |
129 | 132 | ||
130 | void window_change_handler(int sig) | 133 | void |
134 | window_change_handler(int sig) | ||
131 | { | 135 | { |
132 | received_window_change_signal = 1; | 136 | received_window_change_signal = 1; |
133 | signal(SIGWINCH, window_change_handler); | 137 | signal(SIGWINCH, window_change_handler); |
134 | } | 138 | } |
135 | 139 | ||
136 | /* Signal handler for signals that cause the program to terminate. These | 140 | /* Signal handler for signals that cause the program to terminate. These |
137 | signals must be trapped to restore terminal modes. */ | 141 | signals must be trapped to restore terminal modes. */ |
138 | 142 | ||
139 | void signal_handler(int sig) | 143 | void |
144 | signal_handler(int sig) | ||
140 | { | 145 | { |
141 | if (in_raw_mode) | 146 | if (in_raw_mode) |
142 | leave_raw_mode(); | 147 | leave_raw_mode(); |
143 | if (in_non_blocking_mode) | 148 | if (in_non_blocking_mode) |
144 | leave_non_blocking(); | 149 | leave_non_blocking(); |
145 | channel_stop_listening(); | 150 | channel_stop_listening(); |
146 | packet_close(); | 151 | packet_close(); |
147 | fatal("Killed by signal %d.", sig); | 152 | fatal("Killed by signal %d.", sig); |
148 | } | 153 | } |
149 | 154 | ||
150 | /* Returns current time in seconds from Jan 1, 1970 with the maximum available | 155 | /* Returns current time in seconds from Jan 1, 1970 with the maximum available |
151 | resolution. */ | 156 | resolution. */ |
152 | 157 | ||
153 | double get_current_time() | 158 | double |
159 | get_current_time() | ||
154 | { | 160 | { |
155 | struct timeval tv; | 161 | struct timeval tv; |
156 | gettimeofday(&tv, NULL); | 162 | gettimeofday(&tv, NULL); |
157 | return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; | 163 | return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; |
158 | } | 164 | } |
159 | 165 | ||
160 | /* This is called when the interactive is entered. This checks if there | 166 | /* This is called when the interactive is entered. This checks if there |
161 | is an EOF coming on stdin. We must check this explicitly, as select() | 167 | is an EOF coming on stdin. We must check this explicitly, as select() |
162 | does not appear to wake up when redirecting from /dev/null. */ | 168 | does not appear to wake up when redirecting from /dev/null. */ |
163 | 169 | ||
164 | void client_check_initial_eof_on_stdin() | 170 | void |
171 | client_check_initial_eof_on_stdin() | ||
165 | { | 172 | { |
166 | int len; | 173 | int len; |
167 | char buf[1]; | 174 | char buf[1]; |
168 | 175 | ||
169 | /* If standard input is to be "redirected from /dev/null", we simply | 176 | /* If standard input is to be "redirected from /dev/null", we |
170 | mark that we have seen an EOF and send an EOF message to the server. | 177 | simply mark that we have seen an EOF and send an EOF message to |
171 | Otherwise, we try to read a single character; it appears that for some | 178 | the server. Otherwise, we try to read a single character; it |
172 | files, such /dev/null, select() never wakes up for read for this | 179 | appears that for some files, such /dev/null, select() never |
173 | descriptor, which means that we never get EOF. This way we will get | 180 | wakes up for read for this descriptor, which means that we |
174 | the EOF if stdin comes from /dev/null or similar. */ | 181 | never get EOF. This way we will get the EOF if stdin comes |
175 | if (stdin_null_flag) | 182 | from /dev/null or similar. */ |
176 | { | 183 | if (stdin_null_flag) { |
177 | /* Fake EOF on stdin. */ | 184 | /* Fake EOF on stdin. */ |
178 | debug("Sending eof."); | 185 | debug("Sending eof."); |
179 | stdin_eof = 1; | 186 | stdin_eof = 1; |
180 | packet_start(SSH_CMSG_EOF); | 187 | packet_start(SSH_CMSG_EOF); |
181 | packet_send(); | 188 | packet_send(); |
182 | } | 189 | } else { |
183 | else | 190 | /* Enter non-blocking mode for stdin. */ |
184 | { | 191 | enter_non_blocking(); |
185 | /* Enter non-blocking mode for stdin. */ | 192 | |
186 | enter_non_blocking(); | 193 | /* Check for immediate EOF on stdin. */ |
187 | 194 | len = read(fileno(stdin), buf, 1); | |
188 | /* Check for immediate EOF on stdin. */ | 195 | if (len == 0) { |
189 | len = read(fileno(stdin), buf, 1); | 196 | /* EOF. Record that we have seen it and send EOF |
190 | if (len == 0) | 197 | to server. */ |
191 | { | 198 | debug("Sending eof."); |
192 | /* EOF. Record that we have seen it and send EOF to server. */ | 199 | stdin_eof = 1; |
193 | debug("Sending eof."); | 200 | packet_start(SSH_CMSG_EOF); |
194 | stdin_eof = 1; | 201 | packet_send(); |
195 | packet_start(SSH_CMSG_EOF); | 202 | } else if (len > 0) { |
196 | packet_send(); | 203 | /* Got data. We must store the data in the |
204 | buffer, and also process it as an escape | ||
205 | character if appropriate. */ | ||
206 | if ((unsigned char) buf[0] == escape_char) | ||
207 | escape_pending = 1; | ||
208 | else { | ||
209 | buffer_append(&stdin_buffer, buf, 1); | ||
210 | stdin_bytes += 1; | ||
211 | } | ||
212 | } | ||
213 | /* Leave non-blocking mode. */ | ||
214 | leave_non_blocking(); | ||
197 | } | 215 | } |
198 | else | ||
199 | if (len > 0) | ||
200 | { | ||
201 | /* Got data. We must store the data in the buffer, and also | ||
202 | process it as an escape character if appropriate. */ | ||
203 | if ((unsigned char)buf[0] == escape_char) | ||
204 | escape_pending = 1; | ||
205 | else | ||
206 | { | ||
207 | buffer_append(&stdin_buffer, buf, 1); | ||
208 | stdin_bytes += 1; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /* Leave non-blocking mode. */ | ||
213 | leave_non_blocking(); | ||
214 | } | ||
215 | } | 216 | } |
216 | 217 | ||
217 | /* Get packets from the connection input buffer, and process them as long | 218 | /* Get packets from the connection input buffer, and process them as long |
218 | as there are packets available. */ | 219 | as there are packets available. */ |
219 | 220 | ||
220 | void client_process_buffered_input_packets() | 221 | void |
222 | client_process_buffered_input_packets() | ||
221 | { | 223 | { |
222 | int type; | 224 | int type; |
223 | char *data; | 225 | char *data; |
224 | unsigned int data_len; | 226 | unsigned int data_len; |
225 | int payload_len; | 227 | int payload_len; |
226 | 228 | ||
227 | /* Process any buffered packets from the server. */ | 229 | /* Process any buffered packets from the server. */ |
228 | while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) | 230 | while (!quit_pending && |
229 | { | 231 | (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { |
230 | switch (type) | 232 | switch (type) { |
231 | { | 233 | |
232 | 234 | case SSH_SMSG_STDOUT_DATA: | |
233 | case SSH_SMSG_STDOUT_DATA: | 235 | data = packet_get_string(&data_len); |
234 | data = packet_get_string(&data_len); | 236 | packet_integrity_check(payload_len, 4 + data_len, type); |
235 | packet_integrity_check(payload_len, 4 + data_len, type); | 237 | buffer_append(&stdout_buffer, data, data_len); |
236 | buffer_append(&stdout_buffer, data, data_len); | 238 | stdout_bytes += data_len; |
237 | stdout_bytes += data_len; | 239 | memset(data, 0, data_len); |
238 | memset(data, 0, data_len); | 240 | xfree(data); |
239 | xfree(data); | 241 | break; |
240 | break; | 242 | |
241 | 243 | case SSH_SMSG_STDERR_DATA: | |
242 | case SSH_SMSG_STDERR_DATA: | 244 | data = packet_get_string(&data_len); |
243 | data = packet_get_string(&data_len); | 245 | packet_integrity_check(payload_len, 4 + data_len, type); |
244 | packet_integrity_check(payload_len, 4 + data_len, type); | 246 | buffer_append(&stderr_buffer, data, data_len); |
245 | buffer_append(&stderr_buffer, data, data_len); | 247 | stdout_bytes += data_len; |
246 | stdout_bytes += data_len; | 248 | memset(data, 0, data_len); |
247 | memset(data, 0, data_len); | 249 | xfree(data); |
248 | xfree(data); | 250 | break; |
249 | break; | 251 | |
250 | 252 | case SSH_SMSG_EXITSTATUS: | |
251 | case SSH_SMSG_EXITSTATUS: | 253 | packet_integrity_check(payload_len, 4, type); |
252 | packet_integrity_check(payload_len, 4, type); | 254 | exit_status = packet_get_int(); |
253 | exit_status = packet_get_int(); | 255 | /* Acknowledge the exit. */ |
254 | /* Acknowledge the exit. */ | 256 | packet_start(SSH_CMSG_EXIT_CONFIRMATION); |
255 | packet_start(SSH_CMSG_EXIT_CONFIRMATION); | 257 | packet_send(); |
256 | packet_send(); | 258 | /* Must wait for packet to be sent since we are |
257 | /* Must wait for packet to be sent since we are exiting the | 259 | exiting the loop. */ |
258 | loop. */ | 260 | packet_write_wait(); |
259 | packet_write_wait(); | 261 | /* Flag that we want to exit. */ |
260 | /* Flag that we want to exit. */ | 262 | quit_pending = 1; |
261 | quit_pending = 1; | 263 | break; |
262 | break; | 264 | |
263 | 265 | case SSH_SMSG_X11_OPEN: | |
264 | case SSH_SMSG_X11_OPEN: | 266 | x11_input_open(payload_len); |
265 | x11_input_open(payload_len); | 267 | break; |
266 | break; | 268 | |
267 | 269 | case SSH_MSG_PORT_OPEN: | |
268 | case SSH_MSG_PORT_OPEN: | 270 | channel_input_port_open(payload_len); |
269 | channel_input_port_open(payload_len); | 271 | break; |
270 | break; | 272 | |
271 | 273 | case SSH_SMSG_AGENT_OPEN: | |
272 | case SSH_SMSG_AGENT_OPEN: | 274 | packet_integrity_check(payload_len, 4, type); |
273 | packet_integrity_check(payload_len, 4, type); | 275 | auth_input_open_request(); |
274 | auth_input_open_request(); | 276 | break; |
275 | break; | 277 | |
276 | 278 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | |
277 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | 279 | packet_integrity_check(payload_len, 4 + 4, type); |
278 | packet_integrity_check(payload_len, 4 + 4, type); | 280 | channel_input_open_confirmation(); |
279 | channel_input_open_confirmation(); | 281 | break; |
280 | break; | 282 | |
281 | 283 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | |
282 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | 284 | packet_integrity_check(payload_len, 4, type); |
283 | packet_integrity_check(payload_len, 4, type); | 285 | channel_input_open_failure(); |
284 | channel_input_open_failure(); | 286 | break; |
285 | break; | 287 | |
286 | 288 | case SSH_MSG_CHANNEL_DATA: | |
287 | case SSH_MSG_CHANNEL_DATA: | 289 | channel_input_data(payload_len); |
288 | channel_input_data(payload_len); | 290 | break; |
289 | break; | 291 | |
290 | 292 | case SSH_MSG_CHANNEL_CLOSE: | |
291 | case SSH_MSG_CHANNEL_CLOSE: | 293 | packet_integrity_check(payload_len, 4, type); |
292 | packet_integrity_check(payload_len, 4, type); | 294 | channel_input_close(); |
293 | channel_input_close(); | 295 | break; |
294 | break; | 296 | |
295 | 297 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | |
296 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | 298 | packet_integrity_check(payload_len, 4, type); |
297 | packet_integrity_check(payload_len, 4, type); | 299 | channel_input_close_confirmation(); |
298 | channel_input_close_confirmation(); | 300 | break; |
299 | break; | 301 | |
300 | 302 | default: | |
301 | default: | 303 | /* Any unknown packets received during the actual |
302 | /* Any unknown packets received during the actual session | 304 | session cause the session to terminate. This |
303 | cause the session to terminate. This is intended to make | 305 | is intended to make debugging easier since no |
304 | debugging easier since no confirmations are sent. Any | 306 | confirmations are sent. Any compatible |
305 | compatible protocol extensions must be negotiated during | 307 | protocol extensions must be negotiated during |
306 | the preparatory phase. */ | 308 | the preparatory phase. */ |
307 | packet_disconnect("Protocol error during session: type %d", | 309 | packet_disconnect("Protocol error during session: type %d", |
308 | type); | 310 | type); |
311 | } | ||
309 | } | 312 | } |
310 | } | ||
311 | } | 313 | } |
312 | 314 | ||
313 | /* Make packets from buffered stdin data, and buffer them for sending to | 315 | /* Make packets from buffered stdin data, and buffer them for sending to |
314 | the connection. */ | 316 | the connection. */ |
315 | 317 | ||
316 | void client_make_packets_from_stdin_data() | 318 | void |
319 | client_make_packets_from_stdin_data() | ||
317 | { | 320 | { |
318 | unsigned int len; | 321 | unsigned int len; |
319 | 322 | ||
320 | /* Send buffered stdin data to the server. */ | 323 | /* Send buffered stdin data to the server. */ |
321 | while (buffer_len(&stdin_buffer) > 0 && | 324 | while (buffer_len(&stdin_buffer) > 0 && |
322 | packet_not_very_much_data_to_write()) | 325 | packet_not_very_much_data_to_write()) { |
323 | { | 326 | len = buffer_len(&stdin_buffer); |
324 | len = buffer_len(&stdin_buffer); | 327 | /* Keep the packets at reasonable size. */ |
325 | if (len > packet_get_maxsize()) | 328 | if (len > packet_get_maxsize()) |
326 | len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ | 329 | len = packet_get_maxsize(); |
327 | packet_start(SSH_CMSG_STDIN_DATA); | 330 | packet_start(SSH_CMSG_STDIN_DATA); |
328 | packet_put_string(buffer_ptr(&stdin_buffer), len); | 331 | packet_put_string(buffer_ptr(&stdin_buffer), len); |
329 | packet_send(); | 332 | packet_send(); |
330 | buffer_consume(&stdin_buffer, len); | 333 | buffer_consume(&stdin_buffer, len); |
331 | /* If we have a pending EOF, send it now. */ | 334 | /* If we have a pending EOF, send it now. */ |
332 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) | 335 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) { |
333 | { | 336 | packet_start(SSH_CMSG_EOF); |
334 | packet_start(SSH_CMSG_EOF); | 337 | packet_send(); |
335 | packet_send(); | 338 | } |
336 | } | 339 | } |
337 | } | ||
338 | } | 340 | } |
339 | 341 | ||
340 | /* Checks if the client window has changed, and sends a packet about it to | 342 | /* Checks if the client window has changed, and sends a packet about it to |
@@ -342,303 +344,286 @@ void client_make_packets_from_stdin_data() | |||
342 | interrupt on Unix); this just checks the flag and sends a message if | 344 | interrupt on Unix); this just checks the flag and sends a message if |
343 | appropriate. */ | 345 | appropriate. */ |
344 | 346 | ||
345 | void client_check_window_change() | 347 | void |
348 | client_check_window_change() | ||
346 | { | 349 | { |
347 | /* Send possible window change message to the server. */ | 350 | /* Send possible window change message to the server. */ |
348 | if (received_window_change_signal) | 351 | if (received_window_change_signal) { |
349 | { | 352 | struct winsize ws; |
350 | struct winsize ws; | 353 | |
351 | 354 | /* Clear the window change indicator. */ | |
352 | /* Clear the window change indicator. */ | 355 | received_window_change_signal = 0; |
353 | received_window_change_signal = 0; | 356 | |
354 | 357 | /* Read new window size. */ | |
355 | /* Read new window size. */ | 358 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { |
356 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) | 359 | /* Successful, send the packet now. */ |
357 | { | 360 | packet_start(SSH_CMSG_WINDOW_SIZE); |
358 | /* Successful, send the packet now. */ | 361 | packet_put_int(ws.ws_row); |
359 | packet_start(SSH_CMSG_WINDOW_SIZE); | 362 | packet_put_int(ws.ws_col); |
360 | packet_put_int(ws.ws_row); | 363 | packet_put_int(ws.ws_xpixel); |
361 | packet_put_int(ws.ws_col); | 364 | packet_put_int(ws.ws_ypixel); |
362 | packet_put_int(ws.ws_xpixel); | 365 | packet_send(); |
363 | packet_put_int(ws.ws_ypixel); | 366 | } |
364 | packet_send(); | ||
365 | } | 367 | } |
366 | } | ||
367 | } | 368 | } |
368 | 369 | ||
369 | /* Waits until the client can do something (some data becomes available on | 370 | /* Waits until the client can do something (some data becomes available on |
370 | one of the file descriptors). */ | 371 | one of the file descriptors). */ |
371 | 372 | ||
372 | void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset) | 373 | void |
374 | client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) | ||
373 | { | 375 | { |
374 | /* Initialize select masks. */ | 376 | /* Initialize select masks. */ |
375 | FD_ZERO(readset); | 377 | FD_ZERO(readset); |
376 | 378 | ||
377 | /* Read from the connection, unless our buffers are full. */ | 379 | /* Read from the connection, unless our buffers are full. */ |
378 | if (buffer_len(&stdout_buffer) < buffer_high && | 380 | if (buffer_len(&stdout_buffer) < buffer_high && |
379 | buffer_len(&stderr_buffer) < buffer_high && | 381 | buffer_len(&stderr_buffer) < buffer_high && |
380 | channel_not_very_much_buffered_data()) | 382 | channel_not_very_much_buffered_data()) |
381 | FD_SET(connection_in, readset); | 383 | FD_SET(connection_in, readset); |
382 | 384 | ||
383 | /* Read from stdin, unless we have seen EOF or have very much buffered | 385 | /* Read from stdin, unless we have seen EOF or have very much |
384 | data to send to the server. */ | 386 | buffered data to send to the server. */ |
385 | if (!stdin_eof && packet_not_very_much_data_to_write()) | 387 | if (!stdin_eof && packet_not_very_much_data_to_write()) |
386 | FD_SET(fileno(stdin), readset); | 388 | FD_SET(fileno(stdin), readset); |
387 | 389 | ||
388 | FD_ZERO(writeset); | 390 | FD_ZERO(writeset); |
389 | 391 | ||
390 | /* Add any selections by the channel mechanism. */ | 392 | /* Add any selections by the channel mechanism. */ |
391 | channel_prepare_select(readset, writeset); | 393 | channel_prepare_select(readset, writeset); |
392 | 394 | ||
393 | /* Select server connection if have data to write to the server. */ | 395 | /* Select server connection if have data to write to the server. */ |
394 | if (packet_have_data_to_write()) | 396 | if (packet_have_data_to_write()) |
395 | FD_SET(connection_out, writeset); | 397 | FD_SET(connection_out, writeset); |
396 | 398 | ||
397 | /* Select stdout if have data in buffer. */ | 399 | /* Select stdout if have data in buffer. */ |
398 | if (buffer_len(&stdout_buffer) > 0) | 400 | if (buffer_len(&stdout_buffer) > 0) |
399 | FD_SET(fileno(stdout), writeset); | 401 | FD_SET(fileno(stdout), writeset); |
400 | 402 | ||
401 | /* Select stderr if have data in buffer. */ | 403 | /* Select stderr if have data in buffer. */ |
402 | if (buffer_len(&stderr_buffer) > 0) | 404 | if (buffer_len(&stderr_buffer) > 0) |
403 | FD_SET(fileno(stderr), writeset); | 405 | FD_SET(fileno(stderr), writeset); |
404 | 406 | ||
405 | /* Update maximum file descriptor number, if appropriate. */ | 407 | /* Update maximum file descriptor number, if appropriate. */ |
406 | if (channel_max_fd() > max_fd) | 408 | if (channel_max_fd() > max_fd) |
407 | max_fd = channel_max_fd(); | 409 | max_fd = channel_max_fd(); |
408 | 410 | ||
409 | /* Wait for something to happen. This will suspend the process until | 411 | /* Wait for something to happen. This will suspend the process |
410 | some selected descriptor can be read, written, or has some other | 412 | until some selected descriptor can be read, written, or has |
411 | event pending. Note: if you want to implement SSH_MSG_IGNORE | 413 | some other event pending. |
412 | messages to fool traffic analysis, this might be the place to do | 414 | Note: if you want to implement SSH_MSG_IGNORE messages to fool |
413 | it: just have a random timeout for the select, and send a random | 415 | traffic analysis, this might be the place to do it: |
414 | SSH_MSG_IGNORE packet when the timeout expires. */ | 416 | just have a random timeout for the select, and send a random |
415 | if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) | 417 | SSH_MSG_IGNORE packet when the timeout expires. */ |
416 | { | 418 | |
417 | char buf[100]; | 419 | if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { |
418 | /* Some systems fail to clear these automatically. */ | 420 | char buf[100]; |
419 | FD_ZERO(readset); | 421 | /* Some systems fail to clear these automatically. */ |
420 | FD_ZERO(writeset); | 422 | FD_ZERO(readset); |
421 | if (errno == EINTR) | 423 | FD_ZERO(writeset); |
422 | return; | 424 | if (errno == EINTR) |
423 | /* Note: we might still have data in the buffers. */ | 425 | return; |
424 | snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); | 426 | /* Note: we might still have data in the buffers. */ |
425 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 427 | snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); |
426 | stderr_bytes += strlen(buf); | 428 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
427 | quit_pending = 1; | 429 | stderr_bytes += strlen(buf); |
428 | } | 430 | quit_pending = 1; |
431 | } | ||
429 | } | 432 | } |
430 | 433 | ||
431 | void client_suspend_self() | 434 | void |
435 | client_suspend_self() | ||
432 | { | 436 | { |
433 | struct winsize oldws, newws; | 437 | struct winsize oldws, newws; |
434 | 438 | ||
435 | /* Flush stdout and stderr buffers. */ | 439 | /* Flush stdout and stderr buffers. */ |
436 | if (buffer_len(&stdout_buffer) > 0) | 440 | if (buffer_len(&stdout_buffer) > 0) |
437 | write(fileno(stdout), | 441 | write(fileno(stdout), |
438 | buffer_ptr(&stdout_buffer), | 442 | buffer_ptr(&stdout_buffer), |
439 | buffer_len(&stdout_buffer)); | 443 | buffer_len(&stdout_buffer)); |
440 | if (buffer_len(&stderr_buffer) > 0) | 444 | if (buffer_len(&stderr_buffer) > 0) |
441 | write(fileno(stderr), | 445 | write(fileno(stderr), |
442 | buffer_ptr(&stderr_buffer), | 446 | buffer_ptr(&stderr_buffer), |
443 | buffer_len(&stderr_buffer)); | 447 | buffer_len(&stderr_buffer)); |
444 | 448 | ||
445 | /* Leave raw mode. */ | 449 | /* Leave raw mode. */ |
446 | leave_raw_mode(); | 450 | leave_raw_mode(); |
447 | 451 | ||
448 | /* Free (and clear) the buffer to reduce the | 452 | /* Free (and clear) the buffer to reduce the amount of data that |
449 | amount of data that gets written to swap. */ | 453 | gets written to swap. */ |
450 | buffer_free(&stdin_buffer); | 454 | buffer_free(&stdin_buffer); |
451 | buffer_free(&stdout_buffer); | 455 | buffer_free(&stdout_buffer); |
452 | buffer_free(&stderr_buffer); | 456 | buffer_free(&stderr_buffer); |
453 | 457 | ||
454 | /* Save old window size. */ | 458 | /* Save old window size. */ |
455 | ioctl(fileno(stdin), TIOCGWINSZ, &oldws); | 459 | ioctl(fileno(stdin), TIOCGWINSZ, &oldws); |
456 | 460 | ||
457 | /* Send the suspend signal to the program | 461 | /* Send the suspend signal to the program itself. */ |
458 | itself. */ | 462 | kill(getpid(), SIGTSTP); |
459 | kill(getpid(), SIGTSTP); | 463 | |
460 | 464 | /* Check if the window size has changed. */ | |
461 | /* Check if the window size has changed. */ | 465 | if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && |
462 | if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && | 466 | (oldws.ws_row != newws.ws_row || |
463 | (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col || | 467 | oldws.ws_col != newws.ws_col || |
464 | oldws.ws_xpixel != newws.ws_xpixel || | 468 | oldws.ws_xpixel != newws.ws_xpixel || |
465 | oldws.ws_ypixel != newws.ws_ypixel)) | 469 | oldws.ws_ypixel != newws.ws_ypixel)) |
466 | received_window_change_signal = 1; | 470 | received_window_change_signal = 1; |
467 | 471 | ||
468 | /* OK, we have been continued by the user. | 472 | /* OK, we have been continued by the user. Reinitialize buffers. */ |
469 | Reinitialize buffers. */ | 473 | buffer_init(&stdin_buffer); |
470 | buffer_init(&stdin_buffer); | 474 | buffer_init(&stdout_buffer); |
471 | buffer_init(&stdout_buffer); | 475 | buffer_init(&stderr_buffer); |
472 | buffer_init(&stderr_buffer); | 476 | |
473 | 477 | /* Re-enter raw mode. */ | |
474 | /* Re-enter raw mode. */ | 478 | enter_raw_mode(); |
475 | enter_raw_mode(); | ||
476 | } | 479 | } |
477 | 480 | ||
478 | void client_process_input(fd_set *readset) | 481 | void |
482 | client_process_input(fd_set * readset) | ||
479 | { | 483 | { |
480 | int len, pid; | 484 | int len, pid; |
481 | char buf[8192], *s; | 485 | char buf[8192], *s; |
482 | 486 | ||
483 | /* Read input from the server, and add any such data to the buffer of the | 487 | /* Read input from the server, and add any such data to the buffer |
484 | packet subsystem. */ | 488 | of the packet subsystem. */ |
485 | if (FD_ISSET(connection_in, readset)) | 489 | if (FD_ISSET(connection_in, readset)) { |
486 | { | 490 | /* Read as much as possible. */ |
487 | /* Read as much as possible. */ | 491 | len = read(connection_in, buf, sizeof(buf)); |
488 | len = read(connection_in, buf, sizeof(buf)); | 492 | if (len == 0) { |
489 | if (len == 0) | 493 | /* Received EOF. The remote host has closed the connection. */ |
490 | { | 494 | snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", |
491 | /* Received EOF. The remote host has closed the connection. */ | 495 | host); |
492 | snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", | ||
493 | host); | ||
494 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
495 | stderr_bytes += strlen(buf); | ||
496 | quit_pending = 1; | ||
497 | return; | ||
498 | } | ||
499 | |||
500 | /* There is a kernel bug on Solaris that causes select to sometimes | ||
501 | wake up even though there is no data available. */ | ||
502 | if (len < 0 && errno == EAGAIN) | ||
503 | len = 0; | ||
504 | |||
505 | if (len < 0) | ||
506 | { | ||
507 | /* An error has encountered. Perhaps there is a network | ||
508 | problem. */ | ||
509 | snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", | ||
510 | host, strerror(errno)); | ||
511 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
512 | stderr_bytes += strlen(buf); | ||
513 | quit_pending = 1; | ||
514 | return; | ||
515 | } | ||
516 | packet_process_incoming(buf, len); | ||
517 | } | ||
518 | |||
519 | /* Read input from stdin. */ | ||
520 | if (FD_ISSET(fileno(stdin), readset)) | ||
521 | { | ||
522 | /* Read as much as possible. */ | ||
523 | len = read(fileno(stdin), buf, sizeof(buf)); | ||
524 | if (len <= 0) | ||
525 | { | ||
526 | /* Received EOF or error. They are treated similarly, | ||
527 | except that an error message is printed if it was | ||
528 | an error condition. */ | ||
529 | if (len < 0) | ||
530 | { | ||
531 | snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); | ||
532 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
533 | stderr_bytes += strlen(buf); | ||
534 | } | ||
535 | /* Mark that we have seen EOF. */ | ||
536 | stdin_eof = 1; | ||
537 | /* Send an EOF message to the server unless there is data | ||
538 | in the buffer. If there is data in the buffer, no message | ||
539 | will be sent now. Code elsewhere will send the EOF | ||
540 | when the buffer becomes empty if stdin_eof is set. */ | ||
541 | if (buffer_len(&stdin_buffer) == 0) | ||
542 | { | ||
543 | packet_start(SSH_CMSG_EOF); | ||
544 | packet_send(); | ||
545 | } | ||
546 | } | ||
547 | else | ||
548 | if (escape_char == -1) | ||
549 | { | ||
550 | /* Normal successful read, and no escape character. Just | ||
551 | append the data to buffer. */ | ||
552 | buffer_append(&stdin_buffer, buf, len); | ||
553 | stdin_bytes += len; | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | /* Normal, successful read. But we have an escape character | ||
558 | and have to process the characters one by one. */ | ||
559 | unsigned int i; | ||
560 | for (i = 0; i < len; i++) | ||
561 | { | ||
562 | unsigned char ch; | ||
563 | /* Get one character at a time. */ | ||
564 | ch = buf[i]; | ||
565 | |||
566 | /* Check if we have a pending escape character. */ | ||
567 | if (escape_pending) | ||
568 | { | ||
569 | /* We have previously seen an escape character. */ | ||
570 | /* Clear the flag now. */ | ||
571 | escape_pending = 0; | ||
572 | /* Process the escaped character. */ | ||
573 | switch (ch) | ||
574 | { | ||
575 | case '.': | ||
576 | /* Terminate the connection. */ | ||
577 | snprintf(buf, sizeof buf, "%c.\r\n", escape_char); | ||
578 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 496 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
579 | stderr_bytes += strlen(buf); | 497 | stderr_bytes += strlen(buf); |
580 | quit_pending = 1; | 498 | quit_pending = 1; |
581 | return; | 499 | return; |
582 | 500 | } | |
583 | case 'Z' - 64: | 501 | /* There is a kernel bug on Solaris that causes select to |
584 | /* Suspend the program. */ | 502 | sometimes wake up even though there is no data |
585 | /* Print a message to that effect to the user. */ | 503 | available. */ |
586 | snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); | 504 | if (len < 0 && errno == EAGAIN) |
587 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 505 | len = 0; |
588 | stderr_bytes += strlen(buf); | 506 | |
589 | 507 | if (len < 0) { | |
590 | /* Restore terminal modes and suspend. */ | 508 | /* An error has encountered. Perhaps there is a network problem. */ |
591 | client_suspend_self(); | 509 | snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", |
592 | 510 | host, strerror(errno)); | |
593 | /* We have been continued. */ | 511 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
594 | continue; | 512 | stderr_bytes += strlen(buf); |
595 | 513 | quit_pending = 1; | |
596 | case '&': | 514 | return; |
597 | /* Detach the program (continue to serve connections, | 515 | } |
598 | but put in background and no more new | 516 | packet_process_incoming(buf, len); |
599 | connections). */ | 517 | } |
600 | if (!stdin_eof) | 518 | /* Read input from stdin. */ |
601 | { | 519 | if (FD_ISSET(fileno(stdin), readset)) { |
602 | /* Sending SSH_CMSG_EOF alone does not always | 520 | /* Read as much as possible. */ |
603 | appear to be enough. So we try to send an | 521 | len = read(fileno(stdin), buf, sizeof(buf)); |
604 | EOF character first. */ | 522 | if (len <= 0) { |
605 | packet_start(SSH_CMSG_STDIN_DATA); | 523 | /* Received EOF or error. They are treated |
606 | packet_put_string("\004", 1); | 524 | similarly, except that an error message is |
607 | packet_send(); | 525 | printed if it was an error condition. */ |
608 | /* Close stdin. */ | 526 | if (len < 0) { |
609 | stdin_eof = 1; | 527 | snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); |
610 | if (buffer_len(&stdin_buffer) == 0) | 528 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
611 | { | 529 | stderr_bytes += strlen(buf); |
530 | } | ||
531 | /* Mark that we have seen EOF. */ | ||
532 | stdin_eof = 1; | ||
533 | /* Send an EOF message to the server unless there | ||
534 | is data in the buffer. If there is data in the | ||
535 | buffer, no message will be sent now. Code | ||
536 | elsewhere will send the EOF when the buffer | ||
537 | becomes empty if stdin_eof is set. */ | ||
538 | if (buffer_len(&stdin_buffer) == 0) { | ||
612 | packet_start(SSH_CMSG_EOF); | 539 | packet_start(SSH_CMSG_EOF); |
613 | packet_send(); | 540 | packet_send(); |
614 | } | 541 | } |
615 | } | 542 | } else if (escape_char == -1) { |
616 | /* Restore tty modes. */ | 543 | /* Normal successful read, and no escape |
617 | leave_raw_mode(); | 544 | character. Just append the data to buffer. */ |
618 | 545 | buffer_append(&stdin_buffer, buf, len); | |
619 | /* Stop listening for new connections. */ | 546 | stdin_bytes += len; |
620 | channel_stop_listening(); | 547 | } else { |
621 | 548 | /* Normal, successful read. But we have an escape | |
622 | printf("%c& [backgrounded]\n", escape_char); | 549 | character and have to process the characters |
623 | 550 | one by one. */ | |
624 | /* Fork into background. */ | 551 | unsigned int i; |
625 | pid = fork(); | 552 | for (i = 0; i < len; i++) { |
626 | if (pid < 0) | 553 | unsigned char ch; |
627 | { | 554 | /* Get one character at a time. */ |
628 | error("fork: %.100s", strerror(errno)); | 555 | ch = buf[i]; |
629 | continue; | 556 | |
630 | } | 557 | /* Check if we have a pending escape |
631 | if (pid != 0) | 558 | character. */ |
632 | { /* This is the parent. */ | 559 | if (escape_pending) { |
633 | /* The parent just exits. */ | 560 | /* We have previously seen an escape character. */ |
634 | exit(0); | 561 | /* Clear the flag now. */ |
635 | } | 562 | escape_pending = 0; |
636 | 563 | /* Process the escaped character. */ | |
637 | /* The child continues serving connections. */ | 564 | switch (ch) { |
638 | continue; | 565 | case '.': |
639 | 566 | /* Terminate the connection. */ | |
640 | case '?': | 567 | snprintf(buf, sizeof buf, "%c.\r\n", escape_char); |
641 | snprintf(buf, sizeof buf, "%c?\r\n\ | 568 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
569 | stderr_bytes += strlen(buf); | ||
570 | quit_pending = 1; | ||
571 | return; | ||
572 | |||
573 | case 'Z' - 64: | ||
574 | /* Suspend the program. */ | ||
575 | /* Print a message to that effect to the user. */ | ||
576 | snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); | ||
577 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
578 | stderr_bytes += strlen(buf); | ||
579 | |||
580 | /* Restore terminal modes and suspend. */ | ||
581 | client_suspend_self(); | ||
582 | |||
583 | /* We have been continued. */ | ||
584 | continue; | ||
585 | |||
586 | case '&': | ||
587 | /* Detach the program (continue to serve connections, | ||
588 | but put in background and no more new connections). */ | ||
589 | if (!stdin_eof) { | ||
590 | /* Sending SSH_CMSG_EOF alone does not always appear | ||
591 | to be enough. So we try to send an EOF character | ||
592 | first. */ | ||
593 | packet_start(SSH_CMSG_STDIN_DATA); | ||
594 | packet_put_string("\004", 1); | ||
595 | packet_send(); | ||
596 | /* Close stdin. */ | ||
597 | stdin_eof = 1; | ||
598 | if (buffer_len(&stdin_buffer) == 0) { | ||
599 | packet_start(SSH_CMSG_EOF); | ||
600 | packet_send(); | ||
601 | } | ||
602 | } | ||
603 | /* Restore tty modes. */ | ||
604 | leave_raw_mode(); | ||
605 | |||
606 | /* Stop listening for new connections. */ | ||
607 | channel_stop_listening(); | ||
608 | |||
609 | printf("%c& [backgrounded]\n", escape_char); | ||
610 | |||
611 | /* Fork into background. */ | ||
612 | pid = fork(); | ||
613 | if (pid < 0) { | ||
614 | error("fork: %.100s", strerror(errno)); | ||
615 | continue; | ||
616 | } | ||
617 | if (pid != 0) { /* This is the parent. */ | ||
618 | /* The parent just exits. */ | ||
619 | exit(0); | ||
620 | } | ||
621 | /* The child continues serving connections. */ | ||
622 | continue; | ||
623 | |||
624 | case '?': | ||
625 | snprintf(buf, sizeof buf, | ||
626 | "%c?\r\n\ | ||
642 | Supported escape sequences:\r\n\ | 627 | Supported escape sequences:\r\n\ |
643 | ~. - terminate connection\r\n\ | 628 | ~. - terminate connection\r\n\ |
644 | ~^Z - suspend ssh\r\n\ | 629 | ~^Z - suspend ssh\r\n\ |
@@ -647,110 +632,100 @@ Supported escape sequences:\r\n\ | |||
647 | ~? - this message\r\n\ | 632 | ~? - this message\r\n\ |
648 | ~~ - send the escape character by typing it twice\r\n\ | 633 | ~~ - send the escape character by typing it twice\r\n\ |
649 | (Note that escapes are only recognized immediately after newline.)\r\n", | 634 | (Note that escapes are only recognized immediately after newline.)\r\n", |
650 | escape_char); | 635 | escape_char); |
651 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 636 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
652 | continue; | 637 | continue; |
653 | 638 | ||
654 | case '#': | 639 | case '#': |
655 | snprintf(buf, sizeof buf, "%c#\r\n", escape_char); | 640 | snprintf(buf, sizeof buf, "%c#\r\n", escape_char); |
656 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 641 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
657 | s = channel_open_message(); | 642 | s = channel_open_message(); |
658 | buffer_append(&stderr_buffer, s, strlen(s)); | 643 | buffer_append(&stderr_buffer, s, strlen(s)); |
659 | xfree(s); | 644 | xfree(s); |
660 | continue; | 645 | continue; |
661 | 646 | ||
662 | default: | 647 | default: |
663 | if (ch != escape_char) | 648 | if (ch != escape_char) { |
664 | { | 649 | /* Escape character followed by non-special character. |
665 | /* Escape character followed by non-special | 650 | Append both to the input buffer. */ |
666 | character. Append both to the input | 651 | buf[0] = escape_char; |
667 | buffer. */ | 652 | buf[1] = ch; |
668 | buf[0] = escape_char; | 653 | buffer_append(&stdin_buffer, buf, 2); |
669 | buf[1] = ch; | 654 | stdin_bytes += 2; |
670 | buffer_append(&stdin_buffer, buf, 2); | 655 | continue; |
671 | stdin_bytes += 2; | 656 | } |
672 | continue; | 657 | /* Note that escape character typed twice |
673 | } | 658 | falls through here; the latter gets processed |
674 | /* Note that escape character typed twice falls through | 659 | as a normal character below. */ |
675 | here; the latter gets processed as a normal | 660 | break; |
676 | character below. */ | 661 | } |
677 | break; | 662 | } else { |
678 | } | 663 | /* The previous character was not an escape char. Check if this |
679 | } | 664 | is an escape. */ |
680 | else | 665 | if (last_was_cr && ch == escape_char) { |
681 | { | 666 | /* It is. Set the flag and continue to next character. */ |
682 | /* The previous character was not an escape char. | 667 | escape_pending = 1; |
683 | Check if this is an escape. */ | 668 | continue; |
684 | if (last_was_cr && ch == escape_char) | 669 | } |
685 | { | 670 | } |
686 | /* It is. Set the flag and continue to next | 671 | |
687 | character. */ | 672 | /* Normal character. Record whether it was a newline, and append it to the |
688 | escape_pending = 1; | 673 | buffer. */ |
689 | continue; | 674 | last_was_cr = (ch == '\r' || ch == '\n'); |
690 | } | 675 | buf[0] = ch; |
691 | } | 676 | buffer_append(&stdin_buffer, buf, 1); |
692 | 677 | stdin_bytes += 1; | |
693 | /* Normal character. Record whether it was a newline, | 678 | continue; |
694 | and append it to the buffer. */ | 679 | } |
695 | last_was_cr = (ch == '\r' || ch == '\n'); | 680 | } |
696 | buf[0] = ch; | 681 | } |
697 | buffer_append(&stdin_buffer, buf, 1); | ||
698 | stdin_bytes += 1; | ||
699 | continue; | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | } | 682 | } |
704 | 683 | ||
705 | void client_process_output(fd_set *writeset) | 684 | void |
685 | client_process_output(fd_set * writeset) | ||
706 | { | 686 | { |
707 | int len; | 687 | int len; |
708 | char buf[100]; | 688 | char buf[100]; |
709 | 689 | ||
710 | /* Write buffered output to stdout. */ | 690 | /* Write buffered output to stdout. */ |
711 | if (FD_ISSET(fileno(stdout), writeset)) | 691 | if (FD_ISSET(fileno(stdout), writeset)) { |
712 | { | 692 | /* Write as much data as possible. */ |
713 | /* Write as much data as possible. */ | 693 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), |
714 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | 694 | buffer_len(&stdout_buffer)); |
715 | buffer_len(&stdout_buffer)); | 695 | if (len <= 0) { |
716 | if (len <= 0) | 696 | if (errno == EAGAIN) |
717 | { | 697 | len = 0; |
718 | if (errno == EAGAIN) | 698 | else { |
719 | len = 0; | 699 | /* An error or EOF was encountered. Put |
720 | else | 700 | an error message to stderr buffer. */ |
721 | { | 701 | snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); |
722 | /* An error or EOF was encountered. Put an error message | 702 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
723 | to stderr buffer. */ | 703 | stderr_bytes += strlen(buf); |
724 | snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); | 704 | quit_pending = 1; |
725 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 705 | return; |
726 | stderr_bytes += strlen(buf); | 706 | } |
727 | quit_pending = 1; | 707 | } |
728 | return; | 708 | /* Consume printed data from the buffer. */ |
729 | } | 709 | buffer_consume(&stdout_buffer, len); |
710 | } | ||
711 | /* Write buffered output to stderr. */ | ||
712 | if (FD_ISSET(fileno(stderr), writeset)) { | ||
713 | /* Write as much data as possible. */ | ||
714 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | ||
715 | buffer_len(&stderr_buffer)); | ||
716 | if (len <= 0) { | ||
717 | if (errno == EAGAIN) | ||
718 | len = 0; | ||
719 | else { | ||
720 | /* EOF or error, but can't even print | ||
721 | error message. */ | ||
722 | quit_pending = 1; | ||
723 | return; | ||
724 | } | ||
725 | } | ||
726 | /* Consume printed characters from the buffer. */ | ||
727 | buffer_consume(&stderr_buffer, len); | ||
730 | } | 728 | } |
731 | /* Consume printed data from the buffer. */ | ||
732 | buffer_consume(&stdout_buffer, len); | ||
733 | } | ||
734 | |||
735 | /* Write buffered output to stderr. */ | ||
736 | if (FD_ISSET(fileno(stderr), writeset)) | ||
737 | { | ||
738 | /* Write as much data as possible. */ | ||
739 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | ||
740 | buffer_len(&stderr_buffer)); | ||
741 | if (len <= 0) { | ||
742 | if (errno == EAGAIN) | ||
743 | len = 0; | ||
744 | else | ||
745 | { | ||
746 | /* EOF or error, but can't even print error message. */ | ||
747 | quit_pending = 1; | ||
748 | return; | ||
749 | } | ||
750 | } | ||
751 | /* Consume printed characters from the buffer. */ | ||
752 | buffer_consume(&stderr_buffer, len); | ||
753 | } | ||
754 | } | 729 | } |
755 | 730 | ||
756 | /* Implements the interactive session with the server. This is called | 731 | /* Implements the interactive session with the server. This is called |
@@ -759,165 +734,160 @@ void client_process_output(fd_set *writeset) | |||
759 | used as an escape character for terminating or suspending the | 734 | used as an escape character for terminating or suspending the |
760 | session. */ | 735 | session. */ |
761 | 736 | ||
762 | int client_loop(int have_pty, int escape_char_arg) | 737 | int |
738 | client_loop(int have_pty, int escape_char_arg) | ||
763 | { | 739 | { |
764 | extern Options options; | 740 | extern Options options; |
765 | double start_time, total_time; | 741 | double start_time, total_time; |
766 | int len; | 742 | int len; |
767 | char buf[100]; | 743 | char buf[100]; |
768 | 744 | ||
769 | debug("Entering interactive session."); | 745 | debug("Entering interactive session."); |
770 | 746 | ||
771 | start_time = get_current_time(); | 747 | start_time = get_current_time(); |
772 | 748 | ||
773 | /* Initialize variables. */ | 749 | /* Initialize variables. */ |
774 | escape_pending = 0; | 750 | escape_pending = 0; |
775 | last_was_cr = 1; | 751 | last_was_cr = 1; |
776 | exit_status = -1; | 752 | exit_status = -1; |
777 | stdin_eof = 0; | 753 | stdin_eof = 0; |
778 | buffer_high = 64 * 1024; | 754 | buffer_high = 64 * 1024; |
779 | connection_in = packet_get_connection_in(); | 755 | connection_in = packet_get_connection_in(); |
780 | connection_out = packet_get_connection_out(); | 756 | connection_out = packet_get_connection_out(); |
781 | max_fd = connection_in; | 757 | max_fd = connection_in; |
782 | if (connection_out > max_fd) | 758 | if (connection_out > max_fd) |
783 | max_fd = connection_out; | 759 | max_fd = connection_out; |
784 | stdin_bytes = 0; | 760 | stdin_bytes = 0; |
785 | stdout_bytes = 0; | 761 | stdout_bytes = 0; |
786 | stderr_bytes = 0; | 762 | stderr_bytes = 0; |
787 | quit_pending = 0; | 763 | quit_pending = 0; |
788 | escape_char = escape_char_arg; | 764 | escape_char = escape_char_arg; |
789 | 765 | ||
790 | /* Initialize buffers. */ | 766 | /* Initialize buffers. */ |
791 | buffer_init(&stdin_buffer); | 767 | buffer_init(&stdin_buffer); |
792 | buffer_init(&stdout_buffer); | 768 | buffer_init(&stdout_buffer); |
793 | buffer_init(&stderr_buffer); | 769 | buffer_init(&stderr_buffer); |
794 | 770 | ||
795 | /* Set signal handlers to restore non-blocking mode. */ | 771 | /* Set signal handlers to restore non-blocking mode. */ |
796 | signal(SIGINT, signal_handler); | 772 | signal(SIGINT, signal_handler); |
797 | signal(SIGQUIT, signal_handler); | 773 | signal(SIGQUIT, signal_handler); |
798 | signal(SIGTERM, signal_handler); | 774 | signal(SIGTERM, signal_handler); |
799 | signal(SIGPIPE, SIG_IGN); | 775 | signal(SIGPIPE, SIG_IGN); |
800 | if (have_pty) | 776 | if (have_pty) |
801 | signal(SIGWINCH, window_change_handler); | 777 | signal(SIGWINCH, window_change_handler); |
802 | 778 | ||
803 | /* Enter raw mode if have a pseudo terminal. */ | 779 | /* Enter raw mode if have a pseudo terminal. */ |
804 | if (have_pty) | 780 | if (have_pty) |
805 | enter_raw_mode(); | 781 | enter_raw_mode(); |
806 | 782 | ||
807 | /* Check if we should immediately send of on stdin. */ | 783 | /* Check if we should immediately send of on stdin. */ |
808 | client_check_initial_eof_on_stdin(); | 784 | client_check_initial_eof_on_stdin(); |
809 | 785 | ||
810 | /* Main loop of the client for the interactive session mode. */ | 786 | /* Main loop of the client for the interactive session mode. */ |
811 | while (!quit_pending) | 787 | while (!quit_pending) { |
812 | { | 788 | fd_set readset, writeset; |
813 | fd_set readset, writeset; | 789 | |
814 | 790 | /* Precess buffered packets sent by the server. */ | |
815 | /* Precess buffered packets sent by the server. */ | 791 | client_process_buffered_input_packets(); |
816 | client_process_buffered_input_packets(); | 792 | |
817 | 793 | /* Make packets of buffered stdin data, and buffer them | |
818 | /* Make packets of buffered stdin data, and buffer them for sending | 794 | for sending to the server. */ |
819 | to the server. */ | 795 | client_make_packets_from_stdin_data(); |
820 | client_make_packets_from_stdin_data(); | 796 | |
821 | 797 | /* Make packets from buffered channel data, and buffer | |
822 | /* Make packets from buffered channel data, and buffer them for sending | 798 | them for sending to the server. */ |
823 | to the server. */ | 799 | if (packet_not_very_much_data_to_write()) |
824 | if (packet_not_very_much_data_to_write()) | 800 | channel_output_poll(); |
825 | channel_output_poll(); | 801 | |
826 | 802 | /* Check if the window size has changed, and buffer a | |
827 | /* Check if the window size has changed, and buffer a message about | 803 | message about it to the server if so. */ |
828 | it to the server if so. */ | 804 | client_check_window_change(); |
829 | client_check_window_change(); | 805 | |
830 | 806 | if (quit_pending) | |
831 | if (quit_pending) | 807 | break; |
832 | break; | 808 | |
833 | 809 | /* Wait until we have something to do (something becomes | |
834 | /* Wait until we have something to do (something becomes available | 810 | available on one of the descriptors). */ |
835 | on one of the descriptors). */ | 811 | client_wait_until_can_do_something(&readset, &writeset); |
836 | client_wait_until_can_do_something(&readset, &writeset); | 812 | |
837 | 813 | if (quit_pending) | |
838 | if (quit_pending) | 814 | break; |
839 | break; | 815 | |
840 | 816 | /* Do channel operations. */ | |
841 | /* Do channel operations. */ | 817 | channel_after_select(&readset, &writeset); |
842 | channel_after_select(&readset, &writeset); | 818 | |
843 | 819 | /* Process input from the connection and from stdin. | |
844 | /* Process input from the connection and from stdin. Buffer any data | 820 | Buffer any data that is available. */ |
845 | that is available. */ | 821 | client_process_input(&readset); |
846 | client_process_input(&readset); | 822 | |
847 | 823 | /* Process output to stdout and stderr. Output to the | |
848 | /* Process output to stdout and stderr. Output to the connection | 824 | connection is processed elsewhere (above). */ |
849 | is processed elsewhere (above). */ | 825 | client_process_output(&writeset); |
850 | client_process_output(&writeset); | 826 | |
851 | 827 | /* Send as much buffered packet data as possible to the | |
852 | /* Send as much buffered packet data as possible to the sender. */ | 828 | sender. */ |
853 | if (FD_ISSET(connection_out, &writeset)) | 829 | if (FD_ISSET(connection_out, &writeset)) |
854 | packet_write_poll(); | 830 | packet_write_poll(); |
855 | } | 831 | } |
856 | 832 | ||
857 | /* Terminate the session. */ | 833 | /* Terminate the session. */ |
858 | 834 | ||
859 | /* Stop watching for window change. */ | 835 | /* Stop watching for window change. */ |
860 | if (have_pty) | 836 | if (have_pty) |
861 | signal(SIGWINCH, SIG_DFL); | 837 | signal(SIGWINCH, SIG_DFL); |
862 | 838 | ||
863 | /* Stop listening for connections. */ | 839 | /* Stop listening for connections. */ |
864 | channel_stop_listening(); | 840 | channel_stop_listening(); |
865 | 841 | ||
866 | /* In interactive mode (with pseudo tty) display a message indicating that | 842 | /* In interactive mode (with pseudo tty) display a message |
867 | the connection has been closed. */ | 843 | indicating that the connection has been closed. */ |
868 | if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) | 844 | if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { |
869 | { | 845 | snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); |
870 | snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); | 846 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
871 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 847 | stderr_bytes += strlen(buf); |
872 | stderr_bytes += strlen(buf); | ||
873 | } | ||
874 | |||
875 | /* Output any buffered data for stdout. */ | ||
876 | while (buffer_len(&stdout_buffer) > 0) | ||
877 | { | ||
878 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | ||
879 | buffer_len(&stdout_buffer)); | ||
880 | if (len <= 0) | ||
881 | { | ||
882 | error("Write failed flushing stdout buffer."); | ||
883 | break; | ||
884 | } | 848 | } |
885 | buffer_consume(&stdout_buffer, len); | 849 | /* Output any buffered data for stdout. */ |
886 | } | 850 | while (buffer_len(&stdout_buffer) > 0) { |
887 | 851 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | |
888 | /* Output any buffered data for stderr. */ | 852 | buffer_len(&stdout_buffer)); |
889 | while (buffer_len(&stderr_buffer) > 0) | 853 | if (len <= 0) { |
890 | { | 854 | error("Write failed flushing stdout buffer."); |
891 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | 855 | break; |
892 | buffer_len(&stderr_buffer)); | 856 | } |
893 | if (len <= 0) | 857 | buffer_consume(&stdout_buffer, len); |
894 | { | ||
895 | error("Write failed flushing stderr buffer."); | ||
896 | break; | ||
897 | } | 858 | } |
898 | buffer_consume(&stderr_buffer, len); | 859 | |
899 | } | 860 | /* Output any buffered data for stderr. */ |
900 | 861 | while (buffer_len(&stderr_buffer) > 0) { | |
901 | /* Leave raw mode. */ | 862 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), |
902 | if (have_pty) | 863 | buffer_len(&stderr_buffer)); |
903 | leave_raw_mode(); | 864 | if (len <= 0) { |
904 | 865 | error("Write failed flushing stderr buffer."); | |
905 | /* Clear and free any buffers. */ | 866 | break; |
906 | memset(buf, 0, sizeof(buf)); | 867 | } |
907 | buffer_free(&stdin_buffer); | 868 | buffer_consume(&stderr_buffer, len); |
908 | buffer_free(&stdout_buffer); | 869 | } |
909 | buffer_free(&stderr_buffer); | 870 | |
910 | 871 | /* Leave raw mode. */ | |
911 | /* Report bytes transferred, and transfer rates. */ | 872 | if (have_pty) |
912 | total_time = get_current_time() - start_time; | 873 | leave_raw_mode(); |
913 | debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", | 874 | |
914 | stdin_bytes, stdout_bytes, stderr_bytes, total_time); | 875 | /* Clear and free any buffers. */ |
915 | if (total_time > 0) | 876 | memset(buf, 0, sizeof(buf)); |
916 | debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", | 877 | buffer_free(&stdin_buffer); |
917 | stdin_bytes / total_time, stdout_bytes / total_time, | 878 | buffer_free(&stdout_buffer); |
918 | stderr_bytes / total_time); | 879 | buffer_free(&stderr_buffer); |
919 | 880 | ||
920 | /* Return the exit status of the program. */ | 881 | /* Report bytes transferred, and transfer rates. */ |
921 | debug("Exit status %d", exit_status); | 882 | total_time = get_current_time() - start_time; |
922 | return exit_status; | 883 | debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", |
884 | stdin_bytes, stdout_bytes, stderr_bytes, total_time); | ||
885 | if (total_time > 0) | ||
886 | debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", | ||
887 | stdin_bytes / total_time, stdout_bytes / total_time, | ||
888 | stderr_bytes / total_time); | ||
889 | |||
890 | /* Return the exit status of the program. */ | ||
891 | debug("Exit status %d", exit_status); | ||
892 | return exit_status; | ||
923 | } | 893 | } |