Mercurial > dropbear
comparison common-channel.c @ 367:c046b66b76cd channel-fix
propagate from branch 'au.asn.ucc.matt.dropbear' (head 31dcd7a22983ef19d6c63248e415e71d292dd0ec)
to branch 'au.asn.ucc.matt.dropbear.channel-fix' (head 7559a8cc4f6abe2338636f2aced3a395a79c172c)
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 12 Oct 2006 03:01:10 +0000 |
parents | 49fcc9875045 |
children | 8d149b812669 |
comparison
equal
deleted
inserted
replaced
366:59531221b846 | 367:c046b66b76cd |
---|---|
41 unsigned int recvwindow, | 41 unsigned int recvwindow, |
42 unsigned int recvmaxpacket); | 42 unsigned int recvmaxpacket); |
43 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); | 43 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); |
44 static void send_msg_channel_window_adjust(struct Channel *channel, | 44 static void send_msg_channel_window_adjust(struct Channel *channel, |
45 unsigned int incr); | 45 unsigned int incr); |
46 static void send_msg_channel_data(struct Channel *channel, int isextended, | 46 static void send_msg_channel_data(struct Channel *channel, int isextended); |
47 unsigned int exttype); | |
48 static void send_msg_channel_eof(struct Channel *channel); | 47 static void send_msg_channel_eof(struct Channel *channel); |
49 static void send_msg_channel_close(struct Channel *channel); | 48 static void send_msg_channel_close(struct Channel *channel); |
50 static void removechannel(struct Channel *channel); | 49 static void remove_channel(struct Channel *channel); |
51 static void deletechannel(struct Channel *channel); | 50 static void delete_channel(struct Channel *channel); |
52 static void checkinitdone(struct Channel *channel); | 51 static void check_in_progress(struct Channel *channel); |
53 static void checkclose(struct Channel *channel); | 52 static unsigned int write_pending(struct Channel * channel); |
54 | 53 static void check_close(struct Channel *channel); |
55 static void closewritefd(struct Channel * channel); | 54 static void close_chan_fd(struct Channel *channel, int fd, int how); |
56 static void closereadfd(struct Channel * channel, int fd); | |
57 static void closechanfd(struct Channel *channel, int fd, int how); | |
58 | 55 |
59 #define FD_UNINIT (-2) | 56 #define FD_UNINIT (-2) |
60 #define FD_CLOSED (-1) | 57 #define FD_CLOSED (-1) |
61 | 58 |
62 /* Initialise all the channels */ | 59 /* Initialise all the channels */ |
83 | 80 |
84 TRACE(("enter chancleanup")) | 81 TRACE(("enter chancleanup")) |
85 for (i = 0; i < ses.chansize; i++) { | 82 for (i = 0; i < ses.chansize; i++) { |
86 if (ses.channels[i] != NULL) { | 83 if (ses.channels[i] != NULL) { |
87 TRACE(("channel %d closing", i)) | 84 TRACE(("channel %d closing", i)) |
88 removechannel(ses.channels[i]); | 85 remove_channel(ses.channels[i]); |
89 } | 86 } |
90 } | 87 } |
91 m_free(ses.channels); | 88 m_free(ses.channels); |
92 TRACE(("leave chancleanup")) | 89 TRACE(("leave chancleanup")) |
93 } | 90 } |
133 } | 130 } |
134 | 131 |
135 newchan = (struct Channel*)m_malloc(sizeof(struct Channel)); | 132 newchan = (struct Channel*)m_malloc(sizeof(struct Channel)); |
136 newchan->type = type; | 133 newchan->type = type; |
137 newchan->index = i; | 134 newchan->index = i; |
138 newchan->sentclosed = newchan->recvclosed = 0; | 135 newchan->sent_close = newchan->recv_close = 0; |
139 newchan->senteof = newchan->recveof = 0; | 136 newchan->sent_eof = newchan->recv_eof = 0; |
140 | 137 |
141 newchan->remotechan = remotechan; | 138 newchan->remotechan = remotechan; |
142 newchan->transwindow = transwindow; | 139 newchan->transwindow = transwindow; |
143 newchan->transmaxpacket = transmaxpacket; | 140 newchan->transmaxpacket = transmaxpacket; |
144 | 141 |
162 | 159 |
163 return newchan; | 160 return newchan; |
164 } | 161 } |
165 | 162 |
166 /* Returns the channel structure corresponding to the channel in the current | 163 /* Returns the channel structure corresponding to the channel in the current |
167 * data packet (ses.payload must be positioned appropriately) */ | 164 * data packet (ses.payload must be positioned appropriately). |
168 struct Channel* getchannel() { | 165 * A valid channel is always returns, it will fail fatally with an unknown |
166 * channel */ | |
167 static struct Channel* getchannel_msg(const char* kind) { | |
169 | 168 |
170 unsigned int chan; | 169 unsigned int chan; |
171 | 170 |
172 chan = buf_getint(ses.payload); | 171 chan = buf_getint(ses.payload); |
173 if (chan >= ses.chansize || ses.channels[chan] == NULL) { | 172 if (chan >= ses.chansize || ses.channels[chan] == NULL) { |
174 return NULL; | 173 if (kind) { |
174 dropbear_exit("%s for unknown channel %d", kind, chan); | |
175 } else { | |
176 dropbear_exit("Unknown channel %d", chan); | |
177 } | |
175 } | 178 } |
176 return ses.channels[chan]; | 179 return ses.channels[chan]; |
180 } | |
181 | |
182 struct Channel* getchannel() { | |
183 return getchannel_msg(NULL); | |
177 } | 184 } |
178 | 185 |
179 /* Iterate through the channels, performing IO if available */ | 186 /* Iterate through the channels, performing IO if available */ |
180 void channelio(fd_set *readfds, fd_set *writefds) { | 187 void channelio(fd_set *readfds, fd_set *writefds) { |
181 | 188 |
182 struct Channel *channel; | 189 struct Channel *channel; |
183 unsigned int i; | 190 unsigned int i; |
184 int ret; | 191 |
185 | 192 /* foreach channel */ |
186 /* iterate through all the possible channels */ | |
187 for (i = 0; i < ses.chansize; i++) { | 193 for (i = 0; i < ses.chansize; i++) { |
188 | 194 |
189 channel = ses.channels[i]; | 195 channel = ses.channels[i]; |
190 if (channel == NULL) { | 196 if (channel == NULL) { |
191 /* only process in-use channels */ | 197 /* only process in-use channels */ |
192 continue; | 198 continue; |
193 } | 199 } |
194 | 200 |
195 /* read data and send it over the wire */ | 201 /* read data and send it over the wire */ |
196 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { | 202 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { |
197 send_msg_channel_data(channel, 0, 0); | 203 send_msg_channel_data(channel, 0); |
198 } | 204 } |
199 | 205 |
200 /* read stderr data and send it over the wire */ | 206 /* read stderr data and send it over the wire */ |
201 if (channel->extrabuf == NULL && | 207 if (channel->extrabuf == NULL && |
202 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { | 208 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { |
203 send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR); | 209 send_msg_channel_data(channel, 1); |
204 } | |
205 | |
206 /* if we can read from the writefd, it might be closed, so we try to | |
207 * see if it has errors */ | |
208 if (IS_DROPBEAR_SERVER && channel->writefd >= 0 | |
209 && channel->writefd != channel->readfd | |
210 && FD_ISSET(channel->writefd, readfds)) { | |
211 if (channel->initconn) { | |
212 /* Handling for "in progress" connection - this is needed | |
213 * to avoid spinning 100% CPU when we connect to a server | |
214 * which doesn't send anything (tcpfwding) */ | |
215 checkinitdone(channel); | |
216 continue; /* Important not to use the channel after | |
217 checkinitdone(), as it may be NULL */ | |
218 } | |
219 ret = write(channel->writefd, NULL, 0); /* Fake write */ | |
220 if (ret < 0 && errno != EINTR && errno != EAGAIN) { | |
221 closewritefd(channel); | |
222 } | |
223 } | 210 } |
224 | 211 |
225 /* write to program/pipe stdin */ | 212 /* write to program/pipe stdin */ |
226 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { | 213 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { |
227 if (channel->initconn) { | 214 if (channel->initconn) { |
228 checkinitdone(channel); | 215 /* XXX should this go somewhere cleaner? */ |
216 check_in_progress(channel); | |
229 continue; /* Important not to use the channel after | 217 continue; /* Important not to use the channel after |
230 checkinitdone(), as it may be NULL */ | 218 check_in_progress(), as it may be NULL */ |
231 } | 219 } |
232 writechannel(channel, channel->writefd, channel->writebuf); | 220 writechannel(channel, channel->writefd, channel->writebuf); |
233 } | 221 } |
234 | 222 |
235 /* stderr for client mode */ | 223 /* stderr for client mode */ |
236 if (channel->extrabuf != NULL | 224 if (channel->extrabuf != NULL |
237 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { | 225 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { |
238 writechannel(channel, channel->errfd, channel->extrabuf); | 226 writechannel(channel, channel->errfd, channel->extrabuf); |
239 } | 227 } |
240 | 228 |
241 /* now handle any of the channel-closing type stuff */ | 229 /* handle any channel closing etc */ |
242 checkclose(channel); | 230 check_close(channel); |
243 | 231 |
244 } /* foreach channel */ | 232 } |
245 | 233 |
246 /* Listeners such as TCP, X11, agent-auth */ | 234 /* Listeners such as TCP, X11, agent-auth */ |
247 #ifdef USING_LISTENERS | 235 #ifdef USING_LISTENERS |
248 handle_listeners(readfds); | 236 handle_listeners(readfds); |
249 #endif | 237 #endif |
250 } | 238 } |
251 | 239 |
252 | 240 |
253 /* do all the EOF/close type stuff checking for a channel */ | 241 /* Returns true if there is data remaining to be written to stdin or |
254 static void checkclose(struct Channel *channel) { | 242 * stderr of a channel's endpoint. */ |
255 | 243 static unsigned int write_pending(struct Channel * channel) { |
256 TRACE(("checkclose: writefd %d, readfd %d, errfd %d, sentclosed %d, recvclosed %d", | 244 |
245 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) { | |
246 return 1; | |
247 } else if (channel->errfd >= 0 && channel->extrabuf && | |
248 cbuf_getused(channel->writebuf) > 0) { | |
249 return 1; | |
250 } | |
251 return 0; | |
252 } | |
253 | |
254 | |
255 /* EOF/close handling */ | |
256 static void check_close(struct Channel *channel) { | |
257 | |
258 TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d", | |
257 channel->writefd, channel->readfd, | 259 channel->writefd, channel->readfd, |
258 channel->errfd, channel->sentclosed, channel->recvclosed)) | 260 channel->errfd, channel->sent_close, channel->recv_close)) |
259 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", | 261 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", |
260 cbuf_getused(channel->writebuf), | 262 cbuf_getused(channel->writebuf), |
261 channel->writebuf, | 263 channel->writebuf, |
262 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) | 264 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) |
263 | 265 |
264 /* server chansession channels are special, since readfd mightn't | 266 /* A bit of a hack for closing up server session channels */ |
265 * close in the case of "sleep 4 & echo blah" until the sleep is up */ | 267 if (channel->writefd >= 0 |
266 if (channel->type->checkclose) { | 268 && channel->type->check_close |
267 if (channel->type->checkclose(channel)) { | 269 && channel->type->check_close(channel)) { |
268 closewritefd(channel); | 270 TRACE(("channel->type->check_close got hit")) |
269 closereadfd(channel, channel->readfd); | 271 close_chan_fd(channel, channel->writefd, SHUT_WR); |
270 closereadfd(channel, channel->errfd); | 272 } |
271 } | 273 |
272 } | 274 if (channel->recv_close && !write_pending(channel)) { |
273 | 275 if (! channel->sent_close) { |
274 if (!channel->senteof | |
275 && channel->readfd == FD_CLOSED | |
276 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | |
277 send_msg_channel_eof(channel); | |
278 } | |
279 | |
280 if (!channel->sentclosed | |
281 && channel->writefd == FD_CLOSED | |
282 && channel->readfd == FD_CLOSED | |
283 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | |
284 send_msg_channel_close(channel); | |
285 } | |
286 | |
287 /* When either party wishes to terminate the channel, it sends | |
288 * SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST | |
289 * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this | |
290 * message for the channel. The channel is considered closed for a | |
291 * party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and | |
292 * the party may then reuse the channel number. A party MAY send | |
293 * SSH_MSG_CHANNEL_CLOSE without having sent or received | |
294 * SSH_MSG_CHANNEL_EOF. | |
295 * (from draft-ietf-secsh-connect) | |
296 */ | |
297 if (channel->recvclosed) { | |
298 if (! channel->sentclosed) { | |
299 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) | 276 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) |
300 send_msg_channel_close(channel); | 277 send_msg_channel_close(channel); |
301 } | 278 } |
302 removechannel(channel); | 279 remove_channel(channel); |
303 } | 280 return; |
281 } | |
282 | |
283 if (channel->recv_eof && !write_pending(channel)) { | |
284 close_chan_fd(channel, channel->writefd, SHUT_WR); | |
285 } | |
286 | |
287 if (!channel->sent_eof | |
288 && channel->readfd == FD_CLOSED | |
289 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | |
290 send_msg_channel_eof(channel); | |
291 } | |
292 | |
293 if (!channel->sent_close | |
294 && channel->writefd == FD_CLOSED | |
295 && channel->readfd == FD_CLOSED | |
296 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | |
297 send_msg_channel_close(channel); | |
298 } | |
299 | |
304 } | 300 } |
305 | 301 |
306 | 302 |
307 /* Check whether a deferred (EINPROGRESS) connect() was successful, and | 303 /* Check whether a deferred (EINPROGRESS) connect() was successful, and |
308 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so | 304 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so |
309 * it is important that the channel reference isn't used after a call to this | 305 * it is important that the channel reference isn't used after a call to this |
310 * function */ | 306 * function */ |
311 static void checkinitdone(struct Channel *channel) { | 307 static void check_in_progress(struct Channel *channel) { |
312 | 308 |
313 int val; | 309 int val; |
314 socklen_t vallen = sizeof(val); | 310 socklen_t vallen = sizeof(val); |
315 | 311 |
316 TRACE(("enter checkinitdone")) | 312 TRACE(("enter check_in_progress")) |
317 | 313 |
318 if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen) | 314 if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen) |
319 || val != 0) { | 315 || val != 0) { |
320 send_msg_channel_open_failure(channel->remotechan, | 316 send_msg_channel_open_failure(channel->remotechan, |
321 SSH_OPEN_CONNECT_FAILED, "", ""); | 317 SSH_OPEN_CONNECT_FAILED, "", ""); |
322 close(channel->writefd); | 318 close(channel->writefd); |
323 deletechannel(channel); | 319 delete_channel(channel); |
324 TRACE(("leave checkinitdone: fail")) | 320 TRACE(("leave check_in_progress: fail")) |
325 } else { | 321 } else { |
326 send_msg_channel_open_confirmation(channel, channel->recvwindow, | 322 send_msg_channel_open_confirmation(channel, channel->recvwindow, |
327 channel->recvmaxpacket); | 323 channel->recvmaxpacket); |
328 channel->readfd = channel->writefd; | 324 channel->readfd = channel->writefd; |
329 channel->initconn = 0; | 325 channel->initconn = 0; |
330 TRACE(("leave checkinitdone: success")) | 326 TRACE(("leave check_in_progress: success")) |
331 } | 327 } |
332 } | 328 } |
333 | |
334 | 329 |
335 | 330 |
336 /* Send the close message and set the channel as closed */ | 331 /* Send the close message and set the channel as closed */ |
337 static void send_msg_channel_close(struct Channel *channel) { | 332 static void send_msg_channel_close(struct Channel *channel) { |
338 | 333 |
339 TRACE(("enter send_msg_channel_close")) | 334 TRACE(("enter send_msg_channel_close")) |
340 /* XXX server */ | |
341 if (channel->type->closehandler) { | 335 if (channel->type->closehandler) { |
342 channel->type->closehandler(channel); | 336 channel->type->closehandler(channel); |
343 } | 337 } |
344 | 338 |
345 CHECKCLEARTOWRITE(); | 339 CHECKCLEARTOWRITE(); |
347 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); | 341 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); |
348 buf_putint(ses.writepayload, channel->remotechan); | 342 buf_putint(ses.writepayload, channel->remotechan); |
349 | 343 |
350 encrypt_packet(); | 344 encrypt_packet(); |
351 | 345 |
352 channel->senteof = 1; | 346 channel->sent_eof = 1; |
353 channel->sentclosed = 1; | 347 channel->sent_close = 1; |
354 TRACE(("leave send_msg_channel_close")) | 348 TRACE(("leave send_msg_channel_close")) |
355 } | 349 } |
356 | 350 |
357 /* call this when trans/eof channels are closed */ | 351 /* call this when trans/eof channels are closed */ |
358 static void send_msg_channel_eof(struct Channel *channel) { | 352 static void send_msg_channel_eof(struct Channel *channel) { |
363 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF); | 357 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF); |
364 buf_putint(ses.writepayload, channel->remotechan); | 358 buf_putint(ses.writepayload, channel->remotechan); |
365 | 359 |
366 encrypt_packet(); | 360 encrypt_packet(); |
367 | 361 |
368 channel->senteof = 1; | 362 channel->sent_eof = 1; |
369 | 363 |
370 TRACE(("leave send_msg_channel_eof")) | 364 TRACE(("leave send_msg_channel_eof")) |
371 } | 365 } |
372 | 366 |
373 /* Called to write data out to the local side of the channel. | 367 /* Called to write data out to the local side of the channel. |
383 | 377 |
384 /* Write the data out */ | 378 /* Write the data out */ |
385 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); | 379 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); |
386 if (len <= 0) { | 380 if (len <= 0) { |
387 if (len < 0 && errno != EINTR) { | 381 if (len < 0 && errno != EINTR) { |
388 /* no more to write - we close it even if the fd was stderr, since | 382 close_chan_fd(channel, fd, SHUT_WR); |
389 * that's a nasty failure too */ | |
390 closewritefd(channel); | |
391 } | 383 } |
392 TRACE(("leave writechannel: len <= 0")) | 384 TRACE(("leave writechannel: len <= 0")) |
393 return; | 385 return; |
394 } | 386 } |
395 | 387 |
396 cbuf_incrread(cbuf, len); | 388 cbuf_incrread(cbuf, len); |
397 channel->recvdonelen += len; | 389 channel->recvdonelen += len; |
398 | |
399 if (fd == channel->writefd && cbuf_getused(cbuf) == 0 && channel->recveof) { | |
400 /* Check if we're closing up */ | |
401 closewritefd(channel); | |
402 TRACE(("leave writechannel: recveof set")) | |
403 return; | |
404 } | |
405 | 390 |
406 /* Window adjust handling */ | 391 /* Window adjust handling */ |
407 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { | 392 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { |
408 /* Set it back to max window */ | 393 /* Set it back to max window */ |
409 send_msg_channel_window_adjust(channel, channel->recvdonelen); | 394 send_msg_channel_window_adjust(channel, channel->recvdonelen); |
413 | 398 |
414 dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW); | 399 dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW); |
415 dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); | 400 dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); |
416 dropbear_assert(channel->extrabuf == NULL || | 401 dropbear_assert(channel->extrabuf == NULL || |
417 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); | 402 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); |
418 | |
419 | 403 |
420 TRACE(("leave writechannel")) | 404 TRACE(("leave writechannel")) |
421 } | 405 } |
422 | 406 |
423 /* Set the file descriptors for the main select in session.c | 407 /* Set the file descriptors for the main select in session.c |
444 if (channel->extrabuf == NULL && channel->errfd >= 0) { | 428 if (channel->extrabuf == NULL && channel->errfd >= 0) { |
445 FD_SET(channel->errfd, readfds); | 429 FD_SET(channel->errfd, readfds); |
446 } | 430 } |
447 } | 431 } |
448 | 432 |
449 TRACE(("writefd = %d, readfd %d, errfd %d, bufused %d", | |
450 channel->writefd, channel->readfd, | |
451 channel->errfd, | |
452 cbuf_getused(channel->writebuf) )) | |
453 | |
454 /* For checking FD status (ie closure etc) - we don't actually | |
455 * read data from writefd. We don't want to do this for the client, | |
456 * since redirection to /dev/null will make it spin in the select */ | |
457 if (IS_DROPBEAR_SERVER && channel->writefd >= 0 | |
458 && channel->writefd != channel->readfd) { | |
459 FD_SET(channel->writefd, readfds); | |
460 } | |
461 | |
462 /* Stuff from the wire */ | 433 /* Stuff from the wire */ |
463 if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 ) | 434 if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 ) |
464 || channel->initconn) { | 435 || channel->initconn) { |
465 FD_SET(channel->writefd, writefds); | 436 FD_SET(channel->writefd, writefds); |
466 } | 437 } |
485 | 456 |
486 struct Channel * channel; | 457 struct Channel * channel; |
487 | 458 |
488 TRACE(("enter recv_msg_channel_eof")) | 459 TRACE(("enter recv_msg_channel_eof")) |
489 | 460 |
490 channel = getchannel(); | 461 channel = getchannel_msg("EOF"); |
491 if (channel == NULL) { | 462 |
492 dropbear_exit("EOF for unknown channel"); | 463 channel->recv_eof = 1; |
493 } | 464 |
494 | 465 check_close(channel); |
495 channel->recveof = 1; | |
496 if (cbuf_getused(channel->writebuf) == 0 | |
497 && (channel->extrabuf == NULL | |
498 || cbuf_getused(channel->extrabuf) == 0)) { | |
499 closewritefd(channel); | |
500 } | |
501 | |
502 TRACE(("leave recv_msg_channel_eof")) | 466 TRACE(("leave recv_msg_channel_eof")) |
503 } | 467 } |
504 | 468 |
505 | 469 |
506 /* Handle channel closure(), respond in kind and close the channels */ | 470 /* Handle channel closure(), respond in kind and close the channels */ |
508 | 472 |
509 struct Channel * channel; | 473 struct Channel * channel; |
510 | 474 |
511 TRACE(("enter recv_msg_channel_close")) | 475 TRACE(("enter recv_msg_channel_close")) |
512 | 476 |
513 channel = getchannel(); | 477 channel = getchannel_msg("Close"); |
514 if (channel == NULL) { | 478 |
515 /* disconnect ? */ | 479 channel->recv_eof = 1; |
516 dropbear_exit("Close for unknown channel"); | 480 channel->recv_close = 1; |
517 } | 481 |
518 | 482 check_close(channel); |
519 channel->recveof = 1; | |
520 channel->recvclosed = 1; | |
521 | |
522 if (channel->sentclosed) { | |
523 removechannel(channel); | |
524 } | |
525 | |
526 TRACE(("leave recv_msg_channel_close")) | 483 TRACE(("leave recv_msg_channel_close")) |
527 } | 484 } |
528 | 485 |
529 /* Remove a channel entry, this is only executed after both sides have sent | 486 /* Remove a channel entry, this is only executed after both sides have sent |
530 * channel close */ | 487 * channel close */ |
531 static void removechannel(struct Channel * channel) { | 488 static void remove_channel(struct Channel * channel) { |
532 | 489 |
533 TRACE(("enter removechannel")) | 490 TRACE(("enter remove_channel")) |
534 TRACE(("channel index is %d", channel->index)) | 491 TRACE(("channel index is %d", channel->index)) |
535 | 492 |
536 cbuf_free(channel->writebuf); | 493 cbuf_free(channel->writebuf); |
537 channel->writebuf = NULL; | 494 channel->writebuf = NULL; |
538 | 495 |
541 channel->extrabuf = NULL; | 498 channel->extrabuf = NULL; |
542 } | 499 } |
543 | 500 |
544 | 501 |
545 /* close the FDs in case they haven't been done | 502 /* close the FDs in case they haven't been done |
546 * yet (ie they were shutdown etc */ | 503 * yet (they might have been shutdown etc) */ |
547 close(channel->writefd); | 504 close(channel->writefd); |
548 close(channel->readfd); | 505 close(channel->readfd); |
549 close(channel->errfd); | 506 close(channel->errfd); |
550 | 507 |
551 channel->typedata = NULL; | 508 channel->typedata = NULL; |
552 | 509 |
553 deletechannel(channel); | 510 delete_channel(channel); |
554 | 511 |
555 TRACE(("leave removechannel")) | 512 TRACE(("leave remove_channel")) |
556 } | 513 } |
557 | 514 |
558 /* Remove a channel entry */ | 515 /* Remove a channel entry */ |
559 static void deletechannel(struct Channel *channel) { | 516 static void delete_channel(struct Channel *channel) { |
560 | 517 |
561 ses.channels[channel->index] = NULL; | 518 ses.channels[channel->index] = NULL; |
562 m_free(channel); | 519 m_free(channel); |
563 ses.chancount--; | 520 ses.chancount--; |
564 | 521 |
572 struct Channel *channel; | 529 struct Channel *channel; |
573 | 530 |
574 TRACE(("enter recv_msg_channel_request")) | 531 TRACE(("enter recv_msg_channel_request")) |
575 | 532 |
576 channel = getchannel(); | 533 channel = getchannel(); |
577 if (channel == NULL) { | |
578 /* disconnect ? */ | |
579 dropbear_exit("Unknown channel"); | |
580 } | |
581 | 534 |
582 if (channel->type->reqhandler) { | 535 if (channel->type->reqhandler) { |
583 channel->type->reqhandler(channel); | 536 channel->type->reqhandler(channel); |
584 } else { | 537 } else { |
585 send_msg_channel_failure(channel); | 538 send_msg_channel_failure(channel); |
592 /* Reads data from the server's program/shell/etc, and puts it in a | 545 /* Reads data from the server's program/shell/etc, and puts it in a |
593 * channel_data packet to send. | 546 * channel_data packet to send. |
594 * chan is the remote channel, isextended is 0 if it is normal data, 1 | 547 * chan is the remote channel, isextended is 0 if it is normal data, 1 |
595 * if it is extended data. if it is extended, then the type is in | 548 * if it is extended data. if it is extended, then the type is in |
596 * exttype */ | 549 * exttype */ |
597 static void send_msg_channel_data(struct Channel *channel, int isextended, | 550 static void send_msg_channel_data(struct Channel *channel, int isextended) { |
598 unsigned int exttype) { | 551 |
599 | |
600 buffer *buf; | |
601 int len; | 552 int len; |
602 unsigned int maxlen; | 553 size_t maxlen, size_pos; |
603 int fd; | 554 int fd; |
604 | 555 |
605 /* TRACE(("enter send_msg_channel_data")) | |
606 TRACE(("extended = %d type = %d", isextended, exttype))*/ | |
607 | |
608 CHECKCLEARTOWRITE(); | 556 CHECKCLEARTOWRITE(); |
609 | 557 |
610 dropbear_assert(!channel->sentclosed); | 558 dropbear_assert(!channel->sent_close); |
611 | 559 |
612 if (isextended) { | 560 if (isextended) { |
613 fd = channel->errfd; | 561 fd = channel->errfd; |
614 } else { | 562 } else { |
615 fd = channel->readfd; | 563 fd = channel->readfd; |
619 maxlen = MIN(channel->transwindow, channel->transmaxpacket); | 567 maxlen = MIN(channel->transwindow, channel->transmaxpacket); |
620 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and | 568 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and |
621 * exttype if is extended */ | 569 * exttype if is extended */ |
622 maxlen = MIN(maxlen, | 570 maxlen = MIN(maxlen, |
623 ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); | 571 ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); |
572 TRACE(("maxlen %d", maxlen)) | |
624 if (maxlen == 0) { | 573 if (maxlen == 0) { |
625 TRACE(("leave send_msg_channel_data: no window")) | 574 TRACE(("leave send_msg_channel_data: no window")) |
626 return; /* the data will get written later */ | 575 return; |
627 } | 576 } |
577 | |
578 buf_putbyte(ses.writepayload, | |
579 isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); | |
580 buf_putint(ses.writepayload, channel->remotechan); | |
581 if (isextended) { | |
582 buf_putint(ses.writepayload, SSH_EXTENDED_DATA_STDERR); | |
583 } | |
584 /* a dummy size first ...*/ | |
585 size_pos = ses.writepayload->pos; | |
586 buf_putint(ses.writepayload, 0); | |
628 | 587 |
629 /* read the data */ | 588 /* read the data */ |
630 TRACE(("maxlen %d", maxlen)) | 589 len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen); |
631 buf = buf_new(maxlen); | |
632 TRACE(("buf pos %d data %x", buf->pos, buf->data)) | |
633 len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); | |
634 if (len <= 0) { | 590 if (len <= 0) { |
635 /* on error/eof, send eof */ | |
636 if (len == 0 || errno != EINTR) { | 591 if (len == 0 || errno != EINTR) { |
637 closereadfd(channel, fd); | 592 close_chan_fd(channel, fd, SHUT_RD); |
638 } | 593 } |
639 buf_free(buf); | 594 ses.writepayload->len = ses.writepayload->pos = 0; |
640 buf = NULL; | |
641 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", | 595 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", |
642 channel->index)); | 596 channel->index)); |
643 return; | 597 return; |
644 } | 598 } |
645 buf_incrlen(buf, len); | 599 buf_incrwritepos(ses.writepayload, len); |
646 | 600 /* ... real size here */ |
647 buf_putbyte(ses.writepayload, | 601 buf_setpos(ses.writepayload, size_pos); |
648 isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); | 602 buf_putint(ses.writepayload, len); |
649 buf_putint(ses.writepayload, channel->remotechan); | |
650 | |
651 if (isextended) { | |
652 buf_putint(ses.writepayload, exttype); | |
653 } | |
654 | |
655 buf_putstring(ses.writepayload, buf_getptr(buf, len), len); | |
656 buf_free(buf); | |
657 buf = NULL; | |
658 | 603 |
659 channel->transwindow -= len; | 604 channel->transwindow -= len; |
660 | 605 |
661 encrypt_packet(); | 606 encrypt_packet(); |
662 TRACE(("leave send_msg_channel_data")) | 607 TRACE(("leave send_msg_channel_data")) |
666 void recv_msg_channel_data() { | 611 void recv_msg_channel_data() { |
667 | 612 |
668 struct Channel *channel; | 613 struct Channel *channel; |
669 | 614 |
670 channel = getchannel(); | 615 channel = getchannel(); |
671 if (channel == NULL) { | |
672 dropbear_exit("Unknown channel"); | |
673 } | |
674 | 616 |
675 common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf); | 617 common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf); |
676 } | 618 } |
677 | 619 |
678 /* Shared for data and stderr data - when we receive data, put it in a buffer | 620 /* Shared for data and stderr data - when we receive data, put it in a buffer |
685 unsigned int buflen; | 627 unsigned int buflen; |
686 unsigned int len; | 628 unsigned int len; |
687 | 629 |
688 TRACE(("enter recv_msg_channel_data")) | 630 TRACE(("enter recv_msg_channel_data")) |
689 | 631 |
690 if (channel->recveof) { | 632 if (channel->recv_eof) { |
691 dropbear_exit("received data after eof"); | 633 dropbear_exit("received data after eof"); |
692 } | 634 } |
693 | 635 |
694 if (fd < 0) { | 636 if (fd < 0) { |
695 dropbear_exit("received data with bad writefd"); | 637 /* If we have encountered failed write, the far side might still |
638 * be sending data without having yet received our close notification. | |
639 * We just drop the data. */ | |
640 return; | |
696 } | 641 } |
697 | 642 |
698 datalen = buf_getint(ses.payload); | 643 datalen = buf_getint(ses.payload); |
699 | 644 TRACE(("length %d", datalen)) |
700 | 645 |
701 maxdata = cbuf_getavail(cbuf); | 646 maxdata = cbuf_getavail(cbuf); |
702 | 647 |
703 /* Whilst the spec says we "MAY ignore data past the end" this could | 648 /* Whilst the spec says we "MAY ignore data past the end" this could |
704 * lead to corrupted file transfers etc (chunks missed etc). It's better to | 649 * lead to corrupted file transfers etc (chunks missed etc). It's better to |
736 | 681 |
737 struct Channel * channel; | 682 struct Channel * channel; |
738 unsigned int incr; | 683 unsigned int incr; |
739 | 684 |
740 channel = getchannel(); | 685 channel = getchannel(); |
741 if (channel == NULL) { | |
742 dropbear_exit("Unknown channel"); | |
743 } | |
744 | 686 |
745 incr = buf_getint(ses.payload); | 687 incr = buf_getint(ses.payload); |
746 TRACE(("received window increment %d", incr)) | 688 TRACE(("received window increment %d", incr)) |
747 incr = MIN(incr, MAX_TRANS_WIN_INCR); | 689 incr = MIN(incr, MAX_TRANS_WIN_INCR); |
748 | 690 |
765 | 707 |
766 encrypt_packet(); | 708 encrypt_packet(); |
767 } | 709 } |
768 | 710 |
769 /* Handle a new channel request, performing any channel-type-specific setup */ | 711 /* Handle a new channel request, performing any channel-type-specific setup */ |
770 /* XXX server */ | |
771 void recv_msg_channel_open() { | 712 void recv_msg_channel_open() { |
772 | 713 |
773 unsigned char *type; | 714 unsigned char *type; |
774 unsigned int typelen; | 715 unsigned int typelen; |
775 unsigned int remotechan, transwindow, transmaxpacket; | 716 unsigned int remotechan, transwindow, transmaxpacket; |
797 } | 738 } |
798 | 739 |
799 /* Get the channel type. Client and server style invokation will set up a | 740 /* Get the channel type. Client and server style invokation will set up a |
800 * different list for ses.chantypes at startup. We just iterate through | 741 * different list for ses.chantypes at startup. We just iterate through |
801 * this list and find the matching name */ | 742 * this list and find the matching name */ |
743 /* XXX fugly */ | |
802 for (cp = &ses.chantypes[0], chantype = (*cp); | 744 for (cp = &ses.chantypes[0], chantype = (*cp); |
803 chantype != NULL; | 745 chantype != NULL; |
804 cp++, chantype = (*cp)) { | 746 cp++, chantype = (*cp)) { |
805 if (strcmp(type, chantype->name) == 0) { | 747 if (strcmp(type, chantype->name) == 0) { |
806 break; | 748 break; |
822 goto failure; | 764 goto failure; |
823 } | 765 } |
824 | 766 |
825 if (channel->type->inithandler) { | 767 if (channel->type->inithandler) { |
826 ret = channel->type->inithandler(channel); | 768 ret = channel->type->inithandler(channel); |
769 if (ret == SSH_OPEN_IN_PROGRESS) { | |
770 /* We'll send the confirmation later */ | |
771 goto cleanup; | |
772 } | |
827 if (ret > 0) { | 773 if (ret > 0) { |
828 if (ret == SSH_OPEN_IN_PROGRESS) { | |
829 /* We'll send the confirmation later */ | |
830 goto cleanup; | |
831 } | |
832 errtype = ret; | 774 errtype = ret; |
833 deletechannel(channel); | 775 delete_channel(channel); |
834 TRACE(("inithandler returned failure %d", ret)) | 776 TRACE(("inithandler returned failure %d", ret)) |
835 goto failure; | 777 goto failure; |
836 } | 778 } |
837 } | 779 } |
838 | 780 |
911 buf_putint(ses.writepayload, recvmaxpacket); | 853 buf_putint(ses.writepayload, recvmaxpacket); |
912 | 854 |
913 encrypt_packet(); | 855 encrypt_packet(); |
914 TRACE(("leave send_msg_channel_open_confirmation")) | 856 TRACE(("leave send_msg_channel_open_confirmation")) |
915 } | 857 } |
858 | |
859 /* close a fd, how is SHUT_RD or SHUT_WR */ | |
860 static void close_chan_fd(struct Channel *channel, int fd, int how) { | |
861 | |
862 int closein = 0, closeout = 0; | |
863 | |
864 if (channel->type->sepfds) { | |
865 TRACE(("shutdown((%d), %d)", fd, how)) | |
866 shutdown(fd, how); | |
867 if (how == 0) { | |
868 closeout = 1; | |
869 } else { | |
870 closein = 1; | |
871 } | |
872 } else { | |
873 close(fd); | |
874 closein = closeout = 1; | |
875 } | |
876 | |
877 if (closeout && fd == channel->readfd) { | |
878 channel->readfd = FD_CLOSED; | |
879 } | |
880 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { | |
881 channel->errfd = FD_CLOSED; | |
882 } | |
883 | |
884 if (closein && fd == channel->writefd) { | |
885 channel->writefd = FD_CLOSED; | |
886 } | |
887 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { | |
888 channel->errfd = FD_CLOSED; | |
889 } | |
890 | |
891 /* if we called shutdown on it and all references are gone, then we | |
892 * need to close() it to stop it lingering */ | |
893 if (channel->type->sepfds && channel->readfd == FD_CLOSED | |
894 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { | |
895 close(fd); | |
896 } | |
897 } | |
898 | |
916 | 899 |
917 #if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) | 900 #if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) |
918 /* Create a new channel, and start the open request. This is intended | 901 /* Create a new channel, and start the open request. This is intended |
919 * for X11, agent, tcp forwarding, and should be filled with channel-specific | 902 * for X11, agent, tcp forwarding, and should be filled with channel-specific |
920 * options, with the calling function calling encrypt_packet() after | 903 * options, with the calling function calling encrypt_packet() after |
960 int ret; | 943 int ret; |
961 | 944 |
962 TRACE(("enter recv_msg_channel_open_confirmation")) | 945 TRACE(("enter recv_msg_channel_open_confirmation")) |
963 | 946 |
964 channel = getchannel(); | 947 channel = getchannel(); |
965 if (channel == NULL) { | |
966 dropbear_exit("Unknown channel"); | |
967 } | |
968 | 948 |
969 if (!channel->await_open) { | 949 if (!channel->await_open) { |
970 dropbear_exit("unexpected channel reply"); | 950 dropbear_exit("unexpected channel reply"); |
971 } | 951 } |
972 channel->await_open = 0; | 952 channel->await_open = 0; |
980 | 960 |
981 /* Run the inithandler callback */ | 961 /* Run the inithandler callback */ |
982 if (channel->type->inithandler) { | 962 if (channel->type->inithandler) { |
983 ret = channel->type->inithandler(channel); | 963 ret = channel->type->inithandler(channel); |
984 if (ret > 0) { | 964 if (ret > 0) { |
985 removechannel(channel); | 965 remove_channel(channel); |
986 TRACE(("inithandler returned failure %d", ret)) | 966 TRACE(("inithandler returned failure %d", ret)) |
987 } | 967 } |
988 } | 968 } |
989 | 969 |
990 | 970 |
995 void recv_msg_channel_open_failure() { | 975 void recv_msg_channel_open_failure() { |
996 | 976 |
997 struct Channel * channel; | 977 struct Channel * channel; |
998 | 978 |
999 channel = getchannel(); | 979 channel = getchannel(); |
1000 if (channel == NULL) { | |
1001 dropbear_exit("Unknown channel"); | |
1002 } | |
1003 | 980 |
1004 if (!channel->await_open) { | 981 if (!channel->await_open) { |
1005 dropbear_exit("unexpected channel reply"); | 982 dropbear_exit("unexpected channel reply"); |
1006 } | 983 } |
1007 channel->await_open = 0; | 984 channel->await_open = 0; |
1008 | 985 |
1009 removechannel(channel); | 986 remove_channel(channel); |
1010 } | 987 } |
1011 #endif /* USING_LISTENERS */ | 988 #endif /* USING_LISTENERS */ |
1012 | |
1013 /* close a stdout/stderr fd */ | |
1014 static void closereadfd(struct Channel * channel, int fd) { | |
1015 | |
1016 /* don't close it if it is the same as writefd, | |
1017 * unless writefd is already set -1 */ | |
1018 TRACE(("enter closereadfd")) | |
1019 closechanfd(channel, fd, 0); | |
1020 TRACE(("leave closereadfd")) | |
1021 } | |
1022 | |
1023 /* close a stdin fd */ | |
1024 static void closewritefd(struct Channel * channel) { | |
1025 | |
1026 TRACE(("enter closewritefd")) | |
1027 closechanfd(channel, channel->writefd, 1); | |
1028 TRACE(("leave closewritefd")) | |
1029 } | |
1030 | |
1031 /* close a fd, how is 0 for stdout/stderr, 1 for stdin */ | |
1032 static void closechanfd(struct Channel *channel, int fd, int how) { | |
1033 | |
1034 int closein = 0, closeout = 0; | |
1035 | |
1036 /* XXX server */ | |
1037 if (channel->type->sepfds) { | |
1038 TRACE(("shutdown((%d), %d)", fd, how)) | |
1039 shutdown(fd, how); | |
1040 if (how == 0) { | |
1041 closeout = 1; | |
1042 } else { | |
1043 closein = 1; | |
1044 } | |
1045 } else { | |
1046 close(fd); | |
1047 closein = closeout = 1; | |
1048 } | |
1049 | |
1050 if (closeout && fd == channel->readfd) { | |
1051 channel->readfd = FD_CLOSED; | |
1052 } | |
1053 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { | |
1054 channel->errfd = FD_CLOSED; | |
1055 } | |
1056 | |
1057 if (closein && fd == channel->writefd) { | |
1058 channel->writefd = FD_CLOSED; | |
1059 } | |
1060 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { | |
1061 channel->errfd = FD_CLOSED; | |
1062 } | |
1063 | |
1064 /* if we called shutdown on it and all references are gone, then we | |
1065 * need to close() it to stop it lingering */ | |
1066 if (channel->type->sepfds && channel->readfd == FD_CLOSED | |
1067 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { | |
1068 close(fd); | |
1069 } | |
1070 } |