Mercurial > dropbear
comparison common-channel.c @ 118:5312ca05ed48 private-rez
propagate of 717950f4061f1123659ee87c7c168805af920ab7 and 839f98f136788cc1466e4641bf796f96040a085d from branch 'matt.dbclient.authpam' to 'matt.dbclient.rez'
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sun, 12 Sep 2004 04:56:50 +0000 |
parents | 2e9d1f29c50f |
children | 8c2b3506f112 |
comparison
equal
deleted
inserted
replaced
57:3b2a5a1c4347 | 118:5312ca05ed48 |
---|---|
1 /* | 1 /* |
2 * Dropbear - a SSH2 server | 2 * Dropbear SSH |
3 * | 3 * |
4 * Copyright (c) 2002,2003 Matt Johnston | 4 * Copyright (c) 2002-2004 Matt Johnston |
5 * All rights reserved. | 5 * All rights reserved. |
6 * | 6 * |
7 * Permission is hereby granted, free of charge, to any person obtaining a copy | 7 * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 * of this software and associated documentation files (the "Software"), to deal | 8 * of this software and associated documentation files (the "Software"), to deal |
9 * in the Software without restriction, including without limitation the rights | 9 * in the Software without restriction, including without limitation the rights |
27 #include "includes.h" | 27 #include "includes.h" |
28 #include "session.h" | 28 #include "session.h" |
29 #include "packet.h" | 29 #include "packet.h" |
30 #include "ssh.h" | 30 #include "ssh.h" |
31 #include "buffer.h" | 31 #include "buffer.h" |
32 #include "circbuffer.h" | |
32 #include "dbutil.h" | 33 #include "dbutil.h" |
33 #include "channel.h" | 34 #include "channel.h" |
34 #include "ssh.h" | 35 #include "ssh.h" |
35 #include "tcpfwd-direct.h" | |
36 #include "tcpfwd-remote.h" | |
37 #include "listener.h" | 36 #include "listener.h" |
38 | 37 |
39 static void send_msg_channel_open_failure(unsigned int remotechan, int reason, | 38 static void send_msg_channel_open_failure(unsigned int remotechan, int reason, |
40 const unsigned char *text, const unsigned char *lang); | 39 const unsigned char *text, const unsigned char *lang); |
41 static void send_msg_channel_open_confirmation(struct Channel* channel, | 40 static void send_msg_channel_open_confirmation(struct Channel* channel, |
42 unsigned int recvwindow, | 41 unsigned int recvwindow, |
43 unsigned int recvmaxpacket); | 42 unsigned int recvmaxpacket); |
44 static void writechannel(struct Channel *channel); | 43 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); |
45 static void send_msg_channel_window_adjust(struct Channel *channel, | 44 static void send_msg_channel_window_adjust(struct Channel *channel, |
46 unsigned int incr); | 45 unsigned int incr); |
47 static void send_msg_channel_data(struct Channel *channel, int isextended, | 46 static void send_msg_channel_data(struct Channel *channel, int isextended, |
48 unsigned int exttype); | 47 unsigned int exttype); |
49 static void send_msg_channel_eof(struct Channel *channel); | 48 static void send_msg_channel_eof(struct Channel *channel); |
147 newchan->infd = FD_UNINIT; | 146 newchan->infd = FD_UNINIT; |
148 newchan->outfd = FD_UNINIT; | 147 newchan->outfd = FD_UNINIT; |
149 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ | 148 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ |
150 newchan->initconn = 0; | 149 newchan->initconn = 0; |
151 | 150 |
152 newchan->writebuf = buf_new(RECV_MAXWINDOW); | 151 newchan->writebuf = cbuf_new(RECV_MAXWINDOW); |
152 newchan->extrabuf = NULL; /* The user code can set it up */ | |
153 newchan->recvwindow = RECV_MAXWINDOW; | 153 newchan->recvwindow = RECV_MAXWINDOW; |
154 newchan->recvdonelen = 0; | |
154 newchan->recvmaxpacket = RECV_MAXPACKET; | 155 newchan->recvmaxpacket = RECV_MAXPACKET; |
155 | 156 |
156 ses.channels[i] = newchan; | 157 ses.channels[i] = newchan; |
157 ses.chancount++; | 158 ses.chancount++; |
158 | 159 |
160 | 161 |
161 return newchan; | 162 return newchan; |
162 } | 163 } |
163 | 164 |
164 /* Get the channel structure corresponding to a channel number */ | 165 /* Get the channel structure corresponding to a channel number */ |
165 static struct Channel* getchannel(unsigned int chan) { | 166 struct Channel* getchannel(unsigned int chan) { |
166 if (chan >= ses.chansize || ses.channels[chan] == NULL) { | 167 if (chan >= ses.chansize || ses.channels[chan] == NULL) { |
167 return NULL; | 168 return NULL; |
168 } | 169 } |
169 return ses.channels[chan]; | 170 return ses.channels[chan]; |
170 } | 171 } |
172 /* Iterate through the channels, performing IO if available */ | 173 /* Iterate through the channels, performing IO if available */ |
173 void channelio(fd_set *readfd, fd_set *writefd) { | 174 void channelio(fd_set *readfd, fd_set *writefd) { |
174 | 175 |
175 struct Channel *channel; | 176 struct Channel *channel; |
176 unsigned int i; | 177 unsigned int i; |
178 int ret; | |
177 | 179 |
178 /* iterate through all the possible channels */ | 180 /* iterate through all the possible channels */ |
179 for (i = 0; i < ses.chansize; i++) { | 181 for (i = 0; i < ses.chansize; i++) { |
180 | 182 |
181 channel = ses.channels[i]; | 183 channel = ses.channels[i]; |
188 if (channel->outfd >= 0 && FD_ISSET(channel->outfd, readfd)) { | 190 if (channel->outfd >= 0 && FD_ISSET(channel->outfd, readfd)) { |
189 send_msg_channel_data(channel, 0, 0); | 191 send_msg_channel_data(channel, 0, 0); |
190 } | 192 } |
191 | 193 |
192 /* read from program/pipe stderr */ | 194 /* read from program/pipe stderr */ |
193 if (channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) { | 195 if (channel->extrabuf == NULL && |
196 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) { | |
194 send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR); | 197 send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR); |
195 } | 198 } |
196 | 199 |
197 /* if we can read from the infd, it might be closed, so we try to | 200 /* if we can read from the infd, it might be closed, so we try to |
198 * see if it has errors */ | 201 * see if it has errors */ |
199 if (channel->infd >= 0 && channel->infd != channel->outfd | 202 if (channel->infd >= 0 && channel->infd != channel->outfd |
200 && FD_ISSET(channel->infd, readfd)) { | 203 && FD_ISSET(channel->infd, readfd)) { |
201 int ret; | 204 if (channel->initconn) { |
202 ret = write(channel->infd, NULL, 0); | 205 /* Handling for "in progress" connection - this is needed |
206 * to avoid spinning 100% CPU when we connect to a server | |
207 * which doesn't send anything (tcpfwding) */ | |
208 checkinitdone(channel); | |
209 continue; /* Important not to use the channel after | |
210 checkinitdone(), as it may be NULL */ | |
211 } | |
212 ret = write(channel->infd, NULL, 0); /* Fake write */ | |
203 if (ret < 0 && errno != EINTR && errno != EAGAIN) { | 213 if (ret < 0 && errno != EINTR && errno != EAGAIN) { |
204 closeinfd(channel); | 214 closeinfd(channel); |
205 } | 215 } |
206 } | 216 } |
207 | 217 |
209 if (channel->infd >= 0 && FD_ISSET(channel->infd, writefd)) { | 219 if (channel->infd >= 0 && FD_ISSET(channel->infd, writefd)) { |
210 if (channel->initconn) { | 220 if (channel->initconn) { |
211 checkinitdone(channel); | 221 checkinitdone(channel); |
212 continue; /* Important not to use the channel after | 222 continue; /* Important not to use the channel after |
213 checkinitdone(), as it may be NULL */ | 223 checkinitdone(), as it may be NULL */ |
214 } else { | |
215 writechannel(channel); | |
216 } | 224 } |
225 writechannel(channel, channel->infd, channel->writebuf); | |
226 } | |
227 | |
228 /* stderr for client mode */ | |
229 if (channel->extrabuf != NULL | |
230 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) { | |
231 writechannel(channel, channel->errfd, channel->extrabuf); | |
217 } | 232 } |
218 | 233 |
219 /* now handle any of the channel-closing type stuff */ | 234 /* now handle any of the channel-closing type stuff */ |
220 checkclose(channel); | 235 checkclose(channel); |
221 | 236 |
228 } | 243 } |
229 | 244 |
230 | 245 |
231 /* do all the EOF/close type stuff checking for a channel */ | 246 /* do all the EOF/close type stuff checking for a channel */ |
232 static void checkclose(struct Channel *channel) { | 247 static void checkclose(struct Channel *channel) { |
248 | |
249 TRACE(("checkclose: infd %d, outfd %d, errfd %d, sentclosed %d, recvclosed %d", | |
250 channel->infd, channel->outfd, | |
251 channel->errfd, channel->sentclosed, channel->recvclosed)); | |
252 TRACE(("writebuf %d extrabuf %s extrabuf %d", | |
253 cbuf_getused(channel->writebuf), | |
254 channel->writebuf, | |
255 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))); | |
233 | 256 |
234 if (!channel->sentclosed) { | 257 if (!channel->sentclosed) { |
235 | 258 |
236 /* check for exited - currently only used for server sessions, | 259 /* check for exited - currently only used for server sessions, |
237 * if the shell has exited etc */ | 260 * if the shell has exited etc */ |
241 } | 264 } |
242 } | 265 } |
243 | 266 |
244 if (!channel->senteof | 267 if (!channel->senteof |
245 && channel->outfd == FD_CLOSED | 268 && channel->outfd == FD_CLOSED |
246 && channel->errfd == FD_CLOSED) { | 269 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { |
247 send_msg_channel_eof(channel); | 270 send_msg_channel_eof(channel); |
248 } | 271 } |
249 | 272 |
250 if (channel->infd == FD_CLOSED | 273 if (channel->infd == FD_CLOSED |
251 && channel->outfd == FD_CLOSED | 274 && channel->outfd == FD_CLOSED |
252 && channel->errfd == FD_CLOSED) { | 275 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { |
253 send_msg_channel_close(channel); | 276 send_msg_channel_close(channel); |
254 } | 277 } |
255 } | 278 } |
256 | 279 |
257 /* When either party wishes to terminate the channel, it sends | 280 /* When either party wishes to terminate the channel, it sends |
285 | 308 |
286 TRACE(("enter checkinitdone")); | 309 TRACE(("enter checkinitdone")); |
287 | 310 |
288 if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen) | 311 if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen) |
289 || val != 0) { | 312 || val != 0) { |
313 send_msg_channel_open_failure(channel->remotechan, | |
314 SSH_OPEN_CONNECT_FAILED, "", ""); | |
290 close(channel->infd); | 315 close(channel->infd); |
291 deletechannel(channel); | 316 deletechannel(channel); |
292 TRACE(("leave checkinitdone: fail")); | 317 TRACE(("leave checkinitdone: fail")); |
293 } else { | 318 } else { |
319 send_msg_channel_open_confirmation(channel, channel->recvwindow, | |
320 channel->recvmaxpacket); | |
294 channel->outfd = channel->infd; | 321 channel->outfd = channel->infd; |
295 channel->initconn = 0; | 322 channel->initconn = 0; |
296 TRACE(("leave checkinitdone: success")); | 323 TRACE(("leave checkinitdone: success")); |
297 } | 324 } |
298 } | 325 } |
305 TRACE(("enter send_msg_channel_close")); | 332 TRACE(("enter send_msg_channel_close")); |
306 /* XXX server */ | 333 /* XXX server */ |
307 if (channel->type->closehandler) { | 334 if (channel->type->closehandler) { |
308 channel->type->closehandler(channel); | 335 channel->type->closehandler(channel); |
309 } | 336 } |
310 #if 0 | |
311 if (channel->type == CHANNEL_ID_SESSION) { | |
312 send_exitsignalstatus(channel); | |
313 | |
314 closechansess(channel); | |
315 } | |
316 #endif | |
317 | 337 |
318 CHECKCLEARTOWRITE(); | 338 CHECKCLEARTOWRITE(); |
319 | 339 |
320 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); | 340 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); |
321 buf_putint(ses.writepayload, channel->remotechan); | 341 buf_putint(ses.writepayload, channel->remotechan); |
341 channel->senteof = 1; | 361 channel->senteof = 1; |
342 | 362 |
343 TRACE(("leave send_msg_channel_eof")); | 363 TRACE(("leave send_msg_channel_eof")); |
344 } | 364 } |
345 | 365 |
346 /* Called to write data out to the server side of a channel (eg a shell or a | 366 /* Called to write data out to the local side of the channel. |
347 * program. | |
348 * Only called when we know we can write to a channel, writes as much as | 367 * Only called when we know we can write to a channel, writes as much as |
349 * possible */ | 368 * possible */ |
350 static void writechannel(struct Channel* channel) { | 369 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { |
351 | 370 |
352 int len, maxlen; | 371 int len, maxlen; |
353 buffer *buf; | |
354 | 372 |
355 TRACE(("enter writechannel")); | 373 TRACE(("enter writechannel")); |
356 | 374 |
357 buf = channel->writebuf; | 375 maxlen = cbuf_readlen(cbuf); |
358 maxlen = buf->len - buf->pos; | 376 |
359 | 377 /* Write the data out */ |
360 len = write(channel->infd, buf_getptr(buf, maxlen), maxlen); | 378 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); |
361 if (len <= 0) { | 379 if (len <= 0) { |
362 if (len < 0 && errno != EINTR) { | 380 if (len < 0 && errno != EINTR) { |
363 /* no more to write */ | 381 /* no more to write - we close it even if the fd was stderr, since |
382 * that's a nasty failure too */ | |
364 closeinfd(channel); | 383 closeinfd(channel); |
365 } | 384 } |
366 TRACE(("leave writechannel: len <= 0")); | 385 TRACE(("leave writechannel: len <= 0")); |
367 return; | 386 return; |
368 } | 387 } |
369 | 388 |
370 if (len == maxlen) { | 389 cbuf_incrread(cbuf, len); |
371 buf_setpos(buf, 0); | 390 channel->recvdonelen += len; |
372 buf_setlen(buf, 0); | 391 |
373 | 392 if (fd == channel->infd && len == maxlen && channel->recveof) { |
374 if (channel->recveof) { | 393 /* Check if we're closing up */ |
375 /* we're closing up */ | 394 closeinfd(channel); |
376 closeinfd(channel); | 395 TRACE(("leave writechannel: recveof set")); |
377 return; | 396 return; |
378 TRACE(("leave writechannel: recveof set")); | 397 } |
379 } | 398 |
380 | 399 /* Window adjust handling */ |
381 /* extend the window if we're at the end*/ | 400 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { |
382 /* TODO - this is inefficient */ | 401 /* Set it back to max window */ |
383 send_msg_channel_window_adjust(channel, buf->size | 402 send_msg_channel_window_adjust(channel, channel->recvdonelen); |
384 - channel->recvwindow); | 403 channel->recvwindow += channel->recvdonelen; |
385 channel->recvwindow = buf->size; | 404 channel->recvdonelen = 0; |
386 } else { | 405 } |
387 buf_incrpos(buf, len); | 406 |
388 } | 407 assert(channel->recvwindow <= RECV_MAXWINDOW); |
408 assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); | |
409 assert(channel->extrabuf == NULL || | |
410 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); | |
411 | |
412 | |
389 TRACE(("leave writechannel")); | 413 TRACE(("leave writechannel")); |
390 } | 414 } |
391 | 415 |
392 /* Set the file descriptors for the main select in session.c | 416 /* Set the file descriptors for the main select in session.c |
393 * This avoid channels which don't have any window available, are closed, etc*/ | 417 * This avoid channels which don't have any window available, are closed, etc*/ |
401 channel = ses.channels[i]; | 425 channel = ses.channels[i]; |
402 if (channel == NULL) { | 426 if (channel == NULL) { |
403 continue; | 427 continue; |
404 } | 428 } |
405 | 429 |
406 /* stdout and stderr */ | 430 /* Stuff to put over the wire */ |
407 if (channel->transwindow > 0) { | 431 if (channel->transwindow > 0) { |
408 | 432 |
409 /* stdout */ | |
410 if (channel->outfd >= 0) { | 433 if (channel->outfd >= 0) { |
411 /* there's space to read more from the program */ | |
412 FD_SET(channel->outfd, readfd); | 434 FD_SET(channel->outfd, readfd); |
413 } | 435 } |
414 /* stderr */ | 436 |
415 if (channel->errfd >= 0) { | 437 if (channel->extrabuf == NULL && channel->errfd >= 0) { |
416 FD_SET(channel->errfd, readfd); | 438 FD_SET(channel->errfd, readfd); |
417 } | 439 } |
418 } | 440 } |
419 | 441 |
442 /* For checking FD status (ie closure etc) - we don't actually | |
443 * read data from infd */ | |
444 TRACE(("infd = %d, outfd %d, errfd %d, bufused %d", | |
445 channel->infd, channel->outfd, | |
446 channel->errfd, | |
447 cbuf_getused(channel->writebuf) )); | |
420 if (channel->infd >= 0 && channel->infd != channel->outfd) { | 448 if (channel->infd >= 0 && channel->infd != channel->outfd) { |
421 FD_SET(channel->infd, readfd); | 449 FD_SET(channel->infd, readfd); |
422 } | 450 } |
423 | 451 |
424 /* stdin */ | 452 /* Stuff from the wire, to local program/shell/user etc */ |
425 if (channel->infd >= 0 && | 453 if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 ) |
426 (channel->writebuf->pos < channel->writebuf->len || | 454 || channel->initconn) { |
427 channel->initconn)) { | 455 |
428 /* there's space to write more to the program */ | 456 FD_SET(channel->infd, writefd); |
429 FD_SET(channel->infd, writefd); | 457 } |
458 | |
459 if (channel->extrabuf != NULL && channel->errfd >= 0 | |
460 && cbuf_getused(channel->extrabuf) > 0 ) { | |
461 FD_SET(channel->errfd, writefd); | |
430 } | 462 } |
431 | 463 |
432 } /* foreach channel */ | 464 } /* foreach channel */ |
433 | 465 |
434 #ifdef USING_LISTENERS | 466 #ifdef USING_LISTENERS |
453 if (channel == NULL) { | 485 if (channel == NULL) { |
454 dropbear_exit("EOF for unknown channel"); | 486 dropbear_exit("EOF for unknown channel"); |
455 } | 487 } |
456 | 488 |
457 channel->recveof = 1; | 489 channel->recveof = 1; |
458 if (channel->writebuf->len == 0) { | 490 if (cbuf_getused(channel->writebuf) == 0 |
491 && (channel->extrabuf == NULL | |
492 || cbuf_getused(channel->extrabuf) == 0)) { | |
459 closeinfd(channel); | 493 closeinfd(channel); |
460 } | 494 } |
461 | 495 |
462 TRACE(("leave recv_msg_channel_eof")); | 496 TRACE(("leave recv_msg_channel_eof")); |
463 } | 497 } |
495 static void removechannel(struct Channel * channel) { | 529 static void removechannel(struct Channel * channel) { |
496 | 530 |
497 TRACE(("enter removechannel")); | 531 TRACE(("enter removechannel")); |
498 TRACE(("channel index is %d", channel->index)); | 532 TRACE(("channel index is %d", channel->index)); |
499 | 533 |
500 buf_free(channel->writebuf); | 534 cbuf_free(channel->writebuf); |
535 channel->writebuf = NULL; | |
536 | |
537 if (channel->extrabuf) { | |
538 cbuf_free(channel->extrabuf); | |
539 channel->extrabuf = NULL; | |
540 } | |
541 | |
501 | 542 |
502 /* close the FDs in case they haven't been done | 543 /* close the FDs in case they haven't been done |
503 * yet (ie they were shutdown etc */ | 544 * yet (ie they were shutdown etc */ |
504 close(channel->infd); | 545 close(channel->infd); |
505 close(channel->outfd); | 546 close(channel->outfd); |
506 if (channel->errfd >= 0) { | 547 close(channel->errfd); |
507 close(channel->errfd); | 548 |
508 } | 549 channel->typedata = NULL; |
509 | 550 |
510 deletechannel(channel); | 551 deletechannel(channel); |
511 | 552 |
512 TRACE(("leave removechannel")); | 553 TRACE(("leave removechannel")); |
513 } | 554 } |
542 if (channel->type->reqhandler) { | 583 if (channel->type->reqhandler) { |
543 channel->type->reqhandler(channel); | 584 channel->type->reqhandler(channel); |
544 } else { | 585 } else { |
545 send_msg_channel_failure(channel); | 586 send_msg_channel_failure(channel); |
546 } | 587 } |
547 | |
548 #if 0 | |
549 /* handle according to channel type */ | |
550 switch (channel->type) { | |
551 | |
552 case CHANNEL_ID_SESSION: | |
553 TRACE(("continue recv_msg_channel_request: session request")); | |
554 /* XXX server */ | |
555 /* Here we need to do things channel-specific style. Function | |
556 * pointer callbacks perhaps */ | |
557 chansessionrequest(channel); | |
558 break; | |
559 | |
560 default: | |
561 send_msg_channel_failure(channel); | |
562 } | |
563 #endif | |
564 | 588 |
565 TRACE(("leave recv_msg_channel_request")); | 589 TRACE(("leave recv_msg_channel_request")); |
566 | 590 |
567 } | 591 } |
568 | 592 |
602 TRACE(("leave send_msg_channel_data: no window")); | 626 TRACE(("leave send_msg_channel_data: no window")); |
603 return; /* the data will get written later */ | 627 return; /* the data will get written later */ |
604 } | 628 } |
605 | 629 |
606 /* read the data */ | 630 /* read the data */ |
631 TRACE(("maxlen %d", maxlen)); | |
607 buf = buf_new(maxlen); | 632 buf = buf_new(maxlen); |
633 TRACE(("buf pos %d data %x", buf->pos, buf->data)); | |
608 len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); | 634 len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); |
609 if (len <= 0) { | 635 if (len <= 0) { |
610 /* on error/eof, send eof */ | 636 /* on error/eof, send eof */ |
611 if (len == 0 || errno != EINTR) { | 637 if (len == 0 || errno != EINTR) { |
612 closeoutfd(channel, fd); | 638 closeoutfd(channel, fd); |
613 TRACE(("leave send_msg_channel_data: read err %d", channel->index)); | |
614 } | 639 } |
615 buf_free(buf); | 640 buf_free(buf); |
641 buf = NULL; | |
642 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", | |
643 channel->index)); | |
616 return; | 644 return; |
617 } | 645 } |
618 buf_incrlen(buf, len); | 646 buf_incrlen(buf, len); |
619 | 647 |
620 buf_putbyte(ses.writepayload, | 648 buf_putbyte(ses.writepayload, |
625 buf_putint(ses.writepayload, exttype); | 653 buf_putint(ses.writepayload, exttype); |
626 } | 654 } |
627 | 655 |
628 buf_putstring(ses.writepayload, buf_getptr(buf, len), len); | 656 buf_putstring(ses.writepayload, buf_getptr(buf, len), len); |
629 buf_free(buf); | 657 buf_free(buf); |
658 buf = NULL; | |
630 | 659 |
631 channel->transwindow -= len; | 660 channel->transwindow -= len; |
632 | 661 |
633 encrypt_packet(); | 662 encrypt_packet(); |
634 TRACE(("leave send_msg_channel_data")); | 663 TRACE(("leave send_msg_channel_data")); |
635 } | 664 } |
636 | 665 |
637 | 666 /* We receive channel data */ |
638 /* when we receive channel data, put it in a buffer for writing to the program/ | |
639 * shell etc */ | |
640 void recv_msg_channel_data() { | 667 void recv_msg_channel_data() { |
641 | 668 |
642 unsigned int chan; | 669 unsigned int chan; |
643 struct Channel * channel; | 670 struct Channel *channel; |
644 unsigned int datalen; | 671 |
645 unsigned int pos; | |
646 unsigned int maxdata; | |
647 | |
648 TRACE(("enter recv_msg_channel_data")); | |
649 | |
650 chan = buf_getint(ses.payload); | 672 chan = buf_getint(ses.payload); |
651 channel = getchannel(chan); | 673 channel = getchannel(chan); |
674 | |
652 if (channel == NULL) { | 675 if (channel == NULL) { |
653 dropbear_exit("Unknown channel"); | 676 dropbear_exit("Unknown channel"); |
654 } | 677 } |
655 | 678 |
679 common_recv_msg_channel_data(channel, channel->infd, channel->writebuf); | |
680 } | |
681 | |
682 /* Shared for data and stderr data - when we receive data, put it in a buffer | |
683 * for writing to the local file descriptor */ | |
684 void common_recv_msg_channel_data(struct Channel *channel, int fd, | |
685 circbuffer * cbuf) { | |
686 | |
687 unsigned int datalen; | |
688 unsigned int maxdata; | |
689 unsigned int buflen; | |
690 unsigned int len; | |
691 | |
692 TRACE(("enter recv_msg_channel_data")); | |
693 | |
656 if (channel->recveof) { | 694 if (channel->recveof) { |
657 dropbear_exit("received data after eof"); | 695 dropbear_exit("received data after eof"); |
658 } | 696 } |
659 | 697 |
660 if (channel->infd < 0) { | 698 if (fd < 0) { |
661 dropbear_exit("received data with bad infd"); | 699 dropbear_exit("received data with bad infd"); |
662 } | 700 } |
663 | 701 |
664 datalen = buf_getint(ses.payload); | 702 datalen = buf_getint(ses.payload); |
665 | 703 |
666 /* if the client is going to send us more data than we've allocated, then | 704 |
667 * it has ignored the windowsize, so we "MAY ignore all extra data" */ | 705 maxdata = cbuf_getavail(cbuf); |
668 maxdata = channel->writebuf->size - channel->writebuf->pos; | 706 |
707 /* Whilst the spec says we "MAY ignore data past the end" this could | |
708 * lead to corrupted file transfers etc (chunks missed etc). It's better to | |
709 * just die horribly */ | |
669 if (datalen > maxdata) { | 710 if (datalen > maxdata) { |
670 TRACE(("Warning: recv_msg_channel_data: extra data past window")); | 711 dropbear_exit("Oversized packet"); |
671 datalen = maxdata; | 712 } |
672 } | 713 |
673 | 714 /* We may have to run throught twice, if the buffer wraps around. Can't |
674 /* write to the buffer - we always append to the end of the buffer */ | 715 * just "leave it for next time" like with writechannel, since this |
675 pos = channel->writebuf->pos; | 716 * is payload data */ |
676 buf_setpos(channel->writebuf, channel->writebuf->len); | 717 len = datalen; |
677 memcpy(buf_getwriteptr(channel->writebuf, datalen), | 718 while (len > 0) { |
678 buf_getptr(ses.payload, datalen), datalen); | 719 buflen = cbuf_writelen(cbuf); |
679 buf_incrwritepos(channel->writebuf, datalen); | 720 buflen = MIN(buflen, len); |
680 buf_setpos(channel->writebuf, pos); /* revert pos */ | 721 |
681 | 722 memcpy(cbuf_writeptr(cbuf, buflen), |
723 buf_getptr(ses.payload, buflen), buflen); | |
724 cbuf_incrwrite(cbuf, buflen); | |
725 buf_incrpos(ses.payload, buflen); | |
726 len -= buflen; | |
727 } | |
728 | |
729 assert(channel->recvwindow >= datalen); | |
682 channel->recvwindow -= datalen; | 730 channel->recvwindow -= datalen; |
683 | 731 assert(channel->recvwindow <= RECV_MAXWINDOW); |
684 /* matt - this might be for later */ | |
685 /* if (channel->recvwindow < RECV_MINWINDOW) { | |
686 send_msg_channel_window_adjust(channel, | |
687 RECV_MAXWINDOW - channel->recvwindow); | |
688 channel->recvwindow = RECV_MAXWINDOW; | |
689 }*/ | |
690 | 732 |
691 TRACE(("leave recv_msg_channel_data")); | 733 TRACE(("leave recv_msg_channel_data")); |
692 } | 734 } |
693 | 735 |
694 /* Increment the outgoing data window for a channel - the remote end limits | 736 /* Increment the outgoing data window for a channel - the remote end limits |
788 } | 830 } |
789 | 831 |
790 if (channel->type->inithandler) { | 832 if (channel->type->inithandler) { |
791 ret = channel->type->inithandler(channel); | 833 ret = channel->type->inithandler(channel); |
792 if (ret > 0) { | 834 if (ret > 0) { |
835 if (ret == SSH_OPEN_IN_PROGRESS) { | |
836 /* We'll send the confirmation later */ | |
837 goto cleanup; | |
838 } | |
793 errtype = ret; | 839 errtype = ret; |
794 deletechannel(channel); | 840 deletechannel(channel); |
795 TRACE(("inithandler returned failure %d", ret)); | 841 TRACE(("inithandler returned failure %d", ret)); |
796 goto failure; | 842 goto failure; |
797 } | 843 } |
798 } | 844 } |
799 | |
800 #if 0 | |
801 /* type specific initialisation */ | |
802 if (typeval == CHANNEL_ID_SESSION) { | |
803 newchansess(channel); | |
804 #ifndef DISABLE_LOCALTCPFWD | |
805 } else if (typeval == CHANNEL_ID_TCPDIRECT) { | |
806 if (ses.opts->nolocaltcp) { | |
807 errtype = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; | |
808 } else if (newtcpdirect(channel) == DROPBEAR_FAILURE) { | |
809 errtype = SSH_OPEN_CONNECT_FAILED; | |
810 deletechannel(channel); | |
811 goto failure; | |
812 } | |
813 #endif | |
814 } | |
815 #endif | |
816 | 845 |
817 /* success */ | 846 /* success */ |
818 send_msg_channel_open_confirmation(channel, channel->recvwindow, | 847 send_msg_channel_open_confirmation(channel, channel->recvwindow, |
819 channel->recvmaxpacket); | 848 channel->recvmaxpacket); |
820 goto cleanup; | 849 goto cleanup; |
908 TRACE(("leave send_msg_channel_open_init() - FAILED in newchannel()")); | 937 TRACE(("leave send_msg_channel_open_init() - FAILED in newchannel()")); |
909 return DROPBEAR_FAILURE; | 938 return DROPBEAR_FAILURE; |
910 } | 939 } |
911 | 940 |
912 /* set fd non-blocking */ | 941 /* set fd non-blocking */ |
913 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { | 942 setnonblocking(fd); |
914 TRACE(("leave send_msg_channel_open_init() - FAILED in fcntl()")); | |
915 return DROPBEAR_FAILURE; | |
916 } | |
917 | 943 |
918 chan->infd = chan->outfd = fd; | 944 chan->infd = chan->outfd = fd; |
919 ses.maxfd = MAX(ses.maxfd, fd); | 945 ses.maxfd = MAX(ses.maxfd, fd); |
920 | 946 |
921 /* now open the channel connection */ | 947 /* now open the channel connection */ |
1015 } else { | 1041 } else { |
1016 close(fd); | 1042 close(fd); |
1017 closein = closeout = 1; | 1043 closein = closeout = 1; |
1018 } | 1044 } |
1019 | 1045 |
1020 if (closeout && fd == channel->errfd) { | |
1021 channel->errfd = FD_CLOSED; | |
1022 } | |
1023 if (closeout && fd == channel->outfd) { | 1046 if (closeout && fd == channel->outfd) { |
1024 channel->outfd = FD_CLOSED; | 1047 channel->outfd = FD_CLOSED; |
1025 } | 1048 } |
1049 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { | |
1050 channel->errfd = FD_CLOSED; | |
1051 } | |
1052 | |
1026 if (closein && fd == channel->infd) { | 1053 if (closein && fd == channel->infd) { |
1027 channel->infd = FD_CLOSED; | 1054 channel->infd = FD_CLOSED; |
1028 } | 1055 } |
1056 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { | |
1057 channel->errfd = FD_CLOSED; | |
1058 } | |
1029 } | 1059 } |
1030 | 1060 |
1031 #endif /* USING_LISTENERS */ | 1061 #endif /* USING_LISTENERS */ |