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