openssh/nchan.c

523 lines
13 KiB
C
Raw Normal View History

/* $OpenBSD: nchan.c,v 1.62 2008/11/07 18:50:18 stevesk Exp $ */
/*
* Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
1999-10-27 03:42:43 +00:00
#include "includes.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
- deraadt@cvs.openbsd.org 2006/08/03 03:34:42 [OVERVIEW atomicio.c atomicio.h auth-bsdauth.c auth-chall.c auth-krb5.c] [auth-options.c auth-options.h auth-passwd.c auth-rh-rsa.c auth-rhosts.c] [auth-rsa.c auth-skey.c auth.c auth.h auth1.c auth2-chall.c auth2-gss.c] [auth2-hostbased.c auth2-kbdint.c auth2-none.c auth2-passwd.c ] [auth2-pubkey.c auth2.c authfd.c authfd.h authfile.c bufaux.c bufbn.c] [buffer.c buffer.h canohost.c channels.c channels.h cipher-3des1.c] [cipher-bf1.c cipher-ctr.c cipher.c cleanup.c clientloop.c compat.c] [compress.c deattack.c dh.c dispatch.c dns.c dns.h fatal.c groupaccess.c] [groupaccess.h gss-genr.c gss-serv-krb5.c gss-serv.c hostfile.c kex.c] [kex.h kexdh.c kexdhc.c kexdhs.c kexgex.c kexgexc.c kexgexs.c key.c] [key.h log.c log.h mac.c match.c md-sha256.c misc.c misc.h moduli.c] [monitor.c monitor_fdpass.c monitor_mm.c monitor_mm.h monitor_wrap.c] [monitor_wrap.h msg.c nchan.c packet.c progressmeter.c readconf.c] [readconf.h readpass.c rsa.c scard.c scard.h scp.c servconf.c servconf.h] [serverloop.c session.c session.h sftp-client.c sftp-common.c] [sftp-common.h sftp-glob.c sftp-server.c sftp.c ssh-add.c ssh-agent.c] [ssh-dss.c ssh-gss.h ssh-keygen.c ssh-keyscan.c ssh-keysign.c ssh-rsa.c] [ssh.c ssh.h sshconnect.c sshconnect.h sshconnect1.c sshconnect2.c] [sshd.c sshlogin.c sshlogin.h sshpty.c sshpty.h sshtty.c ttymodes.c] [uidswap.c uidswap.h uuencode.c uuencode.h xmalloc.c xmalloc.h] [loginrec.c loginrec.h openbsd-compat/port-aix.c openbsd-compat/port-tun.h] almost entirely get rid of the culture of ".h files that include .h files" ok djm, sort of ok stevesk makes the pain stop in one easy step NB. portable commit contains everything *except* removing includes.h, as that will take a fair bit more work as we move headers that are required for portability workarounds to defines.h. (also, this step wasn't "easy")
2006-08-05 02:39:39 +00:00
#include <stdarg.h>
#include "openbsd-compat/sys-queue.h"
Hopefully things did not get mixed around too much. It compiles under Linux and works. So that is at least a good sign. =) 20010122 - (bal) OpenBSD Resync - markus@cvs.openbsd.org 2001/01/19 12:45:26 GMT 2001 by markus [servconf.c ssh.h sshd.c] only auth-chall.c needs #ifdef SKEY - markus@cvs.openbsd.org 2001/01/19 15:55:10 GMT 2001 by markus [auth-krb4.c auth-options.c auth-rh-rsa.c auth-rhosts.c auth-rsa.c auth1.c auth2.c channels.c clientloop.c dh.c dispatch.c nchan.c packet.c pathname.h readconf.c scp.c servconf.c serverloop.c session.c ssh-add.c ssh-keygen.c ssh-keyscan.c ssh.c ssh.h ssh1.h sshconnect1.c sshd.c ttymodes.c] move ssh1 definitions to ssh1.h, pathnames to pathnames.h - markus@cvs.openbsd.org 2001/01/19 16:48:14 [sshd.8] fix typo; from stevesk@ - markus@cvs.openbsd.org 2001/01/19 16:50:58 [ssh-dss.c] clear and free digest, make consistent with other code (use dlen); from stevesk@ - markus@cvs.openbsd.org 2001/01/20 15:55:20 GMT 2001 by markus [auth-options.c auth-options.h auth-rsa.c auth2.c] pass the filename to auth_parse_options() - markus@cvs.openbsd.org 2001/01/20 17:59:40 GMT 2001 [readconf.c] fix SIGSEGV from -o ""; problem noted by jehsom@togetherweb.com - stevesk@cvs.openbsd.org 2001/01/20 18:20:29 [sshconnect2.c] dh_new_group() does not return NULL. ok markus@ - markus@cvs.openbsd.org 2001/01/20 21:33:42 [ssh-add.c] do not loop forever if askpass does not exist; from andrew@pimlott.ne.mediaone.net - djm@cvs.openbsd.org 2001/01/20 23:00:56 [servconf.c] Check for NULL return from strdelim; ok markus - djm@cvs.openbsd.org 2001/01/20 23:02:07 [readconf.c] KNF; ok markus - jakob@cvs.openbsd.org 2001/01/21 9:00:33 [ssh-keygen.1] remove -R flag; ok markus@ - markus@cvs.openbsd.org 2001/01/21 19:05:40 [atomicio.c automicio.h auth-chall.c auth-krb4.c auth-options.c auth-options.h auth-passwd.c auth-rh-rsa.c auth-rhosts.c auth-rsa.c auth.c auth.h auth1.c auth2-chall.c auth2.c authfd.c authfile.c bufaux.c bufaux.h buffer.c canahost.c canahost.h channels.c cipher.c cli.c clientloop.c clientloop.h compat.c compress.c deattack.c dh.c dispatch.c groupaccess.c hmac.c hostfile.c kex.c key.c key.h log-client.c log-server.c log.c log.h login.c login.h match.c misc.c misc.h nchan.c packet.c pty.c radix.h readconf.c readpass.c readpass.h rsa.c scp.c servconf.c serverloop.c serverloop.h session.c sftp-server.c ssh-add.c ssh-agent.c ssh-dss.c ssh-keygen.c ssh-keyscan.c ssh-rsa.c ssh.c ssh.h sshconnect.c sshconnect.h sshconnect1.c sshconnect2.c sshd.c tildexpand.c tildexpand.h ttysmodes.c uidswap.c xmalloc.c] split ssh.h and try to cleanup the #include mess. remove unnecessary #includes. rename util.[ch] -> misc.[ch] - (bal) renamed 'PIDDIR' to '_PATH_SSH_PIDDIR' to match OpenBSD tree - (bal) Moved #ifdef KRB4 in auth-krb4.c above the #include to resolve conflict when compiling for non-kerb install - (bal) removed the #ifdef SKEY in auth1.c to match Markus' changes on 1/19.
2001-01-22 05:34:40 +00:00
#include "ssh1.h"
#include "ssh2.h"
1999-10-27 03:42:43 +00:00
#include "buffer.h"
#include "packet.h"
#include "channels.h"
#include "compat.h"
Hopefully things did not get mixed around too much. It compiles under Linux and works. So that is at least a good sign. =) 20010122 - (bal) OpenBSD Resync - markus@cvs.openbsd.org 2001/01/19 12:45:26 GMT 2001 by markus [servconf.c ssh.h sshd.c] only auth-chall.c needs #ifdef SKEY - markus@cvs.openbsd.org 2001/01/19 15:55:10 GMT 2001 by markus [auth-krb4.c auth-options.c auth-rh-rsa.c auth-rhosts.c auth-rsa.c auth1.c auth2.c channels.c clientloop.c dh.c dispatch.c nchan.c packet.c pathname.h readconf.c scp.c servconf.c serverloop.c session.c ssh-add.c ssh-keygen.c ssh-keyscan.c ssh.c ssh.h ssh1.h sshconnect1.c sshd.c ttymodes.c] move ssh1 definitions to ssh1.h, pathnames to pathnames.h - markus@cvs.openbsd.org 2001/01/19 16:48:14 [sshd.8] fix typo; from stevesk@ - markus@cvs.openbsd.org 2001/01/19 16:50:58 [ssh-dss.c] clear and free digest, make consistent with other code (use dlen); from stevesk@ - markus@cvs.openbsd.org 2001/01/20 15:55:20 GMT 2001 by markus [auth-options.c auth-options.h auth-rsa.c auth2.c] pass the filename to auth_parse_options() - markus@cvs.openbsd.org 2001/01/20 17:59:40 GMT 2001 [readconf.c] fix SIGSEGV from -o ""; problem noted by jehsom@togetherweb.com - stevesk@cvs.openbsd.org 2001/01/20 18:20:29 [sshconnect2.c] dh_new_group() does not return NULL. ok markus@ - markus@cvs.openbsd.org 2001/01/20 21:33:42 [ssh-add.c] do not loop forever if askpass does not exist; from andrew@pimlott.ne.mediaone.net - djm@cvs.openbsd.org 2001/01/20 23:00:56 [servconf.c] Check for NULL return from strdelim; ok markus - djm@cvs.openbsd.org 2001/01/20 23:02:07 [readconf.c] KNF; ok markus - jakob@cvs.openbsd.org 2001/01/21 9:00:33 [ssh-keygen.1] remove -R flag; ok markus@ - markus@cvs.openbsd.org 2001/01/21 19:05:40 [atomicio.c automicio.h auth-chall.c auth-krb4.c auth-options.c auth-options.h auth-passwd.c auth-rh-rsa.c auth-rhosts.c auth-rsa.c auth.c auth.h auth1.c auth2-chall.c auth2.c authfd.c authfile.c bufaux.c bufaux.h buffer.c canahost.c canahost.h channels.c cipher.c cli.c clientloop.c clientloop.h compat.c compress.c deattack.c dh.c dispatch.c groupaccess.c hmac.c hostfile.c kex.c key.c key.h log-client.c log-server.c log.c log.h login.c login.h match.c misc.c misc.h nchan.c packet.c pty.c radix.h readconf.c readpass.c readpass.h rsa.c scp.c servconf.c serverloop.c serverloop.h session.c sftp-server.c ssh-add.c ssh-agent.c ssh-dss.c ssh-keygen.c ssh-keyscan.c ssh-rsa.c ssh.c ssh.h sshconnect.c sshconnect.h sshconnect1.c sshconnect2.c sshd.c tildexpand.c tildexpand.h ttysmodes.c uidswap.c xmalloc.c] split ssh.h and try to cleanup the #include mess. remove unnecessary #includes. rename util.[ch] -> misc.[ch] - (bal) renamed 'PIDDIR' to '_PATH_SSH_PIDDIR' to match OpenBSD tree - (bal) Moved #ifdef KRB4 in auth-krb4.c above the #include to resolve conflict when compiling for non-kerb install - (bal) removed the #ifdef SKEY in auth1.c to match Markus' changes on 1/19.
2001-01-22 05:34:40 +00:00
#include "log.h"
1999-10-27 03:42:43 +00:00
/*
* SSH Protocol 1.5 aka New Channel Protocol
* Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored.
* Written by Markus Friedl in October 1999
*
* Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the
* tear down of channels:
*
* 1.3: strict request-ack-protocol:
* CLOSE ->
* <- CLOSE_CONFIRM
*
* 1.5: uses variations of:
* IEOF ->
* <- OCLOSE
* <- IEOF
* OCLOSE ->
* i.e. both sides have to close the channel
*
* 2.0: the EOF messages are optional
*
* See the debugging output from 'ssh -v' and 'sshd -d' of
* ssh-1.2.27 as an example.
*
*/
2001-09-20 19:33:33 +00:00
/* functions manipulating channel states */
1999-10-27 03:42:43 +00:00
/*
* EVENTS update channel input/output states execute ACTIONS
1999-10-27 03:42:43 +00:00
*/
/*
* ACTIONS: should never update the channel states
*/
static void chan_send_ieof1(Channel *);
static void chan_send_oclose1(Channel *);
static void chan_send_close2(Channel *);
static void chan_send_eof2(Channel *);
static void chan_send_eow2(Channel *);
/* helper */
static void chan_shutdown_write(Channel *);
static void chan_shutdown_read(Channel *);
static char *ostates[] = { "open", "drain", "wait_ieof", "closed" };
static char *istates[] = { "open", "drain", "wait_oclose", "closed" };
static void
chan_set_istate(Channel *c, u_int next)
{
if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED)
fatal("chan_set_istate: bad state %d -> %d", c->istate, next);
debug2("channel %d: input %s -> %s", c->self, istates[c->istate],
istates[next]);
c->istate = next;
}
static void
chan_set_ostate(Channel *c, u_int next)
{
if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED)
fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next);
debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate],
ostates[next]);
c->ostate = next;
}
/*
* SSH1 specific implementation of event functions
*/
static void
chan_rcvd_oclose1(Channel *c)
{
debug2("channel %d: rcvd oclose", c->self);
switch (c->istate) {
1999-10-27 03:42:43 +00:00
case CHAN_INPUT_WAIT_OCLOSE:
chan_set_istate(c, CHAN_INPUT_CLOSED);
1999-10-27 03:42:43 +00:00
break;
case CHAN_INPUT_OPEN:
chan_shutdown_read(c);
chan_send_ieof1(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
break;
case CHAN_INPUT_WAIT_DRAIN:
/* both local read_failed and remote write_failed */
chan_send_ieof1(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
1999-10-27 03:42:43 +00:00
break;
default:
error("channel %d: protocol error: rcvd_oclose for istate %d",
c->self, c->istate);
return;
1999-10-27 03:42:43 +00:00
}
}
void
chan_read_failed(Channel *c)
{
debug2("channel %d: read failed", c->self);
switch (c->istate) {
1999-10-27 03:42:43 +00:00
case CHAN_INPUT_OPEN:
chan_shutdown_read(c);
chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN);
1999-10-27 03:42:43 +00:00
break;
default:
error("channel %d: chan_read_failed for istate %d",
c->self, c->istate);
1999-10-27 03:42:43 +00:00
break;
}
}
void
chan_ibuf_empty(Channel *c)
{
debug2("channel %d: ibuf empty", c->self);
if (buffer_len(&c->input)) {
error("channel %d: chan_ibuf_empty for non empty buffer",
c->self);
1999-10-27 03:42:43 +00:00
return;
}
switch (c->istate) {
1999-10-27 03:42:43 +00:00
case CHAN_INPUT_WAIT_DRAIN:
if (compat20) {
if (!(c->flags & CHAN_CLOSE_SENT))
chan_send_eof2(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
} else {
chan_send_ieof1(c);
chan_set_istate(c, CHAN_INPUT_WAIT_OCLOSE);
}
1999-10-27 03:42:43 +00:00
break;
default:
error("channel %d: chan_ibuf_empty for istate %d",
c->self, c->istate);
1999-10-27 03:42:43 +00:00
break;
}
}
static void
chan_rcvd_ieof1(Channel *c)
{
debug2("channel %d: rcvd ieof", c->self);
switch (c->ostate) {
1999-10-27 03:42:43 +00:00
case CHAN_OUTPUT_OPEN:
chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
1999-10-27 03:42:43 +00:00
break;
case CHAN_OUTPUT_WAIT_IEOF:
chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
1999-10-27 03:42:43 +00:00
break;
default:
error("channel %d: protocol error: rcvd_ieof for ostate %d",
c->self, c->ostate);
1999-10-27 03:42:43 +00:00
break;
}
}
static void
chan_write_failed1(Channel *c)
{
debug2("channel %d: write failed", c->self);
switch (c->ostate) {
1999-10-27 03:42:43 +00:00
case CHAN_OUTPUT_OPEN:
chan_shutdown_write(c);
chan_send_oclose1(c);
chan_set_ostate(c, CHAN_OUTPUT_WAIT_IEOF);
1999-10-27 03:42:43 +00:00
break;
case CHAN_OUTPUT_WAIT_DRAIN:
chan_shutdown_write(c);
chan_send_oclose1(c);
chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
1999-10-27 03:42:43 +00:00
break;
default:
error("channel %d: chan_write_failed for ostate %d",
c->self, c->ostate);
1999-10-27 03:42:43 +00:00
break;
}
}
void
chan_obuf_empty(Channel *c)
{
debug2("channel %d: obuf empty", c->self);
if (buffer_len(&c->output)) {
error("channel %d: chan_obuf_empty for non empty buffer",
c->self);
1999-10-27 03:42:43 +00:00
return;
}
switch (c->ostate) {
1999-10-27 03:42:43 +00:00
case CHAN_OUTPUT_WAIT_DRAIN:
chan_shutdown_write(c);
if (!compat20)
chan_send_oclose1(c);
chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
1999-10-27 03:42:43 +00:00
break;
default:
error("channel %d: internal error: obuf_empty for ostate %d",
c->self, c->ostate);
1999-10-27 03:42:43 +00:00
break;
}
}
static void
chan_send_ieof1(Channel *c)
{
debug2("channel %d: send ieof", c->self);
switch (c->istate) {
1999-10-27 03:42:43 +00:00
case CHAN_INPUT_OPEN:
case CHAN_INPUT_WAIT_DRAIN:
packet_start(SSH_MSG_CHANNEL_INPUT_EOF);
packet_put_int(c->remote_id);
packet_send();
break;
default:
error("channel %d: cannot send ieof for istate %d",
c->self, c->istate);
1999-10-27 03:42:43 +00:00
break;
}
}
static void
chan_send_oclose1(Channel *c)
{
debug2("channel %d: send oclose", c->self);
switch (c->ostate) {
1999-10-27 03:42:43 +00:00
case CHAN_OUTPUT_OPEN:
case CHAN_OUTPUT_WAIT_DRAIN:
buffer_clear(&c->output);
1999-10-27 03:42:43 +00:00
packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE);
packet_put_int(c->remote_id);
packet_send();
break;
default:
error("channel %d: cannot send oclose for ostate %d",
c->self, c->ostate);
1999-10-27 03:42:43 +00:00
break;
}
}
/*
* the same for SSH2
*/
1999-10-27 03:42:43 +00:00
static void
chan_rcvd_close2(Channel *c)
{
debug2("channel %d: rcvd close", c->self);
if (c->flags & CHAN_CLOSE_RCVD)
error("channel %d: protocol error: close rcvd twice", c->self);
c->flags |= CHAN_CLOSE_RCVD;
if (c->type == SSH_CHANNEL_LARVAL) {
/* tear down larval channels immediately */
chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
chan_set_istate(c, CHAN_INPUT_CLOSED);
return;
}
switch (c->ostate) {
case CHAN_OUTPUT_OPEN:
/*
* wait until a data from the channel is consumed if a CLOSE
* is received
*/
chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
break;
}
switch (c->istate) {
case CHAN_INPUT_OPEN:
chan_shutdown_read(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
break;
case CHAN_INPUT_WAIT_DRAIN:
chan_send_eof2(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
break;
}
1999-10-27 03:42:43 +00:00
}
void
chan_rcvd_eow(Channel *c)
{
debug2("channel %d: rcvd eow", c->self);
switch (c->istate) {
case CHAN_INPUT_OPEN:
chan_shutdown_read(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
break;
}
}
1999-10-27 03:42:43 +00:00
static void
chan_rcvd_eof2(Channel *c)
{
debug2("channel %d: rcvd eof", c->self);
c->flags |= CHAN_EOF_RCVD;
if (c->ostate == CHAN_OUTPUT_OPEN)
chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
1999-10-27 03:42:43 +00:00
}
static void
chan_write_failed2(Channel *c)
{
debug2("channel %d: write failed", c->self);
switch (c->ostate) {
case CHAN_OUTPUT_OPEN:
case CHAN_OUTPUT_WAIT_DRAIN:
chan_shutdown_write(c);
if (strcmp(c->ctype, "session") == 0)
chan_send_eow2(c);
chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
break;
default:
error("channel %d: chan_write_failed for ostate %d",
c->self, c->ostate);
break;
}
}
static void
chan_send_eof2(Channel *c)
{
debug2("channel %d: send eof", c->self);
switch (c->istate) {
case CHAN_INPUT_WAIT_DRAIN:
packet_start(SSH2_MSG_CHANNEL_EOF);
packet_put_int(c->remote_id);
packet_send();
c->flags |= CHAN_EOF_SENT;
break;
default:
error("channel %d: cannot send eof for istate %d",
c->self, c->istate);
break;
}
}
static void
chan_send_close2(Channel *c)
{
debug2("channel %d: send close", c->self);
if (c->ostate != CHAN_OUTPUT_CLOSED ||
c->istate != CHAN_INPUT_CLOSED) {
error("channel %d: cannot send close for istate/ostate %d/%d",
c->self, c->istate, c->ostate);
} else if (c->flags & CHAN_CLOSE_SENT) {
error("channel %d: already sent close", c->self);
} else {
packet_start(SSH2_MSG_CHANNEL_CLOSE);
packet_put_int(c->remote_id);
packet_send();
c->flags |= CHAN_CLOSE_SENT;
}
}
static void
chan_send_eow2(Channel *c)
{
debug2("channel %d: send eow", c->self);
if (c->ostate == CHAN_OUTPUT_CLOSED) {
error("channel %d: must not sent eow on closed output",
c->self);
return;
}
if (!(datafellows & SSH_NEW_OPENSSH))
return;
packet_start(SSH2_MSG_CHANNEL_REQUEST);
packet_put_int(c->remote_id);
packet_put_cstring("eow@openssh.com");
packet_put_char(0);
packet_send();
}
/* shared */
void
chan_rcvd_ieof(Channel *c)
{
if (compat20)
chan_rcvd_eof2(c);
else
chan_rcvd_ieof1(c);
if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN &&
buffer_len(&c->output) == 0 &&
!CHANNEL_EFD_OUTPUT_ACTIVE(c))
chan_obuf_empty(c);
}
void
chan_rcvd_oclose(Channel *c)
{
if (compat20)
chan_rcvd_close2(c);
else
chan_rcvd_oclose1(c);
}
void
chan_write_failed(Channel *c)
{
if (compat20)
chan_write_failed2(c);
else
chan_write_failed1(c);
}
void
chan_mark_dead(Channel *c)
{
c->type = SSH_CHANNEL_ZOMBIE;
}
int
chan_is_dead(Channel *c, int do_send)
{
if (c->type == SSH_CHANNEL_ZOMBIE) {
debug2("channel %d: zombie", c->self);
return 1;
}
if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED)
return 0;
if (!compat20) {
debug2("channel %d: is dead", c->self);
return 1;
}
if ((datafellows & SSH_BUG_EXTEOF) &&
c->extended_usage == CHAN_EXTENDED_WRITE &&
c->efd != -1 &&
buffer_len(&c->extended) > 0) {
2002-06-09 19:41:48 +00:00
debug2("channel %d: active efd: %d len %d",
c->self, c->efd, buffer_len(&c->extended));
return 0;
}
if (!(c->flags & CHAN_CLOSE_SENT)) {
if (do_send) {
chan_send_close2(c);
} else {
/* channel would be dead if we sent a close */
if (c->flags & CHAN_CLOSE_RCVD) {
debug2("channel %d: almost dead",
c->self);
return 1;
}
}
}
if ((c->flags & CHAN_CLOSE_SENT) &&
(c->flags & CHAN_CLOSE_RCVD)) {
debug2("channel %d: is dead", c->self);
return 1;
1999-10-27 03:42:43 +00:00
}
return 0;
1999-10-27 03:42:43 +00:00
}
/* helper */
static void
chan_shutdown_write(Channel *c)
{
buffer_clear(&c->output);
if (compat20 && c->type == SSH_CHANNEL_LARVAL)
return;
/* shutdown failure is allowed if write failed already */
debug2("channel %d: close_write", c->self);
if (c->sock != -1) {
if (shutdown(c->sock, SHUT_WR) < 0)
debug2("channel %d: chan_shutdown_write: "
"shutdown() failed for fd %d: %.100s",
c->self, c->sock, strerror(errno));
} else {
if (channel_close_fd(&c->wfd) < 0)
2003-04-09 10:59:48 +00:00
logit("channel %d: chan_shutdown_write: "
"close() failed for fd %d: %.100s",
c->self, c->wfd, strerror(errno));
}
}
static void
chan_shutdown_read(Channel *c)
{
if (compat20 && c->type == SSH_CHANNEL_LARVAL)
return;
debug2("channel %d: close_read", c->self);
if (c->sock != -1) {
/*
* shutdown(sock, SHUT_READ) may return ENOTCONN if the
* write side has been closed already. (bug on Linux)
* HP-UX may return ENOTCONN also.
*/
if (shutdown(c->sock, SHUT_RD) < 0
&& errno != ENOTCONN)
error("channel %d: chan_shutdown_read: "
"shutdown() failed for fd %d [i%d o%d]: %.100s",
c->self, c->sock, c->istate, c->ostate,
2001-09-20 19:33:33 +00:00
strerror(errno));
} else {
if (channel_close_fd(&c->rfd) < 0)
2003-04-09 10:59:48 +00:00
logit("channel %d: chan_shutdown_read: "
"close() failed for fd %d: %.100s",
c->self, c->rfd, strerror(errno));
}
1999-10-27 03:42:43 +00:00
}