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