xref: /dragonfly/crypto/openssh/nchan.c (revision ee116499)
1*ee116499SAntonio Huete Jimenez /* $OpenBSD: nchan.c,v 1.74 2022/02/01 23:32:51 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
418de8d7fSPeter Avalos  *
518de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
618de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
718de8d7fSPeter Avalos  * are met:
818de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
918de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1018de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1118de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1218de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
1318de8d7fSPeter Avalos  *
1418de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1518de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1618de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1718de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1818de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1918de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2018de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2118de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2218de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2318de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2418de8d7fSPeter Avalos  */
2518de8d7fSPeter Avalos 
2618de8d7fSPeter Avalos #include "includes.h"
2718de8d7fSPeter Avalos 
2818de8d7fSPeter Avalos #include <sys/types.h>
2918de8d7fSPeter Avalos #include <sys/socket.h>
3018de8d7fSPeter Avalos 
3118de8d7fSPeter Avalos #include <errno.h>
3218de8d7fSPeter Avalos #include <string.h>
3318de8d7fSPeter Avalos #include <stdarg.h>
3418de8d7fSPeter Avalos 
3518de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
3618de8d7fSPeter Avalos #include "ssh2.h"
37ce74bacaSMatthew Dillon #include "sshbuf.h"
38ce74bacaSMatthew Dillon #include "ssherr.h"
3918de8d7fSPeter Avalos #include "packet.h"
4018de8d7fSPeter Avalos #include "channels.h"
4118de8d7fSPeter Avalos #include "compat.h"
4218de8d7fSPeter Avalos #include "log.h"
4318de8d7fSPeter Avalos 
4418de8d7fSPeter Avalos /*
4518de8d7fSPeter Avalos  * SSH Protocol 1.5 aka New Channel Protocol
4618de8d7fSPeter Avalos  * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored.
4718de8d7fSPeter Avalos  * Written by Markus Friedl in October 1999
4818de8d7fSPeter Avalos  *
4918de8d7fSPeter Avalos  * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the
5018de8d7fSPeter Avalos  * tear down of channels:
5118de8d7fSPeter Avalos  *
5218de8d7fSPeter Avalos  * 1.3:	strict request-ack-protocol:
5318de8d7fSPeter Avalos  *	CLOSE	->
5418de8d7fSPeter Avalos  *		<-  CLOSE_CONFIRM
5518de8d7fSPeter Avalos  *
5618de8d7fSPeter Avalos  * 1.5:	uses variations of:
5718de8d7fSPeter Avalos  *	IEOF	->
5818de8d7fSPeter Avalos  *		<-  OCLOSE
5918de8d7fSPeter Avalos  *		<-  IEOF
6018de8d7fSPeter Avalos  *	OCLOSE	->
6118de8d7fSPeter Avalos  *	i.e. both sides have to close the channel
6218de8d7fSPeter Avalos  *
6318de8d7fSPeter Avalos  * 2.0: the EOF messages are optional
6418de8d7fSPeter Avalos  *
6518de8d7fSPeter Avalos  * See the debugging output from 'ssh -v' and 'sshd -d' of
6618de8d7fSPeter Avalos  * ssh-1.2.27 as an example.
6718de8d7fSPeter Avalos  *
6818de8d7fSPeter Avalos  */
6918de8d7fSPeter Avalos 
7018de8d7fSPeter Avalos /* functions manipulating channel states */
7118de8d7fSPeter Avalos /*
7218de8d7fSPeter Avalos  * EVENTS update channel input/output states execute ACTIONS
7318de8d7fSPeter Avalos  */
7418de8d7fSPeter Avalos /*
7518de8d7fSPeter Avalos  * ACTIONS: should never update the channel states
7618de8d7fSPeter Avalos  */
77ce74bacaSMatthew Dillon static void	chan_send_eof2(struct ssh *, Channel *);
78ce74bacaSMatthew Dillon static void	chan_send_eow2(struct ssh *, Channel *);
7918de8d7fSPeter Avalos 
8018de8d7fSPeter Avalos /* helper */
81ce74bacaSMatthew Dillon static void	chan_shutdown_write(struct ssh *, Channel *);
82ce74bacaSMatthew Dillon static void	chan_shutdown_read(struct ssh *, Channel *);
83664f4763Szrj static void	chan_shutdown_extended_read(struct ssh *, Channel *);
8418de8d7fSPeter Avalos 
85*ee116499SAntonio Huete Jimenez static const char * const ostates[] = {
86*ee116499SAntonio Huete Jimenez 	"open", "drain", "wait_ieof", "closed",
87*ee116499SAntonio Huete Jimenez };
88*ee116499SAntonio Huete Jimenez static const char * const istates[] = {
89*ee116499SAntonio Huete Jimenez 	"open", "drain", "wait_oclose", "closed",
90*ee116499SAntonio Huete Jimenez };
9118de8d7fSPeter Avalos 
9218de8d7fSPeter Avalos static void
chan_set_istate(Channel * c,u_int next)9318de8d7fSPeter Avalos chan_set_istate(Channel *c, u_int next)
9418de8d7fSPeter Avalos {
9518de8d7fSPeter Avalos 	if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED)
9618de8d7fSPeter Avalos 		fatal("chan_set_istate: bad state %d -> %d", c->istate, next);
9718de8d7fSPeter Avalos 	debug2("channel %d: input %s -> %s", c->self, istates[c->istate],
9818de8d7fSPeter Avalos 	    istates[next]);
9918de8d7fSPeter Avalos 	c->istate = next;
10018de8d7fSPeter Avalos }
101ce74bacaSMatthew Dillon 
10218de8d7fSPeter Avalos static void
chan_set_ostate(Channel * c,u_int next)10318de8d7fSPeter Avalos chan_set_ostate(Channel *c, u_int next)
10418de8d7fSPeter Avalos {
10518de8d7fSPeter Avalos 	if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED)
10618de8d7fSPeter Avalos 		fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next);
10718de8d7fSPeter Avalos 	debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate],
10818de8d7fSPeter Avalos 	    ostates[next]);
10918de8d7fSPeter Avalos 	c->ostate = next;
11018de8d7fSPeter Avalos }
11118de8d7fSPeter Avalos 
11218de8d7fSPeter Avalos void
chan_read_failed(struct ssh * ssh,Channel * c)113ce74bacaSMatthew Dillon chan_read_failed(struct ssh *ssh, Channel *c)
11418de8d7fSPeter Avalos {
11518de8d7fSPeter Avalos 	debug2("channel %d: read failed", c->self);
11618de8d7fSPeter Avalos 	switch (c->istate) {
11718de8d7fSPeter Avalos 	case CHAN_INPUT_OPEN:
118ce74bacaSMatthew Dillon 		chan_shutdown_read(ssh, c);
11918de8d7fSPeter Avalos 		chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN);
12018de8d7fSPeter Avalos 		break;
12118de8d7fSPeter Avalos 	default:
12218de8d7fSPeter Avalos 		error("channel %d: chan_read_failed for istate %d",
12318de8d7fSPeter Avalos 		    c->self, c->istate);
12418de8d7fSPeter Avalos 		break;
12518de8d7fSPeter Avalos 	}
12618de8d7fSPeter Avalos }
127ce74bacaSMatthew Dillon 
12818de8d7fSPeter Avalos void
chan_ibuf_empty(struct ssh * ssh,Channel * c)129ce74bacaSMatthew Dillon chan_ibuf_empty(struct ssh *ssh, Channel *c)
13018de8d7fSPeter Avalos {
13118de8d7fSPeter Avalos 	debug2("channel %d: ibuf empty", c->self);
132ce74bacaSMatthew Dillon 	if (sshbuf_len(c->input)) {
13318de8d7fSPeter Avalos 		error("channel %d: chan_ibuf_empty for non empty buffer",
13418de8d7fSPeter Avalos 		    c->self);
13518de8d7fSPeter Avalos 		return;
13618de8d7fSPeter Avalos 	}
13718de8d7fSPeter Avalos 	switch (c->istate) {
13818de8d7fSPeter Avalos 	case CHAN_INPUT_WAIT_DRAIN:
139856ea928SPeter Avalos 		if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL)))
140ce74bacaSMatthew Dillon 			chan_send_eof2(ssh, c);
14118de8d7fSPeter Avalos 		chan_set_istate(c, CHAN_INPUT_CLOSED);
14218de8d7fSPeter Avalos 		break;
14318de8d7fSPeter Avalos 	default:
14418de8d7fSPeter Avalos 		error("channel %d: chan_ibuf_empty for istate %d",
14518de8d7fSPeter Avalos 		    c->self, c->istate);
14618de8d7fSPeter Avalos 		break;
14718de8d7fSPeter Avalos 	}
14818de8d7fSPeter Avalos }
149ce74bacaSMatthew Dillon 
15018de8d7fSPeter Avalos void
chan_obuf_empty(struct ssh * ssh,Channel * c)151ce74bacaSMatthew Dillon chan_obuf_empty(struct ssh *ssh, Channel *c)
15218de8d7fSPeter Avalos {
15318de8d7fSPeter Avalos 	debug2("channel %d: obuf empty", c->self);
154ce74bacaSMatthew Dillon 	if (sshbuf_len(c->output)) {
15518de8d7fSPeter Avalos 		error("channel %d: chan_obuf_empty for non empty buffer",
15618de8d7fSPeter Avalos 		    c->self);
15718de8d7fSPeter Avalos 		return;
15818de8d7fSPeter Avalos 	}
15918de8d7fSPeter Avalos 	switch (c->ostate) {
16018de8d7fSPeter Avalos 	case CHAN_OUTPUT_WAIT_DRAIN:
161ce74bacaSMatthew Dillon 		chan_shutdown_write(ssh, c);
16218de8d7fSPeter Avalos 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
16318de8d7fSPeter Avalos 		break;
16418de8d7fSPeter Avalos 	default:
16518de8d7fSPeter Avalos 		error("channel %d: internal error: obuf_empty for ostate %d",
16618de8d7fSPeter Avalos 		    c->self, c->ostate);
16718de8d7fSPeter Avalos 		break;
16818de8d7fSPeter Avalos 	}
16918de8d7fSPeter Avalos }
170ce74bacaSMatthew Dillon 
171ce74bacaSMatthew Dillon void
chan_rcvd_eow(struct ssh * ssh,Channel * c)172ce74bacaSMatthew Dillon chan_rcvd_eow(struct ssh *ssh, Channel *c)
17318de8d7fSPeter Avalos {
174ce74bacaSMatthew Dillon 	debug2("channel %d: rcvd eow", c->self);
17518de8d7fSPeter Avalos 	switch (c->istate) {
17618de8d7fSPeter Avalos 	case CHAN_INPUT_OPEN:
177ce74bacaSMatthew Dillon 		chan_shutdown_read(ssh, c);
178ce74bacaSMatthew Dillon 		chan_set_istate(c, CHAN_INPUT_CLOSED);
17918de8d7fSPeter Avalos 		break;
18018de8d7fSPeter Avalos 	}
18118de8d7fSPeter Avalos }
18218de8d7fSPeter Avalos 
18318de8d7fSPeter Avalos static void
chan_send_eof2(struct ssh * ssh,Channel * c)184ce74bacaSMatthew Dillon chan_send_eof2(struct ssh *ssh, Channel *c)
185ce74bacaSMatthew Dillon {
186ce74bacaSMatthew Dillon 	int r;
187ce74bacaSMatthew Dillon 
188ce74bacaSMatthew Dillon 	debug2("channel %d: send eof", c->self);
189ce74bacaSMatthew Dillon 	switch (c->istate) {
190ce74bacaSMatthew Dillon 	case CHAN_INPUT_WAIT_DRAIN:
191ce74bacaSMatthew Dillon 		if (!c->have_remote_id)
19250a69bb5SSascha Wildner 			fatal_f("channel %d: no remote_id", c->self);
193ce74bacaSMatthew Dillon 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EOF)) != 0 ||
194ce74bacaSMatthew Dillon 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
195ce74bacaSMatthew Dillon 		    (r = sshpkt_send(ssh)) != 0)
19650a69bb5SSascha Wildner 			fatal_fr(r, "send CHANNEL_EOF");
197ce74bacaSMatthew Dillon 		c->flags |= CHAN_EOF_SENT;
198ce74bacaSMatthew Dillon 		break;
199ce74bacaSMatthew Dillon 	default:
200ce74bacaSMatthew Dillon 		error("channel %d: cannot send eof for istate %d",
201ce74bacaSMatthew Dillon 		    c->self, c->istate);
202ce74bacaSMatthew Dillon 		break;
203ce74bacaSMatthew Dillon 	}
204ce74bacaSMatthew Dillon }
205ce74bacaSMatthew Dillon 
206ce74bacaSMatthew Dillon static void
chan_send_close2(struct ssh * ssh,Channel * c)207ce74bacaSMatthew Dillon chan_send_close2(struct ssh *ssh, Channel *c)
208ce74bacaSMatthew Dillon {
209ce74bacaSMatthew Dillon 	int r;
210ce74bacaSMatthew Dillon 
211ce74bacaSMatthew Dillon 	debug2("channel %d: send close", c->self);
212ce74bacaSMatthew Dillon 	if (c->ostate != CHAN_OUTPUT_CLOSED ||
213ce74bacaSMatthew Dillon 	    c->istate != CHAN_INPUT_CLOSED) {
214ce74bacaSMatthew Dillon 		error("channel %d: cannot send close for istate/ostate %d/%d",
215ce74bacaSMatthew Dillon 		    c->self, c->istate, c->ostate);
216ce74bacaSMatthew Dillon 	} else if (c->flags & CHAN_CLOSE_SENT) {
217ce74bacaSMatthew Dillon 		error("channel %d: already sent close", c->self);
218ce74bacaSMatthew Dillon 	} else {
219ce74bacaSMatthew Dillon 		if (!c->have_remote_id)
22050a69bb5SSascha Wildner 			fatal_f("channel %d: no remote_id", c->self);
221ce74bacaSMatthew Dillon 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_CLOSE)) != 0 ||
222ce74bacaSMatthew Dillon 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
223ce74bacaSMatthew Dillon 		    (r = sshpkt_send(ssh)) != 0)
22450a69bb5SSascha Wildner 			fatal_fr(r, "send CHANNEL_EOF");
225ce74bacaSMatthew Dillon 		c->flags |= CHAN_CLOSE_SENT;
226ce74bacaSMatthew Dillon 	}
227ce74bacaSMatthew Dillon }
228ce74bacaSMatthew Dillon 
229ce74bacaSMatthew Dillon static void
chan_send_eow2(struct ssh * ssh,Channel * c)230ce74bacaSMatthew Dillon chan_send_eow2(struct ssh *ssh, Channel *c)
231ce74bacaSMatthew Dillon {
232ce74bacaSMatthew Dillon 	int r;
233ce74bacaSMatthew Dillon 
234ce74bacaSMatthew Dillon 	debug2("channel %d: send eow", c->self);
235ce74bacaSMatthew Dillon 	if (c->ostate == CHAN_OUTPUT_CLOSED) {
236ce74bacaSMatthew Dillon 		error("channel %d: must not sent eow on closed output",
237ce74bacaSMatthew Dillon 		    c->self);
238ce74bacaSMatthew Dillon 		return;
239ce74bacaSMatthew Dillon 	}
24050a69bb5SSascha Wildner 	if (!(ssh->compat & SSH_NEW_OPENSSH))
241ce74bacaSMatthew Dillon 		return;
242ce74bacaSMatthew Dillon 	if (!c->have_remote_id)
24350a69bb5SSascha Wildner 		fatal_f("channel %d: no remote_id", c->self);
244ce74bacaSMatthew Dillon 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
245ce74bacaSMatthew Dillon 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
246ce74bacaSMatthew Dillon 	    (r = sshpkt_put_cstring(ssh, "eow@openssh.com")) != 0 ||
247ce74bacaSMatthew Dillon 	    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
248ce74bacaSMatthew Dillon 	    (r = sshpkt_send(ssh)) != 0)
24950a69bb5SSascha Wildner 		fatal_fr(r, "send CHANNEL_EOF");
250ce74bacaSMatthew Dillon }
251ce74bacaSMatthew Dillon 
252ce74bacaSMatthew Dillon /* shared */
253ce74bacaSMatthew Dillon 
254ce74bacaSMatthew Dillon void
chan_rcvd_ieof(struct ssh * ssh,Channel * c)255ce74bacaSMatthew Dillon chan_rcvd_ieof(struct ssh *ssh, Channel *c)
256ce74bacaSMatthew Dillon {
257ce74bacaSMatthew Dillon 	debug2("channel %d: rcvd eof", c->self);
258ce74bacaSMatthew Dillon 	c->flags |= CHAN_EOF_RCVD;
259ce74bacaSMatthew Dillon 	if (c->ostate == CHAN_OUTPUT_OPEN)
260ce74bacaSMatthew Dillon 		chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
261ce74bacaSMatthew Dillon 	if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN &&
262ce74bacaSMatthew Dillon 	    sshbuf_len(c->output) == 0 &&
263ce74bacaSMatthew Dillon 	    !CHANNEL_EFD_OUTPUT_ACTIVE(c))
264ce74bacaSMatthew Dillon 		chan_obuf_empty(ssh, c);
265ce74bacaSMatthew Dillon }
266ce74bacaSMatthew Dillon 
267ce74bacaSMatthew Dillon void
chan_rcvd_oclose(struct ssh * ssh,Channel * c)268ce74bacaSMatthew Dillon chan_rcvd_oclose(struct ssh *ssh, Channel *c)
26918de8d7fSPeter Avalos {
27018de8d7fSPeter Avalos 	debug2("channel %d: rcvd close", c->self);
271856ea928SPeter Avalos 	if (!(c->flags & CHAN_LOCAL)) {
27218de8d7fSPeter Avalos 		if (c->flags & CHAN_CLOSE_RCVD)
273856ea928SPeter Avalos 			error("channel %d: protocol error: close rcvd twice",
274856ea928SPeter Avalos 			    c->self);
27518de8d7fSPeter Avalos 		c->flags |= CHAN_CLOSE_RCVD;
276856ea928SPeter Avalos 	}
27718de8d7fSPeter Avalos 	if (c->type == SSH_CHANNEL_LARVAL) {
27818de8d7fSPeter Avalos 		/* tear down larval channels immediately */
27918de8d7fSPeter Avalos 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
28018de8d7fSPeter Avalos 		chan_set_istate(c, CHAN_INPUT_CLOSED);
28118de8d7fSPeter Avalos 		return;
28218de8d7fSPeter Avalos 	}
28318de8d7fSPeter Avalos 	switch (c->ostate) {
28418de8d7fSPeter Avalos 	case CHAN_OUTPUT_OPEN:
28518de8d7fSPeter Avalos 		/*
28618de8d7fSPeter Avalos 		 * wait until a data from the channel is consumed if a CLOSE
28718de8d7fSPeter Avalos 		 * is received
28818de8d7fSPeter Avalos 		 */
28918de8d7fSPeter Avalos 		chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
29018de8d7fSPeter Avalos 		break;
29118de8d7fSPeter Avalos 	}
29218de8d7fSPeter Avalos 	switch (c->istate) {
29318de8d7fSPeter Avalos 	case CHAN_INPUT_OPEN:
294ce74bacaSMatthew Dillon 		chan_shutdown_read(ssh, c);
295664f4763Szrj 		chan_shutdown_extended_read(ssh, c);
29618de8d7fSPeter Avalos 		chan_set_istate(c, CHAN_INPUT_CLOSED);
29718de8d7fSPeter Avalos 		break;
29818de8d7fSPeter Avalos 	case CHAN_INPUT_WAIT_DRAIN:
299856ea928SPeter Avalos 		if (!(c->flags & CHAN_LOCAL))
300ce74bacaSMatthew Dillon 			chan_send_eof2(ssh, c);
301664f4763Szrj 		chan_shutdown_extended_read(ssh, c);
30218de8d7fSPeter Avalos 		chan_set_istate(c, CHAN_INPUT_CLOSED);
30318de8d7fSPeter Avalos 		break;
30418de8d7fSPeter Avalos 	}
30518de8d7fSPeter Avalos }
306856ea928SPeter Avalos 
30718de8d7fSPeter Avalos void
chan_write_failed(struct ssh * ssh,Channel * c)308ce74bacaSMatthew Dillon chan_write_failed(struct ssh *ssh, Channel *c)
30918de8d7fSPeter Avalos {
31018de8d7fSPeter Avalos 	debug2("channel %d: write failed", c->self);
31118de8d7fSPeter Avalos 	switch (c->ostate) {
31218de8d7fSPeter Avalos 	case CHAN_OUTPUT_OPEN:
31318de8d7fSPeter Avalos 	case CHAN_OUTPUT_WAIT_DRAIN:
314ce74bacaSMatthew Dillon 		chan_shutdown_write(ssh, c);
31518de8d7fSPeter Avalos 		if (strcmp(c->ctype, "session") == 0)
316ce74bacaSMatthew Dillon 			chan_send_eow2(ssh, c);
31718de8d7fSPeter Avalos 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
31818de8d7fSPeter Avalos 		break;
31918de8d7fSPeter Avalos 	default:
32018de8d7fSPeter Avalos 		error("channel %d: chan_write_failed for ostate %d",
32118de8d7fSPeter Avalos 		    c->self, c->ostate);
32218de8d7fSPeter Avalos 		break;
32318de8d7fSPeter Avalos 	}
32418de8d7fSPeter Avalos }
32518de8d7fSPeter Avalos 
32618de8d7fSPeter Avalos void
chan_mark_dead(struct ssh * ssh,Channel * c)327ce74bacaSMatthew Dillon chan_mark_dead(struct ssh *ssh, Channel *c)
32818de8d7fSPeter Avalos {
32918de8d7fSPeter Avalos 	c->type = SSH_CHANNEL_ZOMBIE;
33018de8d7fSPeter Avalos }
33118de8d7fSPeter Avalos 
33218de8d7fSPeter Avalos int
chan_is_dead(struct ssh * ssh,Channel * c,int do_send)333ce74bacaSMatthew Dillon chan_is_dead(struct ssh *ssh, Channel *c, int do_send)
33418de8d7fSPeter Avalos {
33518de8d7fSPeter Avalos 	if (c->type == SSH_CHANNEL_ZOMBIE) {
33618de8d7fSPeter Avalos 		debug2("channel %d: zombie", c->self);
33718de8d7fSPeter Avalos 		return 1;
33818de8d7fSPeter Avalos 	}
33918de8d7fSPeter Avalos 	if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED)
34018de8d7fSPeter Avalos 		return 0;
34150a69bb5SSascha Wildner 	if ((ssh->compat & SSH_BUG_EXTEOF) &&
34218de8d7fSPeter Avalos 	    c->extended_usage == CHAN_EXTENDED_WRITE &&
34318de8d7fSPeter Avalos 	    c->efd != -1 &&
344ce74bacaSMatthew Dillon 	    sshbuf_len(c->extended) > 0) {
345ce74bacaSMatthew Dillon 		debug2("channel %d: active efd: %d len %zu",
346ce74bacaSMatthew Dillon 		    c->self, c->efd, sshbuf_len(c->extended));
34718de8d7fSPeter Avalos 		return 0;
34818de8d7fSPeter Avalos 	}
349856ea928SPeter Avalos 	if (c->flags & CHAN_LOCAL) {
350856ea928SPeter Avalos 		debug2("channel %d: is dead (local)", c->self);
351856ea928SPeter Avalos 		return 1;
352856ea928SPeter Avalos 	}
35318de8d7fSPeter Avalos 	if (!(c->flags & CHAN_CLOSE_SENT)) {
35418de8d7fSPeter Avalos 		if (do_send) {
355ce74bacaSMatthew Dillon 			chan_send_close2(ssh, c);
35618de8d7fSPeter Avalos 		} else {
35718de8d7fSPeter Avalos 			/* channel would be dead if we sent a close */
35818de8d7fSPeter Avalos 			if (c->flags & CHAN_CLOSE_RCVD) {
35918de8d7fSPeter Avalos 				debug2("channel %d: almost dead",
36018de8d7fSPeter Avalos 				    c->self);
36118de8d7fSPeter Avalos 				return 1;
36218de8d7fSPeter Avalos 			}
36318de8d7fSPeter Avalos 		}
36418de8d7fSPeter Avalos 	}
36518de8d7fSPeter Avalos 	if ((c->flags & CHAN_CLOSE_SENT) &&
36618de8d7fSPeter Avalos 	    (c->flags & CHAN_CLOSE_RCVD)) {
36718de8d7fSPeter Avalos 		debug2("channel %d: is dead", c->self);
36818de8d7fSPeter Avalos 		return 1;
36918de8d7fSPeter Avalos 	}
37018de8d7fSPeter Avalos 	return 0;
37118de8d7fSPeter Avalos }
37218de8d7fSPeter Avalos 
37318de8d7fSPeter Avalos /* helper */
37418de8d7fSPeter Avalos static void
chan_shutdown_write(struct ssh * ssh,Channel * c)375ce74bacaSMatthew Dillon chan_shutdown_write(struct ssh *ssh, Channel *c)
37618de8d7fSPeter Avalos {
377ce74bacaSMatthew Dillon 	sshbuf_reset(c->output);
378ce74bacaSMatthew Dillon 	if (c->type == SSH_CHANNEL_LARVAL)
37918de8d7fSPeter Avalos 		return;
38018de8d7fSPeter Avalos 	/* shutdown failure is allowed if write failed already */
38150a69bb5SSascha Wildner 	debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
38250a69bb5SSascha Wildner 	    c->self, c->istate, c->ostate, c->sock, c->wfd, c->efd,
383664f4763Szrj 	    channel_format_extended_usage(c));
38418de8d7fSPeter Avalos 	if (c->sock != -1) {
3850cbfa66cSDaniel Fojt 		if (shutdown(c->sock, SHUT_WR) == -1) {
38650a69bb5SSascha Wildner 			debug2_f("channel %d: shutdown() failed for "
38750a69bb5SSascha Wildner 			    "fd %d [i%d o%d]: %.100s", c->self, c->sock,
38850a69bb5SSascha Wildner 			    c->istate, c->ostate, strerror(errno));
389664f4763Szrj 		}
39018de8d7fSPeter Avalos 	} else {
39150a69bb5SSascha Wildner 		if (channel_close_fd(ssh, c, &c->wfd) < 0) {
39250a69bb5SSascha Wildner 			logit_f("channel %d: close() failed for "
39350a69bb5SSascha Wildner 			    "fd %d [i%d o%d]: %.100s", c->self, c->wfd,
39450a69bb5SSascha Wildner 			    c->istate, c->ostate, strerror(errno));
395664f4763Szrj 		}
39618de8d7fSPeter Avalos 	}
39718de8d7fSPeter Avalos }
398ce74bacaSMatthew Dillon 
39918de8d7fSPeter Avalos static void
chan_shutdown_read(struct ssh * ssh,Channel * c)400ce74bacaSMatthew Dillon chan_shutdown_read(struct ssh *ssh, Channel *c)
40118de8d7fSPeter Avalos {
402ce74bacaSMatthew Dillon 	if (c->type == SSH_CHANNEL_LARVAL)
40318de8d7fSPeter Avalos 		return;
40450a69bb5SSascha Wildner 	debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
40550a69bb5SSascha Wildner 	    c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd,
406664f4763Szrj 	    channel_format_extended_usage(c));
40718de8d7fSPeter Avalos 	if (c->sock != -1) {
40818de8d7fSPeter Avalos 		/*
40918de8d7fSPeter Avalos 		 * shutdown(sock, SHUT_READ) may return ENOTCONN if the
41018de8d7fSPeter Avalos 		 * write side has been closed already. (bug on Linux)
41118de8d7fSPeter Avalos 		 * HP-UX may return ENOTCONN also.
41218de8d7fSPeter Avalos 		 */
4130cbfa66cSDaniel Fojt 		if (shutdown(c->sock, SHUT_RD) == -1 && errno != ENOTCONN) {
41450a69bb5SSascha Wildner 			error_f("channel %d: shutdown() failed for "
41550a69bb5SSascha Wildner 			    "fd %d [i%d o%d]: %.100s", c->self, c->sock,
41650a69bb5SSascha Wildner 			    c->istate, c->ostate, strerror(errno));
417664f4763Szrj 		}
41818de8d7fSPeter Avalos 	} else {
41950a69bb5SSascha Wildner 		if (channel_close_fd(ssh, c, &c->rfd) < 0) {
42050a69bb5SSascha Wildner 			logit_f("channel %d: close() failed for "
42150a69bb5SSascha Wildner 			    "fd %d [i%d o%d]: %.100s", c->self, c->rfd,
42250a69bb5SSascha Wildner 			    c->istate, c->ostate, strerror(errno));
423664f4763Szrj 		}
424664f4763Szrj 	}
425664f4763Szrj }
426664f4763Szrj 
427664f4763Szrj static void
chan_shutdown_extended_read(struct ssh * ssh,Channel * c)428664f4763Szrj chan_shutdown_extended_read(struct ssh *ssh, Channel *c)
429664f4763Szrj {
430664f4763Szrj 	if (c->type == SSH_CHANNEL_LARVAL || c->efd == -1)
431664f4763Szrj 		return;
432664f4763Szrj 	if (c->extended_usage != CHAN_EXTENDED_READ &&
433664f4763Szrj 	    c->extended_usage != CHAN_EXTENDED_IGNORE)
434664f4763Szrj 		return;
43550a69bb5SSascha Wildner 	debug_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
43650a69bb5SSascha Wildner 	    c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd,
437664f4763Szrj 	    channel_format_extended_usage(c));
43850a69bb5SSascha Wildner 	if (channel_close_fd(ssh, c, &c->efd) < 0) {
43950a69bb5SSascha Wildner 		logit_f("channel %d: close() failed for "
44050a69bb5SSascha Wildner 		    "extended fd %d [i%d o%d]: %.100s", c->self, c->efd,
44150a69bb5SSascha Wildner 		    c->istate, c->ostate, strerror(errno));
44218de8d7fSPeter Avalos 	}
44318de8d7fSPeter Avalos }
444