diff options
Diffstat (limited to 'serverloop.c')
-rw-r--r-- | serverloop.c | 1112 |
1 files changed, 548 insertions, 564 deletions
diff --git a/serverloop.c b/serverloop.c index 9961170a5..fc959baef 100644 --- a/serverloop.c +++ b/serverloop.c | |||
@@ -1,17 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | serverloop.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Sun Sep 10 00:30:37 1995 ylo |
6 | 6 | * Server main loop for handling the interactive session. | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | */ |
8 | All rights reserved | ||
9 | |||
10 | Created: Sun Sep 10 00:30:37 1995 ylo | ||
11 | |||
12 | Server main loop for handling the interactive session. | ||
13 | |||
14 | */ | ||
15 | 8 | ||
16 | #include "includes.h" | 9 | #include "includes.h" |
17 | #include "xmalloc.h" | 10 | #include "xmalloc.h" |
@@ -43,603 +36,594 @@ static int max_fd; /* Max file descriptor number for select(). */ | |||
43 | /* This SIGCHLD kludge is used to detect when the child exits. The server | 36 | /* This SIGCHLD kludge is used to detect when the child exits. The server |
44 | will exit after that, as soon as forwarded connections have terminated. */ | 37 | will exit after that, as soon as forwarded connections have terminated. */ |
45 | 38 | ||
46 | static int child_pid; /* Pid of the child. */ | 39 | static int child_pid; /* Pid of the child. */ |
47 | static volatile int child_terminated; /* The child has terminated. */ | 40 | static volatile int child_terminated; /* The child has terminated. */ |
48 | static volatile int child_wait_status; /* Status from wait(). */ | 41 | static volatile int child_wait_status; /* Status from wait(). */ |
49 | 42 | ||
50 | void sigchld_handler(int sig) | 43 | void |
44 | sigchld_handler(int sig) | ||
51 | { | 45 | { |
52 | int save_errno = errno; | 46 | int save_errno = errno; |
53 | int wait_pid; | 47 | int wait_pid; |
54 | debug("Received SIGCHLD."); | 48 | debug("Received SIGCHLD."); |
55 | wait_pid = wait((int *)&child_wait_status); | 49 | wait_pid = wait((int *) &child_wait_status); |
56 | if (wait_pid != -1) | 50 | if (wait_pid != -1) { |
57 | { | 51 | if (wait_pid != child_pid) |
58 | if (wait_pid != child_pid) | 52 | error("Strange, got SIGCHLD and wait returned pid %d but child is %d", |
59 | error("Strange, got SIGCHLD and wait returned pid %d but child is %d", | 53 | wait_pid, child_pid); |
60 | wait_pid, child_pid); | 54 | if (WIFEXITED(child_wait_status) || |
61 | if (WIFEXITED(child_wait_status) || | 55 | WIFSIGNALED(child_wait_status)) |
62 | WIFSIGNALED(child_wait_status)) | 56 | child_terminated = 1; |
63 | child_terminated = 1; | 57 | } |
64 | } | 58 | signal(SIGCHLD, sigchld_handler); |
65 | signal(SIGCHLD, sigchld_handler); | 59 | errno = save_errno; |
66 | errno = save_errno; | ||
67 | } | 60 | } |
68 | 61 | ||
69 | /* Process any buffered packets that have been received from the client. */ | 62 | /* |
70 | 63 | * Process any buffered packets that have been received from the client. | |
71 | void process_buffered_input_packets() | 64 | */ |
65 | void | ||
66 | process_buffered_input_packets() | ||
72 | { | 67 | { |
73 | int type; | 68 | int type; |
74 | char *data; | 69 | char *data; |
75 | unsigned int data_len; | 70 | unsigned int data_len; |
76 | int row, col, xpixel, ypixel; | 71 | int row, col, xpixel, ypixel; |
77 | int payload_len; | 72 | int payload_len; |
78 | 73 | ||
79 | /* Process buffered packets from the client. */ | 74 | /* Process buffered packets from the client. */ |
80 | while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) | 75 | while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { |
81 | { | 76 | switch (type) { |
82 | switch (type) | 77 | case SSH_CMSG_STDIN_DATA: |
83 | { | 78 | /* Stdin data from the client. Append it to the buffer. */ |
84 | case SSH_CMSG_STDIN_DATA: | 79 | /* Ignore any data if the client has closed stdin. */ |
85 | /* Stdin data from the client. Append it to the buffer. */ | 80 | if (fdin == -1) |
86 | if (fdin == -1) | 81 | break; |
87 | break; /* Ignore any data if the client has closed stdin. */ | 82 | data = packet_get_string(&data_len); |
88 | data = packet_get_string(&data_len); | 83 | packet_integrity_check(payload_len, (4 + data_len), type); |
89 | packet_integrity_check(payload_len, (4 + data_len), type); | 84 | buffer_append(&stdin_buffer, data, data_len); |
90 | buffer_append(&stdin_buffer, data, data_len); | 85 | memset(data, 0, data_len); |
91 | memset(data, 0, data_len); | 86 | xfree(data); |
92 | xfree(data); | 87 | break; |
93 | break; | 88 | |
94 | 89 | case SSH_CMSG_EOF: | |
95 | case SSH_CMSG_EOF: | 90 | /* Eof from the client. The stdin descriptor to |
96 | /* Eof from the client. The stdin descriptor to the program | 91 | the program will be closed when all buffered |
97 | will be closed when all buffered data has drained. */ | 92 | data has drained. */ |
98 | debug("EOF received for stdin."); | 93 | debug("EOF received for stdin."); |
99 | packet_integrity_check(payload_len, 0, type); | 94 | packet_integrity_check(payload_len, 0, type); |
100 | stdin_eof = 1; | 95 | stdin_eof = 1; |
101 | break; | 96 | break; |
102 | 97 | ||
103 | case SSH_CMSG_WINDOW_SIZE: | 98 | case SSH_CMSG_WINDOW_SIZE: |
104 | debug("Window change received."); | 99 | debug("Window change received."); |
105 | packet_integrity_check(payload_len, 4*4, type); | 100 | packet_integrity_check(payload_len, 4 * 4, type); |
106 | row = packet_get_int(); | 101 | row = packet_get_int(); |
107 | col = packet_get_int(); | 102 | col = packet_get_int(); |
108 | xpixel = packet_get_int(); | 103 | xpixel = packet_get_int(); |
109 | ypixel = packet_get_int(); | 104 | ypixel = packet_get_int(); |
110 | if (fdin != -1) | 105 | if (fdin != -1) |
111 | pty_change_window_size(fdin, row, col, xpixel, ypixel); | 106 | pty_change_window_size(fdin, row, col, xpixel, ypixel); |
112 | break; | 107 | break; |
113 | 108 | ||
114 | case SSH_MSG_PORT_OPEN: | 109 | case SSH_MSG_PORT_OPEN: |
115 | debug("Received port open request."); | 110 | debug("Received port open request."); |
116 | channel_input_port_open(payload_len); | 111 | channel_input_port_open(payload_len); |
117 | break; | 112 | break; |
118 | 113 | ||
119 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | 114 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: |
120 | debug("Received channel open confirmation."); | 115 | debug("Received channel open confirmation."); |
121 | packet_integrity_check(payload_len, 4 + 4, type); | 116 | packet_integrity_check(payload_len, 4 + 4, type); |
122 | channel_input_open_confirmation(); | 117 | channel_input_open_confirmation(); |
123 | break; | 118 | break; |
124 | 119 | ||
125 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | 120 | case SSH_MSG_CHANNEL_OPEN_FAILURE: |
126 | debug("Received channel open failure."); | 121 | debug("Received channel open failure."); |
127 | packet_integrity_check(payload_len, 4, type); | 122 | packet_integrity_check(payload_len, 4, type); |
128 | channel_input_open_failure(); | 123 | channel_input_open_failure(); |
129 | break; | 124 | break; |
130 | 125 | ||
131 | case SSH_MSG_CHANNEL_DATA: | 126 | case SSH_MSG_CHANNEL_DATA: |
132 | channel_input_data(payload_len); | 127 | channel_input_data(payload_len); |
133 | break; | 128 | break; |
134 | 129 | ||
135 | case SSH_MSG_CHANNEL_CLOSE: | 130 | case SSH_MSG_CHANNEL_CLOSE: |
136 | debug("Received channel close."); | 131 | debug("Received channel close."); |
137 | packet_integrity_check(payload_len, 4, type); | 132 | packet_integrity_check(payload_len, 4, type); |
138 | channel_input_close(); | 133 | channel_input_close(); |
139 | break; | 134 | break; |
140 | 135 | ||
141 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | 136 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: |
142 | debug("Received channel close confirmation."); | 137 | debug("Received channel close confirmation."); |
143 | packet_integrity_check(payload_len, 4, type); | 138 | packet_integrity_check(payload_len, 4, type); |
144 | channel_input_close_confirmation(); | 139 | channel_input_close_confirmation(); |
145 | break; | 140 | break; |
146 | 141 | ||
147 | default: | 142 | default: |
148 | /* In this phase, any unexpected messages cause a protocol | 143 | /* In this phase, any unexpected messages cause a |
149 | error. This is to ease debugging; also, since no | 144 | protocol error. This is to ease debugging; |
150 | confirmations are sent messages, unprocessed unknown | 145 | also, since no confirmations are sent messages, |
151 | messages could cause strange problems. Any compatible | 146 | unprocessed unknown messages could cause |
152 | protocol extensions must be negotiated before entering the | 147 | strange problems. Any compatible protocol |
153 | interactive session. */ | 148 | extensions must be negotiated before entering |
154 | packet_disconnect("Protocol error during session: type %d", | 149 | the interactive session. */ |
155 | type); | 150 | packet_disconnect("Protocol error during session: type %d", |
151 | type); | ||
152 | } | ||
156 | } | 153 | } |
157 | } | ||
158 | } | 154 | } |
159 | 155 | ||
160 | /* Make packets from buffered stderr data, and buffer it for sending | 156 | /* |
161 | to the client. */ | 157 | * Make packets from buffered stderr data, and buffer it for sending |
162 | 158 | * to the client. | |
163 | void make_packets_from_stderr_data() | 159 | */ |
160 | void | ||
161 | make_packets_from_stderr_data() | ||
164 | { | 162 | { |
165 | int len; | 163 | int len; |
166 | 164 | ||
167 | /* Send buffered stderr data to the client. */ | 165 | /* Send buffered stderr data to the client. */ |
168 | while (buffer_len(&stderr_buffer) > 0 && | 166 | while (buffer_len(&stderr_buffer) > 0 && |
169 | packet_not_very_much_data_to_write()) | 167 | packet_not_very_much_data_to_write()) { |
170 | { | 168 | len = buffer_len(&stderr_buffer); |
171 | len = buffer_len(&stderr_buffer); | 169 | if (packet_is_interactive()) { |
172 | if (packet_is_interactive()) | 170 | if (len > 512) |
173 | { | 171 | len = 512; |
174 | if (len > 512) | 172 | } else { |
175 | len = 512; | 173 | /* Keep the packets at reasonable size. */ |
176 | } | 174 | if (len > packet_get_maxsize()) |
177 | else | 175 | len = packet_get_maxsize(); |
178 | { | 176 | } |
179 | if (len > packet_get_maxsize()) | 177 | packet_start(SSH_SMSG_STDERR_DATA); |
180 | len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ | 178 | packet_put_string(buffer_ptr(&stderr_buffer), len); |
179 | packet_send(); | ||
180 | buffer_consume(&stderr_buffer, len); | ||
181 | stderr_bytes += len; | ||
181 | } | 182 | } |
182 | packet_start(SSH_SMSG_STDERR_DATA); | ||
183 | packet_put_string(buffer_ptr(&stderr_buffer), len); | ||
184 | packet_send(); | ||
185 | buffer_consume(&stderr_buffer, len); | ||
186 | stderr_bytes += len; | ||
187 | } | ||
188 | } | 183 | } |
189 | 184 | ||
190 | /* Make packets from buffered stdout data, and buffer it for sending to the | 185 | /* |
191 | client. */ | 186 | * Make packets from buffered stdout data, and buffer it for sending to the |
192 | 187 | * client. | |
193 | void make_packets_from_stdout_data() | 188 | */ |
189 | void | ||
190 | make_packets_from_stdout_data() | ||
194 | { | 191 | { |
195 | int len; | 192 | int len; |
196 | 193 | ||
197 | /* Send buffered stdout data to the client. */ | 194 | /* Send buffered stdout data to the client. */ |
198 | while (buffer_len(&stdout_buffer) > 0 && | 195 | while (buffer_len(&stdout_buffer) > 0 && |
199 | packet_not_very_much_data_to_write()) | 196 | packet_not_very_much_data_to_write()) { |
200 | { | 197 | len = buffer_len(&stdout_buffer); |
201 | len = buffer_len(&stdout_buffer); | 198 | if (packet_is_interactive()) { |
202 | if (packet_is_interactive()) | 199 | if (len > 512) |
203 | { | 200 | len = 512; |
204 | if (len > 512) | 201 | } else { |
205 | len = 512; | 202 | /* Keep the packets at reasonable size. */ |
206 | } | 203 | if (len > packet_get_maxsize()) |
207 | else | 204 | len = packet_get_maxsize(); |
208 | { | 205 | } |
209 | if (len > packet_get_maxsize()) | 206 | packet_start(SSH_SMSG_STDOUT_DATA); |
210 | len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ | 207 | packet_put_string(buffer_ptr(&stdout_buffer), len); |
208 | packet_send(); | ||
209 | buffer_consume(&stdout_buffer, len); | ||
210 | stdout_bytes += len; | ||
211 | } | 211 | } |
212 | packet_start(SSH_SMSG_STDOUT_DATA); | ||
213 | packet_put_string(buffer_ptr(&stdout_buffer), len); | ||
214 | packet_send(); | ||
215 | buffer_consume(&stdout_buffer, len); | ||
216 | stdout_bytes += len; | ||
217 | } | ||
218 | } | 212 | } |
219 | 213 | ||
220 | /* Sleep in select() until we can do something. This will initialize the | 214 | /* |
221 | select masks. Upon return, the masks will indicate which descriptors | 215 | * Sleep in select() until we can do something. This will initialize the |
222 | have data or can accept data. Optionally, a maximum time can be specified | 216 | * select masks. Upon return, the masks will indicate which descriptors |
223 | for the duration of the wait (0 = infinite). */ | 217 | * have data or can accept data. Optionally, a maximum time can be specified |
224 | 218 | * for the duration of the wait (0 = infinite). | |
225 | void wait_until_can_do_something(fd_set *readset, fd_set *writeset, | 219 | */ |
226 | unsigned int max_time_milliseconds) | 220 | void |
221 | wait_until_can_do_something(fd_set * readset, fd_set * writeset, | ||
222 | unsigned int max_time_milliseconds) | ||
227 | { | 223 | { |
228 | struct timeval tv, *tvp; | 224 | struct timeval tv, *tvp; |
229 | int ret; | 225 | int ret; |
230 | 226 | ||
231 | /* When select fails we restart from here. */ | 227 | /* When select fails we restart from here. */ |
232 | retry_select: | 228 | retry_select: |
233 | 229 | ||
234 | /* Initialize select() masks. */ | 230 | /* Initialize select() masks. */ |
235 | FD_ZERO(readset); | 231 | FD_ZERO(readset); |
236 | 232 | ||
237 | /* Read packets from the client unless we have too much buffered stdin | 233 | /* Read packets from the client unless we have too much buffered |
238 | or channel data. */ | 234 | stdin or channel data. */ |
239 | if (buffer_len(&stdin_buffer) < 4096 && | 235 | if (buffer_len(&stdin_buffer) < 4096 && |
240 | channel_not_very_much_buffered_data()) | 236 | channel_not_very_much_buffered_data()) |
241 | FD_SET(connection_in, readset); | 237 | FD_SET(connection_in, readset); |
242 | 238 | ||
243 | /* If there is not too much data already buffered going to the client, | 239 | /* If there is not too much data already buffered going to the |
244 | try to get some more data from the program. */ | 240 | client, try to get some more data from the program. */ |
245 | if (packet_not_very_much_data_to_write()) | 241 | if (packet_not_very_much_data_to_write()) { |
246 | { | 242 | if (!fdout_eof) |
247 | if (!fdout_eof) | 243 | FD_SET(fdout, readset); |
248 | FD_SET(fdout, readset); | 244 | if (!fderr_eof) |
249 | if (!fderr_eof) | 245 | FD_SET(fderr, readset); |
250 | FD_SET(fderr, readset); | 246 | } |
251 | } | 247 | FD_ZERO(writeset); |
252 | 248 | ||
253 | FD_ZERO(writeset); | 249 | /* Set masks for channel descriptors. */ |
254 | 250 | channel_prepare_select(readset, writeset); | |
255 | /* Set masks for channel descriptors. */ | 251 | |
256 | channel_prepare_select(readset, writeset); | 252 | /* If we have buffered packet data going to the client, mark that |
257 | 253 | descriptor. */ | |
258 | /* If we have buffered packet data going to the client, mark that | 254 | if (packet_have_data_to_write()) |
259 | descriptor. */ | 255 | FD_SET(connection_out, writeset); |
260 | if (packet_have_data_to_write()) | 256 | |
261 | FD_SET(connection_out, writeset); | 257 | /* If we have buffered data, try to write some of that data to the |
262 | 258 | program. */ | |
263 | /* If we have buffered data, try to write some of that data to the | 259 | if (fdin != -1 && buffer_len(&stdin_buffer) > 0) |
264 | program. */ | 260 | FD_SET(fdin, writeset); |
265 | if (fdin != -1 && buffer_len(&stdin_buffer) > 0) | 261 | |
266 | FD_SET(fdin, writeset); | 262 | /* Update the maximum descriptor number if appropriate. */ |
267 | 263 | if (channel_max_fd() > max_fd) | |
268 | /* Update the maximum descriptor number if appropriate. */ | 264 | max_fd = channel_max_fd(); |
269 | if (channel_max_fd() > max_fd) | 265 | |
270 | max_fd = channel_max_fd(); | 266 | /* If child has terminated and there is enough buffer space to |
271 | 267 | read from it, then read as much as is available and exit. */ | |
272 | /* If child has terminated and there is enough buffer space to read from | 268 | if (child_terminated && packet_not_very_much_data_to_write()) |
273 | it, then read as much as is available and exit. */ | 269 | if (max_time_milliseconds == 0) |
274 | if (child_terminated && packet_not_very_much_data_to_write()) | 270 | max_time_milliseconds = 100; |
275 | if (max_time_milliseconds == 0) | 271 | |
276 | max_time_milliseconds = 100; | 272 | if (max_time_milliseconds == 0) |
277 | 273 | tvp = NULL; | |
278 | if (max_time_milliseconds == 0) | 274 | else { |
279 | tvp = NULL; | 275 | tv.tv_sec = max_time_milliseconds / 1000; |
280 | else | 276 | tv.tv_usec = 1000 * (max_time_milliseconds % 1000); |
281 | { | 277 | tvp = &tv; |
282 | tv.tv_sec = max_time_milliseconds / 1000; | 278 | } |
283 | tv.tv_usec = 1000 * (max_time_milliseconds % 1000); | ||
284 | tvp = &tv; | ||
285 | } | ||
286 | |||
287 | /* Wait for something to happen, or the timeout to expire. */ | ||
288 | ret = select(max_fd + 1, readset, writeset, NULL, tvp); | ||
289 | |||
290 | if (ret < 0) | ||
291 | { | ||
292 | if (errno != EINTR) | ||
293 | error("select: %.100s", strerror(errno)); | ||
294 | else | ||
295 | goto retry_select; | ||
296 | } | ||
297 | } | ||
298 | 279 | ||
299 | /* Processes input from the client and the program. Input data is stored | 280 | /* Wait for something to happen, or the timeout to expire. */ |
300 | in buffers and processed later. */ | 281 | ret = select(max_fd + 1, readset, writeset, NULL, tvp); |
301 | 282 | ||
302 | void process_input(fd_set *readset) | 283 | if (ret < 0) { |
303 | { | 284 | if (errno != EINTR) |
304 | int len; | 285 | error("select: %.100s", strerror(errno)); |
305 | char buf[16384]; | 286 | else |
306 | 287 | goto retry_select; | |
307 | /* Read and buffer any input data from the client. */ | ||
308 | if (FD_ISSET(connection_in, readset)) | ||
309 | { | ||
310 | len = read(connection_in, buf, sizeof(buf)); | ||
311 | if (len == 0) | ||
312 | fatal("Connection closed by remote host."); | ||
313 | |||
314 | /* There is a kernel bug on Solaris that causes select to sometimes | ||
315 | wake up even though there is no data available. */ | ||
316 | if (len < 0 && errno == EAGAIN) | ||
317 | len = 0; | ||
318 | |||
319 | if (len < 0) | ||
320 | fatal("Read error from remote host: %.100s", strerror(errno)); | ||
321 | |||
322 | /* Buffer any received data. */ | ||
323 | packet_process_incoming(buf, len); | ||
324 | } | ||
325 | |||
326 | /* Read and buffer any available stdout data from the program. */ | ||
327 | if (!fdout_eof && FD_ISSET(fdout, readset)) | ||
328 | { | ||
329 | len = read(fdout, buf, sizeof(buf)); | ||
330 | if (len <= 0) | ||
331 | fdout_eof = 1; | ||
332 | else | ||
333 | { | ||
334 | buffer_append(&stdout_buffer, buf, len); | ||
335 | fdout_bytes += len; | ||
336 | } | 288 | } |
337 | } | ||
338 | |||
339 | /* Read and buffer any available stderr data from the program. */ | ||
340 | if (!fderr_eof && FD_ISSET(fderr, readset)) | ||
341 | { | ||
342 | len = read(fderr, buf, sizeof(buf)); | ||
343 | if (len <= 0) | ||
344 | fderr_eof = 1; | ||
345 | else | ||
346 | buffer_append(&stderr_buffer, buf, len); | ||
347 | } | ||
348 | } | 289 | } |
349 | 290 | ||
350 | /* Sends data from internal buffers to client program stdin. */ | 291 | /* |
292 | * Processes input from the client and the program. Input data is stored | ||
293 | * in buffers and processed later. | ||
294 | */ | ||
295 | void | ||
296 | process_input(fd_set * readset) | ||
297 | { | ||
298 | int len; | ||
299 | char buf[16384]; | ||
300 | |||
301 | /* Read and buffer any input data from the client. */ | ||
302 | if (FD_ISSET(connection_in, readset)) { | ||
303 | len = read(connection_in, buf, sizeof(buf)); | ||
304 | if (len == 0) { | ||
305 | verbose("Connection closed by remote host."); | ||
306 | fatal_cleanup(); | ||
307 | } | ||
308 | /* There is a kernel bug on Solaris that causes select to | ||
309 | sometimes wake up even though there is no data | ||
310 | available. */ | ||
311 | if (len < 0 && errno == EAGAIN) | ||
312 | len = 0; | ||
313 | |||
314 | if (len < 0) { | ||
315 | verbose("Read error from remote host: %.100s", strerror(errno)); | ||
316 | fatal_cleanup(); | ||
317 | } | ||
318 | /* Buffer any received data. */ | ||
319 | packet_process_incoming(buf, len); | ||
320 | } | ||
321 | /* Read and buffer any available stdout data from the program. */ | ||
322 | if (!fdout_eof && FD_ISSET(fdout, readset)) { | ||
323 | len = read(fdout, buf, sizeof(buf)); | ||
324 | if (len <= 0) | ||
325 | fdout_eof = 1; | ||
326 | else { | ||
327 | buffer_append(&stdout_buffer, buf, len); | ||
328 | fdout_bytes += len; | ||
329 | } | ||
330 | } | ||
331 | /* Read and buffer any available stderr data from the program. */ | ||
332 | if (!fderr_eof && FD_ISSET(fderr, readset)) { | ||
333 | len = read(fderr, buf, sizeof(buf)); | ||
334 | if (len <= 0) | ||
335 | fderr_eof = 1; | ||
336 | else | ||
337 | buffer_append(&stderr_buffer, buf, len); | ||
338 | } | ||
339 | } | ||
351 | 340 | ||
352 | void process_output(fd_set *writeset) | 341 | /* |
342 | * Sends data from internal buffers to client program stdin. | ||
343 | */ | ||
344 | void | ||
345 | process_output(fd_set * writeset) | ||
353 | { | 346 | { |
354 | int len; | 347 | int len; |
355 | 348 | ||
356 | /* Write buffered data to program stdin. */ | 349 | /* Write buffered data to program stdin. */ |
357 | if (fdin != -1 && FD_ISSET(fdin, writeset)) | 350 | if (fdin != -1 && FD_ISSET(fdin, writeset)) { |
358 | { | 351 | len = write(fdin, buffer_ptr(&stdin_buffer), |
359 | len = write(fdin, buffer_ptr(&stdin_buffer), | 352 | buffer_len(&stdin_buffer)); |
360 | buffer_len(&stdin_buffer)); | 353 | if (len <= 0) { |
361 | if (len <= 0) | ||
362 | { | ||
363 | #ifdef USE_PIPES | 354 | #ifdef USE_PIPES |
364 | close(fdin); | 355 | close(fdin); |
365 | #else | 356 | #else |
366 | if (fdout == -1) | 357 | if (fdout == -1) |
367 | close(fdin); | 358 | close(fdin); |
368 | else | 359 | else |
369 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ | 360 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ |
370 | #endif | 361 | #endif |
371 | fdin = -1; | 362 | fdin = -1; |
372 | } | 363 | } else { |
373 | else | 364 | /* Successful write. Consume the data from the buffer. */ |
374 | { | 365 | buffer_consume(&stdin_buffer, len); |
375 | /* Successful write. Consume the data from the buffer. */ | 366 | /* Update the count of bytes written to the program. */ |
376 | buffer_consume(&stdin_buffer, len); | 367 | stdin_bytes += len; |
377 | /* Update the count of bytes written to the program. */ | 368 | } |
378 | stdin_bytes += len; | ||
379 | } | 369 | } |
380 | } | 370 | /* Send any buffered packet data to the client. */ |
381 | 371 | if (FD_ISSET(connection_out, writeset)) | |
382 | /* Send any buffered packet data to the client. */ | 372 | packet_write_poll(); |
383 | if (FD_ISSET(connection_out, writeset)) | ||
384 | packet_write_poll(); | ||
385 | } | 373 | } |
386 | 374 | ||
387 | /* Wait until all buffered output has been sent to the client. | 375 | /* |
388 | This is used when the program terminates. */ | 376 | * Wait until all buffered output has been sent to the client. |
389 | 377 | * This is used when the program terminates. | |
390 | void drain_output() | 378 | */ |
379 | void | ||
380 | drain_output() | ||
391 | { | 381 | { |
392 | /* Send any buffered stdout data to the client. */ | 382 | /* Send any buffered stdout data to the client. */ |
393 | if (buffer_len(&stdout_buffer) > 0) | 383 | if (buffer_len(&stdout_buffer) > 0) { |
394 | { | 384 | packet_start(SSH_SMSG_STDOUT_DATA); |
395 | packet_start(SSH_SMSG_STDOUT_DATA); | 385 | packet_put_string(buffer_ptr(&stdout_buffer), |
396 | packet_put_string(buffer_ptr(&stdout_buffer), | 386 | buffer_len(&stdout_buffer)); |
397 | buffer_len(&stdout_buffer)); | 387 | packet_send(); |
398 | packet_send(); | 388 | /* Update the count of sent bytes. */ |
399 | /* Update the count of sent bytes. */ | 389 | stdout_bytes += buffer_len(&stdout_buffer); |
400 | stdout_bytes += buffer_len(&stdout_buffer); | 390 | } |
401 | } | 391 | /* Send any buffered stderr data to the client. */ |
402 | 392 | if (buffer_len(&stderr_buffer) > 0) { | |
403 | /* Send any buffered stderr data to the client. */ | 393 | packet_start(SSH_SMSG_STDERR_DATA); |
404 | if (buffer_len(&stderr_buffer) > 0) | 394 | packet_put_string(buffer_ptr(&stderr_buffer), |
405 | { | 395 | buffer_len(&stderr_buffer)); |
406 | packet_start(SSH_SMSG_STDERR_DATA); | 396 | packet_send(); |
407 | packet_put_string(buffer_ptr(&stderr_buffer), | 397 | /* Update the count of sent bytes. */ |
408 | buffer_len(&stderr_buffer)); | 398 | stderr_bytes += buffer_len(&stderr_buffer); |
409 | packet_send(); | 399 | } |
410 | /* Update the count of sent bytes. */ | 400 | /* Wait until all buffered data has been written to the client. */ |
411 | stderr_bytes += buffer_len(&stderr_buffer); | 401 | packet_write_wait(); |
412 | } | ||
413 | |||
414 | /* Wait until all buffered data has been written to the client. */ | ||
415 | packet_write_wait(); | ||
416 | } | 402 | } |
417 | 403 | ||
418 | /* Performs the interactive session. This handles data transmission between | 404 | /* |
419 | the client and the program. Note that the notion of stdin, stdout, and | 405 | * Performs the interactive session. This handles data transmission between |
420 | stderr in this function is sort of reversed: this function writes to | 406 | * the client and the program. Note that the notion of stdin, stdout, and |
421 | stdin (of the child program), and reads from stdout and stderr (of the | 407 | * stderr in this function is sort of reversed: this function writes to |
422 | child program). */ | 408 | * stdin (of the child program), and reads from stdout and stderr (of the |
423 | 409 | * child program). | |
424 | void server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) | 410 | */ |
411 | void | ||
412 | server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) | ||
425 | { | 413 | { |
426 | int wait_status, wait_pid; /* Status and pid returned by wait(). */ | 414 | int wait_status, wait_pid; /* Status and pid returned by wait(). */ |
427 | int waiting_termination = 0; /* Have displayed waiting close message. */ | 415 | int waiting_termination = 0; /* Have displayed waiting close message. */ |
428 | unsigned int max_time_milliseconds; | 416 | unsigned int max_time_milliseconds; |
429 | unsigned int previous_stdout_buffer_bytes; | 417 | unsigned int previous_stdout_buffer_bytes; |
430 | unsigned int stdout_buffer_bytes; | 418 | unsigned int stdout_buffer_bytes; |
431 | int type; | 419 | int type; |
432 | 420 | ||
433 | debug("Entering interactive session."); | 421 | debug("Entering interactive session."); |
434 | 422 | ||
435 | /* Initialize the SIGCHLD kludge. */ | 423 | /* Initialize the SIGCHLD kludge. */ |
436 | child_pid = pid; | 424 | child_pid = pid; |
437 | child_terminated = 0; | 425 | child_terminated = 0; |
438 | signal(SIGCHLD, sigchld_handler); | 426 | signal(SIGCHLD, sigchld_handler); |
439 | 427 | ||
440 | /* Initialize our global variables. */ | 428 | /* Initialize our global variables. */ |
441 | fdin = fdin_arg; | 429 | fdin = fdin_arg; |
442 | fdout = fdout_arg; | 430 | fdout = fdout_arg; |
443 | fderr = fderr_arg; | 431 | fderr = fderr_arg; |
444 | connection_in = packet_get_connection_in(); | 432 | connection_in = packet_get_connection_in(); |
445 | connection_out = packet_get_connection_out(); | 433 | connection_out = packet_get_connection_out(); |
446 | 434 | ||
447 | previous_stdout_buffer_bytes = 0; | 435 | previous_stdout_buffer_bytes = 0; |
448 | 436 | ||
449 | /* Set approximate I/O buffer size. */ | 437 | /* Set approximate I/O buffer size. */ |
450 | if (packet_is_interactive()) | 438 | if (packet_is_interactive()) |
451 | buffer_high = 4096; | 439 | buffer_high = 4096; |
452 | else | 440 | else |
453 | buffer_high = 64 * 1024; | 441 | buffer_high = 64 * 1024; |
454 | 442 | ||
455 | /* Initialize max_fd to the maximum of the known file descriptors. */ | 443 | /* Initialize max_fd to the maximum of the known file descriptors. */ |
456 | max_fd = fdin; | 444 | max_fd = fdin; |
457 | if (fdout > max_fd) | 445 | if (fdout > max_fd) |
458 | max_fd = fdout; | 446 | max_fd = fdout; |
459 | if (fderr != -1 && fderr > max_fd) | 447 | if (fderr != -1 && fderr > max_fd) |
460 | max_fd = fderr; | 448 | max_fd = fderr; |
461 | if (connection_in > max_fd) | 449 | if (connection_in > max_fd) |
462 | max_fd = connection_in; | 450 | max_fd = connection_in; |
463 | if (connection_out > max_fd) | 451 | if (connection_out > max_fd) |
464 | max_fd = connection_out; | 452 | max_fd = connection_out; |
465 | 453 | ||
466 | /* Initialize Initialize buffers. */ | 454 | /* Initialize Initialize buffers. */ |
467 | buffer_init(&stdin_buffer); | 455 | buffer_init(&stdin_buffer); |
468 | buffer_init(&stdout_buffer); | 456 | buffer_init(&stdout_buffer); |
469 | buffer_init(&stderr_buffer); | 457 | buffer_init(&stderr_buffer); |
470 | 458 | ||
471 | /* If we have no separate fderr (which is the case when we have a pty - there | 459 | /* If we have no separate fderr (which is the case when we have a |
472 | we cannot make difference between data sent to stdout and stderr), | 460 | pty - there we cannot make difference between data sent to |
473 | indicate that we have seen an EOF from stderr. This way we don\'t | 461 | stdout and stderr), indicate that we have seen an EOF from |
474 | need to check the descriptor everywhere. */ | 462 | stderr. This way we don\'t need to check the descriptor |
475 | if (fderr == -1) | 463 | everywhere. */ |
476 | fderr_eof = 1; | 464 | if (fderr == -1) |
477 | 465 | fderr_eof = 1; | |
478 | /* Main loop of the server for the interactive session mode. */ | 466 | |
479 | for (;;) | 467 | /* Main loop of the server for the interactive session mode. */ |
480 | { | 468 | for (;;) { |
481 | fd_set readset, writeset; | 469 | fd_set readset, writeset; |
482 | 470 | ||
483 | /* Process buffered packets from the client. */ | 471 | /* Process buffered packets from the client. */ |
484 | process_buffered_input_packets(); | 472 | process_buffered_input_packets(); |
485 | 473 | ||
486 | /* If we have received eof, and there is no more pending input data, | 474 | /* If we have received eof, and there is no more pending |
487 | cause a real eof by closing fdin. */ | 475 | input data, cause a real eof by closing fdin. */ |
488 | if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) | 476 | if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { |
489 | { | ||
490 | #ifdef USE_PIPES | 477 | #ifdef USE_PIPES |
491 | close(fdin); | 478 | close(fdin); |
492 | #else | 479 | #else |
493 | if (fdout == -1) | 480 | if (fdout == -1) |
494 | close(fdin); | 481 | close(fdin); |
495 | else | 482 | else |
496 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ | 483 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ |
497 | #endif | 484 | #endif |
498 | fdin = -1; | 485 | fdin = -1; |
486 | } | ||
487 | /* Make packets from buffered stderr data to send to the | ||
488 | client. */ | ||
489 | make_packets_from_stderr_data(); | ||
490 | |||
491 | /* Make packets from buffered stdout data to send to the | ||
492 | client. If there is very little to send, this arranges | ||
493 | to not send them now, but to wait a short while to see | ||
494 | if we are getting more data. This is necessary, as some | ||
495 | systems wake up readers from a pty after each separate | ||
496 | character. */ | ||
497 | max_time_milliseconds = 0; | ||
498 | stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
499 | if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && | ||
500 | stdout_buffer_bytes != previous_stdout_buffer_bytes) { | ||
501 | /* try again after a while */ | ||
502 | max_time_milliseconds = 10; | ||
503 | } else { | ||
504 | /* Send it now. */ | ||
505 | make_packets_from_stdout_data(); | ||
506 | } | ||
507 | previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
508 | |||
509 | /* Send channel data to the client. */ | ||
510 | if (packet_not_very_much_data_to_write()) | ||
511 | channel_output_poll(); | ||
512 | |||
513 | /* Bail out of the loop if the program has closed its | ||
514 | output descriptors, and we have no more data to send to | ||
515 | the client, and there is no pending buffered data. */ | ||
516 | if (fdout_eof && fderr_eof && !packet_have_data_to_write() && | ||
517 | buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { | ||
518 | if (!channel_still_open()) | ||
519 | goto quit; | ||
520 | if (!waiting_termination) { | ||
521 | const char *s = "Waiting for forwarded connections to terminate...\r\n"; | ||
522 | char *cp; | ||
523 | waiting_termination = 1; | ||
524 | buffer_append(&stderr_buffer, s, strlen(s)); | ||
525 | |||
526 | /* Display list of open channels. */ | ||
527 | cp = channel_open_message(); | ||
528 | buffer_append(&stderr_buffer, cp, strlen(cp)); | ||
529 | xfree(cp); | ||
530 | } | ||
531 | } | ||
532 | /* Sleep in select() until we can do something. */ | ||
533 | wait_until_can_do_something(&readset, &writeset, | ||
534 | max_time_milliseconds); | ||
535 | |||
536 | /* Process any channel events. */ | ||
537 | channel_after_select(&readset, &writeset); | ||
538 | |||
539 | /* Process input from the client and from program stdout/stderr. */ | ||
540 | process_input(&readset); | ||
541 | |||
542 | /* Process output to the client and to program stdin. */ | ||
543 | process_output(&writeset); | ||
499 | } | 544 | } |
500 | 545 | ||
501 | /* Make packets from buffered stderr data to send to the client. */ | 546 | quit: |
502 | make_packets_from_stderr_data(); | 547 | /* Cleanup and termination code. */ |
503 | |||
504 | /* Make packets from buffered stdout data to send to the client. | ||
505 | If there is very little to send, this arranges to not send them | ||
506 | now, but to wait a short while to see if we are getting more data. | ||
507 | This is necessary, as some systems wake up readers from a pty after | ||
508 | each separate character. */ | ||
509 | max_time_milliseconds = 0; | ||
510 | stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
511 | if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && | ||
512 | stdout_buffer_bytes != previous_stdout_buffer_bytes) | ||
513 | max_time_milliseconds = 10; /* try again after a while */ | ||
514 | else | ||
515 | make_packets_from_stdout_data(); /* Send it now. */ | ||
516 | previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
517 | |||
518 | /* Send channel data to the client. */ | ||
519 | if (packet_not_very_much_data_to_write()) | ||
520 | channel_output_poll(); | ||
521 | |||
522 | /* Bail out of the loop if the program has closed its output descriptors, | ||
523 | and we have no more data to send to the client, and there is no | ||
524 | pending buffered data. */ | ||
525 | if (fdout_eof && fderr_eof && !packet_have_data_to_write() && | ||
526 | buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) | ||
527 | { | ||
528 | if (!channel_still_open()) | ||
529 | goto quit; | ||
530 | if (!waiting_termination) | ||
531 | { | ||
532 | const char *s = | ||
533 | "Waiting for forwarded connections to terminate...\r\n"; | ||
534 | char *cp; | ||
535 | waiting_termination = 1; | ||
536 | buffer_append(&stderr_buffer, s, strlen(s)); | ||
537 | |||
538 | /* Display list of open channels. */ | ||
539 | cp = channel_open_message(); | ||
540 | buffer_append(&stderr_buffer, cp, strlen(cp)); | ||
541 | xfree(cp); | ||
542 | } | ||
543 | } | ||
544 | 548 | ||
545 | /* Sleep in select() until we can do something. */ | 549 | /* Wait until all output has been sent to the client. */ |
546 | wait_until_can_do_something(&readset, &writeset, | 550 | drain_output(); |
547 | max_time_milliseconds); | ||
548 | |||
549 | /* Process any channel events. */ | ||
550 | channel_after_select(&readset, &writeset); | ||
551 | |||
552 | /* Process input from the client and from program stdout/stderr. */ | ||
553 | process_input(&readset); | ||
554 | |||
555 | /* Process output to the client and to program stdin. */ | ||
556 | process_output(&writeset); | ||
557 | } | ||
558 | |||
559 | quit: | ||
560 | /* Cleanup and termination code. */ | ||
561 | |||
562 | /* Wait until all output has been sent to the client. */ | ||
563 | drain_output(); | ||
564 | |||
565 | debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", | ||
566 | stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); | ||
567 | |||
568 | /* Free and clear the buffers. */ | ||
569 | buffer_free(&stdin_buffer); | ||
570 | buffer_free(&stdout_buffer); | ||
571 | buffer_free(&stderr_buffer); | ||
572 | |||
573 | /* Close the file descriptors. */ | ||
574 | if (fdout != -1) | ||
575 | close(fdout); | ||
576 | fdout = -1; | ||
577 | fdout_eof = 1; | ||
578 | if (fderr != -1) | ||
579 | close(fderr); | ||
580 | fderr = -1; | ||
581 | fderr_eof = 1; | ||
582 | if (fdin != -1) | ||
583 | close(fdin); | ||
584 | fdin = -1; | ||
585 | |||
586 | /* Stop listening for channels; this removes unix domain sockets. */ | ||
587 | channel_stop_listening(); | ||
588 | |||
589 | /* Wait for the child to exit. Get its exit status. */ | ||
590 | wait_pid = wait(&wait_status); | ||
591 | if (wait_pid < 0) | ||
592 | { | ||
593 | /* It is possible that the wait was handled by SIGCHLD handler. This | ||
594 | may result in either: this call returning with EINTR, or: this | ||
595 | call returning ECHILD. */ | ||
596 | if (child_terminated) | ||
597 | wait_status = child_wait_status; | ||
598 | else | ||
599 | packet_disconnect("wait: %.100s", strerror(errno)); | ||
600 | } | ||
601 | else | ||
602 | { | ||
603 | /* Check if it matches the process we forked. */ | ||
604 | if (wait_pid != pid) | ||
605 | error("Strange, wait returned pid %d, expected %d", wait_pid, pid); | ||
606 | } | ||
607 | |||
608 | /* We no longer want our SIGCHLD handler to be called. */ | ||
609 | signal(SIGCHLD, SIG_DFL); | ||
610 | |||
611 | /* Check if it exited normally. */ | ||
612 | if (WIFEXITED(wait_status)) | ||
613 | { | ||
614 | /* Yes, normal exit. Get exit status and send it to the client. */ | ||
615 | debug("Command exited with status %d.", WEXITSTATUS(wait_status)); | ||
616 | packet_start(SSH_SMSG_EXITSTATUS); | ||
617 | packet_put_int(WEXITSTATUS(wait_status)); | ||
618 | packet_send(); | ||
619 | packet_write_wait(); | ||
620 | |||
621 | /* Wait for exit confirmation. Note that there might be other | ||
622 | packets coming before it; however, the program has already died | ||
623 | so we just ignore them. The client is supposed to respond with | ||
624 | the confirmation when it receives the exit status. */ | ||
625 | do | ||
626 | { | ||
627 | int plen; | ||
628 | type = packet_read(&plen); | ||
629 | } | ||
630 | while (type != SSH_CMSG_EXIT_CONFIRMATION); | ||
631 | 551 | ||
632 | debug("Received exit confirmation."); | 552 | debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", |
633 | return; | 553 | stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); |
634 | } | ||
635 | 554 | ||
636 | /* Check if the program terminated due to a signal. */ | 555 | /* Free and clear the buffers. */ |
637 | if (WIFSIGNALED(wait_status)) | 556 | buffer_free(&stdin_buffer); |
638 | packet_disconnect("Command terminated on signal %d.", | 557 | buffer_free(&stdout_buffer); |
639 | WTERMSIG(wait_status)); | 558 | buffer_free(&stderr_buffer); |
640 | 559 | ||
641 | /* Some weird exit cause. Just exit. */ | 560 | /* Close the file descriptors. */ |
642 | packet_disconnect("wait returned status %04x.", wait_status); | 561 | if (fdout != -1) |
643 | /*NOTREACHED*/ | 562 | close(fdout); |
644 | } | 563 | fdout = -1; |
564 | fdout_eof = 1; | ||
565 | if (fderr != -1) | ||
566 | close(fderr); | ||
567 | fderr = -1; | ||
568 | fderr_eof = 1; | ||
569 | if (fdin != -1) | ||
570 | close(fdin); | ||
571 | fdin = -1; | ||
572 | |||
573 | /* Stop listening for channels; this removes unix domain sockets. */ | ||
574 | channel_stop_listening(); | ||
575 | |||
576 | /* Wait for the child to exit. Get its exit status. */ | ||
577 | wait_pid = wait(&wait_status); | ||
578 | if (wait_pid < 0) { | ||
579 | /* | ||
580 | * It is possible that the wait was handled by SIGCHLD | ||
581 | * handler. This may result in either: this call | ||
582 | * returning with EINTR, or: this call returning ECHILD. | ||
583 | */ | ||
584 | if (child_terminated) | ||
585 | wait_status = child_wait_status; | ||
586 | else | ||
587 | packet_disconnect("wait: %.100s", strerror(errno)); | ||
588 | } else { | ||
589 | /* Check if it matches the process we forked. */ | ||
590 | if (wait_pid != pid) | ||
591 | error("Strange, wait returned pid %d, expected %d", | ||
592 | wait_pid, pid); | ||
593 | } | ||
645 | 594 | ||
595 | /* We no longer want our SIGCHLD handler to be called. */ | ||
596 | signal(SIGCHLD, SIG_DFL); | ||
597 | |||
598 | /* Check if it exited normally. */ | ||
599 | if (WIFEXITED(wait_status)) { | ||
600 | /* Yes, normal exit. Get exit status and send it to the client. */ | ||
601 | debug("Command exited with status %d.", WEXITSTATUS(wait_status)); | ||
602 | packet_start(SSH_SMSG_EXITSTATUS); | ||
603 | packet_put_int(WEXITSTATUS(wait_status)); | ||
604 | packet_send(); | ||
605 | packet_write_wait(); | ||
606 | |||
607 | /* Wait for exit confirmation. Note that there might be | ||
608 | other packets coming before it; however, the program | ||
609 | has already died so we just ignore them. The client is | ||
610 | supposed to respond with the confirmation when it | ||
611 | receives the exit status. */ | ||
612 | do { | ||
613 | int plen; | ||
614 | type = packet_read(&plen); | ||
615 | } | ||
616 | while (type != SSH_CMSG_EXIT_CONFIRMATION); | ||
617 | |||
618 | debug("Received exit confirmation."); | ||
619 | return; | ||
620 | } | ||
621 | /* Check if the program terminated due to a signal. */ | ||
622 | if (WIFSIGNALED(wait_status)) | ||
623 | packet_disconnect("Command terminated on signal %d.", | ||
624 | WTERMSIG(wait_status)); | ||
625 | |||
626 | /* Some weird exit cause. Just exit. */ | ||
627 | packet_disconnect("wait returned status %04x.", wait_status); | ||
628 | /* NOTREACHED */ | ||
629 | } | ||