nchan.c revision 204917
1238106Sdes/* $OpenBSD: nchan.c,v 1.63 2010/01/26 01:28:35 djm Exp $ */ 2238106Sdes/* 3238106Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. 4238106Sdes * 5238106Sdes * Redistribution and use in source and binary forms, with or without 6238106Sdes * modification, are permitted provided that the following conditions 7238106Sdes * are met: 8238106Sdes * 1. Redistributions of source code must retain the above copyright 9238106Sdes * notice, this list of conditions and the following disclaimer. 10238106Sdes * 2. Redistributions in binary form must reproduce the above copyright 11238106Sdes * notice, this list of conditions and the following disclaimer in the 12238106Sdes * documentation and/or other materials provided with the distribution. 13238106Sdes * 14238106Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15238106Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16238106Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17238106Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18238106Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19238106Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20238106Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21238106Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22238106Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23238106Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24238106Sdes */ 25238106Sdes 26238106Sdes#include "includes.h" 27238106Sdes 28238106Sdes#include <sys/types.h> 29238106Sdes#include <sys/socket.h> 30238106Sdes 31238106Sdes#include <errno.h> 32238106Sdes#include <string.h> 33238106Sdes#include <stdarg.h> 34238106Sdes 35238106Sdes#include "openbsd-compat/sys-queue.h" 36238106Sdes#include "ssh1.h" 37238106Sdes#include "ssh2.h" 38238106Sdes#include "buffer.h" 39238106Sdes#include "packet.h" 40238106Sdes#include "channels.h" 41238106Sdes#include "compat.h" 42238106Sdes#include "log.h" 43238106Sdes 44238106Sdes/* 45238106Sdes * SSH Protocol 1.5 aka New Channel Protocol 46238106Sdes * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. 47238106Sdes * Written by Markus Friedl in October 1999 48238106Sdes * 49238106Sdes * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the 50238106Sdes * tear down of channels: 51238106Sdes * 52238106Sdes * 1.3: strict request-ack-protocol: 53238106Sdes * CLOSE -> 54238106Sdes * <- CLOSE_CONFIRM 55238106Sdes * 56238106Sdes * 1.5: uses variations of: 57238106Sdes * IEOF -> 58238106Sdes * <- OCLOSE 59238106Sdes * <- IEOF 60238106Sdes * OCLOSE -> 61238106Sdes * i.e. both sides have to close the channel 62238106Sdes * 63238106Sdes * 2.0: the EOF messages are optional 64238106Sdes * 65238106Sdes * See the debugging output from 'ssh -v' and 'sshd -d' of 66238106Sdes * ssh-1.2.27 as an example. 67238106Sdes * 68238106Sdes */ 69238106Sdes 70238106Sdes/* functions manipulating channel states */ 71238106Sdes/* 72238106Sdes * EVENTS update channel input/output states execute ACTIONS 73238106Sdes */ 74238106Sdes/* 75238106Sdes * ACTIONS: should never update the channel states 76238106Sdes */ 77238106Sdesstatic void chan_send_ieof1(Channel *); 78238106Sdesstatic void chan_send_oclose1(Channel *); 79238106Sdesstatic void chan_send_close2(Channel *); 80238106Sdesstatic void chan_send_eof2(Channel *); 81238106Sdesstatic void chan_send_eow2(Channel *); 82238106Sdes 83238106Sdes/* helper */ 84238106Sdesstatic void chan_shutdown_write(Channel *); 85238106Sdesstatic void chan_shutdown_read(Channel *); 86238106Sdes 87238106Sdesstatic char *ostates[] = { "open", "drain", "wait_ieof", "closed" }; 88238106Sdesstatic char *istates[] = { "open", "drain", "wait_oclose", "closed" }; 89238106Sdes 90238106Sdesstatic void 91238106Sdeschan_set_istate(Channel *c, u_int next) 92238106Sdes{ 93238106Sdes if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED) 94238106Sdes fatal("chan_set_istate: bad state %d -> %d", c->istate, next); 95238106Sdes debug2("channel %d: input %s -> %s", c->self, istates[c->istate], 96238106Sdes istates[next]); 97238106Sdes c->istate = next; 98238106Sdes} 99238106Sdesstatic void 100238106Sdeschan_set_ostate(Channel *c, u_int next) 101238106Sdes{ 102238106Sdes if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED) 103238106Sdes fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next); 104238106Sdes debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate], 105238106Sdes ostates[next]); 106238106Sdes c->ostate = next; 107238106Sdes} 108238106Sdes 109238106Sdes/* 110238106Sdes * SSH1 specific implementation of event functions 111238106Sdes */ 112238106Sdes 113238106Sdesstatic void 114238106Sdeschan_rcvd_oclose1(Channel *c) 115238106Sdes{ 116238106Sdes debug2("channel %d: rcvd oclose", c->self); 117238106Sdes switch (c->istate) { 118238106Sdes case CHAN_INPUT_WAIT_OCLOSE: 119238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 120238106Sdes break; 121238106Sdes case CHAN_INPUT_OPEN: 122238106Sdes chan_shutdown_read(c); 123238106Sdes chan_send_ieof1(c); 124238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 125238106Sdes break; 126238106Sdes case CHAN_INPUT_WAIT_DRAIN: 127238106Sdes /* both local read_failed and remote write_failed */ 128238106Sdes chan_send_ieof1(c); 129238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 130238106Sdes break; 131238106Sdes default: 132238106Sdes error("channel %d: protocol error: rcvd_oclose for istate %d", 133238106Sdes c->self, c->istate); 134238106Sdes return; 135238106Sdes } 136238106Sdes} 137238106Sdesvoid 138238106Sdeschan_read_failed(Channel *c) 139238106Sdes{ 140238106Sdes debug2("channel %d: read failed", c->self); 141238106Sdes switch (c->istate) { 142238106Sdes case CHAN_INPUT_OPEN: 143238106Sdes chan_shutdown_read(c); 144238106Sdes chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN); 145238106Sdes break; 146238106Sdes default: 147238106Sdes error("channel %d: chan_read_failed for istate %d", 148238106Sdes c->self, c->istate); 149238106Sdes break; 150238106Sdes } 151238106Sdes} 152238106Sdesvoid 153238106Sdeschan_ibuf_empty(Channel *c) 154238106Sdes{ 155238106Sdes debug2("channel %d: ibuf empty", c->self); 156238106Sdes if (buffer_len(&c->input)) { 157238106Sdes error("channel %d: chan_ibuf_empty for non empty buffer", 158238106Sdes c->self); 159238106Sdes return; 160238106Sdes } 161238106Sdes switch (c->istate) { 162238106Sdes case CHAN_INPUT_WAIT_DRAIN: 163238106Sdes if (compat20) { 164238106Sdes if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL))) 165238106Sdes chan_send_eof2(c); 166238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 167238106Sdes } else { 168238106Sdes chan_send_ieof1(c); 169238106Sdes chan_set_istate(c, CHAN_INPUT_WAIT_OCLOSE); 170238106Sdes } 171238106Sdes break; 172238106Sdes default: 173238106Sdes error("channel %d: chan_ibuf_empty for istate %d", 174238106Sdes c->self, c->istate); 175238106Sdes break; 176238106Sdes } 177238106Sdes} 178238106Sdesstatic void 179238106Sdeschan_rcvd_ieof1(Channel *c) 180238106Sdes{ 181238106Sdes debug2("channel %d: rcvd ieof", c->self); 182238106Sdes switch (c->ostate) { 183238106Sdes case CHAN_OUTPUT_OPEN: 184238106Sdes chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); 185238106Sdes break; 186238106Sdes case CHAN_OUTPUT_WAIT_IEOF: 187238106Sdes chan_set_ostate(c, CHAN_OUTPUT_CLOSED); 188238106Sdes break; 189238106Sdes default: 190238106Sdes error("channel %d: protocol error: rcvd_ieof for ostate %d", 191238106Sdes c->self, c->ostate); 192238106Sdes break; 193238106Sdes } 194238106Sdes} 195238106Sdesstatic void 196238106Sdeschan_write_failed1(Channel *c) 197238106Sdes{ 198238106Sdes debug2("channel %d: write failed", c->self); 199238106Sdes switch (c->ostate) { 200238106Sdes case CHAN_OUTPUT_OPEN: 201238106Sdes chan_shutdown_write(c); 202238106Sdes chan_send_oclose1(c); 203238106Sdes chan_set_ostate(c, CHAN_OUTPUT_WAIT_IEOF); 204238106Sdes break; 205238106Sdes case CHAN_OUTPUT_WAIT_DRAIN: 206238106Sdes chan_shutdown_write(c); 207238106Sdes chan_send_oclose1(c); 208238106Sdes chan_set_ostate(c, CHAN_OUTPUT_CLOSED); 209238106Sdes break; 210238106Sdes default: 211238106Sdes error("channel %d: chan_write_failed for ostate %d", 212238106Sdes c->self, c->ostate); 213238106Sdes break; 214238106Sdes } 215238106Sdes} 216238106Sdesvoid 217238106Sdeschan_obuf_empty(Channel *c) 218238106Sdes{ 219238106Sdes debug2("channel %d: obuf empty", c->self); 220238106Sdes if (buffer_len(&c->output)) { 221238106Sdes error("channel %d: chan_obuf_empty for non empty buffer", 222238106Sdes c->self); 223238106Sdes return; 224238106Sdes } 225238106Sdes switch (c->ostate) { 226238106Sdes case CHAN_OUTPUT_WAIT_DRAIN: 227238106Sdes chan_shutdown_write(c); 228238106Sdes if (!compat20) 229238106Sdes chan_send_oclose1(c); 230238106Sdes chan_set_ostate(c, CHAN_OUTPUT_CLOSED); 231238106Sdes break; 232238106Sdes default: 233238106Sdes error("channel %d: internal error: obuf_empty for ostate %d", 234238106Sdes c->self, c->ostate); 235238106Sdes break; 236238106Sdes } 237238106Sdes} 238238106Sdesstatic void 239238106Sdeschan_send_ieof1(Channel *c) 240238106Sdes{ 241238106Sdes debug2("channel %d: send ieof", c->self); 242238106Sdes switch (c->istate) { 243238106Sdes case CHAN_INPUT_OPEN: 244238106Sdes case CHAN_INPUT_WAIT_DRAIN: 245238106Sdes packet_start(SSH_MSG_CHANNEL_INPUT_EOF); 246238106Sdes packet_put_int(c->remote_id); 247238106Sdes packet_send(); 248238106Sdes break; 249238106Sdes default: 250238106Sdes error("channel %d: cannot send ieof for istate %d", 251238106Sdes c->self, c->istate); 252238106Sdes break; 253238106Sdes } 254238106Sdes} 255238106Sdesstatic void 256238106Sdeschan_send_oclose1(Channel *c) 257238106Sdes{ 258238106Sdes debug2("channel %d: send oclose", c->self); 259238106Sdes switch (c->ostate) { 260238106Sdes case CHAN_OUTPUT_OPEN: 261238106Sdes case CHAN_OUTPUT_WAIT_DRAIN: 262238106Sdes buffer_clear(&c->output); 263238106Sdes packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE); 264238106Sdes packet_put_int(c->remote_id); 265238106Sdes packet_send(); 266238106Sdes break; 267238106Sdes default: 268238106Sdes error("channel %d: cannot send oclose for ostate %d", 269238106Sdes c->self, c->ostate); 270238106Sdes break; 271238106Sdes } 272238106Sdes} 273238106Sdes 274238106Sdes/* 275238106Sdes * the same for SSH2 276238106Sdes */ 277238106Sdesstatic void 278238106Sdeschan_rcvd_close2(Channel *c) 279238106Sdes{ 280238106Sdes debug2("channel %d: rcvd close", c->self); 281238106Sdes if (!(c->flags & CHAN_LOCAL)) { 282249141Sdes if (c->flags & CHAN_CLOSE_RCVD) 283238106Sdes error("channel %d: protocol error: close rcvd twice", 284249141Sdes c->self); 285238106Sdes c->flags |= CHAN_CLOSE_RCVD; 286238106Sdes } 287238106Sdes if (c->type == SSH_CHANNEL_LARVAL) { 288238106Sdes /* tear down larval channels immediately */ 289238106Sdes chan_set_ostate(c, CHAN_OUTPUT_CLOSED); 290238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 291238106Sdes return; 292238106Sdes } 293238106Sdes switch (c->ostate) { 294238106Sdes case CHAN_OUTPUT_OPEN: 295238106Sdes /* 296238106Sdes * wait until a data from the channel is consumed if a CLOSE 297238106Sdes * is received 298238106Sdes */ 299238106Sdes chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); 300238106Sdes break; 301238106Sdes } 302238106Sdes switch (c->istate) { 303238106Sdes case CHAN_INPUT_OPEN: 304238106Sdes chan_shutdown_read(c); 305238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 306238106Sdes break; 307238106Sdes case CHAN_INPUT_WAIT_DRAIN: 308238106Sdes if (!(c->flags & CHAN_LOCAL)) 309238106Sdes chan_send_eof2(c); 310238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 311238106Sdes break; 312238106Sdes } 313238106Sdes} 314238106Sdes 315238106Sdesvoid 316238106Sdeschan_rcvd_eow(Channel *c) 317238106Sdes{ 318238106Sdes debug2("channel %d: rcvd eow", c->self); 319238106Sdes switch (c->istate) { 320238106Sdes case CHAN_INPUT_OPEN: 321238106Sdes chan_shutdown_read(c); 322238106Sdes chan_set_istate(c, CHAN_INPUT_CLOSED); 323238106Sdes break; 324238106Sdes } 325238106Sdes} 326238106Sdesstatic void 327238106Sdeschan_rcvd_eof2(Channel *c) 328238106Sdes{ 329238106Sdes debug2("channel %d: rcvd eof", c->self); 330238106Sdes c->flags |= CHAN_EOF_RCVD; 331238106Sdes if (c->ostate == CHAN_OUTPUT_OPEN) 332238106Sdes chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); 333238106Sdes} 334238106Sdesstatic void 335238106Sdeschan_write_failed2(Channel *c) 336238106Sdes{ 337238106Sdes debug2("channel %d: write failed", c->self); 338238106Sdes switch (c->ostate) { 339238106Sdes case CHAN_OUTPUT_OPEN: 340238106Sdes case CHAN_OUTPUT_WAIT_DRAIN: 341238106Sdes chan_shutdown_write(c); 342238106Sdes if (strcmp(c->ctype, "session") == 0) 343238106Sdes chan_send_eow2(c); 344238106Sdes chan_set_ostate(c, CHAN_OUTPUT_CLOSED); 345238106Sdes break; 346238106Sdes default: 347238106Sdes error("channel %d: chan_write_failed for ostate %d", 348238106Sdes c->self, c->ostate); 349238106Sdes break; 350238106Sdes } 351238106Sdes} 352238106Sdesstatic void 353238106Sdeschan_send_eof2(Channel *c) 354238106Sdes{ 355238106Sdes debug2("channel %d: send eof", c->self); 356238106Sdes switch (c->istate) { 357238106Sdes case CHAN_INPUT_WAIT_DRAIN: 358238106Sdes packet_start(SSH2_MSG_CHANNEL_EOF); 359238106Sdes packet_put_int(c->remote_id); 360238106Sdes packet_send(); 361238106Sdes c->flags |= CHAN_EOF_SENT; 362238106Sdes break; 363238106Sdes default: 364238106Sdes error("channel %d: cannot send eof for istate %d", 365238106Sdes c->self, c->istate); 366238106Sdes break; 367238106Sdes } 368238106Sdes} 369238106Sdesstatic void 370238106Sdeschan_send_close2(Channel *c) 371238106Sdes{ 372238106Sdes debug2("channel %d: send close", c->self); 373238106Sdes if (c->ostate != CHAN_OUTPUT_CLOSED || 374238106Sdes c->istate != CHAN_INPUT_CLOSED) { 375238106Sdes error("channel %d: cannot send close for istate/ostate %d/%d", 376238106Sdes c->self, c->istate, c->ostate); 377238106Sdes } else if (c->flags & CHAN_CLOSE_SENT) { 378238106Sdes error("channel %d: already sent close", c->self); 379238106Sdes } else { 380238106Sdes packet_start(SSH2_MSG_CHANNEL_CLOSE); 381238106Sdes packet_put_int(c->remote_id); 382238106Sdes packet_send(); 383238106Sdes c->flags |= CHAN_CLOSE_SENT; 384238106Sdes } 385238106Sdes} 386238106Sdesstatic void 387238106Sdeschan_send_eow2(Channel *c) 388238106Sdes{ 389238106Sdes debug2("channel %d: send eow", c->self); 390238106Sdes if (c->ostate == CHAN_OUTPUT_CLOSED) { 391238106Sdes error("channel %d: must not sent eow on closed output", 392238106Sdes c->self); 393238106Sdes return; 394238106Sdes } 395238106Sdes if (!(datafellows & SSH_NEW_OPENSSH)) 396238106Sdes return; 397238106Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 398238106Sdes packet_put_int(c->remote_id); 399238106Sdes packet_put_cstring("eow@openssh.com"); 400238106Sdes packet_put_char(0); 401238106Sdes packet_send(); 402238106Sdes} 403238106Sdes 404238106Sdes/* shared */ 405238106Sdes 406238106Sdesvoid 407238106Sdeschan_rcvd_ieof(Channel *c) 408238106Sdes{ 409238106Sdes if (compat20) 410238106Sdes chan_rcvd_eof2(c); 411238106Sdes else 412238106Sdes chan_rcvd_ieof1(c); 413238106Sdes if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN && 414238106Sdes buffer_len(&c->output) == 0 && 415238106Sdes !CHANNEL_EFD_OUTPUT_ACTIVE(c)) 416238106Sdes chan_obuf_empty(c); 417238106Sdes} 418238106Sdesvoid 419238106Sdeschan_rcvd_oclose(Channel *c) 420238106Sdes{ 421238106Sdes if (compat20) 422238106Sdes chan_rcvd_close2(c); 423238106Sdes else 424238106Sdes chan_rcvd_oclose1(c); 425238106Sdes} 426238106Sdesvoid 427238106Sdeschan_write_failed(Channel *c) 428238106Sdes{ 429238106Sdes if (compat20) 430238106Sdes chan_write_failed2(c); 431238106Sdes else 432238106Sdes chan_write_failed1(c); 433238106Sdes} 434238106Sdes 435238106Sdesvoid 436238106Sdeschan_mark_dead(Channel *c) 437238106Sdes{ 438238106Sdes c->type = SSH_CHANNEL_ZOMBIE; 439238106Sdes} 440238106Sdes 441238106Sdesint 442238106Sdeschan_is_dead(Channel *c, int do_send) 443238106Sdes{ 444238106Sdes if (c->type == SSH_CHANNEL_ZOMBIE) { 445238106Sdes debug2("channel %d: zombie", c->self); 446238106Sdes return 1; 447238106Sdes } 448238106Sdes if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED) 449238106Sdes return 0; 450238106Sdes if (!compat20) { 451238106Sdes debug2("channel %d: is dead", c->self); 452238106Sdes return 1; 453238106Sdes } 454238106Sdes if ((datafellows & SSH_BUG_EXTEOF) && 455238106Sdes c->extended_usage == CHAN_EXTENDED_WRITE && 456238106Sdes c->efd != -1 && 457238106Sdes buffer_len(&c->extended) > 0) { 458238106Sdes debug2("channel %d: active efd: %d len %d", 459238106Sdes c->self, c->efd, buffer_len(&c->extended)); 460238106Sdes return 0; 461238106Sdes } 462238106Sdes if (c->flags & CHAN_LOCAL) { 463238106Sdes debug2("channel %d: is dead (local)", c->self); 464238106Sdes return 1; 465238106Sdes } 466238106Sdes if (!(c->flags & CHAN_CLOSE_SENT)) { 467238106Sdes if (do_send) { 468238106Sdes chan_send_close2(c); 469238106Sdes } else { 470238106Sdes /* channel would be dead if we sent a close */ 471238106Sdes if (c->flags & CHAN_CLOSE_RCVD) { 472238106Sdes debug2("channel %d: almost dead", 473238106Sdes c->self); 474238106Sdes return 1; 475238106Sdes } 476238106Sdes } 477238106Sdes } 478238106Sdes if ((c->flags & CHAN_CLOSE_SENT) && 479249141Sdes (c->flags & CHAN_CLOSE_RCVD)) { 480238106Sdes debug2("channel %d: is dead", c->self); 481238106Sdes return 1; 482238106Sdes } 483238106Sdes return 0; 484238106Sdes} 485238106Sdes 486238106Sdes/* helper */ 487238106Sdesstatic void 488238106Sdeschan_shutdown_write(Channel *c) 489238106Sdes{ 490238106Sdes buffer_clear(&c->output); 491238106Sdes if (compat20 && c->type == SSH_CHANNEL_LARVAL) 492238106Sdes return; 493238106Sdes /* shutdown failure is allowed if write failed already */ 494238106Sdes debug2("channel %d: close_write", c->self); 495238106Sdes if (c->sock != -1) { 496238106Sdes if (shutdown(c->sock, SHUT_WR) < 0) 497238106Sdes debug2("channel %d: chan_shutdown_write: " 498238106Sdes "shutdown() failed for fd %d: %.100s", 499238106Sdes c->self, c->sock, strerror(errno)); 500238106Sdes } else { 501238106Sdes if (channel_close_fd(&c->wfd) < 0) 502238106Sdes logit("channel %d: chan_shutdown_write: " 503238106Sdes "close() failed for fd %d: %.100s", 504238106Sdes c->self, c->wfd, strerror(errno)); 505238106Sdes } 506238106Sdes} 507238106Sdesstatic void 508238106Sdeschan_shutdown_read(Channel *c) 509238106Sdes{ 510238106Sdes if (compat20 && c->type == SSH_CHANNEL_LARVAL) 511238106Sdes return; 512238106Sdes debug2("channel %d: close_read", c->self); 513238106Sdes if (c->sock != -1) { 514238106Sdes /* 515238106Sdes * shutdown(sock, SHUT_READ) may return ENOTCONN if the 516238106Sdes * write side has been closed already. (bug on Linux) 517238106Sdes * HP-UX may return ENOTCONN also. 518238106Sdes */ 519238106Sdes if (shutdown(c->sock, SHUT_RD) < 0 520238106Sdes && errno != ENOTCONN) 521238106Sdes error("channel %d: chan_shutdown_read: " 522238106Sdes "shutdown() failed for fd %d [i%d o%d]: %.100s", 523238106Sdes c->self, c->sock, c->istate, c->ostate, 524238106Sdes strerror(errno)); 525238106Sdes } else { 526238106Sdes if (channel_close_fd(&c->rfd) < 0) 527238106Sdes logit("channel %d: chan_shutdown_read: " 528238106Sdes "close() failed for fd %d: %.100s", 529238106Sdes c->self, c->rfd, strerror(errno)); 530238106Sdes } 531238106Sdes} 532238106Sdes