diff options
author | Colin Watson <cjwatson@debian.org> | 2006-05-12 08:53:37 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2006-05-12 08:53:37 +0000 |
commit | 2ee73b36b9a35daeaa4b065046882dc1f5f551b6 (patch) | |
tree | f64a4ace625514e94759878c0b94ab0a79805bbd /channels.c | |
parent | 3c190ec8e469477ea65fbf4cc83062c65c281434 (diff) | |
parent | 3e2e0ac10674d77618c4c7339e18b83ced247492 (diff) |
Merge 4.3p2 to the trunk.
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 168 |
1 files changed, 130 insertions, 38 deletions
diff --git a/channels.c b/channels.c index 707b57d86..92448da77 100644 --- a/channels.c +++ b/channels.c | |||
@@ -39,7 +39,7 @@ | |||
39 | */ | 39 | */ |
40 | 40 | ||
41 | #include "includes.h" | 41 | #include "includes.h" |
42 | RCSID("$OpenBSD: channels.c,v 1.223 2005/07/17 07:17:54 djm Exp $"); | 42 | RCSID("$OpenBSD: channels.c,v 1.232 2006/01/30 12:22:22 reyk Exp $"); |
43 | 43 | ||
44 | #include "ssh.h" | 44 | #include "ssh.h" |
45 | #include "ssh1.h" | 45 | #include "ssh1.h" |
@@ -58,8 +58,6 @@ RCSID("$OpenBSD: channels.c,v 1.223 2005/07/17 07:17:54 djm Exp $"); | |||
58 | 58 | ||
59 | /* -- channel core */ | 59 | /* -- channel core */ |
60 | 60 | ||
61 | #define CHAN_RBUF 16*1024 | ||
62 | |||
63 | /* | 61 | /* |
64 | * Pointer to an array containing all allocated channels. The array is | 62 | * Pointer to an array containing all allocated channels. The array is |
65 | * dynamically extended as needed. | 63 | * dynamically extended as needed. |
@@ -142,23 +140,51 @@ static void port_open_helper(Channel *c, char *rtype); | |||
142 | /* -- channel core */ | 140 | /* -- channel core */ |
143 | 141 | ||
144 | Channel * | 142 | Channel * |
145 | channel_lookup(int id) | 143 | channel_by_id(int id) |
146 | { | 144 | { |
147 | Channel *c; | 145 | Channel *c; |
148 | 146 | ||
149 | if (id < 0 || (u_int)id >= channels_alloc) { | 147 | if (id < 0 || (u_int)id >= channels_alloc) { |
150 | logit("channel_lookup: %d: bad id", id); | 148 | logit("channel_by_id: %d: bad id", id); |
151 | return NULL; | 149 | return NULL; |
152 | } | 150 | } |
153 | c = channels[id]; | 151 | c = channels[id]; |
154 | if (c == NULL) { | 152 | if (c == NULL) { |
155 | logit("channel_lookup: %d: bad id: channel free", id); | 153 | logit("channel_by_id: %d: bad id: channel free", id); |
156 | return NULL; | 154 | return NULL; |
157 | } | 155 | } |
158 | return c; | 156 | return c; |
159 | } | 157 | } |
160 | 158 | ||
161 | /* | 159 | /* |
160 | * Returns the channel if it is allowed to receive protocol messages. | ||
161 | * Private channels, like listening sockets, may not receive messages. | ||
162 | */ | ||
163 | Channel * | ||
164 | channel_lookup(int id) | ||
165 | { | ||
166 | Channel *c; | ||
167 | |||
168 | if ((c = channel_by_id(id)) == NULL) | ||
169 | return (NULL); | ||
170 | |||
171 | switch(c->type) { | ||
172 | case SSH_CHANNEL_X11_OPEN: | ||
173 | case SSH_CHANNEL_LARVAL: | ||
174 | case SSH_CHANNEL_CONNECTING: | ||
175 | case SSH_CHANNEL_DYNAMIC: | ||
176 | case SSH_CHANNEL_OPENING: | ||
177 | case SSH_CHANNEL_OPEN: | ||
178 | case SSH_CHANNEL_INPUT_DRAINING: | ||
179 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
180 | return (c); | ||
181 | break; | ||
182 | } | ||
183 | logit("Non-public channel %d, type %d.", id, c->type); | ||
184 | return (NULL); | ||
185 | } | ||
186 | |||
187 | /* | ||
162 | * Register filedescriptors for a channel, used when allocating a channel or | 188 | * Register filedescriptors for a channel, used when allocating a channel or |
163 | * when the channel consumer/producer is ready, e.g. shell exec'd | 189 | * when the channel consumer/producer is ready, e.g. shell exec'd |
164 | */ | 190 | */ |
@@ -269,9 +295,11 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, | |||
269 | c->force_drain = 0; | 295 | c->force_drain = 0; |
270 | c->single_connection = 0; | 296 | c->single_connection = 0; |
271 | c->detach_user = NULL; | 297 | c->detach_user = NULL; |
298 | c->detach_close = 0; | ||
272 | c->confirm = NULL; | 299 | c->confirm = NULL; |
273 | c->confirm_ctx = NULL; | 300 | c->confirm_ctx = NULL; |
274 | c->input_filter = NULL; | 301 | c->input_filter = NULL; |
302 | c->output_filter = NULL; | ||
275 | debug("channel %d: new [%s]", found, remote_name); | 303 | debug("channel %d: new [%s]", found, remote_name); |
276 | return c; | 304 | return c; |
277 | } | 305 | } |
@@ -628,29 +656,32 @@ channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) | |||
628 | c->confirm_ctx = ctx; | 656 | c->confirm_ctx = ctx; |
629 | } | 657 | } |
630 | void | 658 | void |
631 | channel_register_cleanup(int id, channel_callback_fn *fn) | 659 | channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) |
632 | { | 660 | { |
633 | Channel *c = channel_lookup(id); | 661 | Channel *c = channel_by_id(id); |
634 | 662 | ||
635 | if (c == NULL) { | 663 | if (c == NULL) { |
636 | logit("channel_register_cleanup: %d: bad id", id); | 664 | logit("channel_register_cleanup: %d: bad id", id); |
637 | return; | 665 | return; |
638 | } | 666 | } |
639 | c->detach_user = fn; | 667 | c->detach_user = fn; |
668 | c->detach_close = do_close; | ||
640 | } | 669 | } |
641 | void | 670 | void |
642 | channel_cancel_cleanup(int id) | 671 | channel_cancel_cleanup(int id) |
643 | { | 672 | { |
644 | Channel *c = channel_lookup(id); | 673 | Channel *c = channel_by_id(id); |
645 | 674 | ||
646 | if (c == NULL) { | 675 | if (c == NULL) { |
647 | logit("channel_cancel_cleanup: %d: bad id", id); | 676 | logit("channel_cancel_cleanup: %d: bad id", id); |
648 | return; | 677 | return; |
649 | } | 678 | } |
650 | c->detach_user = NULL; | 679 | c->detach_user = NULL; |
680 | c->detach_close = 0; | ||
651 | } | 681 | } |
652 | void | 682 | void |
653 | channel_register_filter(int id, channel_filter_fn *fn) | 683 | channel_register_filter(int id, channel_infilter_fn *ifn, |
684 | channel_outfilter_fn *ofn) | ||
654 | { | 685 | { |
655 | Channel *c = channel_lookup(id); | 686 | Channel *c = channel_lookup(id); |
656 | 687 | ||
@@ -658,7 +689,8 @@ channel_register_filter(int id, channel_filter_fn *fn) | |||
658 | logit("channel_register_filter: %d: bad id", id); | 689 | logit("channel_register_filter: %d: bad id", id); |
659 | return; | 690 | return; |
660 | } | 691 | } |
661 | c->input_filter = fn; | 692 | c->input_filter = ifn; |
693 | c->output_filter = ofn; | ||
662 | } | 694 | } |
663 | 695 | ||
664 | void | 696 | void |
@@ -1227,6 +1259,19 @@ port_open_helper(Channel *c, char *rtype) | |||
1227 | xfree(remote_ipaddr); | 1259 | xfree(remote_ipaddr); |
1228 | } | 1260 | } |
1229 | 1261 | ||
1262 | static void | ||
1263 | channel_set_reuseaddr(int fd) | ||
1264 | { | ||
1265 | int on = 1; | ||
1266 | |||
1267 | /* | ||
1268 | * Set socket options. | ||
1269 | * Allow local port reuse in TIME_WAIT. | ||
1270 | */ | ||
1271 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) | ||
1272 | error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); | ||
1273 | } | ||
1274 | |||
1230 | /* | 1275 | /* |
1231 | * This socket is listening for connections to a forwarded TCP/IP port. | 1276 | * This socket is listening for connections to a forwarded TCP/IP port. |
1232 | */ | 1277 | */ |
@@ -1398,6 +1443,8 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) | |||
1398 | debug2("channel %d: filter stops", c->self); | 1443 | debug2("channel %d: filter stops", c->self); |
1399 | chan_read_failed(c); | 1444 | chan_read_failed(c); |
1400 | } | 1445 | } |
1446 | } else if (c->datagram) { | ||
1447 | buffer_put_string(&c->input, buf, len); | ||
1401 | } else { | 1448 | } else { |
1402 | buffer_append(&c->input, buf, len); | 1449 | buffer_append(&c->input, buf, len); |
1403 | } | 1450 | } |
@@ -1408,7 +1455,7 @@ static int | |||
1408 | channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) | 1455 | channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) |
1409 | { | 1456 | { |
1410 | struct termios tio; | 1457 | struct termios tio; |
1411 | u_char *data; | 1458 | u_char *data = NULL, *buf; |
1412 | u_int dlen; | 1459 | u_int dlen; |
1413 | int len; | 1460 | int len; |
1414 | 1461 | ||
@@ -1416,14 +1463,45 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) | |||
1416 | if (c->wfd != -1 && | 1463 | if (c->wfd != -1 && |
1417 | FD_ISSET(c->wfd, writeset) && | 1464 | FD_ISSET(c->wfd, writeset) && |
1418 | buffer_len(&c->output) > 0) { | 1465 | buffer_len(&c->output) > 0) { |
1419 | data = buffer_ptr(&c->output); | 1466 | if (c->output_filter != NULL) { |
1420 | dlen = buffer_len(&c->output); | 1467 | if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { |
1468 | debug2("channel %d: filter stops", c->self); | ||
1469 | if (c->type != SSH_CHANNEL_OPEN) | ||
1470 | chan_mark_dead(c); | ||
1471 | else | ||
1472 | chan_write_failed(c); | ||
1473 | return -1; | ||
1474 | } | ||
1475 | } else if (c->datagram) { | ||
1476 | buf = data = buffer_get_string(&c->output, &dlen); | ||
1477 | } else { | ||
1478 | buf = data = buffer_ptr(&c->output); | ||
1479 | dlen = buffer_len(&c->output); | ||
1480 | } | ||
1481 | |||
1482 | if (c->datagram) { | ||
1483 | /* ignore truncated writes, datagrams might get lost */ | ||
1484 | c->local_consumed += dlen + 4; | ||
1485 | len = write(c->wfd, buf, dlen); | ||
1486 | xfree(data); | ||
1487 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) | ||
1488 | return 1; | ||
1489 | if (len <= 0) { | ||
1490 | if (c->type != SSH_CHANNEL_OPEN) | ||
1491 | chan_mark_dead(c); | ||
1492 | else | ||
1493 | chan_write_failed(c); | ||
1494 | return -1; | ||
1495 | } | ||
1496 | return 1; | ||
1497 | } | ||
1421 | #ifdef _AIX | 1498 | #ifdef _AIX |
1422 | /* XXX: Later AIX versions can't push as much data to tty */ | 1499 | /* XXX: Later AIX versions can't push as much data to tty */ |
1423 | if (compat20 && c->wfd_isatty) | 1500 | if (compat20 && c->wfd_isatty) |
1424 | dlen = MIN(dlen, 8*1024); | 1501 | dlen = MIN(dlen, 8*1024); |
1425 | #endif | 1502 | #endif |
1426 | len = write(c->wfd, data, dlen); | 1503 | |
1504 | len = write(c->wfd, buf, dlen); | ||
1427 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) | 1505 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) |
1428 | return 1; | 1506 | return 1; |
1429 | if (len <= 0) { | 1507 | if (len <= 0) { |
@@ -1440,14 +1518,14 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) | |||
1440 | } | 1518 | } |
1441 | return -1; | 1519 | return -1; |
1442 | } | 1520 | } |
1443 | if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { | 1521 | if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { |
1444 | if (tcgetattr(c->wfd, &tio) == 0 && | 1522 | if (tcgetattr(c->wfd, &tio) == 0 && |
1445 | !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { | 1523 | !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { |
1446 | /* | 1524 | /* |
1447 | * Simulate echo to reduce the impact of | 1525 | * Simulate echo to reduce the impact of |
1448 | * traffic analysis. We need to match the | 1526 | * traffic analysis. We need to match the |
1449 | * size of a SSH2_MSG_CHANNEL_DATA message | 1527 | * size of a SSH2_MSG_CHANNEL_DATA message |
1450 | * (4 byte channel id + data) | 1528 | * (4 byte channel id + buf) |
1451 | */ | 1529 | */ |
1452 | packet_send_ignore(4 + len); | 1530 | packet_send_ignore(4 + len); |
1453 | packet_send(); | 1531 | packet_send(); |
@@ -1666,7 +1744,7 @@ channel_garbage_collect(Channel *c) | |||
1666 | if (c == NULL) | 1744 | if (c == NULL) |
1667 | return; | 1745 | return; |
1668 | if (c->detach_user != NULL) { | 1746 | if (c->detach_user != NULL) { |
1669 | if (!chan_is_dead(c, 0)) | 1747 | if (!chan_is_dead(c, c->detach_close)) |
1670 | return; | 1748 | return; |
1671 | debug2("channel %d: gc: notify user", c->self); | 1749 | debug2("channel %d: gc: notify user", c->self); |
1672 | c->detach_user(c->self, NULL); | 1750 | c->detach_user(c->self, NULL); |
@@ -1776,6 +1854,22 @@ channel_output_poll(void) | |||
1776 | if ((c->istate == CHAN_INPUT_OPEN || | 1854 | if ((c->istate == CHAN_INPUT_OPEN || |
1777 | c->istate == CHAN_INPUT_WAIT_DRAIN) && | 1855 | c->istate == CHAN_INPUT_WAIT_DRAIN) && |
1778 | (len = buffer_len(&c->input)) > 0) { | 1856 | (len = buffer_len(&c->input)) > 0) { |
1857 | if (c->datagram) { | ||
1858 | if (len > 0) { | ||
1859 | u_char *data; | ||
1860 | u_int dlen; | ||
1861 | |||
1862 | data = buffer_get_string(&c->input, | ||
1863 | &dlen); | ||
1864 | packet_start(SSH2_MSG_CHANNEL_DATA); | ||
1865 | packet_put_int(c->remote_id); | ||
1866 | packet_put_string(data, dlen); | ||
1867 | packet_send(); | ||
1868 | c->remote_window -= dlen + 4; | ||
1869 | xfree(data); | ||
1870 | } | ||
1871 | continue; | ||
1872 | } | ||
1779 | /* | 1873 | /* |
1780 | * Send some data for the other side over the secure | 1874 | * Send some data for the other side over the secure |
1781 | * connection. | 1875 | * connection. |
@@ -1898,7 +1992,10 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) | |||
1898 | c->local_window -= data_len; | 1992 | c->local_window -= data_len; |
1899 | } | 1993 | } |
1900 | packet_check_eom(); | 1994 | packet_check_eom(); |
1901 | buffer_append(&c->output, data, data_len); | 1995 | if (c->datagram) |
1996 | buffer_put_string(&c->output, data, data_len); | ||
1997 | else | ||
1998 | buffer_append(&c->output, data, data_len); | ||
1902 | xfree(data); | 1999 | xfree(data); |
1903 | } | 2000 | } |
1904 | 2001 | ||
@@ -2129,9 +2226,8 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) | |||
2129 | id = packet_get_int(); | 2226 | id = packet_get_int(); |
2130 | c = channel_lookup(id); | 2227 | c = channel_lookup(id); |
2131 | 2228 | ||
2132 | if (c == NULL || c->type != SSH_CHANNEL_OPEN) { | 2229 | if (c == NULL) { |
2133 | logit("Received window adjust for " | 2230 | logit("Received window adjust for non-open channel %d.", id); |
2134 | "non-open channel %d.", id); | ||
2135 | return; | 2231 | return; |
2136 | } | 2232 | } |
2137 | adjust = packet_get_int(); | 2233 | adjust = packet_get_int(); |
@@ -2188,7 +2284,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por | |||
2188 | const char *host_to_connect, u_short port_to_connect, int gateway_ports) | 2284 | const char *host_to_connect, u_short port_to_connect, int gateway_ports) |
2189 | { | 2285 | { |
2190 | Channel *c; | 2286 | Channel *c; |
2191 | int sock, r, success = 0, on = 1, wildcard = 0, is_client; | 2287 | int sock, r, success = 0, wildcard = 0, is_client; |
2192 | struct addrinfo hints, *ai, *aitop; | 2288 | struct addrinfo hints, *ai, *aitop; |
2193 | const char *host, *addr; | 2289 | const char *host, *addr; |
2194 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | 2290 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
@@ -2275,13 +2371,8 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por | |||
2275 | verbose("socket: %.100s", strerror(errno)); | 2371 | verbose("socket: %.100s", strerror(errno)); |
2276 | continue; | 2372 | continue; |
2277 | } | 2373 | } |
2278 | /* | 2374 | |
2279 | * Set socket options. | 2375 | channel_set_reuseaddr(sock); |
2280 | * Allow local port reuse in TIME_WAIT. | ||
2281 | */ | ||
2282 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, | ||
2283 | sizeof(on)) == -1) | ||
2284 | error("setsockopt SO_REUSEADDR: %s", strerror(errno)); | ||
2285 | 2376 | ||
2286 | debug("Local forwarding listening on %s port %s.", ntop, strport); | 2377 | debug("Local forwarding listening on %s port %s.", ntop, strport); |
2287 | 2378 | ||
@@ -2453,7 +2544,7 @@ channel_request_rforward_cancel(const char *host, u_short port) | |||
2453 | 2544 | ||
2454 | permitted_opens[i].listen_port = 0; | 2545 | permitted_opens[i].listen_port = 0; |
2455 | permitted_opens[i].port_to_connect = 0; | 2546 | permitted_opens[i].port_to_connect = 0; |
2456 | free(permitted_opens[i].host_to_connect); | 2547 | xfree(permitted_opens[i].host_to_connect); |
2457 | permitted_opens[i].host_to_connect = NULL; | 2548 | permitted_opens[i].host_to_connect = NULL; |
2458 | } | 2549 | } |
2459 | 2550 | ||
@@ -2668,6 +2759,9 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2668 | char strport[NI_MAXSERV]; | 2759 | char strport[NI_MAXSERV]; |
2669 | int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; | 2760 | int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; |
2670 | 2761 | ||
2762 | if (chanids == NULL) | ||
2763 | return -1; | ||
2764 | |||
2671 | for (display_number = x11_display_offset; | 2765 | for (display_number = x11_display_offset; |
2672 | display_number < MAX_DISPLAYS; | 2766 | display_number < MAX_DISPLAYS; |
2673 | display_number++) { | 2767 | display_number++) { |
@@ -2708,6 +2802,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2708 | error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); | 2802 | error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); |
2709 | } | 2803 | } |
2710 | #endif | 2804 | #endif |
2805 | channel_set_reuseaddr(sock); | ||
2711 | if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { | 2806 | if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { |
2712 | debug2("bind port %d: %.100s", port, strerror(errno)); | 2807 | debug2("bind port %d: %.100s", port, strerror(errno)); |
2713 | close(sock); | 2808 | close(sock); |
@@ -2753,8 +2848,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2753 | } | 2848 | } |
2754 | 2849 | ||
2755 | /* Allocate a channel for each socket. */ | 2850 | /* Allocate a channel for each socket. */ |
2756 | if (chanids != NULL) | 2851 | *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); |
2757 | *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); | ||
2758 | for (n = 0; n < num_socks; n++) { | 2852 | for (n = 0; n < num_socks; n++) { |
2759 | sock = socks[n]; | 2853 | sock = socks[n]; |
2760 | nc = channel_new("x11 listener", | 2854 | nc = channel_new("x11 listener", |
@@ -2762,11 +2856,9 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2762 | CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, | 2856 | CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, |
2763 | 0, "X11 inet listener", 1); | 2857 | 0, "X11 inet listener", 1); |
2764 | nc->single_connection = single_connection; | 2858 | nc->single_connection = single_connection; |
2765 | if (*chanids != NULL) | 2859 | (*chanids)[n] = nc->self; |
2766 | (*chanids)[n] = nc->self; | ||
2767 | } | 2860 | } |
2768 | if (*chanids != NULL) | 2861 | (*chanids)[n] = -1; |
2769 | (*chanids)[n] = -1; | ||
2770 | 2862 | ||
2771 | /* Return the display number for the DISPLAY environment variable. */ | 2863 | /* Return the display number for the DISPLAY environment variable. */ |
2772 | *display_numberp = display_number; | 2864 | *display_numberp = display_number; |
@@ -2952,7 +3044,7 @@ deny_input_open(int type, u_int32_t seq, void *ctxt) | |||
2952 | error("deny_input_open: type %d", type); | 3044 | error("deny_input_open: type %d", type); |
2953 | break; | 3045 | break; |
2954 | } | 3046 | } |
2955 | error("Warning: this is probably a break in attempt by a malicious server."); | 3047 | error("Warning: this is probably a break-in attempt by a malicious server."); |
2956 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | 3048 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
2957 | packet_put_int(rchan); | 3049 | packet_put_int(rchan); |
2958 | packet_send(); | 3050 | packet_send(); |