nchan.c revision 57429
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.10 2000/01/10 10:15:28 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
40static void chan_send_ieof(Channel *c);
41static void chan_send_oclose(Channel *c);
42static void chan_shutdown_write(Channel *c);
43static void chan_shutdown_read(Channel *c);
44static void chan_delete_if_full_closed(Channel *c);
45
46/*
47 * EVENTS update channel input/output states execute ACTIONS
48 */
49
50/* events concerning the INPUT from socket for channel (istate) */
51void
52chan_rcvd_oclose(Channel *c)
53{
54	switch (c->istate) {
55	case CHAN_INPUT_WAIT_OCLOSE:
56		debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self);
57		c->istate = CHAN_INPUT_CLOSED;
58		break;
59	case CHAN_INPUT_OPEN:
60		debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
61		chan_shutdown_read(c);
62		chan_send_ieof(c);
63		c->istate = CHAN_INPUT_CLOSED;
64		break;
65	case CHAN_INPUT_WAIT_DRAIN:
66		/* both local read_failed and remote write_failed  */
67		log("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
68		debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
69		chan_send_ieof(c);
70		c->istate = CHAN_INPUT_CLOSED;
71		break;
72	default:
73		error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate);
74		return;
75	}
76	chan_delete_if_full_closed(c);
77}
78void
79chan_read_failed(Channel *c)
80{
81	switch (c->istate) {
82	case CHAN_INPUT_OPEN:
83		debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self);
84		chan_shutdown_read(c);
85		c->istate = CHAN_INPUT_WAIT_DRAIN;
86		break;
87	default:
88		error("internal error: we do not read, but chan_read_failed %d for istate %d",
89		      c->self, c->istate);
90		break;
91	}
92}
93void
94chan_ibuf_empty(Channel *c)
95{
96	if (buffer_len(&c->input)) {
97		error("internal error: chan_ibuf_empty %d for non empty buffer", c->self);
98		return;
99	}
100	switch (c->istate) {
101	case CHAN_INPUT_WAIT_DRAIN:
102		debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self);
103		chan_send_ieof(c);
104		c->istate = CHAN_INPUT_WAIT_OCLOSE;
105		break;
106	default:
107		error("internal error: chan_ibuf_empty %d for istate %d", c->self, c->istate);
108		break;
109	}
110}
111
112/* events concerning the OUTPUT from channel for socket (ostate) */
113void
114chan_rcvd_ieof(Channel *c)
115{
116	switch (c->ostate) {
117	case CHAN_OUTPUT_OPEN:
118		debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self);
119		c->ostate = CHAN_OUTPUT_WAIT_DRAIN;
120		break;
121	case CHAN_OUTPUT_WAIT_IEOF:
122		debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self);
123		c->ostate = CHAN_OUTPUT_CLOSED;
124		chan_delete_if_full_closed(c);
125		break;
126	default:
127		error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate);
128		break;
129	}
130}
131void
132chan_write_failed(Channel *c)
133{
134	switch (c->ostate) {
135	case CHAN_OUTPUT_OPEN:
136		debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self);
137		chan_send_oclose(c);
138		c->ostate = CHAN_OUTPUT_WAIT_IEOF;
139		break;
140	case CHAN_OUTPUT_WAIT_DRAIN:
141		debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self);
142		chan_send_oclose(c);
143		c->ostate = CHAN_OUTPUT_CLOSED;
144		chan_delete_if_full_closed(c);
145		break;
146	default:
147		error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate);
148		break;
149	}
150}
151void
152chan_obuf_empty(Channel *c)
153{
154	if (buffer_len(&c->output)) {
155		debug("internal error: chan_obuf_empty %d for non empty buffer", c->self);
156		return;
157	}
158	switch (c->ostate) {
159	case CHAN_OUTPUT_WAIT_DRAIN:
160		debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self);
161		chan_send_oclose(c);
162		c->ostate = CHAN_OUTPUT_CLOSED;
163		chan_delete_if_full_closed(c);
164		break;
165	default:
166		error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate);
167		break;
168	}
169}
170
171/*
172 * ACTIONS: should never update the channel states: c->istate or c->ostate
173 */
174static void
175chan_send_ieof(Channel *c)
176{
177	switch (c->istate) {
178	case CHAN_INPUT_OPEN:
179	case CHAN_INPUT_WAIT_DRAIN:
180		packet_start(SSH_MSG_CHANNEL_INPUT_EOF);
181		packet_put_int(c->remote_id);
182		packet_send();
183		break;
184	default:
185		error("internal error: channel %d: cannot send IEOF for istate %d", c->self, c->istate);
186		break;
187	}
188}
189static void
190chan_send_oclose(Channel *c)
191{
192	switch (c->ostate) {
193	case CHAN_OUTPUT_OPEN:
194	case CHAN_OUTPUT_WAIT_DRAIN:
195		chan_shutdown_write(c);
196		buffer_consume(&c->output, buffer_len(&c->output));
197		packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE);
198		packet_put_int(c->remote_id);
199		packet_send();
200		break;
201	default:
202		error("internal error: channel %d: cannot send OCLOSE for ostate %d", c->self, c->istate);
203		break;
204	}
205}
206
207/* helper */
208static void
209chan_shutdown_write(Channel *c)
210{
211	/* shutdown failure is allowed if write failed already */
212	debug("channel %d: shutdown_write", c->self);
213	if (shutdown(c->sock, SHUT_WR) < 0)
214		debug("chan_shutdown_write failed for #%d/fd%d: %.100s",
215		      c->self, c->sock, strerror(errno));
216}
217static void
218chan_shutdown_read(Channel *c)
219{
220	debug("channel %d: shutdown_read", c->self);
221	if (shutdown(c->sock, SHUT_RD) < 0)
222		error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s",
223		      c->self, c->sock, c->istate, c->ostate, strerror(errno));
224}
225static void
226chan_delete_if_full_closed(Channel *c)
227{
228	if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
229		debug("channel %d: full closed", c->self);
230		channel_free(c->self);
231	}
232}
233void
234chan_init_iostates(Channel *c)
235{
236	c->ostate = CHAN_OUTPUT_OPEN;
237	c->istate = CHAN_INPUT_OPEN;
238}
239