]>
Commit | Line | Data |
---|---|---|
aa3378df | 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 | ||
8efc0c15 | 30 | #include "includes.h" |
31 | RCSID("$Id$"); | |
32 | ||
33 | #include "ssh.h" | |
34 | ||
35 | #include "buffer.h" | |
36 | #include "packet.h" | |
37 | #include "channels.h" | |
38 | #include "nchan.h" | |
39 | ||
40 | static void chan_send_ieof(Channel *c); | |
41 | static void chan_send_oclose(Channel *c); | |
42 | static void chan_shutdown_write(Channel *c); | |
43 | static void chan_shutdown_read(Channel *c); | |
48e671d5 | 44 | static void chan_delete_if_full_closed(Channel *c); |
8efc0c15 | 45 | |
46 | /* | |
5260325f | 47 | * EVENTS update channel input/output states execute ACTIONS |
8efc0c15 | 48 | */ |
5260325f | 49 | |
8efc0c15 | 50 | /* events concerning the INPUT from socket for channel (istate) */ |
51 | void | |
5260325f | 52 | chan_rcvd_oclose(Channel *c) |
53 | { | |
54 | switch (c->istate) { | |
8efc0c15 | 55 | case CHAN_INPUT_WAIT_OCLOSE: |
56 | debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); | |
5260325f | 57 | c->istate = CHAN_INPUT_CLOSED; |
8efc0c15 | 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); | |
5260325f | 63 | c->istate = CHAN_INPUT_CLOSED; |
48e671d5 | 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; | |
8efc0c15 | 71 | break; |
72 | default: | |
57112b5a | 73 | error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate); |
48e671d5 | 74 | return; |
8efc0c15 | 75 | } |
48e671d5 | 76 | chan_delete_if_full_closed(c); |
8efc0c15 | 77 | } |
78 | void | |
5260325f | 79 | chan_read_failed(Channel *c) |
80 | { | |
81 | switch (c->istate) { | |
8efc0c15 | 82 | case CHAN_INPUT_OPEN: |
83 | debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); | |
84 | chan_shutdown_read(c); | |
5260325f | 85 | c->istate = CHAN_INPUT_WAIT_DRAIN; |
8efc0c15 | 86 | break; |
87 | default: | |
57112b5a | 88 | error("internal error: we do not read, but chan_read_failed %d for istate %d", |
5260325f | 89 | c->self, c->istate); |
8efc0c15 | 90 | break; |
91 | } | |
92 | } | |
93 | void | |
5260325f | 94 | chan_ibuf_empty(Channel *c) |
95 | { | |
96 | if (buffer_len(&c->input)) { | |
57112b5a | 97 | error("internal error: chan_ibuf_empty %d for non empty buffer", c->self); |
8efc0c15 | 98 | return; |
99 | } | |
5260325f | 100 | switch (c->istate) { |
8efc0c15 | 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); | |
5260325f | 104 | c->istate = CHAN_INPUT_WAIT_OCLOSE; |
8efc0c15 | 105 | break; |
106 | default: | |
57112b5a | 107 | error("internal error: chan_ibuf_empty %d for istate %d", c->self, c->istate); |
8efc0c15 | 108 | break; |
109 | } | |
110 | } | |
5260325f | 111 | |
8efc0c15 | 112 | /* events concerning the OUTPUT from channel for socket (ostate) */ |
113 | void | |
5260325f | 114 | chan_rcvd_ieof(Channel *c) |
115 | { | |
116 | switch (c->ostate) { | |
8efc0c15 | 117 | case CHAN_OUTPUT_OPEN: |
118 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); | |
5260325f | 119 | c->ostate = CHAN_OUTPUT_WAIT_DRAIN; |
8efc0c15 | 120 | break; |
121 | case CHAN_OUTPUT_WAIT_IEOF: | |
122 | debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); | |
5260325f | 123 | c->ostate = CHAN_OUTPUT_CLOSED; |
48e671d5 | 124 | chan_delete_if_full_closed(c); |
8efc0c15 | 125 | break; |
126 | default: | |
57112b5a | 127 | error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate); |
8efc0c15 | 128 | break; |
129 | } | |
130 | } | |
131 | void | |
5260325f | 132 | chan_write_failed(Channel *c) |
133 | { | |
134 | switch (c->ostate) { | |
8efc0c15 | 135 | case CHAN_OUTPUT_OPEN: |
136 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); | |
137 | chan_send_oclose(c); | |
5260325f | 138 | c->ostate = CHAN_OUTPUT_WAIT_IEOF; |
8efc0c15 | 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); | |
5260325f | 143 | c->ostate = CHAN_OUTPUT_CLOSED; |
48e671d5 | 144 | chan_delete_if_full_closed(c); |
8efc0c15 | 145 | break; |
146 | default: | |
57112b5a | 147 | error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate); |
8efc0c15 | 148 | break; |
149 | } | |
150 | } | |
151 | void | |
5260325f | 152 | chan_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); | |
8efc0c15 | 156 | return; |
157 | } | |
5260325f | 158 | switch (c->ostate) { |
8efc0c15 | 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); | |
5260325f | 162 | c->ostate = CHAN_OUTPUT_CLOSED; |
48e671d5 | 163 | chan_delete_if_full_closed(c); |
8efc0c15 | 164 | break; |
165 | default: | |
57112b5a | 166 | error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate); |
8efc0c15 | 167 | break; |
168 | } | |
169 | } | |
5260325f | 170 | |
8efc0c15 | 171 | /* |
5260325f | 172 | * ACTIONS: should never update the channel states: c->istate or c->ostate |
8efc0c15 | 173 | */ |
174 | static void | |
5260325f | 175 | chan_send_ieof(Channel *c) |
176 | { | |
177 | switch (c->istate) { | |
8efc0c15 | 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: | |
57112b5a | 185 | error("internal error: channel %d: cannot send IEOF for istate %d", c->self, c->istate); |
8efc0c15 | 186 | break; |
187 | } | |
188 | } | |
189 | static void | |
5260325f | 190 | chan_send_oclose(Channel *c) |
191 | { | |
192 | switch (c->ostate) { | |
8efc0c15 | 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: | |
57112b5a | 202 | error("internal error: channel %d: cannot send OCLOSE for ostate %d", c->self, c->istate); |
8efc0c15 | 203 | break; |
204 | } | |
205 | } | |
5260325f | 206 | |
8efc0c15 | 207 | /* helper */ |
208 | static void | |
5260325f | 209 | chan_shutdown_write(Channel *c) |
210 | { | |
aa3378df | 211 | /* shutdown failure is allowed if write failed already */ |
8efc0c15 | 212 | debug("channel %d: shutdown_write", c->self); |
5260325f | 213 | if (shutdown(c->sock, SHUT_WR) < 0) |
aa3378df | 214 | debug("chan_shutdown_write failed for #%d/fd%d: %.100s", |
5260325f | 215 | c->self, c->sock, strerror(errno)); |
8efc0c15 | 216 | } |
217 | static void | |
5260325f | 218 | chan_shutdown_read(Channel *c) |
219 | { | |
8efc0c15 | 220 | debug("channel %d: shutdown_read", c->self); |
5260325f | 221 | if (shutdown(c->sock, SHUT_RD) < 0) |
48e671d5 | 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)); | |
8efc0c15 | 224 | } |
225 | static void | |
48e671d5 | 226 | chan_delete_if_full_closed(Channel *c) |
5260325f | 227 | { |
228 | if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { | |
48e671d5 | 229 | debug("channel %d: full closed", c->self); |
8efc0c15 | 230 | channel_free(c->self); |
231 | } | |
232 | } | |
233 | void | |
5260325f | 234 | chan_init_iostates(Channel *c) |
235 | { | |
236 | c->ostate = CHAN_OUTPUT_OPEN; | |
237 | c->istate = CHAN_INPUT_OPEN; | |
8efc0c15 | 238 | } |