xref: /freebsd/crypto/openssh/nchan.c (revision a04a10f8915401e0ac20dce0ade6c5b6e1bb13da)
1511b41d2SMark Murray /*
2511b41d2SMark Murray  * Copyright (c) 1999 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  * 3. All advertising materials mentioning features or use of this software
13511b41d2SMark Murray  *    must display the following acknowledgement:
14511b41d2SMark Murray  *      This product includes software developed by Markus Friedl.
15511b41d2SMark Murray  * 4. The name of the author may not be used to endorse or promote products
16511b41d2SMark Murray  *    derived from this software without specific prior written permission.
17511b41d2SMark Murray  *
18511b41d2SMark Murray  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19511b41d2SMark Murray  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20511b41d2SMark Murray  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21511b41d2SMark Murray  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22511b41d2SMark Murray  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23511b41d2SMark Murray  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24511b41d2SMark Murray  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25511b41d2SMark Murray  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26511b41d2SMark Murray  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27511b41d2SMark Murray  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28511b41d2SMark Murray  */
29511b41d2SMark Murray 
30511b41d2SMark Murray #include "includes.h"
31a04a10f8SKris Kennaway RCSID("$Id: nchan.c,v 1.17 2000/05/08 17:44:54 markus Exp $");
32511b41d2SMark Murray 
33511b41d2SMark Murray #include "ssh.h"
34511b41d2SMark Murray 
35511b41d2SMark Murray #include "buffer.h"
36511b41d2SMark Murray #include "packet.h"
37511b41d2SMark Murray #include "channels.h"
38511b41d2SMark Murray #include "nchan.h"
39511b41d2SMark Murray 
40a04a10f8SKris Kennaway #include "ssh2.h"
41a04a10f8SKris Kennaway #include "compat.h"
42511b41d2SMark Murray 
43a04a10f8SKris Kennaway /* functions manipulating channel states */
44511b41d2SMark Murray /*
45511b41d2SMark Murray  * EVENTS update channel input/output states execute ACTIONS
46511b41d2SMark Murray  */
47511b41d2SMark Murray /* events concerning the INPUT from socket for channel (istate) */
48a04a10f8SKris Kennaway chan_event_fn *chan_rcvd_oclose			= NULL;
49a04a10f8SKris Kennaway chan_event_fn *chan_read_failed			= NULL;
50a04a10f8SKris Kennaway chan_event_fn *chan_ibuf_empty			= NULL;
51a04a10f8SKris Kennaway /* events concerning the OUTPUT from channel for socket (ostate) */
52a04a10f8SKris Kennaway chan_event_fn *chan_rcvd_ieof			= NULL;
53a04a10f8SKris Kennaway chan_event_fn *chan_write_failed		= NULL;
54a04a10f8SKris Kennaway chan_event_fn *chan_obuf_empty			= NULL;
55a04a10f8SKris Kennaway /*
56a04a10f8SKris Kennaway  * ACTIONS: should never update the channel states
57a04a10f8SKris Kennaway  */
58a04a10f8SKris Kennaway static void	chan_send_ieof1(Channel *c);
59a04a10f8SKris Kennaway static void	chan_send_oclose1(Channel *c);
60a04a10f8SKris Kennaway static void	chan_send_close2(Channel *c);
61a04a10f8SKris Kennaway static void	chan_send_eof2(Channel *c);
62a04a10f8SKris Kennaway 
63a04a10f8SKris Kennaway /* channel cleanup */
64a04a10f8SKris Kennaway chan_event_fn *chan_delete_if_full_closed	= NULL;
65a04a10f8SKris Kennaway 
66a04a10f8SKris Kennaway /* helper */
67a04a10f8SKris Kennaway static void	chan_shutdown_write(Channel *c);
68a04a10f8SKris Kennaway static void	chan_shutdown_read(Channel *c);
69a04a10f8SKris Kennaway 
70a04a10f8SKris Kennaway /*
71a04a10f8SKris Kennaway  * SSH1 specific implementation of event functions
72a04a10f8SKris Kennaway  */
73a04a10f8SKris Kennaway 
74a04a10f8SKris Kennaway static void
75a04a10f8SKris Kennaway chan_rcvd_oclose1(Channel *c)
76511b41d2SMark Murray {
77a04a10f8SKris Kennaway 	debug("channel %d: rcvd oclose", c->self);
78511b41d2SMark Murray 	switch (c->istate) {
79511b41d2SMark Murray 	case CHAN_INPUT_WAIT_OCLOSE:
80a04a10f8SKris Kennaway 		debug("channel %d: input wait_oclose -> closed", c->self);
81511b41d2SMark Murray 		c->istate = CHAN_INPUT_CLOSED;
82511b41d2SMark Murray 		break;
83511b41d2SMark Murray 	case CHAN_INPUT_OPEN:
84a04a10f8SKris Kennaway 		debug("channel %d: input open -> closed", c->self);
85511b41d2SMark Murray 		chan_shutdown_read(c);
86a04a10f8SKris Kennaway 		chan_send_ieof1(c);
87511b41d2SMark Murray 		c->istate = CHAN_INPUT_CLOSED;
88511b41d2SMark Murray 		break;
89511b41d2SMark Murray 	case CHAN_INPUT_WAIT_DRAIN:
90511b41d2SMark Murray 		/* both local read_failed and remote write_failed  */
91a04a10f8SKris Kennaway 		log("channel %d: input drain -> closed", c->self);
92a04a10f8SKris Kennaway 		chan_send_ieof1(c);
93511b41d2SMark Murray 		c->istate = CHAN_INPUT_CLOSED;
94511b41d2SMark Murray 		break;
95511b41d2SMark Murray 	default:
96a04a10f8SKris Kennaway 		error("channel %d: protocol error: chan_rcvd_oclose for istate %d",
97a04a10f8SKris Kennaway 		    c->self, c->istate);
98511b41d2SMark Murray 		return;
99511b41d2SMark Murray 	}
100511b41d2SMark Murray }
101a04a10f8SKris Kennaway static void
102a04a10f8SKris Kennaway chan_read_failed_12(Channel *c)
103511b41d2SMark Murray {
104a04a10f8SKris Kennaway 	debug("channel %d: read failed", c->self);
105511b41d2SMark Murray 	switch (c->istate) {
106511b41d2SMark Murray 	case CHAN_INPUT_OPEN:
107a04a10f8SKris Kennaway 		debug("channel %d: input open -> drain", c->self);
108511b41d2SMark Murray 		chan_shutdown_read(c);
109511b41d2SMark Murray 		c->istate = CHAN_INPUT_WAIT_DRAIN;
110a04a10f8SKris Kennaway 		if (buffer_len(&c->input) == 0) {
111a04a10f8SKris Kennaway 			debug("channel %d: input: no drain shortcut", c->self);
112a04a10f8SKris Kennaway 			chan_ibuf_empty(c);
113a04a10f8SKris Kennaway 		}
114511b41d2SMark Murray 		break;
115511b41d2SMark Murray 	default:
116a04a10f8SKris Kennaway 		error("channel %d: internal error: we do not read, but chan_read_failed for istate %d",
117511b41d2SMark Murray 		    c->self, c->istate);
118511b41d2SMark Murray 		break;
119511b41d2SMark Murray 	}
120511b41d2SMark Murray }
121a04a10f8SKris Kennaway static void
122a04a10f8SKris Kennaway chan_ibuf_empty1(Channel *c)
123511b41d2SMark Murray {
124a04a10f8SKris Kennaway 	debug("channel %d: ibuf empty", c->self);
125511b41d2SMark Murray 	if (buffer_len(&c->input)) {
126a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_ibuf_empty for non empty buffer",
127a04a10f8SKris Kennaway 		    c->self);
128511b41d2SMark Murray 		return;
129511b41d2SMark Murray 	}
130511b41d2SMark Murray 	switch (c->istate) {
131511b41d2SMark Murray 	case CHAN_INPUT_WAIT_DRAIN:
132a04a10f8SKris Kennaway 		debug("channel %d: input drain -> wait_oclose", c->self);
133a04a10f8SKris Kennaway 		chan_send_ieof1(c);
134511b41d2SMark Murray 		c->istate = CHAN_INPUT_WAIT_OCLOSE;
135511b41d2SMark Murray 		break;
136511b41d2SMark Murray 	default:
137a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_ibuf_empty for istate %d",
138a04a10f8SKris Kennaway 		    c->self, c->istate);
139511b41d2SMark Murray 		break;
140511b41d2SMark Murray 	}
141511b41d2SMark Murray }
142a04a10f8SKris Kennaway static void
143a04a10f8SKris Kennaway chan_rcvd_ieof1(Channel *c)
144511b41d2SMark Murray {
145a04a10f8SKris Kennaway 	debug("channel %d: rcvd ieof", c->self);
146a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
147a04a10f8SKris Kennaway 		debug("channel %d: non-open", c->self);
148a04a10f8SKris Kennaway 		if (c->istate == CHAN_INPUT_OPEN) {
149a04a10f8SKris Kennaway 			debug("channel %d: non-open: input open -> wait_oclose", c->self);
150a04a10f8SKris Kennaway 			chan_shutdown_read(c);
151a04a10f8SKris Kennaway 			chan_send_ieof1(c);
152a04a10f8SKris Kennaway 			c->istate = CHAN_INPUT_WAIT_OCLOSE;
153a04a10f8SKris Kennaway 		} else {
154a04a10f8SKris Kennaway 			error("channel %d: istate %d != open", c->self, c->istate);
155a04a10f8SKris Kennaway 		}
156a04a10f8SKris Kennaway 		if (c->ostate == CHAN_OUTPUT_OPEN) {
157a04a10f8SKris Kennaway 			debug("channel %d: non-open: output open -> closed", c->self);
158a04a10f8SKris Kennaway 			chan_send_oclose1(c);
159a04a10f8SKris Kennaway 			c->ostate = CHAN_OUTPUT_CLOSED;
160a04a10f8SKris Kennaway 		} else {
161a04a10f8SKris Kennaway 			error("channel %d: ostate %d != open", c->self, c->ostate);
162a04a10f8SKris Kennaway 		}
163a04a10f8SKris Kennaway 		return;
164a04a10f8SKris Kennaway 	}
165511b41d2SMark Murray 	switch (c->ostate) {
166511b41d2SMark Murray 	case CHAN_OUTPUT_OPEN:
167a04a10f8SKris Kennaway 		debug("channel %d: output open -> drain", c->self);
168511b41d2SMark Murray 		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
169511b41d2SMark Murray 		break;
170511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_IEOF:
171a04a10f8SKris Kennaway 		debug("channel %d: output wait_ieof -> closed", c->self);
172511b41d2SMark Murray 		c->ostate = CHAN_OUTPUT_CLOSED;
173511b41d2SMark Murray 		break;
174511b41d2SMark Murray 	default:
175a04a10f8SKris Kennaway 		error("channel %d: protocol error: chan_rcvd_ieof for ostate %d",
176a04a10f8SKris Kennaway 		    c->self, c->ostate);
177511b41d2SMark Murray 		break;
178511b41d2SMark Murray 	}
179511b41d2SMark Murray }
180a04a10f8SKris Kennaway static void
181a04a10f8SKris Kennaway chan_write_failed1(Channel *c)
182511b41d2SMark Murray {
183a04a10f8SKris Kennaway 	debug("channel %d: write failed", c->self);
184511b41d2SMark Murray 	switch (c->ostate) {
185511b41d2SMark Murray 	case CHAN_OUTPUT_OPEN:
186a04a10f8SKris Kennaway 		debug("channel %d: output open -> wait_ieof", c->self);
187a04a10f8SKris Kennaway 		chan_send_oclose1(c);
188511b41d2SMark Murray 		c->ostate = CHAN_OUTPUT_WAIT_IEOF;
189511b41d2SMark Murray 		break;
190511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_DRAIN:
191a04a10f8SKris Kennaway 		debug("channel %d: output wait_drain -> closed", c->self);
192a04a10f8SKris Kennaway 		chan_send_oclose1(c);
193511b41d2SMark Murray 		c->ostate = CHAN_OUTPUT_CLOSED;
194511b41d2SMark Murray 		break;
195511b41d2SMark Murray 	default:
196a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_write_failed for ostate %d",
197a04a10f8SKris Kennaway 		    c->self, c->ostate);
198511b41d2SMark Murray 		break;
199511b41d2SMark Murray 	}
200511b41d2SMark Murray }
201a04a10f8SKris Kennaway static void
202a04a10f8SKris Kennaway chan_obuf_empty1(Channel *c)
203511b41d2SMark Murray {
204a04a10f8SKris Kennaway 	debug("channel %d: obuf empty", c->self);
205511b41d2SMark Murray 	if (buffer_len(&c->output)) {
206a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_obuf_empty for non empty buffer",
207a04a10f8SKris Kennaway 		    c->self);
208511b41d2SMark Murray 		return;
209511b41d2SMark Murray 	}
210511b41d2SMark Murray 	switch (c->ostate) {
211511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_DRAIN:
212a04a10f8SKris Kennaway 		debug("channel %d: output drain -> closed", c->self);
213a04a10f8SKris Kennaway 		chan_send_oclose1(c);
214511b41d2SMark Murray 		c->ostate = CHAN_OUTPUT_CLOSED;
215511b41d2SMark Murray 		break;
216511b41d2SMark Murray 	default:
217a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_obuf_empty for ostate %d",
218a04a10f8SKris Kennaway 		    c->self, c->ostate);
219511b41d2SMark Murray 		break;
220511b41d2SMark Murray 	}
221511b41d2SMark Murray }
222511b41d2SMark Murray static void
223a04a10f8SKris Kennaway chan_send_ieof1(Channel *c)
224511b41d2SMark Murray {
225a04a10f8SKris Kennaway 	debug("channel %d: send ieof", c->self);
226511b41d2SMark Murray 	switch (c->istate) {
227511b41d2SMark Murray 	case CHAN_INPUT_OPEN:
228511b41d2SMark Murray 	case CHAN_INPUT_WAIT_DRAIN:
229511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_INPUT_EOF);
230511b41d2SMark Murray 		packet_put_int(c->remote_id);
231511b41d2SMark Murray 		packet_send();
232511b41d2SMark Murray 		break;
233511b41d2SMark Murray 	default:
234a04a10f8SKris Kennaway 		error("channel %d: internal error: cannot send ieof for istate %d",
235a04a10f8SKris Kennaway 		    c->self, c->istate);
236511b41d2SMark Murray 		break;
237511b41d2SMark Murray 	}
238511b41d2SMark Murray }
239511b41d2SMark Murray static void
240a04a10f8SKris Kennaway chan_send_oclose1(Channel *c)
241511b41d2SMark Murray {
242a04a10f8SKris Kennaway 	debug("channel %d: send oclose", c->self);
243511b41d2SMark Murray 	switch (c->ostate) {
244511b41d2SMark Murray 	case CHAN_OUTPUT_OPEN:
245511b41d2SMark Murray 	case CHAN_OUTPUT_WAIT_DRAIN:
246511b41d2SMark Murray 		chan_shutdown_write(c);
247511b41d2SMark Murray 		buffer_consume(&c->output, buffer_len(&c->output));
248511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE);
249511b41d2SMark Murray 		packet_put_int(c->remote_id);
250511b41d2SMark Murray 		packet_send();
251511b41d2SMark Murray 		break;
252511b41d2SMark Murray 	default:
253a04a10f8SKris Kennaway 		error("channel %d: internal error: cannot send oclose for ostate %d",
254a04a10f8SKris Kennaway 		     c->self, c->ostate);
255511b41d2SMark Murray 		break;
256511b41d2SMark Murray 	}
257511b41d2SMark Murray }
258a04a10f8SKris Kennaway static void
259a04a10f8SKris Kennaway chan_delete_if_full_closed1(Channel *c)
260a04a10f8SKris Kennaway {
261a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
262a04a10f8SKris Kennaway 		debug("channel %d: full closed", c->self);
263a04a10f8SKris Kennaway 		channel_free(c->self);
264a04a10f8SKris Kennaway 	}
265a04a10f8SKris Kennaway }
266a04a10f8SKris Kennaway 
267a04a10f8SKris Kennaway /*
268a04a10f8SKris Kennaway  * the same for SSH2
269a04a10f8SKris Kennaway  */
270a04a10f8SKris Kennaway static void
271a04a10f8SKris Kennaway chan_rcvd_oclose2(Channel *c)
272a04a10f8SKris Kennaway {
273a04a10f8SKris Kennaway 	debug("channel %d: rcvd close", c->self);
274a04a10f8SKris Kennaway 	if (c->flags & CHAN_CLOSE_RCVD)
275a04a10f8SKris Kennaway 		error("channel %d: protocol error: close rcvd twice", c->self);
276a04a10f8SKris Kennaway 	c->flags |= CHAN_CLOSE_RCVD;
277a04a10f8SKris Kennaway 	if (c->type == SSH_CHANNEL_LARVAL) {
278a04a10f8SKris Kennaway 		/* tear down larval channels immediately */
279a04a10f8SKris Kennaway 		c->ostate = CHAN_OUTPUT_CLOSED;
280a04a10f8SKris Kennaway 		c->istate = CHAN_INPUT_CLOSED;
281a04a10f8SKris Kennaway 		return;
282a04a10f8SKris Kennaway 	}
283a04a10f8SKris Kennaway 	switch (c->ostate) {
284a04a10f8SKris Kennaway 	case CHAN_OUTPUT_OPEN:
285a04a10f8SKris Kennaway 		/* wait until a data from the channel is consumed if a CLOSE is received */
286a04a10f8SKris Kennaway 		debug("channel %d: output open -> drain", c->self);
287a04a10f8SKris Kennaway 		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
288a04a10f8SKris Kennaway 		break;
289a04a10f8SKris Kennaway 	}
290a04a10f8SKris Kennaway 	switch (c->istate) {
291a04a10f8SKris Kennaway 	case CHAN_INPUT_OPEN:
292a04a10f8SKris Kennaway 		debug("channel %d: input open -> closed", c->self);
293a04a10f8SKris Kennaway 		chan_shutdown_read(c);
294a04a10f8SKris Kennaway 		break;
295a04a10f8SKris Kennaway 	case CHAN_INPUT_WAIT_DRAIN:
296a04a10f8SKris Kennaway 		debug("channel %d: input drain -> closed", c->self);
297a04a10f8SKris Kennaway 		chan_send_eof2(c);
298a04a10f8SKris Kennaway 		break;
299a04a10f8SKris Kennaway 	}
300a04a10f8SKris Kennaway 	c->istate = CHAN_INPUT_CLOSED;
301a04a10f8SKris Kennaway }
302a04a10f8SKris Kennaway static void
303a04a10f8SKris Kennaway chan_ibuf_empty2(Channel *c)
304a04a10f8SKris Kennaway {
305a04a10f8SKris Kennaway 	debug("channel %d: ibuf empty", c->self);
306a04a10f8SKris Kennaway 	if (buffer_len(&c->input)) {
307a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_ibuf_empty for non empty buffer",
308a04a10f8SKris Kennaway 		     c->self);
309a04a10f8SKris Kennaway 		return;
310a04a10f8SKris Kennaway 	}
311a04a10f8SKris Kennaway 	switch (c->istate) {
312a04a10f8SKris Kennaway 	case CHAN_INPUT_WAIT_DRAIN:
313a04a10f8SKris Kennaway 		debug("channel %d: input drain -> closed", c->self);
314a04a10f8SKris Kennaway 		if (!(c->flags & CHAN_CLOSE_SENT))
315a04a10f8SKris Kennaway 			chan_send_eof2(c);
316a04a10f8SKris Kennaway 		c->istate = CHAN_INPUT_CLOSED;
317a04a10f8SKris Kennaway 		break;
318a04a10f8SKris Kennaway 	default:
319a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_ibuf_empty for istate %d",
320a04a10f8SKris Kennaway 		     c->self, c->istate);
321a04a10f8SKris Kennaway 		break;
322a04a10f8SKris Kennaway 	}
323a04a10f8SKris Kennaway }
324a04a10f8SKris Kennaway static void
325a04a10f8SKris Kennaway chan_rcvd_ieof2(Channel *c)
326a04a10f8SKris Kennaway {
327a04a10f8SKris Kennaway 	debug("channel %d: rcvd eof", c->self);
328a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN) {
329a04a10f8SKris Kennaway 		debug("channel %d: output open -> drain", c->self);
330a04a10f8SKris Kennaway 		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
331a04a10f8SKris Kennaway 	}
332a04a10f8SKris Kennaway }
333a04a10f8SKris Kennaway static void
334a04a10f8SKris Kennaway chan_write_failed2(Channel *c)
335a04a10f8SKris Kennaway {
336a04a10f8SKris Kennaway 	debug("channel %d: write failed", c->self);
337a04a10f8SKris Kennaway 	switch (c->ostate) {
338a04a10f8SKris Kennaway 	case CHAN_OUTPUT_OPEN:
339a04a10f8SKris Kennaway 		debug("channel %d: output open -> closed", c->self);
340a04a10f8SKris Kennaway 		chan_shutdown_write(c); /* ?? */
341a04a10f8SKris Kennaway 		c->ostate = CHAN_OUTPUT_CLOSED;
342a04a10f8SKris Kennaway 		break;
343a04a10f8SKris Kennaway 	case CHAN_OUTPUT_WAIT_DRAIN:
344a04a10f8SKris Kennaway 		debug("channel %d: output drain -> closed", c->self);
345a04a10f8SKris Kennaway 		chan_shutdown_write(c);
346a04a10f8SKris Kennaway 		c->ostate = CHAN_OUTPUT_CLOSED;
347a04a10f8SKris Kennaway 		break;
348a04a10f8SKris Kennaway 	default:
349a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_write_failed for ostate %d",
350a04a10f8SKris Kennaway 		    c->self, c->ostate);
351a04a10f8SKris Kennaway 		break;
352a04a10f8SKris Kennaway 	}
353a04a10f8SKris Kennaway }
354a04a10f8SKris Kennaway static void
355a04a10f8SKris Kennaway chan_obuf_empty2(Channel *c)
356a04a10f8SKris Kennaway {
357a04a10f8SKris Kennaway 	debug("channel %d: obuf empty", c->self);
358a04a10f8SKris Kennaway 	if (buffer_len(&c->output)) {
359a04a10f8SKris Kennaway 		error("internal error: chan_obuf_empty %d for non empty buffer",
360a04a10f8SKris Kennaway 		    c->self);
361a04a10f8SKris Kennaway 		return;
362a04a10f8SKris Kennaway 	}
363a04a10f8SKris Kennaway 	switch (c->ostate) {
364a04a10f8SKris Kennaway 	case CHAN_OUTPUT_WAIT_DRAIN:
365a04a10f8SKris Kennaway 		debug("channel %d: output drain -> closed", c->self);
366a04a10f8SKris Kennaway 		chan_shutdown_write(c);
367a04a10f8SKris Kennaway 		c->ostate = CHAN_OUTPUT_CLOSED;
368a04a10f8SKris Kennaway 		break;
369a04a10f8SKris Kennaway 	default:
370a04a10f8SKris Kennaway 		error("channel %d: internal error: chan_obuf_empty for ostate %d",
371a04a10f8SKris Kennaway 		    c->self, c->ostate);
372a04a10f8SKris Kennaway 		break;
373a04a10f8SKris Kennaway 	}
374a04a10f8SKris Kennaway }
375a04a10f8SKris Kennaway static void
376a04a10f8SKris Kennaway chan_send_eof2(Channel *c)
377a04a10f8SKris Kennaway {
378a04a10f8SKris Kennaway 	debug("channel %d: send eof", c->self);
379a04a10f8SKris Kennaway 	switch (c->istate) {
380a04a10f8SKris Kennaway 	case CHAN_INPUT_WAIT_DRAIN:
381a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_CHANNEL_EOF);
382a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
383a04a10f8SKris Kennaway 		packet_send();
384a04a10f8SKris Kennaway 		break;
385a04a10f8SKris Kennaway 	default:
386a04a10f8SKris Kennaway 		error("channel %d: internal error: cannot send eof for istate %d",
387a04a10f8SKris Kennaway 		    c->self, c->istate);
388a04a10f8SKris Kennaway 		break;
389a04a10f8SKris Kennaway 	}
390a04a10f8SKris Kennaway }
391a04a10f8SKris Kennaway static void
392a04a10f8SKris Kennaway chan_send_close2(Channel *c)
393a04a10f8SKris Kennaway {
394a04a10f8SKris Kennaway 	debug("channel %d: send close", c->self);
395a04a10f8SKris Kennaway 	if (c->ostate != CHAN_OUTPUT_CLOSED ||
396a04a10f8SKris Kennaway 	    c->istate != CHAN_INPUT_CLOSED) {
397a04a10f8SKris Kennaway 		error("channel %d: internal error: cannot send close for istate/ostate %d/%d",
398a04a10f8SKris Kennaway 		    c->self, c->istate, c->ostate);
399a04a10f8SKris Kennaway 	} else if (c->flags & CHAN_CLOSE_SENT) {
400a04a10f8SKris Kennaway 		error("channel %d: internal error: already sent close", c->self);
401a04a10f8SKris Kennaway 	} else {
402a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_CHANNEL_CLOSE);
403a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
404a04a10f8SKris Kennaway 		packet_send();
405a04a10f8SKris Kennaway 		c->flags |= CHAN_CLOSE_SENT;
406a04a10f8SKris Kennaway 	}
407a04a10f8SKris Kennaway }
408a04a10f8SKris Kennaway static void
409a04a10f8SKris Kennaway chan_delete_if_full_closed2(Channel *c)
410a04a10f8SKris Kennaway {
411a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
412a04a10f8SKris Kennaway 		if (!(c->flags & CHAN_CLOSE_SENT)) {
413a04a10f8SKris Kennaway 			chan_send_close2(c);
414a04a10f8SKris Kennaway 		}
415a04a10f8SKris Kennaway 		if ((c->flags & CHAN_CLOSE_SENT) &&
416a04a10f8SKris Kennaway 		    (c->flags & CHAN_CLOSE_RCVD)) {
417a04a10f8SKris Kennaway 			debug("channel %d: full closed2", c->self);
418a04a10f8SKris Kennaway 			channel_free(c->self);
419a04a10f8SKris Kennaway 		}
420a04a10f8SKris Kennaway 	}
421a04a10f8SKris Kennaway }
422a04a10f8SKris Kennaway 
423a04a10f8SKris Kennaway /* shared */
424a04a10f8SKris Kennaway void
425a04a10f8SKris Kennaway chan_init_iostates(Channel *c)
426a04a10f8SKris Kennaway {
427a04a10f8SKris Kennaway 	c->ostate = CHAN_OUTPUT_OPEN;
428a04a10f8SKris Kennaway 	c->istate = CHAN_INPUT_OPEN;
429a04a10f8SKris Kennaway 	c->flags = 0;
430a04a10f8SKris Kennaway }
431a04a10f8SKris Kennaway 
432a04a10f8SKris Kennaway /* init */
433a04a10f8SKris Kennaway void
434a04a10f8SKris Kennaway chan_init(void)
435a04a10f8SKris Kennaway {
436a04a10f8SKris Kennaway 	if (compat20) {
437a04a10f8SKris Kennaway 		chan_rcvd_oclose		= chan_rcvd_oclose2;
438a04a10f8SKris Kennaway 		chan_read_failed		= chan_read_failed_12;
439a04a10f8SKris Kennaway 		chan_ibuf_empty			= chan_ibuf_empty2;
440a04a10f8SKris Kennaway 
441a04a10f8SKris Kennaway 		chan_rcvd_ieof			= chan_rcvd_ieof2;
442a04a10f8SKris Kennaway 		chan_write_failed		= chan_write_failed2;
443a04a10f8SKris Kennaway 		chan_obuf_empty			= chan_obuf_empty2;
444a04a10f8SKris Kennaway 
445a04a10f8SKris Kennaway 		chan_delete_if_full_closed	= chan_delete_if_full_closed2;
446a04a10f8SKris Kennaway 	} else {
447a04a10f8SKris Kennaway 		chan_rcvd_oclose		= chan_rcvd_oclose1;
448a04a10f8SKris Kennaway 		chan_read_failed		= chan_read_failed_12;
449a04a10f8SKris Kennaway 		chan_ibuf_empty			= chan_ibuf_empty1;
450a04a10f8SKris Kennaway 
451a04a10f8SKris Kennaway 		chan_rcvd_ieof			= chan_rcvd_ieof1;
452a04a10f8SKris Kennaway 		chan_write_failed		= chan_write_failed1;
453a04a10f8SKris Kennaway 		chan_obuf_empty			= chan_obuf_empty1;
454a04a10f8SKris Kennaway 
455a04a10f8SKris Kennaway 		chan_delete_if_full_closed	= chan_delete_if_full_closed1;
456a04a10f8SKris Kennaway 	}
457a04a10f8SKris Kennaway }
458511b41d2SMark Murray 
459511b41d2SMark Murray /* helper */
460511b41d2SMark Murray static void
461511b41d2SMark Murray chan_shutdown_write(Channel *c)
462511b41d2SMark Murray {
463a04a10f8SKris Kennaway 	buffer_consume(&c->output, buffer_len(&c->output));
464a04a10f8SKris Kennaway 	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
465a04a10f8SKris Kennaway 		return;
466511b41d2SMark Murray 	/* shutdown failure is allowed if write failed already */
467a04a10f8SKris Kennaway 	debug("channel %d: close_write", c->self);
468a04a10f8SKris Kennaway 	if (c->sock != -1) {
469511b41d2SMark Murray 		if (shutdown(c->sock, SHUT_WR) < 0)
470a04a10f8SKris Kennaway 			debug("channel %d: chan_shutdown_write: shutdown() failed for fd%d: %.100s",
471511b41d2SMark Murray 			    c->self, c->sock, strerror(errno));
472a04a10f8SKris Kennaway 	} else {
473a04a10f8SKris Kennaway 		if (close(c->wfd) < 0)
474a04a10f8SKris Kennaway 			log("channel %d: chan_shutdown_write: close() failed for fd%d: %.100s",
475a04a10f8SKris Kennaway 			    c->self, c->wfd, strerror(errno));
476a04a10f8SKris Kennaway 		c->wfd = -1;
477a04a10f8SKris Kennaway 	}
478511b41d2SMark Murray }
479511b41d2SMark Murray static void
480511b41d2SMark Murray chan_shutdown_read(Channel *c)
481511b41d2SMark Murray {
482a04a10f8SKris Kennaway 	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
483a04a10f8SKris Kennaway 		return;
484a04a10f8SKris Kennaway 	debug("channel %d: close_read", c->self);
485a04a10f8SKris Kennaway 	if (c->sock != -1) {
486511b41d2SMark Murray 		if (shutdown(c->sock, SHUT_RD) < 0)
487a04a10f8SKris Kennaway 			error("channel %d: chan_shutdown_read: shutdown() failed for fd%d [i%d o%d]: %.100s",
488511b41d2SMark Murray 			    c->self, c->sock, c->istate, c->ostate, strerror(errno));
489a04a10f8SKris Kennaway 	} else {
490a04a10f8SKris Kennaway 		if (close(c->rfd) < 0)
491a04a10f8SKris Kennaway 			log("channel %d: chan_shutdown_read: close() failed for fd%d: %.100s",
492a04a10f8SKris Kennaway 			    c->self, c->rfd, strerror(errno));
493a04a10f8SKris Kennaway 		c->rfd = -1;
494511b41d2SMark Murray 	}
495511b41d2SMark Murray }
496