1*ee116499SAntonio Huete Jimenez /* $OpenBSD: nchan.c,v 1.74 2022/02/01 23:32:51 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
418de8d7fSPeter Avalos *
518de8d7fSPeter Avalos * Redistribution and use in source and binary forms, with or without
618de8d7fSPeter Avalos * modification, are permitted provided that the following conditions
718de8d7fSPeter Avalos * are met:
818de8d7fSPeter Avalos * 1. Redistributions of source code must retain the above copyright
918de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer.
1018de8d7fSPeter Avalos * 2. Redistributions in binary form must reproduce the above copyright
1118de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer in the
1218de8d7fSPeter Avalos * documentation and/or other materials provided with the distribution.
1318de8d7fSPeter Avalos *
1418de8d7fSPeter Avalos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1518de8d7fSPeter Avalos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1618de8d7fSPeter Avalos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1718de8d7fSPeter Avalos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1818de8d7fSPeter Avalos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1918de8d7fSPeter Avalos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2018de8d7fSPeter Avalos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2118de8d7fSPeter Avalos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2218de8d7fSPeter Avalos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2318de8d7fSPeter Avalos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2418de8d7fSPeter Avalos */
2518de8d7fSPeter Avalos
2618de8d7fSPeter Avalos #include "includes.h"
2718de8d7fSPeter Avalos
2818de8d7fSPeter Avalos #include <sys/types.h>
2918de8d7fSPeter Avalos #include <sys/socket.h>
3018de8d7fSPeter Avalos
3118de8d7fSPeter Avalos #include <errno.h>
3218de8d7fSPeter Avalos #include <string.h>
3318de8d7fSPeter Avalos #include <stdarg.h>
3418de8d7fSPeter Avalos
3518de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
3618de8d7fSPeter Avalos #include "ssh2.h"
37ce74bacaSMatthew Dillon #include "sshbuf.h"
38ce74bacaSMatthew Dillon #include "ssherr.h"
3918de8d7fSPeter Avalos #include "packet.h"
4018de8d7fSPeter Avalos #include "channels.h"
4118de8d7fSPeter Avalos #include "compat.h"
4218de8d7fSPeter Avalos #include "log.h"
4318de8d7fSPeter Avalos
4418de8d7fSPeter Avalos /*
4518de8d7fSPeter Avalos * SSH Protocol 1.5 aka New Channel Protocol
4618de8d7fSPeter Avalos * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored.
4718de8d7fSPeter Avalos * Written by Markus Friedl in October 1999
4818de8d7fSPeter Avalos *
4918de8d7fSPeter Avalos * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the
5018de8d7fSPeter Avalos * tear down of channels:
5118de8d7fSPeter Avalos *
5218de8d7fSPeter Avalos * 1.3: strict request-ack-protocol:
5318de8d7fSPeter Avalos * CLOSE ->
5418de8d7fSPeter Avalos * <- CLOSE_CONFIRM
5518de8d7fSPeter Avalos *
5618de8d7fSPeter Avalos * 1.5: uses variations of:
5718de8d7fSPeter Avalos * IEOF ->
5818de8d7fSPeter Avalos * <- OCLOSE
5918de8d7fSPeter Avalos * <- IEOF
6018de8d7fSPeter Avalos * OCLOSE ->
6118de8d7fSPeter Avalos * i.e. both sides have to close the channel
6218de8d7fSPeter Avalos *
6318de8d7fSPeter Avalos * 2.0: the EOF messages are optional
6418de8d7fSPeter Avalos *
6518de8d7fSPeter Avalos * See the debugging output from 'ssh -v' and 'sshd -d' of
6618de8d7fSPeter Avalos * ssh-1.2.27 as an example.
6718de8d7fSPeter Avalos *
6818de8d7fSPeter Avalos */
6918de8d7fSPeter Avalos
7018de8d7fSPeter Avalos /* functions manipulating channel states */
7118de8d7fSPeter Avalos /*
7218de8d7fSPeter Avalos * EVENTS update channel input/output states execute ACTIONS
7318de8d7fSPeter Avalos */
7418de8d7fSPeter Avalos /*
7518de8d7fSPeter Avalos * ACTIONS: should never update the channel states
7618de8d7fSPeter Avalos */
77ce74bacaSMatthew Dillon static void chan_send_eof2(struct ssh *, Channel *);
78ce74bacaSMatthew Dillon static void chan_send_eow2(struct ssh *, Channel *);
7918de8d7fSPeter Avalos
8018de8d7fSPeter Avalos /* helper */
81ce74bacaSMatthew Dillon static void chan_shutdown_write(struct ssh *, Channel *);
82ce74bacaSMatthew Dillon static void chan_shutdown_read(struct ssh *, Channel *);
83664f4763Szrj static void chan_shutdown_extended_read(struct ssh *, Channel *);
8418de8d7fSPeter Avalos
85*ee116499SAntonio Huete Jimenez static const char * const ostates[] = {
86*ee116499SAntonio Huete Jimenez "open", "drain", "wait_ieof", "closed",
87*ee116499SAntonio Huete Jimenez };
88*ee116499SAntonio Huete Jimenez static const char * const istates[] = {
89*ee116499SAntonio Huete Jimenez "open", "drain", "wait_oclose", "closed",
90*ee116499SAntonio Huete Jimenez };
9118de8d7fSPeter Avalos
9218de8d7fSPeter Avalos static void
chan_set_istate(Channel * c,u_int next)9318de8d7fSPeter Avalos chan_set_istate(Channel *c, u_int next)
9418de8d7fSPeter Avalos {
9518de8d7fSPeter Avalos if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED)
9618de8d7fSPeter Avalos fatal("chan_set_istate: bad state %d -> %d", c->istate, next);
9718de8d7fSPeter Avalos debug2("channel %d: input %s -> %s", c->self, istates[c->istate],
9818de8d7fSPeter Avalos istates[next]);
9918de8d7fSPeter Avalos c->istate = next;
10018de8d7fSPeter Avalos }
101ce74bacaSMatthew Dillon
10218de8d7fSPeter Avalos static void
chan_set_ostate(Channel * c,u_int next)10318de8d7fSPeter Avalos chan_set_ostate(Channel *c, u_int next)
10418de8d7fSPeter Avalos {
10518de8d7fSPeter Avalos if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED)
10618de8d7fSPeter Avalos fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next);
10718de8d7fSPeter Avalos debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate],
10818de8d7fSPeter Avalos ostates[next]);
10918de8d7fSPeter Avalos c->ostate = next;
11018de8d7fSPeter Avalos }
11118de8d7fSPeter Avalos
11218de8d7fSPeter Avalos void
chan_read_failed(struct ssh * ssh,Channel * c)113ce74bacaSMatthew Dillon chan_read_failed(struct ssh *ssh, Channel *c)
11418de8d7fSPeter Avalos {
11518de8d7fSPeter Avalos debug2("channel %d: read failed", c->self);
11618de8d7fSPeter Avalos switch (c->istate) {
11718de8d7fSPeter Avalos case CHAN_INPUT_OPEN:
118ce74bacaSMatthew Dillon chan_shutdown_read(ssh, c);
11918de8d7fSPeter Avalos chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN);
12018de8d7fSPeter Avalos break;
12118de8d7fSPeter Avalos default:
12218de8d7fSPeter Avalos error("channel %d: chan_read_failed for istate %d",
12318de8d7fSPeter Avalos c->self, c->istate);
12418de8d7fSPeter Avalos break;
12518de8d7fSPeter Avalos }
12618de8d7fSPeter Avalos }
127ce74bacaSMatthew Dillon
12818de8d7fSPeter Avalos void
chan_ibuf_empty(struct ssh * ssh,Channel * c)129ce74bacaSMatthew Dillon chan_ibuf_empty(struct ssh *ssh, Channel *c)
13018de8d7fSPeter Avalos {
13118de8d7fSPeter Avalos debug2("channel %d: ibuf empty", c->self);
132ce74bacaSMatthew Dillon if (sshbuf_len(c->input)) {
13318de8d7fSPeter Avalos error("channel %d: chan_ibuf_empty for non empty buffer",
13418de8d7fSPeter Avalos c->self);
13518de8d7fSPeter Avalos return;
13618de8d7fSPeter Avalos }
13718de8d7fSPeter Avalos switch (c->istate) {
13818de8d7fSPeter Avalos case CHAN_INPUT_WAIT_DRAIN:
139856ea928SPeter Avalos if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL)))
140ce74bacaSMatthew Dillon chan_send_eof2(ssh, c);
14118de8d7fSPeter Avalos chan_set_istate(c, CHAN_INPUT_CLOSED);
14218de8d7fSPeter Avalos break;
14318de8d7fSPeter Avalos default:
14418de8d7fSPeter Avalos error("channel %d: chan_ibuf_empty for istate %d",
14518de8d7fSPeter Avalos c->self, c->istate);
14618de8d7fSPeter Avalos break;
14718de8d7fSPeter Avalos }
14818de8d7fSPeter Avalos }
149ce74bacaSMatthew Dillon
15018de8d7fSPeter Avalos void
chan_obuf_empty(struct ssh * ssh,Channel * c)151ce74bacaSMatthew Dillon chan_obuf_empty(struct ssh *ssh, Channel *c)
15218de8d7fSPeter Avalos {
15318de8d7fSPeter Avalos debug2("channel %d: obuf empty", c->self);
154ce74bacaSMatthew Dillon if (sshbuf_len(c->output)) {
15518de8d7fSPeter Avalos error("channel %d: chan_obuf_empty for non empty buffer",
15618de8d7fSPeter Avalos c->self);
15718de8d7fSPeter Avalos return;
15818de8d7fSPeter Avalos }
15918de8d7fSPeter Avalos switch (c->ostate) {
16018de8d7fSPeter Avalos case CHAN_OUTPUT_WAIT_DRAIN:
161ce74bacaSMatthew Dillon chan_shutdown_write(ssh, c);
16218de8d7fSPeter Avalos chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
16318de8d7fSPeter Avalos break;
16418de8d7fSPeter Avalos default:
16518de8d7fSPeter Avalos error("channel %d: internal error: obuf_empty for ostate %d",
16618de8d7fSPeter Avalos c->self, c->ostate);
16718de8d7fSPeter Avalos break;
16818de8d7fSPeter Avalos }
16918de8d7fSPeter Avalos }
170ce74bacaSMatthew Dillon
171ce74bacaSMatthew Dillon void
chan_rcvd_eow(struct ssh * ssh,Channel * c)172ce74bacaSMatthew Dillon chan_rcvd_eow(struct ssh *ssh, Channel *c)
17318de8d7fSPeter Avalos {
174ce74bacaSMatthew Dillon debug2("channel %d: rcvd eow", c->self);
17518de8d7fSPeter Avalos switch (c->istate) {
17618de8d7fSPeter Avalos case CHAN_INPUT_OPEN:
177ce74bacaSMatthew Dillon chan_shutdown_read(ssh, c);
178ce74bacaSMatthew Dillon chan_set_istate(c, CHAN_INPUT_CLOSED);
17918de8d7fSPeter Avalos break;
18018de8d7fSPeter Avalos }
18118de8d7fSPeter Avalos }
18218de8d7fSPeter Avalos
18318de8d7fSPeter Avalos static void
chan_send_eof2(struct ssh * ssh,Channel * c)184ce74bacaSMatthew Dillon chan_send_eof2(struct ssh *ssh, Channel *c)
185ce74bacaSMatthew Dillon {
186ce74bacaSMatthew Dillon int r;
187ce74bacaSMatthew Dillon
188ce74bacaSMatthew Dillon debug2("channel %d: send eof", c->self);
189ce74bacaSMatthew Dillon switch (c->istate) {
190ce74bacaSMatthew Dillon case CHAN_INPUT_WAIT_DRAIN:
191ce74bacaSMatthew Dillon if (!c->have_remote_id)
19250a69bb5SSascha Wildner fatal_f("channel %d: no remote_id", c->self);
193ce74bacaSMatthew Dillon if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EOF)) != 0 ||
194ce74bacaSMatthew Dillon (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
195ce74bacaSMatthew Dillon (r = sshpkt_send(ssh)) != 0)
19650a69bb5SSascha Wildner fatal_fr(r, "send CHANNEL_EOF");
197ce74bacaSMatthew Dillon c->flags |= CHAN_EOF_SENT;
198ce74bacaSMatthew Dillon break;
199ce74bacaSMatthew Dillon default:
200ce74bacaSMatthew Dillon error("channel %d: cannot send eof for istate %d",
201ce74bacaSMatthew Dillon c->self, c->istate);
202ce74bacaSMatthew Dillon break;
203ce74bacaSMatthew Dillon }
204ce74bacaSMatthew Dillon }
205ce74bacaSMatthew Dillon
206ce74bacaSMatthew Dillon static void
chan_send_close2(struct ssh * ssh,Channel * c)207ce74bacaSMatthew Dillon chan_send_close2(struct ssh *ssh, Channel *c)
208ce74bacaSMatthew Dillon {
209ce74bacaSMatthew Dillon int r;
210ce74bacaSMatthew Dillon
211ce74bacaSMatthew Dillon debug2("channel %d: send close", c->self);
212ce74bacaSMatthew Dillon if (c->ostate != CHAN_OUTPUT_CLOSED ||
213ce74bacaSMatthew Dillon c->istate != CHAN_INPUT_CLOSED) {
214ce74bacaSMatthew Dillon error("channel %d: cannot send close for istate/ostate %d/%d",
215ce74bacaSMatthew Dillon c->self, c->istate, c->ostate);
216ce74bacaSMatthew Dillon } else if (c->flags & CHAN_CLOSE_SENT) {
217ce74bacaSMatthew Dillon error("channel %d: already sent close", c->self);
218ce74bacaSMatthew Dillon } else {
219ce74bacaSMatthew Dillon if (!c->have_remote_id)
22050a69bb5SSascha Wildner fatal_f("channel %d: no remote_id", c->self);
221ce74bacaSMatthew Dillon if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_CLOSE)) != 0 ||
222ce74bacaSMatthew Dillon (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
223ce74bacaSMatthew Dillon (r = sshpkt_send(ssh)) != 0)
22450a69bb5SSascha Wildner fatal_fr(r, "send CHANNEL_EOF");
225ce74bacaSMatthew Dillon c->flags |= CHAN_CLOSE_SENT;
226ce74bacaSMatthew Dillon }
227ce74bacaSMatthew Dillon }
228ce74bacaSMatthew Dillon
229ce74bacaSMatthew Dillon static void
chan_send_eow2(struct ssh * ssh,Channel * c)230ce74bacaSMatthew Dillon chan_send_eow2(struct ssh *ssh, Channel *c)
231ce74bacaSMatthew Dillon {
232ce74bacaSMatthew Dillon int r;
233ce74bacaSMatthew Dillon
234ce74bacaSMatthew Dillon debug2("channel %d: send eow", c->self);
235ce74bacaSMatthew Dillon if (c->ostate == CHAN_OUTPUT_CLOSED) {
236ce74bacaSMatthew Dillon error("channel %d: must not sent eow on closed output",
237ce74bacaSMatthew Dillon c->self);
238ce74bacaSMatthew Dillon return;
239ce74bacaSMatthew Dillon }
24050a69bb5SSascha Wildner if (!(ssh->compat & SSH_NEW_OPENSSH))
241ce74bacaSMatthew Dillon return;
242ce74bacaSMatthew Dillon if (!c->have_remote_id)
24350a69bb5SSascha Wildner fatal_f("channel %d: no remote_id", c->self);
244ce74bacaSMatthew Dillon if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
245ce74bacaSMatthew Dillon (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
246ce74bacaSMatthew Dillon (r = sshpkt_put_cstring(ssh, "eow@openssh.com")) != 0 ||
247ce74bacaSMatthew Dillon (r = sshpkt_put_u8(ssh, 0)) != 0 ||
248ce74bacaSMatthew Dillon (r = sshpkt_send(ssh)) != 0)
24950a69bb5SSascha Wildner fatal_fr(r, "send CHANNEL_EOF");
250ce74bacaSMatthew Dillon }
251ce74bacaSMatthew Dillon
252ce74bacaSMatthew Dillon /* shared */
253ce74bacaSMatthew Dillon
254ce74bacaSMatthew Dillon void
chan_rcvd_ieof(struct ssh * ssh,Channel * c)255ce74bacaSMatthew Dillon chan_rcvd_ieof(struct ssh *ssh, Channel *c)
256ce74bacaSMatthew Dillon {
257ce74bacaSMatthew Dillon debug2("channel %d: rcvd eof", c->self);
258ce74bacaSMatthew Dillon c->flags |= CHAN_EOF_RCVD;
259ce74bacaSMatthew Dillon if (c->ostate == CHAN_OUTPUT_OPEN)
260ce74bacaSMatthew Dillon chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
261ce74bacaSMatthew Dillon if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN &&
262ce74bacaSMatthew Dillon sshbuf_len(c->output) == 0 &&
263ce74bacaSMatthew Dillon !CHANNEL_EFD_OUTPUT_ACTIVE(c))
264ce74bacaSMatthew Dillon chan_obuf_empty(ssh, c);
265ce74bacaSMatthew Dillon }
266ce74bacaSMatthew Dillon
267ce74bacaSMatthew Dillon void
chan_rcvd_oclose(struct ssh * ssh,Channel * c)268ce74bacaSMatthew Dillon chan_rcvd_oclose(struct ssh *ssh, Channel *c)
26918de8d7fSPeter Avalos {
27018de8d7fSPeter Avalos debug2("channel %d: rcvd close", c->self);
271856ea928SPeter Avalos if (!(c->flags & CHAN_LOCAL)) {
27218de8d7fSPeter Avalos if (c->flags & CHAN_CLOSE_RCVD)
273856ea928SPeter Avalos error("channel %d: protocol error: close rcvd twice",
274856ea928SPeter Avalos c->self);
27518de8d7fSPeter Avalos c->flags |= CHAN_CLOSE_RCVD;
276856ea928SPeter Avalos }
27718de8d7fSPeter Avalos if (c->type == SSH_CHANNEL_LARVAL) {
27818de8d7fSPeter Avalos /* tear down larval channels immediately */
27918de8d7fSPeter Avalos chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
28018de8d7fSPeter Avalos chan_set_istate(c, CHAN_INPUT_CLOSED);
28118de8d7fSPeter Avalos return;
28218de8d7fSPeter Avalos }
28318de8d7fSPeter Avalos switch (c->ostate) {
28418de8d7fSPeter Avalos case CHAN_OUTPUT_OPEN:
28518de8d7fSPeter Avalos /*
28618de8d7fSPeter Avalos * wait until a data from the channel is consumed if a CLOSE
28718de8d7fSPeter Avalos * is received
28818de8d7fSPeter Avalos */
28918de8d7fSPeter Avalos chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN);
29018de8d7fSPeter Avalos break;
29118de8d7fSPeter Avalos }
29218de8d7fSPeter Avalos switch (c->istate) {
29318de8d7fSPeter Avalos case CHAN_INPUT_OPEN:
294ce74bacaSMatthew Dillon chan_shutdown_read(ssh, c);
295664f4763Szrj chan_shutdown_extended_read(ssh, c);
29618de8d7fSPeter Avalos chan_set_istate(c, CHAN_INPUT_CLOSED);
29718de8d7fSPeter Avalos break;
29818de8d7fSPeter Avalos case CHAN_INPUT_WAIT_DRAIN:
299856ea928SPeter Avalos if (!(c->flags & CHAN_LOCAL))
300ce74bacaSMatthew Dillon chan_send_eof2(ssh, c);
301664f4763Szrj chan_shutdown_extended_read(ssh, c);
30218de8d7fSPeter Avalos chan_set_istate(c, CHAN_INPUT_CLOSED);
30318de8d7fSPeter Avalos break;
30418de8d7fSPeter Avalos }
30518de8d7fSPeter Avalos }
306856ea928SPeter Avalos
30718de8d7fSPeter Avalos void
chan_write_failed(struct ssh * ssh,Channel * c)308ce74bacaSMatthew Dillon chan_write_failed(struct ssh *ssh, Channel *c)
30918de8d7fSPeter Avalos {
31018de8d7fSPeter Avalos debug2("channel %d: write failed", c->self);
31118de8d7fSPeter Avalos switch (c->ostate) {
31218de8d7fSPeter Avalos case CHAN_OUTPUT_OPEN:
31318de8d7fSPeter Avalos case CHAN_OUTPUT_WAIT_DRAIN:
314ce74bacaSMatthew Dillon chan_shutdown_write(ssh, c);
31518de8d7fSPeter Avalos if (strcmp(c->ctype, "session") == 0)
316ce74bacaSMatthew Dillon chan_send_eow2(ssh, c);
31718de8d7fSPeter Avalos chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
31818de8d7fSPeter Avalos break;
31918de8d7fSPeter Avalos default:
32018de8d7fSPeter Avalos error("channel %d: chan_write_failed for ostate %d",
32118de8d7fSPeter Avalos c->self, c->ostate);
32218de8d7fSPeter Avalos break;
32318de8d7fSPeter Avalos }
32418de8d7fSPeter Avalos }
32518de8d7fSPeter Avalos
32618de8d7fSPeter Avalos void
chan_mark_dead(struct ssh * ssh,Channel * c)327ce74bacaSMatthew Dillon chan_mark_dead(struct ssh *ssh, Channel *c)
32818de8d7fSPeter Avalos {
32918de8d7fSPeter Avalos c->type = SSH_CHANNEL_ZOMBIE;
33018de8d7fSPeter Avalos }
33118de8d7fSPeter Avalos
33218de8d7fSPeter Avalos int
chan_is_dead(struct ssh * ssh,Channel * c,int do_send)333ce74bacaSMatthew Dillon chan_is_dead(struct ssh *ssh, Channel *c, int do_send)
33418de8d7fSPeter Avalos {
33518de8d7fSPeter Avalos if (c->type == SSH_CHANNEL_ZOMBIE) {
33618de8d7fSPeter Avalos debug2("channel %d: zombie", c->self);
33718de8d7fSPeter Avalos return 1;
33818de8d7fSPeter Avalos }
33918de8d7fSPeter Avalos if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED)
34018de8d7fSPeter Avalos return 0;
34150a69bb5SSascha Wildner if ((ssh->compat & SSH_BUG_EXTEOF) &&
34218de8d7fSPeter Avalos c->extended_usage == CHAN_EXTENDED_WRITE &&
34318de8d7fSPeter Avalos c->efd != -1 &&
344ce74bacaSMatthew Dillon sshbuf_len(c->extended) > 0) {
345ce74bacaSMatthew Dillon debug2("channel %d: active efd: %d len %zu",
346ce74bacaSMatthew Dillon c->self, c->efd, sshbuf_len(c->extended));
34718de8d7fSPeter Avalos return 0;
34818de8d7fSPeter Avalos }
349856ea928SPeter Avalos if (c->flags & CHAN_LOCAL) {
350856ea928SPeter Avalos debug2("channel %d: is dead (local)", c->self);
351856ea928SPeter Avalos return 1;
352856ea928SPeter Avalos }
35318de8d7fSPeter Avalos if (!(c->flags & CHAN_CLOSE_SENT)) {
35418de8d7fSPeter Avalos if (do_send) {
355ce74bacaSMatthew Dillon chan_send_close2(ssh, c);
35618de8d7fSPeter Avalos } else {
35718de8d7fSPeter Avalos /* channel would be dead if we sent a close */
35818de8d7fSPeter Avalos if (c->flags & CHAN_CLOSE_RCVD) {
35918de8d7fSPeter Avalos debug2("channel %d: almost dead",
36018de8d7fSPeter Avalos c->self);
36118de8d7fSPeter Avalos return 1;
36218de8d7fSPeter Avalos }
36318de8d7fSPeter Avalos }
36418de8d7fSPeter Avalos }
36518de8d7fSPeter Avalos if ((c->flags & CHAN_CLOSE_SENT) &&
36618de8d7fSPeter Avalos (c->flags & CHAN_CLOSE_RCVD)) {
36718de8d7fSPeter Avalos debug2("channel %d: is dead", c->self);
36818de8d7fSPeter Avalos return 1;
36918de8d7fSPeter Avalos }
37018de8d7fSPeter Avalos return 0;
37118de8d7fSPeter Avalos }
37218de8d7fSPeter Avalos
37318de8d7fSPeter Avalos /* helper */
37418de8d7fSPeter Avalos static void
chan_shutdown_write(struct ssh * ssh,Channel * c)375ce74bacaSMatthew Dillon chan_shutdown_write(struct ssh *ssh, Channel *c)
37618de8d7fSPeter Avalos {
377ce74bacaSMatthew Dillon sshbuf_reset(c->output);
378ce74bacaSMatthew Dillon if (c->type == SSH_CHANNEL_LARVAL)
37918de8d7fSPeter Avalos return;
38018de8d7fSPeter Avalos /* shutdown failure is allowed if write failed already */
38150a69bb5SSascha Wildner debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
38250a69bb5SSascha Wildner c->self, c->istate, c->ostate, c->sock, c->wfd, c->efd,
383664f4763Szrj channel_format_extended_usage(c));
38418de8d7fSPeter Avalos if (c->sock != -1) {
3850cbfa66cSDaniel Fojt if (shutdown(c->sock, SHUT_WR) == -1) {
38650a69bb5SSascha Wildner debug2_f("channel %d: shutdown() failed for "
38750a69bb5SSascha Wildner "fd %d [i%d o%d]: %.100s", c->self, c->sock,
38850a69bb5SSascha Wildner c->istate, c->ostate, strerror(errno));
389664f4763Szrj }
39018de8d7fSPeter Avalos } else {
39150a69bb5SSascha Wildner if (channel_close_fd(ssh, c, &c->wfd) < 0) {
39250a69bb5SSascha Wildner logit_f("channel %d: close() failed for "
39350a69bb5SSascha Wildner "fd %d [i%d o%d]: %.100s", c->self, c->wfd,
39450a69bb5SSascha Wildner c->istate, c->ostate, strerror(errno));
395664f4763Szrj }
39618de8d7fSPeter Avalos }
39718de8d7fSPeter Avalos }
398ce74bacaSMatthew Dillon
39918de8d7fSPeter Avalos static void
chan_shutdown_read(struct ssh * ssh,Channel * c)400ce74bacaSMatthew Dillon chan_shutdown_read(struct ssh *ssh, Channel *c)
40118de8d7fSPeter Avalos {
402ce74bacaSMatthew Dillon if (c->type == SSH_CHANNEL_LARVAL)
40318de8d7fSPeter Avalos return;
40450a69bb5SSascha Wildner debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
40550a69bb5SSascha Wildner c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd,
406664f4763Szrj channel_format_extended_usage(c));
40718de8d7fSPeter Avalos if (c->sock != -1) {
40818de8d7fSPeter Avalos /*
40918de8d7fSPeter Avalos * shutdown(sock, SHUT_READ) may return ENOTCONN if the
41018de8d7fSPeter Avalos * write side has been closed already. (bug on Linux)
41118de8d7fSPeter Avalos * HP-UX may return ENOTCONN also.
41218de8d7fSPeter Avalos */
4130cbfa66cSDaniel Fojt if (shutdown(c->sock, SHUT_RD) == -1 && errno != ENOTCONN) {
41450a69bb5SSascha Wildner error_f("channel %d: shutdown() failed for "
41550a69bb5SSascha Wildner "fd %d [i%d o%d]: %.100s", c->self, c->sock,
41650a69bb5SSascha Wildner c->istate, c->ostate, strerror(errno));
417664f4763Szrj }
41818de8d7fSPeter Avalos } else {
41950a69bb5SSascha Wildner if (channel_close_fd(ssh, c, &c->rfd) < 0) {
42050a69bb5SSascha Wildner logit_f("channel %d: close() failed for "
42150a69bb5SSascha Wildner "fd %d [i%d o%d]: %.100s", c->self, c->rfd,
42250a69bb5SSascha Wildner c->istate, c->ostate, strerror(errno));
423664f4763Szrj }
424664f4763Szrj }
425664f4763Szrj }
426664f4763Szrj
427664f4763Szrj static void
chan_shutdown_extended_read(struct ssh * ssh,Channel * c)428664f4763Szrj chan_shutdown_extended_read(struct ssh *ssh, Channel *c)
429664f4763Szrj {
430664f4763Szrj if (c->type == SSH_CHANNEL_LARVAL || c->efd == -1)
431664f4763Szrj return;
432664f4763Szrj if (c->extended_usage != CHAN_EXTENDED_READ &&
433664f4763Szrj c->extended_usage != CHAN_EXTENDED_IGNORE)
434664f4763Szrj return;
43550a69bb5SSascha Wildner debug_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])",
43650a69bb5SSascha Wildner c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd,
437664f4763Szrj channel_format_extended_usage(c));
43850a69bb5SSascha Wildner if (channel_close_fd(ssh, c, &c->efd) < 0) {
43950a69bb5SSascha Wildner logit_f("channel %d: close() failed for "
44050a69bb5SSascha Wildner "extended fd %d [i%d o%d]: %.100s", c->self, c->efd,
44150a69bb5SSascha Wildner c->istate, c->ostate, strerror(errno));
44218de8d7fSPeter Avalos }
44318de8d7fSPeter Avalos }
444