1*3d9fd9fcSEd Maste /* $OpenBSD: nchan.c,v 1.76 2024/07/25 22:40:08 djm Exp $ */
2511b41d2SMark Murray /*
3ae1f160dSDag-Erling Smørgrav * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
4511b41d2SMark Murray *
5511b41d2SMark Murray * Redistribution and use in source and binary forms, with or without
6511b41d2SMark Murray * modification, are permitted provided that the following conditions
7511b41d2SMark Murray * are met:
8511b41d2SMark Murray * 1. Redistributions of source code must retain the above copyright
9511b41d2SMark Murray * notice, this list of conditions and the following disclaimer.
10511b41d2SMark Murray * 2. Redistributions in binary form must reproduce the above copyright
11511b41d2SMark Murray * notice, this list of conditions and the following disclaimer in the
12511b41d2SMark Murray * documentation and/or other materials provided with the distribution.
13511b41d2SMark Murray *
14511b41d2SMark Murray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15511b41d2SMark Murray * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16511b41d2SMark Murray * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17511b41d2SMark Murray * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18511b41d2SMark Murray * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19511b41d2SMark Murray * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20511b41d2SMark Murray * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21511b41d2SMark Murray * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22511b41d2SMark Murray * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23511b41d2SMark Murray * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24511b41d2SMark Murray */
25511b41d2SMark Murray
26511b41d2SMark Murray #include "includes.h"
27761efaa7SDag-Erling Smørgrav
28761efaa7SDag-Erling Smørgrav #include <sys/types.h>
29761efaa7SDag-Erling Smørgrav #include <sys/socket.h>
30761efaa7SDag-Erling Smørgrav
31761efaa7SDag-Erling Smørgrav #include <errno.h>
32761efaa7SDag-Erling Smørgrav #include <string.h>
33761efaa7SDag-Erling Smørgrav #include <stdarg.h>
34511b41d2SMark Murray
35d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
361e8db6e2SBrian Feldman #include "ssh2.h"
374f52dfbbSDag-Erling Smørgrav #include "sshbuf.h"
384f52dfbbSDag-Erling Smørgrav #include "ssherr.h"
39511b41d2SMark Murray #include "packet.h"
40511b41d2SMark Murray #include "channels.h"
41a04a10f8SKris Kennaway #include "compat.h"
421e8db6e2SBrian Feldman #include "log.h"
43511b41d2SMark Murray
44ae1f160dSDag-Erling Smørgrav /*
45ae1f160dSDag-Erling Smørgrav * SSH Protocol 1.5 aka New Channel Protocol
46ae1f160dSDag-Erling Smørgrav * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored.
47ae1f160dSDag-Erling Smørgrav * Written by Markus Friedl in October 1999
48ae1f160dSDag-Erling Smørgrav *
49ae1f160dSDag-Erling Smørgrav * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the
50ae1f160dSDag-Erling Smørgrav * tear down of channels:
51ae1f160dSDag-Erling Smørgrav *
52ae1f160dSDag-Erling Smørgrav * 1.3: strict request-ack-protocol:
53ae1f160dSDag-Erling Smørgrav * CLOSE ->
54ae1f160dSDag-Erling Smørgrav * <- CLOSE_CONFIRM
55ae1f160dSDag-Erling Smørgrav *
56ae1f160dSDag-Erling Smørgrav * 1.5: uses variations of:
57ae1f160dSDag-Erling Smørgrav * IEOF ->
58ae1f160dSDag-Erling Smørgrav * <- OCLOSE
59ae1f160dSDag-Erling Smørgrav * <- IEOF
60ae1f160dSDag-Erling Smørgrav * OCLOSE ->
61ae1f160dSDag-Erling Smørgrav * i.e. both sides have to close the channel
62ae1f160dSDag-Erling Smørgrav *
63ae1f160dSDag-Erling Smørgrav * 2.0: the EOF messages are optional
64ae1f160dSDag-Erling Smørgrav *
65ae1f160dSDag-Erling Smørgrav * See the debugging output from 'ssh -v' and 'sshd -d' of
66ae1f160dSDag-Erling Smørgrav * ssh-1.2.27 as an example.
67ae1f160dSDag-Erling Smørgrav *
68ae1f160dSDag-Erling Smørgrav */
69ae1f160dSDag-Erling Smørgrav
70a04a10f8SKris Kennaway /* functions manipulating channel states */
71511b41d2SMark Murray /*
72511b41d2SMark Murray * EVENTS update channel input/output states execute ACTIONS
73511b41d2SMark Murray */
74a04a10f8SKris Kennaway /*
75a04a10f8SKris Kennaway * ACTIONS: should never update the channel states
76a04a10f8SKris Kennaway */
774f52dfbbSDag-Erling Smørgrav static void chan_send_eof2(struct ssh *, Channel *);
784f52dfbbSDag-Erling Smørgrav static void chan_send_eow2(struct ssh *, Channel *);
79a04a10f8SKris Kennaway
80a04a10f8SKris Kennaway /* helper */
814f52dfbbSDag-Erling Smørgrav static void chan_shutdown_write(struct ssh *, Channel *);
824f52dfbbSDag-Erling Smørgrav static void chan_shutdown_read(struct ssh *, Channel *);
832f513db7SEd Maste static void chan_shutdown_extended_read(struct ssh *, Channel *);
84ae1f160dSDag-Erling Smørgrav
851323ec57SEd Maste static const char * const ostates[] = {
861323ec57SEd Maste "open", "drain", "wait_ieof", "closed",
871323ec57SEd Maste };
881323ec57SEd Maste static const char * const istates[] = {
891323ec57SEd Maste "open", "drain", "wait_oclose", "closed",
901323ec57SEd Maste };
91ae1f160dSDag-Erling Smørgrav
92ae1f160dSDag-Erling Smørgrav static void
chan_set_istate(Channel * c,u_int next)93ae1f160dSDag-Erling Smørgrav chan_set_istate(Channel *c, u_int next)
94ae1f160dSDag-Erling Smørgrav {
95ae1f160dSDag-Erling Smørgrav if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED)
96ae1f160dSDag-Erling Smørgrav fatal("chan_set_istate: bad state %d -> %d", c->istate, next);
97d95e11bfSDag-Erling Smørgrav debug2("channel %d: input %s -> %s", c->self, istates[c->istate],
98ae1f160dSDag-Erling Smørgrav istates[next]);
99ae1f160dSDag-Erling Smørgrav c->istate = next;
100ae1f160dSDag-Erling Smørgrav }
1014f52dfbbSDag-Erling Smørgrav
102ae1f160dSDag-Erling Smørgrav static void
chan_set_ostate(Channel * c,u_int next)103ae1f160dSDag-Erling Smørgrav chan_set_ostate(Channel *c, u_int next)
104ae1f160dSDag-Erling Smørgrav {
105ae1f160dSDag-Erling Smørgrav if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED)
106ae1f160dSDag-Erling Smørgrav fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next);
107d95e11bfSDag-Erling Smørgrav debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate],
108ae1f160dSDag-Erling Smørgrav ostates[next]);
109ae1f160dSDag-Erling Smørgrav c->ostate = next;
110ae1f160dSDag-Erling Smørgrav }
111a04a10f8SKris Kennaway
112ae1f160dSDag-Erling Smørgrav void
chan_read_failed(struct ssh * ssh,Channel * c)1134f52dfbbSDag-Erling Smørgrav chan_read_failed(struct ssh *ssh, Channel *c)
114511b41d2SMark Murray {
115d95e11bfSDag-Erling Smørgrav debug2("channel %d: read failed", c->self);
116511b41d2SMark Murray switch (c->istate) {
117511b41d2SMark Murray case CHAN_INPUT_OPEN:
1184f52dfbbSDag-Erling Smørgrav chan_shutdown_read(ssh, c);
119ae1f160dSDag-Erling Smørgrav chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN);
120511b41d2SMark Murray break;
121511b41d2SMark Murray default:
122ae1f160dSDag-Erling Smørgrav error("channel %d: chan_read_failed for istate %d",
123511b41d2SMark Murray c->self, c->istate);
124511b41d2SMark Murray break;
125511b41d2SMark Murray }
126511b41d2SMark Murray }
1274f52dfbbSDag-Erling Smørgrav
128ae1f160dSDag-Erling Smørgrav void
chan_ibuf_empty(struct ssh * ssh,Channel * c)1294f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(struct ssh *ssh, Channel *c)
130511b41d2SMark Murray {
131d95e11bfSDag-Erling Smørgrav debug2("channel %d: ibuf empty", c->self);
1324f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input)) {
133ae1f160dSDag-Erling Smørgrav error("channel %d: chan_ibuf_empty for non empty buffer",
134a04a10f8SKris Kennaway c->self);
135511b41d2SMark Murray return;
136511b41d2SMark Murray }
137511b41d2SMark Murray switch (c->istate) {
138511b41d2SMark Murray case CHAN_INPUT_WAIT_DRAIN:
139b15c8340SDag-Erling Smørgrav if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL)))
1404f52dfbbSDag-Erling Smørgrav chan_send_eof2(ssh, c);
141ae1f160dSDag-Erling Smørgrav chan_set_istate(c, CHAN_INPUT_CLOSED);
142511b41d2SMark Murray break;
143511b41d2SMark Murray default:
144ae1f160dSDag-Erling Smørgrav error("channel %d: chan_ibuf_empty for istate %d",
145a04a10f8SKris Kennaway c->self, c->istate);
146511b41d2SMark Murray break;
147511b41d2SMark Murray }
148511b41d2SMark Murray }
1494f52dfbbSDag-Erling Smørgrav
150ae1f160dSDag-Erling Smørgrav void
chan_obuf_empty(struct ssh * ssh,Channel * c)1514f52dfbbSDag-Erling Smørgrav chan_obuf_empty(struct ssh *ssh, Channel *c)
152511b41d2SMark Murray {
153d95e11bfSDag-Erling Smørgrav debug2("channel %d: obuf empty", c->self);
1544f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output)) {
155ae1f160dSDag-Erling Smørgrav error("channel %d: chan_obuf_empty for non empty buffer",
156a04a10f8SKris Kennaway c->self);
157511b41d2SMark Murray return;
158511b41d2SMark Murray }
159511b41d2SMark Murray switch (c->ostate) {
160511b41d2SMark Murray case CHAN_OUTPUT_WAIT_DRAIN:
1614f52dfbbSDag-Erling Smørgrav chan_shutdown_write(ssh, c);
162ae1f160dSDag-Erling Smørgrav chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
163511b41d2SMark Murray break;
164511b41d2SMark Murray default:
165ae1f160dSDag-Erling Smørgrav error("channel %d: internal error: obuf_empty for ostate %d",
166a04a10f8SKris Kennaway c->self, c->ostate);
167511b41d2SMark Murray break;
168511b41d2SMark Murray }
169511b41d2SMark Murray }
1704f52dfbbSDag-Erling Smørgrav
1714f52dfbbSDag-Erling Smørgrav void
chan_rcvd_eow(struct ssh * ssh,Channel * c)1724f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(struct ssh *ssh, Channel *c)
173511b41d2SMark Murray {
1744f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd eow", c->self);
175511b41d2SMark Murray switch (c->istate) {
176511b41d2SMark Murray case CHAN_INPUT_OPEN:
1774f52dfbbSDag-Erling Smørgrav chan_shutdown_read(ssh, c);
1784f52dfbbSDag-Erling Smørgrav chan_set_istate(c, CHAN_INPUT_CLOSED);
179511b41d2SMark Murray break;
180511b41d2SMark Murray }
181511b41d2SMark Murray }
182a04a10f8SKris Kennaway
183a04a10f8SKris Kennaway static void
chan_send_eof2(struct ssh * ssh,Channel * c)1844f52dfbbSDag-Erling Smørgrav chan_send_eof2(struct ssh *ssh, Channel *c)
1854f52dfbbSDag-Erling Smørgrav {
1864f52dfbbSDag-Erling Smørgrav int r;
1874f52dfbbSDag-Erling Smørgrav
1884f52dfbbSDag-Erling Smørgrav debug2("channel %d: send eof", c->self);
1894f52dfbbSDag-Erling Smørgrav switch (c->istate) {
1904f52dfbbSDag-Erling Smørgrav case CHAN_INPUT_WAIT_DRAIN:
1914f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id)
19219261079SEd Maste fatal_f("channel %d: no remote_id", c->self);
1934f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EOF)) != 0 ||
1944f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
1954f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0)
19619261079SEd Maste fatal_fr(r, "send CHANNEL_EOF");
1974f52dfbbSDag-Erling Smørgrav c->flags |= CHAN_EOF_SENT;
1984f52dfbbSDag-Erling Smørgrav break;
1994f52dfbbSDag-Erling Smørgrav default:
2004f52dfbbSDag-Erling Smørgrav error("channel %d: cannot send eof for istate %d",
2014f52dfbbSDag-Erling Smørgrav c->self, c->istate);
2024f52dfbbSDag-Erling Smørgrav break;
2034f52dfbbSDag-Erling Smørgrav }
2044f52dfbbSDag-Erling Smørgrav }
2054f52dfbbSDag-Erling Smørgrav
2064f52dfbbSDag-Erling Smørgrav static void
chan_send_close2(struct ssh * ssh,Channel * c)2074f52dfbbSDag-Erling Smørgrav chan_send_close2(struct ssh *ssh, Channel *c)
2084f52dfbbSDag-Erling Smørgrav {
2094f52dfbbSDag-Erling Smørgrav int r;
2104f52dfbbSDag-Erling Smørgrav
211*3d9fd9fcSEd Maste debug2("channel %d: send_close2", c->self);
2124f52dfbbSDag-Erling Smørgrav if (c->ostate != CHAN_OUTPUT_CLOSED ||
2134f52dfbbSDag-Erling Smørgrav c->istate != CHAN_INPUT_CLOSED) {
2144f52dfbbSDag-Erling Smørgrav error("channel %d: cannot send close for istate/ostate %d/%d",
2154f52dfbbSDag-Erling Smørgrav c->self, c->istate, c->ostate);
2164f52dfbbSDag-Erling Smørgrav } else if (c->flags & CHAN_CLOSE_SENT) {
2174f52dfbbSDag-Erling Smørgrav error("channel %d: already sent close", c->self);
2184f52dfbbSDag-Erling Smørgrav } else {
2194f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id)
22019261079SEd Maste fatal_f("channel %d: no remote_id", c->self);
221*3d9fd9fcSEd Maste debug2("channel %d: send close for remote id %u", c->self,
222*3d9fd9fcSEd Maste c->remote_id);
2234f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_CLOSE)) != 0 ||
2244f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
2254f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0)
22619261079SEd Maste fatal_fr(r, "send CHANNEL_EOF");
2274f52dfbbSDag-Erling Smørgrav c->flags |= CHAN_CLOSE_SENT;
2284f52dfbbSDag-Erling Smørgrav }
2294f52dfbbSDag-Erling Smørgrav }
2304f52dfbbSDag-Erling Smørgrav
2314f52dfbbSDag-Erling Smørgrav static void
chan_send_eow2(struct ssh * ssh,Channel * c)2324f52dfbbSDag-Erling Smørgrav chan_send_eow2(struct ssh *ssh, Channel *c)
2334f52dfbbSDag-Erling Smørgrav {
2344f52dfbbSDag-Erling Smørgrav int r;
2354f52dfbbSDag-Erling Smørgrav
2364f52dfbbSDag-Erling Smørgrav debug2("channel %d: send eow", c->self);
2374f52dfbbSDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_CLOSED) {
2384f52dfbbSDag-Erling Smørgrav error("channel %d: must not sent eow on closed output",
2394f52dfbbSDag-Erling Smørgrav c->self);
2404f52dfbbSDag-Erling Smørgrav return;
2414f52dfbbSDag-Erling Smørgrav }
24219261079SEd Maste if (!(ssh->compat & SSH_NEW_OPENSSH))
2434f52dfbbSDag-Erling Smørgrav return;
2444f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id)
24519261079SEd Maste fatal_f("channel %d: no remote_id", c->self);
2464f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
2474f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
2484f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "eow@openssh.com")) != 0 ||
2494f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 ||
2504f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0)
25119261079SEd Maste fatal_fr(r, "send CHANNEL_EOF");
2524f52dfbbSDag-Erling Smørgrav }
2534f52dfbbSDag-Erling Smørgrav
2544f52dfbbSDag-Erling Smørgrav /* shared */
2554f52dfbbSDag-Erling Smørgrav
2564f52dfbbSDag-Erling Smørgrav void
chan_rcvd_ieof(struct ssh * ssh,Channel * c)2574f52dfbbSDag-Erling Smørgrav chan_rcvd_ieof(struct ssh *ssh, Channel *c)
2584f52dfbbSDag-Erling Smørgrav {
2594f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd eof", c->self);
2604f52dfbbSDag-Erling Smørgrav c->flags |= CHAN_EOF_RCVD;
2614f52dfbbSDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN)
2624f52dfbbSDag-Erling Smørgrav chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
2634f52dfbbSDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN &&
2644f52dfbbSDag-Erling Smørgrav sshbuf_len(c->output) == 0 &&
2654f52dfbbSDag-Erling Smørgrav !CHANNEL_EFD_OUTPUT_ACTIVE(c))
2664f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c);
2674f52dfbbSDag-Erling Smørgrav }
2684f52dfbbSDag-Erling Smørgrav
2694f52dfbbSDag-Erling Smørgrav void
chan_rcvd_oclose(struct ssh * ssh,Channel * c)2704f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(struct ssh *ssh, Channel *c)
271a04a10f8SKris Kennaway {
272d95e11bfSDag-Erling Smørgrav debug2("channel %d: rcvd close", c->self);
273b15c8340SDag-Erling Smørgrav if (!(c->flags & CHAN_LOCAL)) {
274a04a10f8SKris Kennaway if (c->flags & CHAN_CLOSE_RCVD)
275b15c8340SDag-Erling Smørgrav error("channel %d: protocol error: close rcvd twice",
276b15c8340SDag-Erling Smørgrav c->self);
277a04a10f8SKris Kennaway c->flags |= CHAN_CLOSE_RCVD;
278b15c8340SDag-Erling Smørgrav }
279a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_LARVAL) {
280a04a10f8SKris Kennaway /* tear down larval channels immediately */
281ae1f160dSDag-Erling Smørgrav chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
282ae1f160dSDag-Erling Smørgrav chan_set_istate(c, CHAN_INPUT_CLOSED);
283a04a10f8SKris Kennaway return;
284a04a10f8SKris Kennaway }
285a04a10f8SKris Kennaway switch (c->ostate) {
286a04a10f8SKris Kennaway case CHAN_OUTPUT_OPEN:
287ae1f160dSDag-Erling Smørgrav /*
288ae1f160dSDag-Erling Smørgrav * wait until a data from the channel is consumed if a CLOSE
289ae1f160dSDag-Erling Smørgrav * is received
290ae1f160dSDag-Erling Smørgrav */
291ae1f160dSDag-Erling Smørgrav chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
292a04a10f8SKris Kennaway break;
293a04a10f8SKris Kennaway }
294a04a10f8SKris Kennaway switch (c->istate) {
295a04a10f8SKris Kennaway case CHAN_INPUT_OPEN:
2964f52dfbbSDag-Erling Smørgrav chan_shutdown_read(ssh, c);
2972f513db7SEd Maste chan_shutdown_extended_read(ssh, c);
298ae1f160dSDag-Erling Smørgrav chan_set_istate(c, CHAN_INPUT_CLOSED);
299a04a10f8SKris Kennaway break;
300a04a10f8SKris Kennaway case CHAN_INPUT_WAIT_DRAIN:
301b15c8340SDag-Erling Smørgrav if (!(c->flags & CHAN_LOCAL))
3024f52dfbbSDag-Erling Smørgrav chan_send_eof2(ssh, c);
3032f513db7SEd Maste chan_shutdown_extended_read(ssh, c);
304ae1f160dSDag-Erling Smørgrav chan_set_istate(c, CHAN_INPUT_CLOSED);
305a04a10f8SKris Kennaway break;
306a04a10f8SKris Kennaway }
307a04a10f8SKris Kennaway }
308b15c8340SDag-Erling Smørgrav
309d4af9e69SDag-Erling Smørgrav void
chan_write_failed(struct ssh * ssh,Channel * c)3104f52dfbbSDag-Erling Smørgrav chan_write_failed(struct ssh *ssh, Channel *c)
311a04a10f8SKris Kennaway {
312d95e11bfSDag-Erling Smørgrav debug2("channel %d: write failed", c->self);
313a04a10f8SKris Kennaway switch (c->ostate) {
314a04a10f8SKris Kennaway case CHAN_OUTPUT_OPEN:
315a04a10f8SKris Kennaway case CHAN_OUTPUT_WAIT_DRAIN:
3164f52dfbbSDag-Erling Smørgrav chan_shutdown_write(ssh, c);
317d4af9e69SDag-Erling Smørgrav if (strcmp(c->ctype, "session") == 0)
3184f52dfbbSDag-Erling Smørgrav chan_send_eow2(ssh, c);
319ae1f160dSDag-Erling Smørgrav chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
320a04a10f8SKris Kennaway break;
321a04a10f8SKris Kennaway default:
322ae1f160dSDag-Erling Smørgrav error("channel %d: chan_write_failed for ostate %d",
323a04a10f8SKris Kennaway c->self, c->ostate);
324a04a10f8SKris Kennaway break;
325a04a10f8SKris Kennaway }
326a04a10f8SKris Kennaway }
3271e8db6e2SBrian Feldman
328ae1f160dSDag-Erling Smørgrav void
chan_mark_dead(struct ssh * ssh,Channel * c)3294f52dfbbSDag-Erling Smørgrav chan_mark_dead(struct ssh *ssh, Channel *c)
330ae1f160dSDag-Erling Smørgrav {
331ae1f160dSDag-Erling Smørgrav c->type = SSH_CHANNEL_ZOMBIE;
332ae1f160dSDag-Erling Smørgrav }
333ae1f160dSDag-Erling Smørgrav
334ae1f160dSDag-Erling Smørgrav int
chan_is_dead(struct ssh * ssh,Channel * c,int do_send)3354f52dfbbSDag-Erling Smørgrav chan_is_dead(struct ssh *ssh, Channel *c, int do_send)
336ae1f160dSDag-Erling Smørgrav {
337ae1f160dSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_ZOMBIE) {
338d95e11bfSDag-Erling Smørgrav debug2("channel %d: zombie", c->self);
339ae1f160dSDag-Erling Smørgrav return 1;
340ae1f160dSDag-Erling Smørgrav }
3411e8db6e2SBrian Feldman if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED)
3421e8db6e2SBrian Feldman return 0;
34319261079SEd Maste if ((ssh->compat & SSH_BUG_EXTEOF) &&
344545d5ecaSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_WRITE &&
345545d5ecaSDag-Erling Smørgrav c->efd != -1 &&
3464f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) > 0) {
3474f52dfbbSDag-Erling Smørgrav debug2("channel %d: active efd: %d len %zu",
3484f52dfbbSDag-Erling Smørgrav c->self, c->efd, sshbuf_len(c->extended));
349545d5ecaSDag-Erling Smørgrav return 0;
350545d5ecaSDag-Erling Smørgrav }
351b15c8340SDag-Erling Smørgrav if (c->flags & CHAN_LOCAL) {
352b15c8340SDag-Erling Smørgrav debug2("channel %d: is dead (local)", c->self);
353b15c8340SDag-Erling Smørgrav return 1;
354b15c8340SDag-Erling Smørgrav }
355a04a10f8SKris Kennaway if (!(c->flags & CHAN_CLOSE_SENT)) {
356d74d50a8SDag-Erling Smørgrav if (do_send) {
3574f52dfbbSDag-Erling Smørgrav chan_send_close2(ssh, c);
358ae1f160dSDag-Erling Smørgrav } else {
359ae1f160dSDag-Erling Smørgrav /* channel would be dead if we sent a close */
360ae1f160dSDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_RCVD) {
361d95e11bfSDag-Erling Smørgrav debug2("channel %d: almost dead",
362ae1f160dSDag-Erling Smørgrav c->self);
363ae1f160dSDag-Erling Smørgrav return 1;
364ae1f160dSDag-Erling Smørgrav }
365ae1f160dSDag-Erling Smørgrav }
366a04a10f8SKris Kennaway }
367a04a10f8SKris Kennaway if ((c->flags & CHAN_CLOSE_SENT) &&
368a04a10f8SKris Kennaway (c->flags & CHAN_CLOSE_RCVD)) {
369d95e11bfSDag-Erling Smørgrav debug2("channel %d: is dead", c->self);
3701e8db6e2SBrian Feldman return 1;
371a04a10f8SKris Kennaway }
3721e8db6e2SBrian Feldman return 0;
373a04a10f8SKris Kennaway }
374a04a10f8SKris Kennaway
375511b41d2SMark Murray /* helper */
376511b41d2SMark Murray static void
chan_shutdown_write(struct ssh * ssh,Channel * c)3774f52dfbbSDag-Erling Smørgrav chan_shutdown_write(struct ssh *ssh, Channel *c)
378511b41d2SMark Murray {
3794f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->output);
3804f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_LARVAL)
381a04a10f8SKris Kennaway return;
382511b41d2SMark Murray /* shutdown failure is allowed if write failed already */
38319261079SEd Maste debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
38419261079SEd Maste c->self, c->istate, c->ostate, c->sock, c->wfd, c->efd,
3852f513db7SEd Maste channel_format_extended_usage(c));
386a04a10f8SKris Kennaway if (c->sock != -1) {
38719261079SEd Maste if (shutdown(c->sock, SHUT_WR) == -1) {
38819261079SEd Maste debug2_f("channel %d: shutdown() failed for "
38919261079SEd Maste "fd %d [i%d o%d]: %.100s", c->self, c->sock,
39019261079SEd Maste c->istate, c->ostate, strerror(errno));
3912f513db7SEd Maste }
392a04a10f8SKris Kennaway } else {
39319261079SEd Maste if (channel_close_fd(ssh, c, &c->wfd) < 0) {
39419261079SEd Maste logit_f("channel %d: close() failed for "
39519261079SEd Maste "fd %d [i%d o%d]: %.100s", c->self, c->wfd,
39619261079SEd Maste c->istate, c->ostate, strerror(errno));
3972f513db7SEd Maste }
398a04a10f8SKris Kennaway }
399511b41d2SMark Murray }
4004f52dfbbSDag-Erling Smørgrav
401511b41d2SMark Murray static void
chan_shutdown_read(struct ssh * ssh,Channel * c)4024f52dfbbSDag-Erling Smørgrav chan_shutdown_read(struct ssh *ssh, Channel *c)
403511b41d2SMark Murray {
4044f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_LARVAL)
405a04a10f8SKris Kennaway return;
40619261079SEd Maste debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
40719261079SEd Maste c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd,
4082f513db7SEd Maste channel_format_extended_usage(c));
409a04a10f8SKris Kennaway if (c->sock != -1) {
41083d2307dSDag-Erling Smørgrav /*
41183d2307dSDag-Erling Smørgrav * shutdown(sock, SHUT_READ) may return ENOTCONN if the
41283d2307dSDag-Erling Smørgrav * write side has been closed already. (bug on Linux)
41383d2307dSDag-Erling Smørgrav * HP-UX may return ENOTCONN also.
41483d2307dSDag-Erling Smørgrav */
41519261079SEd Maste if (shutdown(c->sock, SHUT_RD) == -1 && errno != ENOTCONN) {
41619261079SEd Maste error_f("channel %d: shutdown() failed for "
41719261079SEd Maste "fd %d [i%d o%d]: %.100s", c->self, c->sock,
41819261079SEd Maste c->istate, c->ostate, strerror(errno));
4192f513db7SEd Maste }
420a04a10f8SKris Kennaway } else {
42119261079SEd Maste if (channel_close_fd(ssh, c, &c->rfd) < 0) {
42219261079SEd Maste logit_f("channel %d: close() failed for "
42319261079SEd Maste "fd %d [i%d o%d]: %.100s", c->self, c->rfd,
42419261079SEd Maste c->istate, c->ostate, strerror(errno));
4252f513db7SEd Maste }
4262f513db7SEd Maste }
4272f513db7SEd Maste }
4282f513db7SEd Maste
4292f513db7SEd Maste static void
chan_shutdown_extended_read(struct ssh * ssh,Channel * c)4302f513db7SEd Maste chan_shutdown_extended_read(struct ssh *ssh, Channel *c)
4312f513db7SEd Maste {
4322f513db7SEd Maste if (c->type == SSH_CHANNEL_LARVAL || c->efd == -1)
4332f513db7SEd Maste return;
4342f513db7SEd Maste if (c->extended_usage != CHAN_EXTENDED_READ &&
4352f513db7SEd Maste c->extended_usage != CHAN_EXTENDED_IGNORE)
4362f513db7SEd Maste return;
43719261079SEd Maste debug_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
43819261079SEd Maste c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd,
4392f513db7SEd Maste channel_format_extended_usage(c));
44019261079SEd Maste if (channel_close_fd(ssh, c, &c->efd) < 0) {
44119261079SEd Maste logit_f("channel %d: close() failed for "
44219261079SEd Maste "extended fd %d [i%d o%d]: %.100s", c->self, c->efd,
44319261079SEd Maste c->istate, c->ostate, strerror(errno));
444511b41d2SMark Murray }
445511b41d2SMark Murray }
446