xref: /freebsd/crypto/openssh/nchan.c (revision 545d5eca429a5967b3300cb527d49cae8184e79f)
1511b41d2SMark Murray /*
2ae1f160dSDag-Erling Smørgrav  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
3511b41d2SMark Murray  *
4511b41d2SMark Murray  * Redistribution and use in source and binary forms, with or without
5511b41d2SMark Murray  * modification, are permitted provided that the following conditions
6511b41d2SMark Murray  * are met:
7511b41d2SMark Murray  * 1. Redistributions of source code must retain the above copyright
8511b41d2SMark Murray  *    notice, this list of conditions and the following disclaimer.
9511b41d2SMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
10511b41d2SMark Murray  *    notice, this list of conditions and the following disclaimer in the
11511b41d2SMark Murray  *    documentation and/or other materials provided with the distribution.
12511b41d2SMark Murray  *
13511b41d2SMark Murray  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14511b41d2SMark Murray  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15511b41d2SMark Murray  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16511b41d2SMark Murray  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17511b41d2SMark Murray  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18511b41d2SMark Murray  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19511b41d2SMark Murray  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20511b41d2SMark Murray  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21511b41d2SMark Murray  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22511b41d2SMark Murray  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23511b41d2SMark Murray  */
24511b41d2SMark Murray 
25511b41d2SMark Murray #include "includes.h"
26545d5ecaSDag-Erling Smørgrav RCSID("$OpenBSD: nchan.c,v 1.47 2002/06/19 00:27:55 deraadt Exp $");
27511b41d2SMark Murray 
281e8db6e2SBrian Feldman #include "ssh1.h"
291e8db6e2SBrian Feldman #include "ssh2.h"
30511b41d2SMark Murray #include "buffer.h"
31511b41d2SMark Murray #include "packet.h"
32511b41d2SMark Murray #include "channels.h"
33a04a10f8SKris Kennaway #include "compat.h"
341e8db6e2SBrian Feldman #include "log.h"
35511b41d2SMark Murray 
36ae1f160dSDag-Erling Smørgrav /*
37ae1f160dSDag-Erling Smørgrav  * SSH Protocol 1.5 aka New Channel Protocol
38ae1f160dSDag-Erling Smørgrav  * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored.
39ae1f160dSDag-Erling Smørgrav  * Written by Markus Friedl in October 1999
40ae1f160dSDag-Erling Smørgrav  *
41ae1f160dSDag-Erling Smørgrav  * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the
42ae1f160dSDag-Erling Smørgrav  * tear down of channels:
43ae1f160dSDag-Erling Smørgrav  *
44ae1f160dSDag-Erling Smørgrav  * 1.3:	strict request-ack-protocol:
45ae1f160dSDag-Erling Smørgrav  * 	CLOSE	->
46ae1f160dSDag-Erling Smørgrav  * 		<-  CLOSE_CONFIRM
47ae1f160dSDag-Erling Smørgrav  *
48ae1f160dSDag-Erling Smørgrav  * 1.5:	uses variations of:
49ae1f160dSDag-Erling Smørgrav  * 	IEOF	->
50ae1f160dSDag-Erling Smørgrav  * 		<-  OCLOSE
51ae1f160dSDag-Erling Smørgrav  * 		<-  IEOF
52ae1f160dSDag-Erling Smørgrav  * 	OCLOSE	->
53ae1f160dSDag-Erling Smørgrav  * 	i.e. both sides have to close the channel
54ae1f160dSDag-Erling Smørgrav  *
55ae1f160dSDag-Erling Smørgrav  * 2.0: the EOF messages are optional
56ae1f160dSDag-Erling Smørgrav  *
57ae1f160dSDag-Erling Smørgrav  * See the debugging output from 'ssh -v' and 'sshd -d' of
58ae1f160dSDag-Erling Smørgrav  * ssh-1.2.27 as an example.
59ae1f160dSDag-Erling Smørgrav  *
60ae1f160dSDag-Erling Smørgrav  */
61ae1f160dSDag-Erling Smørgrav 
62a04a10f8SKris Kennaway /* functions manipulating channel states */
63511b41d2SMark Murray /*
64511b41d2SMark Murray  * EVENTS update channel input/output states execute ACTIONS
65511b41d2SMark Murray  */
66a04a10f8SKris Kennaway /*
67a04a10f8SKris Kennaway  * ACTIONS: should never update the channel states
68a04a10f8SKris Kennaway  */
69ae1f160dSDag-Erling Smørgrav static void	chan_send_ieof1(Channel *);
70ae1f160dSDag-Erling Smørgrav static void	chan_send_oclose1(Channel *);
71ae1f160dSDag-Erling Smørgrav static void	chan_send_close2(Channel *);
72ae1f160dSDag-Erling Smørgrav static void	chan_send_eof2(Channel *);
73a04a10f8SKris Kennaway 
74a04a10f8SKris Kennaway /* helper */
75ae1f160dSDag-Erling Smørgrav static void	chan_shutdown_write(Channel *);
76ae1f160dSDag-Erling Smørgrav static void	chan_shutdown_read(Channel *);
77ae1f160dSDag-Erling Smørgrav 
78ae1f160dSDag-Erling Smørgrav static char *ostates[] = { "open", "drain", "wait_ieof", "closed" };
79ae1f160dSDag-Erling Smørgrav static char *istates[] = { "open", "drain", "wait_oclose", "closed" };
80ae1f160dSDag-Erling Smørgrav 
81ae1f160dSDag-Erling Smørgrav static void
82ae1f160dSDag-Erling Smørgrav chan_set_istate(Channel *c, u_int next)
83ae1f160dSDag-Erling Smørgrav {
84ae1f160dSDag-Erling Smørgrav 	if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED)
85ae1f160dSDag-Erling Smørgrav 		fatal("chan_set_istate: bad state %d -> %d", c->istate, next);
86ae1f160dSDag-Erling Smørgrav 	debug("channel %d: input %s -> %s", c->self, istates[c->istate],
87ae1f160dSDag-Erling Smørgrav 	    istates[next]);
88ae1f160dSDag-Erling Smørgrav 	c->istate = next;
89ae1f160dSDag-Erling Smørgrav }
90ae1f160dSDag-Erling Smørgrav static void
91ae1f160dSDag-Erling Smørgrav chan_set_ostate(Channel *c, u_int next)
92ae1f160dSDag-Erling Smørgrav {
93ae1f160dSDag-Erling Smørgrav 	if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED)
94ae1f160dSDag-Erling Smørgrav 		fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next);
95ae1f160dSDag-Erling Smørgrav 	debug("channel %d: output %s -> %s", c->self, ostates[c->ostate],
96ae1f160dSDag-Erling Smørgrav 	    ostates[next]);
97ae1f160dSDag-Erling Smørgrav 	c->ostate = next;
98ae1f160dSDag-Erling Smørgrav }
99a04a10f8SKris Kennaway 
100a04a10f8SKris Kennaway /*
101a04a10f8SKris Kennaway  * SSH1 specific implementation of event functions
102a04a10f8SKris Kennaway  */
103a04a10f8SKris Kennaway 
104a04a10f8SKris Kennaway static void
105a04a10f8SKris Kennaway chan_rcvd_oclose1(Channel *c)
106511b41d2SMark Murray {
107a04a10f8SKris Kennaway 	debug("channel %d: rcvd oclose", c->self);
108511b41d2SMark Murray 	switch (c->istate) {
109511b41d2SMark Murray 	case CHAN_INPUT_WAIT_OCLOSE:
110ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_CLOSED);
111511b41d2SMark Murray 		break;
112511b41d2SMark Murray 	case CHAN_INPUT_OPEN:
113511b41d2SMark Murray 		chan_shutdown_read(c);
114a04a10f8SKris Kennaway 		chan_send_ieof1(c);
115ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_CLOSED);
116511b41d2SMark Murray 		break;
117511b41d2SMark Murray 	case CHAN_INPUT_WAIT_DRAIN:
118511b41d2SMark Murray 		/* both local read_failed and remote write_failed  */
119a04a10f8SKris Kennaway 		chan_send_ieof1(c);
120ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_CLOSED);
121511b41d2SMark Murray 		break;
122511b41d2SMark Murray 	default:
123ae1f160dSDag-Erling Smørgrav 		error("channel %d: protocol error: rcvd_oclose for istate %d",
124a04a10f8SKris Kennaway 		    c->self, c->istate);
125511b41d2SMark Murray 		return;
126511b41d2SMark Murray 	}
127511b41d2SMark Murray }
128ae1f160dSDag-Erling Smørgrav void
129ae1f160dSDag-Erling Smørgrav chan_read_failed(Channel *c)
130511b41d2SMark Murray {
131a04a10f8SKris Kennaway 	debug("channel %d: read failed", c->self);
132511b41d2SMark Murray 	switch (c->istate) {
133511b41d2SMark Murray 	case CHAN_INPUT_OPEN:
134511b41d2SMark Murray 		chan_shutdown_read(c);
135ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN);
136511b41d2SMark Murray 		break;
137511b41d2SMark Murray 	default:
138ae1f160dSDag-Erling Smørgrav 		error("channel %d: chan_read_failed for istate %d",
139511b41d2SMark Murray 		    c->self, c->istate);
140511b41d2SMark Murray 		break;
141511b41d2SMark Murray 	}
142511b41d2SMark Murray }
143ae1f160dSDag-Erling Smørgrav void
144ae1f160dSDag-Erling Smørgrav chan_ibuf_empty(Channel *c)
145511b41d2SMark Murray {
146a04a10f8SKris Kennaway 	debug("channel %d: ibuf empty", c->self);
147511b41d2SMark Murray 	if (buffer_len(&c->input)) {
148ae1f160dSDag-Erling Smørgrav 		error("channel %d: chan_ibuf_empty for non empty buffer",
149a04a10f8SKris Kennaway 		    c->self);
150511b41d2SMark Murray 		return;
151511b41d2SMark Murray 	}
152511b41d2SMark Murray 	switch (c->istate) {
153511b41d2SMark Murray 	case CHAN_INPUT_WAIT_DRAIN:
154ae1f160dSDag-Erling Smørgrav 		if (compat20) {
155ae1f160dSDag-Erling Smørgrav 			if (!(c->flags & CHAN_CLOSE_SENT))
156ae1f160dSDag-Erling Smørgrav 				chan_send_eof2(c);
157ae1f160dSDag-Erling Smørgrav 			chan_set_istate(c, CHAN_INPUT_CLOSED);
158ae1f160dSDag-Erling Smørgrav 		} else {
159a04a10f8SKris Kennaway 			chan_send_ieof1(c);
160ae1f160dSDag-Erling Smørgrav 			chan_set_istate(c, CHAN_INPUT_WAIT_OCLOSE);
161ae1f160dSDag-Erling Smørgrav 		}
162511b41d2SMark Murray 		break;
163511b41d2SMark Murray 	default:
164ae1f160dSDag-Erling Smørgrav 		error("channel %d: chan_ibuf_empty for istate %d",
165a04a10f8SKris Kennaway 		    c->self, c->istate);
166511b41d2SMark Murray 		break;
167511b41d2SMark Murray 	}
168511b41d2SMark Murray }
169a04a10f8SKris Kennaway static void
170a04a10f8SKris Kennaway chan_rcvd_ieof1(Channel *c)
171511b41d2SMark Murray {
172a04a10f8SKris Kennaway 	debug("channel %d: rcvd ieof", c->self);
173511b41d2SMark Murray 	switch (c->ostate) {
174511b41d2SMark Murray 	case CHAN_OUTPUT_OPEN:
175ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
176511b41d2SMark Murray 		break;
177511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_IEOF:
178ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
179511b41d2SMark Murray 		break;
180511b41d2SMark Murray 	default:
181ae1f160dSDag-Erling Smørgrav 		error("channel %d: protocol error: rcvd_ieof for ostate %d",
182a04a10f8SKris Kennaway 		    c->self, c->ostate);
183511b41d2SMark Murray 		break;
184511b41d2SMark Murray 	}
185511b41d2SMark Murray }
186a04a10f8SKris Kennaway static void
187a04a10f8SKris Kennaway chan_write_failed1(Channel *c)
188511b41d2SMark Murray {
189a04a10f8SKris Kennaway 	debug("channel %d: write failed", c->self);
190511b41d2SMark Murray 	switch (c->ostate) {
191511b41d2SMark Murray 	case CHAN_OUTPUT_OPEN:
192ae1f160dSDag-Erling Smørgrav 		chan_shutdown_write(c);
193a04a10f8SKris Kennaway 		chan_send_oclose1(c);
194ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_WAIT_IEOF);
195511b41d2SMark Murray 		break;
196511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_DRAIN:
197ae1f160dSDag-Erling Smørgrav 		chan_shutdown_write(c);
198a04a10f8SKris Kennaway 		chan_send_oclose1(c);
199ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
200511b41d2SMark Murray 		break;
201511b41d2SMark Murray 	default:
202ae1f160dSDag-Erling Smørgrav 		error("channel %d: chan_write_failed for ostate %d",
203a04a10f8SKris Kennaway 		    c->self, c->ostate);
204511b41d2SMark Murray 		break;
205511b41d2SMark Murray 	}
206511b41d2SMark Murray }
207ae1f160dSDag-Erling Smørgrav void
208ae1f160dSDag-Erling Smørgrav chan_obuf_empty(Channel *c)
209511b41d2SMark Murray {
210a04a10f8SKris Kennaway 	debug("channel %d: obuf empty", c->self);
211511b41d2SMark Murray 	if (buffer_len(&c->output)) {
212ae1f160dSDag-Erling Smørgrav 		error("channel %d: chan_obuf_empty for non empty buffer",
213a04a10f8SKris Kennaway 		    c->self);
214511b41d2SMark Murray 		return;
215511b41d2SMark Murray 	}
216511b41d2SMark Murray 	switch (c->ostate) {
217511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_DRAIN:
218ae1f160dSDag-Erling Smørgrav 		chan_shutdown_write(c);
219ae1f160dSDag-Erling Smørgrav 		if (!compat20)
220a04a10f8SKris Kennaway 			chan_send_oclose1(c);
221ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
222511b41d2SMark Murray 		break;
223511b41d2SMark Murray 	default:
224ae1f160dSDag-Erling Smørgrav 		error("channel %d: internal error: obuf_empty for ostate %d",
225a04a10f8SKris Kennaway 		    c->self, c->ostate);
226511b41d2SMark Murray 		break;
227511b41d2SMark Murray 	}
228511b41d2SMark Murray }
229511b41d2SMark Murray static void
230a04a10f8SKris Kennaway chan_send_ieof1(Channel *c)
231511b41d2SMark Murray {
232a04a10f8SKris Kennaway 	debug("channel %d: send ieof", c->self);
233511b41d2SMark Murray 	switch (c->istate) {
234511b41d2SMark Murray 	case CHAN_INPUT_OPEN:
235511b41d2SMark Murray 	case CHAN_INPUT_WAIT_DRAIN:
236511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_INPUT_EOF);
237511b41d2SMark Murray 		packet_put_int(c->remote_id);
238511b41d2SMark Murray 		packet_send();
239511b41d2SMark Murray 		break;
240511b41d2SMark Murray 	default:
241ae1f160dSDag-Erling Smørgrav 		error("channel %d: cannot send ieof for istate %d",
242a04a10f8SKris Kennaway 		    c->self, c->istate);
243511b41d2SMark Murray 		break;
244511b41d2SMark Murray 	}
245511b41d2SMark Murray }
246511b41d2SMark Murray static void
247a04a10f8SKris Kennaway chan_send_oclose1(Channel *c)
248511b41d2SMark Murray {
249a04a10f8SKris Kennaway 	debug("channel %d: send oclose", c->self);
250511b41d2SMark Murray 	switch (c->ostate) {
251511b41d2SMark Murray 	case CHAN_OUTPUT_OPEN:
252511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_DRAIN:
253ae1f160dSDag-Erling Smørgrav 		buffer_clear(&c->output);
254511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE);
255511b41d2SMark Murray 		packet_put_int(c->remote_id);
256511b41d2SMark Murray 		packet_send();
257511b41d2SMark Murray 		break;
258511b41d2SMark Murray 	default:
259ae1f160dSDag-Erling Smørgrav 		error("channel %d: cannot send oclose for ostate %d",
260a04a10f8SKris Kennaway 		    c->self, c->ostate);
261511b41d2SMark Murray 		break;
262511b41d2SMark Murray 	}
263511b41d2SMark Murray }
264a04a10f8SKris Kennaway 
265a04a10f8SKris Kennaway /*
266a04a10f8SKris Kennaway  * the same for SSH2
267a04a10f8SKris Kennaway  */
268a04a10f8SKris Kennaway static void
269ae1f160dSDag-Erling Smørgrav chan_rcvd_close2(Channel *c)
270a04a10f8SKris Kennaway {
271a04a10f8SKris Kennaway 	debug("channel %d: rcvd close", c->self);
272a04a10f8SKris Kennaway 	if (c->flags & CHAN_CLOSE_RCVD)
273a04a10f8SKris Kennaway 		error("channel %d: protocol error: close rcvd twice", c->self);
274a04a10f8SKris Kennaway 	c->flags |= CHAN_CLOSE_RCVD;
275a04a10f8SKris Kennaway 	if (c->type == SSH_CHANNEL_LARVAL) {
276a04a10f8SKris Kennaway 		/* tear down larval channels immediately */
277ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
278ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_CLOSED);
279a04a10f8SKris Kennaway 		return;
280a04a10f8SKris Kennaway 	}
281a04a10f8SKris Kennaway 	switch (c->ostate) {
282a04a10f8SKris Kennaway 	case CHAN_OUTPUT_OPEN:
283ae1f160dSDag-Erling Smørgrav 		/*
284ae1f160dSDag-Erling Smørgrav 		 * wait until a data from the channel is consumed if a CLOSE
285ae1f160dSDag-Erling Smørgrav 		 * is received
286ae1f160dSDag-Erling Smørgrav 		 */
287ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
288a04a10f8SKris Kennaway 		break;
289a04a10f8SKris Kennaway 	}
290a04a10f8SKris Kennaway 	switch (c->istate) {
291a04a10f8SKris Kennaway 	case CHAN_INPUT_OPEN:
292a04a10f8SKris Kennaway 		chan_shutdown_read(c);
293ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_CLOSED);
294a04a10f8SKris Kennaway 		break;
295a04a10f8SKris Kennaway 	case CHAN_INPUT_WAIT_DRAIN:
296a04a10f8SKris Kennaway 		chan_send_eof2(c);
297ae1f160dSDag-Erling Smørgrav 		chan_set_istate(c, CHAN_INPUT_CLOSED);
298a04a10f8SKris Kennaway 		break;
299a04a10f8SKris Kennaway 	}
300a04a10f8SKris Kennaway }
301a04a10f8SKris Kennaway static void
302ae1f160dSDag-Erling Smørgrav chan_rcvd_eof2(Channel *c)
303a04a10f8SKris Kennaway {
304a04a10f8SKris Kennaway 	debug("channel %d: rcvd eof", c->self);
305545d5ecaSDag-Erling Smørgrav 	c->flags |= CHAN_EOF_RCVD;
306ae1f160dSDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_OPEN)
307ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
308a04a10f8SKris Kennaway }
309a04a10f8SKris Kennaway static void
310a04a10f8SKris Kennaway chan_write_failed2(Channel *c)
311a04a10f8SKris Kennaway {
312a04a10f8SKris Kennaway 	debug("channel %d: write failed", c->self);
313a04a10f8SKris Kennaway 	switch (c->ostate) {
314a04a10f8SKris Kennaway 	case CHAN_OUTPUT_OPEN:
315a04a10f8SKris Kennaway 	case CHAN_OUTPUT_WAIT_DRAIN:
316a04a10f8SKris Kennaway 		chan_shutdown_write(c);
317ae1f160dSDag-Erling Smørgrav 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
318a04a10f8SKris Kennaway 		break;
319a04a10f8SKris Kennaway 	default:
320ae1f160dSDag-Erling Smørgrav 		error("channel %d: chan_write_failed for ostate %d",
321a04a10f8SKris Kennaway 		    c->self, c->ostate);
322a04a10f8SKris Kennaway 		break;
323a04a10f8SKris Kennaway 	}
324a04a10f8SKris Kennaway }
325a04a10f8SKris Kennaway static void
326a04a10f8SKris Kennaway chan_send_eof2(Channel *c)
327a04a10f8SKris Kennaway {
328a04a10f8SKris Kennaway 	debug("channel %d: send eof", c->self);
329a04a10f8SKris Kennaway 	switch (c->istate) {
330a04a10f8SKris Kennaway 	case CHAN_INPUT_WAIT_DRAIN:
331a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_CHANNEL_EOF);
332a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
333a04a10f8SKris Kennaway 		packet_send();
334545d5ecaSDag-Erling Smørgrav 		c->flags |= CHAN_EOF_SENT;
335a04a10f8SKris Kennaway 		break;
336a04a10f8SKris Kennaway 	default:
337ae1f160dSDag-Erling Smørgrav 		error("channel %d: cannot send eof for istate %d",
338a04a10f8SKris Kennaway 		    c->self, c->istate);
339a04a10f8SKris Kennaway 		break;
340a04a10f8SKris Kennaway 	}
341a04a10f8SKris Kennaway }
342a04a10f8SKris Kennaway static void
343a04a10f8SKris Kennaway chan_send_close2(Channel *c)
344a04a10f8SKris Kennaway {
345a04a10f8SKris Kennaway 	debug("channel %d: send close", c->self);
346a04a10f8SKris Kennaway 	if (c->ostate != CHAN_OUTPUT_CLOSED ||
347a04a10f8SKris Kennaway 	    c->istate != CHAN_INPUT_CLOSED) {
348ae1f160dSDag-Erling Smørgrav 		error("channel %d: cannot send close for istate/ostate %d/%d",
349a04a10f8SKris Kennaway 		    c->self, c->istate, c->ostate);
350a04a10f8SKris Kennaway 	} else if (c->flags & CHAN_CLOSE_SENT) {
351ae1f160dSDag-Erling Smørgrav 		error("channel %d: already sent close", c->self);
352a04a10f8SKris Kennaway 	} else {
353a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_CHANNEL_CLOSE);
354a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
355a04a10f8SKris Kennaway 		packet_send();
356a04a10f8SKris Kennaway 		c->flags |= CHAN_CLOSE_SENT;
357a04a10f8SKris Kennaway 	}
358a04a10f8SKris Kennaway }
3591e8db6e2SBrian Feldman 
3601e8db6e2SBrian Feldman /* shared */
3611e8db6e2SBrian Feldman 
362ae1f160dSDag-Erling Smørgrav void
363ae1f160dSDag-Erling Smørgrav chan_rcvd_ieof(Channel *c)
364a04a10f8SKris Kennaway {
365ae1f160dSDag-Erling Smørgrav 	if (compat20)
366ae1f160dSDag-Erling Smørgrav 		chan_rcvd_eof2(c);
367ae1f160dSDag-Erling Smørgrav 	else
368ae1f160dSDag-Erling Smørgrav 		chan_rcvd_ieof1(c);
369ae1f160dSDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN &&
370545d5ecaSDag-Erling Smørgrav 	    buffer_len(&c->output) == 0 &&
371545d5ecaSDag-Erling Smørgrav 	    !CHANNEL_EFD_OUTPUT_ACTIVE(c))
372ae1f160dSDag-Erling Smørgrav 		chan_obuf_empty(c);
373ae1f160dSDag-Erling Smørgrav }
374ae1f160dSDag-Erling Smørgrav void
375ae1f160dSDag-Erling Smørgrav chan_rcvd_oclose(Channel *c)
376ae1f160dSDag-Erling Smørgrav {
377ae1f160dSDag-Erling Smørgrav 	if (compat20)
378ae1f160dSDag-Erling Smørgrav 		chan_rcvd_close2(c);
379ae1f160dSDag-Erling Smørgrav 	else
380ae1f160dSDag-Erling Smørgrav 		chan_rcvd_oclose1(c);
381ae1f160dSDag-Erling Smørgrav }
382ae1f160dSDag-Erling Smørgrav void
383ae1f160dSDag-Erling Smørgrav chan_write_failed(Channel *c)
384ae1f160dSDag-Erling Smørgrav {
385ae1f160dSDag-Erling Smørgrav 	if (compat20)
386ae1f160dSDag-Erling Smørgrav 		chan_write_failed2(c);
387ae1f160dSDag-Erling Smørgrav 	else
388ae1f160dSDag-Erling Smørgrav 		chan_write_failed1(c);
389ae1f160dSDag-Erling Smørgrav }
390ae1f160dSDag-Erling Smørgrav 
391ae1f160dSDag-Erling Smørgrav void
392ae1f160dSDag-Erling Smørgrav chan_mark_dead(Channel *c)
393ae1f160dSDag-Erling Smørgrav {
394ae1f160dSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_ZOMBIE;
395ae1f160dSDag-Erling Smørgrav }
396ae1f160dSDag-Erling Smørgrav 
397ae1f160dSDag-Erling Smørgrav int
398ae1f160dSDag-Erling Smørgrav chan_is_dead(Channel *c, int send)
399ae1f160dSDag-Erling Smørgrav {
400ae1f160dSDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_ZOMBIE) {
401ae1f160dSDag-Erling Smørgrav 		debug("channel %d: zombie", c->self);
402ae1f160dSDag-Erling Smørgrav 		return 1;
403ae1f160dSDag-Erling Smørgrav 	}
4041e8db6e2SBrian Feldman 	if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED)
4051e8db6e2SBrian Feldman 		return 0;
4061e8db6e2SBrian Feldman 	if (!compat20) {
4071e8db6e2SBrian Feldman 		debug("channel %d: is dead", c->self);
4081e8db6e2SBrian Feldman 		return 1;
4091e8db6e2SBrian Feldman 	}
410545d5ecaSDag-Erling Smørgrav 	if ((datafellows & SSH_BUG_EXTEOF) &&
411545d5ecaSDag-Erling Smørgrav 	    c->extended_usage == CHAN_EXTENDED_WRITE &&
412545d5ecaSDag-Erling Smørgrav 	    c->efd != -1 &&
413545d5ecaSDag-Erling Smørgrav 	    buffer_len(&c->extended) > 0) {
414545d5ecaSDag-Erling Smørgrav 		debug2("channel %d: active efd: %d len %d",
415545d5ecaSDag-Erling Smørgrav 		    c->self, c->efd, buffer_len(&c->extended));
416545d5ecaSDag-Erling Smørgrav 		return 0;
417545d5ecaSDag-Erling Smørgrav 	}
418a04a10f8SKris Kennaway 	if (!(c->flags & CHAN_CLOSE_SENT)) {
419ae1f160dSDag-Erling Smørgrav 		if (send) {
420a04a10f8SKris Kennaway 			chan_send_close2(c);
421ae1f160dSDag-Erling Smørgrav 		} else {
422ae1f160dSDag-Erling Smørgrav 			/* channel would be dead if we sent a close */
423ae1f160dSDag-Erling Smørgrav 			if (c->flags & CHAN_CLOSE_RCVD) {
424ae1f160dSDag-Erling Smørgrav 				debug("channel %d: almost dead",
425ae1f160dSDag-Erling Smørgrav 				    c->self);
426ae1f160dSDag-Erling Smørgrav 				return 1;
427ae1f160dSDag-Erling Smørgrav 			}
428ae1f160dSDag-Erling Smørgrav 		}
429a04a10f8SKris Kennaway 	}
430a04a10f8SKris Kennaway 	if ((c->flags & CHAN_CLOSE_SENT) &&
431a04a10f8SKris Kennaway 	    (c->flags & CHAN_CLOSE_RCVD)) {
4321e8db6e2SBrian Feldman 		debug("channel %d: is dead", c->self);
4331e8db6e2SBrian Feldman 		return 1;
434a04a10f8SKris Kennaway 	}
4351e8db6e2SBrian Feldman 	return 0;
436a04a10f8SKris Kennaway }
437a04a10f8SKris Kennaway 
438511b41d2SMark Murray /* helper */
439511b41d2SMark Murray static void
440511b41d2SMark Murray chan_shutdown_write(Channel *c)
441511b41d2SMark Murray {
442ae1f160dSDag-Erling Smørgrav 	buffer_clear(&c->output);
443a04a10f8SKris Kennaway 	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
444a04a10f8SKris Kennaway 		return;
445511b41d2SMark Murray 	/* shutdown failure is allowed if write failed already */
446a04a10f8SKris Kennaway 	debug("channel %d: close_write", c->self);
447a04a10f8SKris Kennaway 	if (c->sock != -1) {
448511b41d2SMark Murray 		if (shutdown(c->sock, SHUT_WR) < 0)
449ae1f160dSDag-Erling Smørgrav 			debug("channel %d: chan_shutdown_write: "
450ae1f160dSDag-Erling Smørgrav 			    "shutdown() failed for fd%d: %.100s",
451511b41d2SMark Murray 			    c->self, c->sock, strerror(errno));
452a04a10f8SKris Kennaway 	} else {
453ae1f160dSDag-Erling Smørgrav 		if (channel_close_fd(&c->wfd) < 0)
454ae1f160dSDag-Erling Smørgrav 			log("channel %d: chan_shutdown_write: "
455ae1f160dSDag-Erling Smørgrav 			    "close() failed for fd%d: %.100s",
456a04a10f8SKris Kennaway 			    c->self, c->wfd, strerror(errno));
457a04a10f8SKris Kennaway 	}
458511b41d2SMark Murray }
459511b41d2SMark Murray static void
460511b41d2SMark Murray chan_shutdown_read(Channel *c)
461511b41d2SMark Murray {
462a04a10f8SKris Kennaway 	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
463a04a10f8SKris Kennaway 		return;
464a04a10f8SKris Kennaway 	debug("channel %d: close_read", c->self);
465a04a10f8SKris Kennaway 	if (c->sock != -1) {
466511b41d2SMark Murray 		if (shutdown(c->sock, SHUT_RD) < 0)
467ae1f160dSDag-Erling Smørgrav 			error("channel %d: chan_shutdown_read: "
468ae1f160dSDag-Erling Smørgrav 			    "shutdown() failed for fd%d [i%d o%d]: %.100s",
469ae1f160dSDag-Erling Smørgrav 			    c->self, c->sock, c->istate, c->ostate,
470ae1f160dSDag-Erling Smørgrav 			    strerror(errno));
471a04a10f8SKris Kennaway 	} else {
472ae1f160dSDag-Erling Smørgrav 		if (channel_close_fd(&c->rfd) < 0)
473ae1f160dSDag-Erling Smørgrav 			log("channel %d: chan_shutdown_read: "
474ae1f160dSDag-Erling Smørgrav 			    "close() failed for fd%d: %.100s",
475a04a10f8SKris Kennaway 			    c->self, c->rfd, strerror(errno));
476511b41d2SMark Murray 	}
477511b41d2SMark Murray }
478