nchan.c revision 60573
1/*
2 * Copyright (c) 1999 Markus Friedl.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 *    must display the following acknowledgement:
14 *      This product includes software developed by Markus Friedl.
15 * 4. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "includes.h"
31RCSID("$Id: nchan.c,v 1.17 2000/05/08 17:44:54 markus Exp $");
32
33#include "ssh.h"
34
35#include "buffer.h"
36#include "packet.h"
37#include "channels.h"
38#include "nchan.h"
39
40#include "ssh2.h"
41#include "compat.h"
42
43/* functions manipulating channel states */
44/*
45 * EVENTS update channel input/output states execute ACTIONS
46 */
47/* events concerning the INPUT from socket for channel (istate) */
48chan_event_fn *chan_rcvd_oclose			= NULL;
49chan_event_fn *chan_read_failed			= NULL;
50chan_event_fn *chan_ibuf_empty			= NULL;
51/* events concerning the OUTPUT from channel for socket (ostate) */
52chan_event_fn *chan_rcvd_ieof			= NULL;
53chan_event_fn *chan_write_failed		= NULL;
54chan_event_fn *chan_obuf_empty			= NULL;
55/*
56 * ACTIONS: should never update the channel states
57 */
58static void	chan_send_ieof1(Channel *c);
59static void	chan_send_oclose1(Channel *c);
60static void	chan_send_close2(Channel *c);
61static void	chan_send_eof2(Channel *c);
62
63/* channel cleanup */
64chan_event_fn *chan_delete_if_full_closed	= NULL;
65
66/* helper */
67static void	chan_shutdown_write(Channel *c);
68static void	chan_shutdown_read(Channel *c);
69
70/*
71 * SSH1 specific implementation of event functions
72 */
73
74static void
75chan_rcvd_oclose1(Channel *c)
76{
77	debug("channel %d: rcvd oclose", c->self);
78	switch (c->istate) {
79	case CHAN_INPUT_WAIT_OCLOSE:
80		debug("channel %d: input wait_oclose -> closed", c->self);
81		c->istate = CHAN_INPUT_CLOSED;
82		break;
83	case CHAN_INPUT_OPEN:
84		debug("channel %d: input open -> closed", c->self);
85		chan_shutdown_read(c);
86		chan_send_ieof1(c);
87		c->istate = CHAN_INPUT_CLOSED;
88		break;
89	case CHAN_INPUT_WAIT_DRAIN:
90		/* both local read_failed and remote write_failed  */
91		log("channel %d: input drain -> closed", c->self);
92		chan_send_ieof1(c);
93		c->istate = CHAN_INPUT_CLOSED;
94		break;
95	default:
96		error("channel %d: protocol error: chan_rcvd_oclose for istate %d",
97		    c->self, c->istate);
98		return;
99	}
100}
101static void
102chan_read_failed_12(Channel *c)
103{
104	debug("channel %d: read failed", c->self);
105	switch (c->istate) {
106	case CHAN_INPUT_OPEN:
107		debug("channel %d: input open -> drain", c->self);
108		chan_shutdown_read(c);
109		c->istate = CHAN_INPUT_WAIT_DRAIN;
110		if (buffer_len(&c->input) == 0) {
111			debug("channel %d: input: no drain shortcut", c->self);
112			chan_ibuf_empty(c);
113		}
114		break;
115	default:
116		error("channel %d: internal error: we do not read, but chan_read_failed for istate %d",
117		    c->self, c->istate);
118		break;
119	}
120}
121static void
122chan_ibuf_empty1(Channel *c)
123{
124	debug("channel %d: ibuf empty", c->self);
125	if (buffer_len(&c->input)) {
126		error("channel %d: internal error: chan_ibuf_empty for non empty buffer",
127		    c->self);
128		return;
129	}
130	switch (c->istate) {
131	case CHAN_INPUT_WAIT_DRAIN:
132		debug("channel %d: input drain -> wait_oclose", c->self);
133		chan_send_ieof1(c);
134		c->istate = CHAN_INPUT_WAIT_OCLOSE;
135		break;
136	default:
137		error("channel %d: internal error: chan_ibuf_empty for istate %d",
138		    c->self, c->istate);
139		break;
140	}
141}
142static void
143chan_rcvd_ieof1(Channel *c)
144{
145	debug("channel %d: rcvd ieof", c->self);
146	if (c->type != SSH_CHANNEL_OPEN) {
147		debug("channel %d: non-open", c->self);
148		if (c->istate == CHAN_INPUT_OPEN) {
149			debug("channel %d: non-open: input open -> wait_oclose", c->self);
150			chan_shutdown_read(c);
151			chan_send_ieof1(c);
152			c->istate = CHAN_INPUT_WAIT_OCLOSE;
153		} else {
154			error("channel %d: istate %d != open", c->self, c->istate);
155		}
156		if (c->ostate == CHAN_OUTPUT_OPEN) {
157			debug("channel %d: non-open: output open -> closed", c->self);
158			chan_send_oclose1(c);
159			c->ostate = CHAN_OUTPUT_CLOSED;
160		} else {
161			error("channel %d: ostate %d != open", c->self, c->ostate);
162		}
163		return;
164	}
165	switch (c->ostate) {
166	case CHAN_OUTPUT_OPEN:
167		debug("channel %d: output open -> drain", c->self);
168		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
169		break;
170	case CHAN_OUTPUT_WAIT_IEOF:
171		debug("channel %d: output wait_ieof -> closed", c->self);
172		c->ostate = CHAN_OUTPUT_CLOSED;
173		break;
174	default:
175		error("channel %d: protocol error: chan_rcvd_ieof for ostate %d",
176		    c->self, c->ostate);
177		break;
178	}
179}
180static void
181chan_write_failed1(Channel *c)
182{
183	debug("channel %d: write failed", c->self);
184	switch (c->ostate) {
185	case CHAN_OUTPUT_OPEN:
186		debug("channel %d: output open -> wait_ieof", c->self);
187		chan_send_oclose1(c);
188		c->ostate = CHAN_OUTPUT_WAIT_IEOF;
189		break;
190	case CHAN_OUTPUT_WAIT_DRAIN:
191		debug("channel %d: output wait_drain -> closed", c->self);
192		chan_send_oclose1(c);
193		c->ostate = CHAN_OUTPUT_CLOSED;
194		break;
195	default:
196		error("channel %d: internal error: chan_write_failed for ostate %d",
197		    c->self, c->ostate);
198		break;
199	}
200}
201static void
202chan_obuf_empty1(Channel *c)
203{
204	debug("channel %d: obuf empty", c->self);
205	if (buffer_len(&c->output)) {
206		error("channel %d: internal error: chan_obuf_empty for non empty buffer",
207		    c->self);
208		return;
209	}
210	switch (c->ostate) {
211	case CHAN_OUTPUT_WAIT_DRAIN:
212		debug("channel %d: output drain -> closed", c->self);
213		chan_send_oclose1(c);
214		c->ostate = CHAN_OUTPUT_CLOSED;
215		break;
216	default:
217		error("channel %d: internal error: chan_obuf_empty for ostate %d",
218		    c->self, c->ostate);
219		break;
220	}
221}
222static void
223chan_send_ieof1(Channel *c)
224{
225	debug("channel %d: send ieof", c->self);
226	switch (c->istate) {
227	case CHAN_INPUT_OPEN:
228	case CHAN_INPUT_WAIT_DRAIN:
229		packet_start(SSH_MSG_CHANNEL_INPUT_EOF);
230		packet_put_int(c->remote_id);
231		packet_send();
232		break;
233	default:
234		error("channel %d: internal error: cannot send ieof for istate %d",
235		    c->self, c->istate);
236		break;
237	}
238}
239static void
240chan_send_oclose1(Channel *c)
241{
242	debug("channel %d: send oclose", c->self);
243	switch (c->ostate) {
244	case CHAN_OUTPUT_OPEN:
245	case CHAN_OUTPUT_WAIT_DRAIN:
246		chan_shutdown_write(c);
247		buffer_consume(&c->output, buffer_len(&c->output));
248		packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE);
249		packet_put_int(c->remote_id);
250		packet_send();
251		break;
252	default:
253		error("channel %d: internal error: cannot send oclose for ostate %d",
254		     c->self, c->ostate);
255		break;
256	}
257}
258static void
259chan_delete_if_full_closed1(Channel *c)
260{
261	if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
262		debug("channel %d: full closed", c->self);
263		channel_free(c->self);
264	}
265}
266
267/*
268 * the same for SSH2
269 */
270static void
271chan_rcvd_oclose2(Channel *c)
272{
273	debug("channel %d: rcvd close", c->self);
274	if (c->flags & CHAN_CLOSE_RCVD)
275		error("channel %d: protocol error: close rcvd twice", c->self);
276	c->flags |= CHAN_CLOSE_RCVD;
277	if (c->type == SSH_CHANNEL_LARVAL) {
278		/* tear down larval channels immediately */
279		c->ostate = CHAN_OUTPUT_CLOSED;
280		c->istate = CHAN_INPUT_CLOSED;
281		return;
282	}
283	switch (c->ostate) {
284	case CHAN_OUTPUT_OPEN:
285		/* wait until a data from the channel is consumed if a CLOSE is received */
286		debug("channel %d: output open -> drain", c->self);
287		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
288		break;
289	}
290	switch (c->istate) {
291	case CHAN_INPUT_OPEN:
292		debug("channel %d: input open -> closed", c->self);
293		chan_shutdown_read(c);
294		break;
295	case CHAN_INPUT_WAIT_DRAIN:
296		debug("channel %d: input drain -> closed", c->self);
297		chan_send_eof2(c);
298		break;
299	}
300	c->istate = CHAN_INPUT_CLOSED;
301}
302static void
303chan_ibuf_empty2(Channel *c)
304{
305	debug("channel %d: ibuf empty", c->self);
306	if (buffer_len(&c->input)) {
307		error("channel %d: internal error: chan_ibuf_empty for non empty buffer",
308		     c->self);
309		return;
310	}
311	switch (c->istate) {
312	case CHAN_INPUT_WAIT_DRAIN:
313		debug("channel %d: input drain -> closed", c->self);
314		if (!(c->flags & CHAN_CLOSE_SENT))
315			chan_send_eof2(c);
316		c->istate = CHAN_INPUT_CLOSED;
317		break;
318	default:
319		error("channel %d: internal error: chan_ibuf_empty for istate %d",
320		     c->self, c->istate);
321		break;
322	}
323}
324static void
325chan_rcvd_ieof2(Channel *c)
326{
327	debug("channel %d: rcvd eof", c->self);
328	if (c->ostate == CHAN_OUTPUT_OPEN) {
329		debug("channel %d: output open -> drain", c->self);
330		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
331	}
332}
333static void
334chan_write_failed2(Channel *c)
335{
336	debug("channel %d: write failed", c->self);
337	switch (c->ostate) {
338	case CHAN_OUTPUT_OPEN:
339		debug("channel %d: output open -> closed", c->self);
340		chan_shutdown_write(c); /* ?? */
341		c->ostate = CHAN_OUTPUT_CLOSED;
342		break;
343	case CHAN_OUTPUT_WAIT_DRAIN:
344		debug("channel %d: output drain -> closed", c->self);
345		chan_shutdown_write(c);
346		c->ostate = CHAN_OUTPUT_CLOSED;
347		break;
348	default:
349		error("channel %d: internal error: chan_write_failed for ostate %d",
350		    c->self, c->ostate);
351		break;
352	}
353}
354static void
355chan_obuf_empty2(Channel *c)
356{
357	debug("channel %d: obuf empty", c->self);
358	if (buffer_len(&c->output)) {
359		error("internal error: chan_obuf_empty %d for non empty buffer",
360		    c->self);
361		return;
362	}
363	switch (c->ostate) {
364	case CHAN_OUTPUT_WAIT_DRAIN:
365		debug("channel %d: output drain -> closed", c->self);
366		chan_shutdown_write(c);
367		c->ostate = CHAN_OUTPUT_CLOSED;
368		break;
369	default:
370		error("channel %d: internal error: chan_obuf_empty for ostate %d",
371		    c->self, c->ostate);
372		break;
373	}
374}
375static void
376chan_send_eof2(Channel *c)
377{
378	debug("channel %d: send eof", c->self);
379	switch (c->istate) {
380	case CHAN_INPUT_WAIT_DRAIN:
381		packet_start(SSH2_MSG_CHANNEL_EOF);
382		packet_put_int(c->remote_id);
383		packet_send();
384		break;
385	default:
386		error("channel %d: internal error: cannot send eof for istate %d",
387		    c->self, c->istate);
388		break;
389	}
390}
391static void
392chan_send_close2(Channel *c)
393{
394	debug("channel %d: send close", c->self);
395	if (c->ostate != CHAN_OUTPUT_CLOSED ||
396	    c->istate != CHAN_INPUT_CLOSED) {
397		error("channel %d: internal error: cannot send close for istate/ostate %d/%d",
398		    c->self, c->istate, c->ostate);
399	} else if (c->flags & CHAN_CLOSE_SENT) {
400		error("channel %d: internal error: already sent close", c->self);
401	} else {
402		packet_start(SSH2_MSG_CHANNEL_CLOSE);
403		packet_put_int(c->remote_id);
404		packet_send();
405		c->flags |= CHAN_CLOSE_SENT;
406	}
407}
408static void
409chan_delete_if_full_closed2(Channel *c)
410{
411	if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
412		if (!(c->flags & CHAN_CLOSE_SENT)) {
413			chan_send_close2(c);
414		}
415		if ((c->flags & CHAN_CLOSE_SENT) &&
416		    (c->flags & CHAN_CLOSE_RCVD)) {
417			debug("channel %d: full closed2", c->self);
418			channel_free(c->self);
419		}
420	}
421}
422
423/* shared */
424void
425chan_init_iostates(Channel *c)
426{
427	c->ostate = CHAN_OUTPUT_OPEN;
428	c->istate = CHAN_INPUT_OPEN;
429	c->flags = 0;
430}
431
432/* init */
433void
434chan_init(void)
435{
436	if (compat20) {
437		chan_rcvd_oclose		= chan_rcvd_oclose2;
438		chan_read_failed		= chan_read_failed_12;
439		chan_ibuf_empty			= chan_ibuf_empty2;
440
441		chan_rcvd_ieof			= chan_rcvd_ieof2;
442		chan_write_failed		= chan_write_failed2;
443		chan_obuf_empty			= chan_obuf_empty2;
444
445		chan_delete_if_full_closed	= chan_delete_if_full_closed2;
446	} else {
447		chan_rcvd_oclose		= chan_rcvd_oclose1;
448		chan_read_failed		= chan_read_failed_12;
449		chan_ibuf_empty			= chan_ibuf_empty1;
450
451		chan_rcvd_ieof			= chan_rcvd_ieof1;
452		chan_write_failed		= chan_write_failed1;
453		chan_obuf_empty			= chan_obuf_empty1;
454
455		chan_delete_if_full_closed	= chan_delete_if_full_closed1;
456	}
457}
458
459/* helper */
460static void
461chan_shutdown_write(Channel *c)
462{
463	buffer_consume(&c->output, buffer_len(&c->output));
464	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
465		return;
466	/* shutdown failure is allowed if write failed already */
467	debug("channel %d: close_write", c->self);
468	if (c->sock != -1) {
469		if (shutdown(c->sock, SHUT_WR) < 0)
470			debug("channel %d: chan_shutdown_write: shutdown() failed for fd%d: %.100s",
471			    c->self, c->sock, strerror(errno));
472	} else {
473		if (close(c->wfd) < 0)
474			log("channel %d: chan_shutdown_write: close() failed for fd%d: %.100s",
475			    c->self, c->wfd, strerror(errno));
476		c->wfd = -1;
477	}
478}
479static void
480chan_shutdown_read(Channel *c)
481{
482	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
483		return;
484	debug("channel %d: close_read", c->self);
485	if (c->sock != -1) {
486		if (shutdown(c->sock, SHUT_RD) < 0)
487			error("channel %d: chan_shutdown_read: shutdown() failed for fd%d [i%d o%d]: %.100s",
488			    c->self, c->sock, c->istate, c->ostate, strerror(errno));
489	} else {
490		if (close(c->rfd) < 0)
491			log("channel %d: chan_shutdown_read: close() failed for fd%d: %.100s",
492			    c->self, c->rfd, strerror(errno));
493		c->rfd = -1;
494	}
495}
496