diff options
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 344 |
1 files changed, 229 insertions, 115 deletions
diff --git a/channels.c b/channels.c index 900ab848f..c293eadf1 100644 --- a/channels.c +++ b/channels.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: channels.c,v 1.270 2007/06/25 08:20:03 dtucker Exp $ */ | 1 | /* $OpenBSD: channels.c,v 1.286 2008/07/16 11:52:19 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -61,6 +61,7 @@ | |||
61 | #include <unistd.h> | 61 | #include <unistd.h> |
62 | #include <stdarg.h> | 62 | #include <stdarg.h> |
63 | 63 | ||
64 | #include "openbsd-compat/sys-queue.h" | ||
64 | #include "xmalloc.h" | 65 | #include "xmalloc.h" |
65 | #include "ssh.h" | 66 | #include "ssh.h" |
66 | #include "ssh1.h" | 67 | #include "ssh1.h" |
@@ -164,6 +165,10 @@ static int IPv4or6 = AF_UNSPEC; | |||
164 | /* helper */ | 165 | /* helper */ |
165 | static void port_open_helper(Channel *c, char *rtype); | 166 | static void port_open_helper(Channel *c, char *rtype); |
166 | 167 | ||
168 | /* non-blocking connect helpers */ | ||
169 | static int connect_next(struct channel_connect *); | ||
170 | static void channel_connect_ctx_free(struct channel_connect *); | ||
171 | |||
167 | /* -- channel core */ | 172 | /* -- channel core */ |
168 | 173 | ||
169 | Channel * | 174 | Channel * |
@@ -216,7 +221,7 @@ channel_lookup(int id) | |||
216 | */ | 221 | */ |
217 | static void | 222 | static void |
218 | channel_register_fds(Channel *c, int rfd, int wfd, int efd, | 223 | channel_register_fds(Channel *c, int rfd, int wfd, int efd, |
219 | int extusage, int nonblock) | 224 | int extusage, int nonblock, int is_tty) |
220 | { | 225 | { |
221 | /* Update the maximum file descriptor value. */ | 226 | /* Update the maximum file descriptor value. */ |
222 | channel_max_fd = MAX(channel_max_fd, rfd); | 227 | channel_max_fd = MAX(channel_max_fd, rfd); |
@@ -232,18 +237,9 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, | |||
232 | c->efd = efd; | 237 | c->efd = efd; |
233 | c->extended_usage = extusage; | 238 | c->extended_usage = extusage; |
234 | 239 | ||
235 | /* XXX ugly hack: nonblock is only set by the server */ | 240 | if ((c->isatty = is_tty) != 0) |
236 | if (nonblock && isatty(c->rfd)) { | ||
237 | debug2("channel %d: rfd %d isatty", c->self, c->rfd); | 241 | debug2("channel %d: rfd %d isatty", c->self, c->rfd); |
238 | c->isatty = 1; | 242 | c->wfd_isatty = is_tty || isatty(c->wfd); |
239 | if (!isatty(c->wfd)) { | ||
240 | error("channel %d: wfd %d is not a tty?", | ||
241 | c->self, c->wfd); | ||
242 | } | ||
243 | } else { | ||
244 | c->isatty = 0; | ||
245 | } | ||
246 | c->wfd_isatty = isatty(c->wfd); | ||
247 | 243 | ||
248 | /* enable nonblocking mode */ | 244 | /* enable nonblocking mode */ |
249 | if (nonblock) { | 245 | if (nonblock) { |
@@ -303,7 +299,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, | |||
303 | c->ostate = CHAN_OUTPUT_OPEN; | 299 | c->ostate = CHAN_OUTPUT_OPEN; |
304 | c->istate = CHAN_INPUT_OPEN; | 300 | c->istate = CHAN_INPUT_OPEN; |
305 | c->flags = 0; | 301 | c->flags = 0; |
306 | channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); | 302 | channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); |
307 | c->self = found; | 303 | c->self = found; |
308 | c->type = type; | 304 | c->type = type; |
309 | c->ctype = ctype; | 305 | c->ctype = ctype; |
@@ -319,10 +315,13 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, | |||
319 | c->single_connection = 0; | 315 | c->single_connection = 0; |
320 | c->detach_user = NULL; | 316 | c->detach_user = NULL; |
321 | c->detach_close = 0; | 317 | c->detach_close = 0; |
322 | c->confirm = NULL; | 318 | c->open_confirm = NULL; |
323 | c->confirm_ctx = NULL; | 319 | c->open_confirm_ctx = NULL; |
324 | c->input_filter = NULL; | 320 | c->input_filter = NULL; |
325 | c->output_filter = NULL; | 321 | c->output_filter = NULL; |
322 | c->filter_ctx = NULL; | ||
323 | c->filter_cleanup = NULL; | ||
324 | TAILQ_INIT(&c->status_confirms); | ||
326 | debug("channel %d: new [%s]", found, remote_name); | 325 | debug("channel %d: new [%s]", found, remote_name); |
327 | return c; | 326 | return c; |
328 | } | 327 | } |
@@ -379,6 +378,7 @@ channel_free(Channel *c) | |||
379 | { | 378 | { |
380 | char *s; | 379 | char *s; |
381 | u_int i, n; | 380 | u_int i, n; |
381 | struct channel_confirm *cc; | ||
382 | 382 | ||
383 | for (n = 0, i = 0; i < channels_alloc; i++) | 383 | for (n = 0, i = 0; i < channels_alloc; i++) |
384 | if (channels[i]) | 384 | if (channels[i]) |
@@ -402,6 +402,15 @@ channel_free(Channel *c) | |||
402 | xfree(c->remote_name); | 402 | xfree(c->remote_name); |
403 | c->remote_name = NULL; | 403 | c->remote_name = NULL; |
404 | } | 404 | } |
405 | while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { | ||
406 | if (cc->abandon_cb != NULL) | ||
407 | cc->abandon_cb(c, cc->ctx); | ||
408 | TAILQ_REMOVE(&c->status_confirms, cc, entry); | ||
409 | bzero(cc, sizeof(*cc)); | ||
410 | xfree(cc); | ||
411 | } | ||
412 | if (c->filter_cleanup != NULL && c->filter_ctx != NULL) | ||
413 | c->filter_cleanup(c->self, c->filter_ctx); | ||
405 | channels[c->self] = NULL; | 414 | channels[c->self] = NULL; |
406 | xfree(c); | 415 | xfree(c); |
407 | } | 416 | } |
@@ -660,16 +669,33 @@ channel_request_start(int id, char *service, int wantconfirm) | |||
660 | } | 669 | } |
661 | 670 | ||
662 | void | 671 | void |
663 | channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) | 672 | channel_register_status_confirm(int id, channel_confirm_cb *cb, |
673 | channel_confirm_abandon_cb *abandon_cb, void *ctx) | ||
674 | { | ||
675 | struct channel_confirm *cc; | ||
676 | Channel *c; | ||
677 | |||
678 | if ((c = channel_lookup(id)) == NULL) | ||
679 | fatal("channel_register_expect: %d: bad id", id); | ||
680 | |||
681 | cc = xmalloc(sizeof(*cc)); | ||
682 | cc->cb = cb; | ||
683 | cc->abandon_cb = abandon_cb; | ||
684 | cc->ctx = ctx; | ||
685 | TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); | ||
686 | } | ||
687 | |||
688 | void | ||
689 | channel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) | ||
664 | { | 690 | { |
665 | Channel *c = channel_lookup(id); | 691 | Channel *c = channel_lookup(id); |
666 | 692 | ||
667 | if (c == NULL) { | 693 | if (c == NULL) { |
668 | logit("channel_register_comfirm: %d: bad id", id); | 694 | logit("channel_register_open_comfirm: %d: bad id", id); |
669 | return; | 695 | return; |
670 | } | 696 | } |
671 | c->confirm = fn; | 697 | c->open_confirm = fn; |
672 | c->confirm_ctx = ctx; | 698 | c->open_confirm_ctx = ctx; |
673 | } | 699 | } |
674 | 700 | ||
675 | void | 701 | void |
@@ -700,7 +726,7 @@ channel_cancel_cleanup(int id) | |||
700 | 726 | ||
701 | void | 727 | void |
702 | channel_register_filter(int id, channel_infilter_fn *ifn, | 728 | channel_register_filter(int id, channel_infilter_fn *ifn, |
703 | channel_outfilter_fn *ofn) | 729 | channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) |
704 | { | 730 | { |
705 | Channel *c = channel_lookup(id); | 731 | Channel *c = channel_lookup(id); |
706 | 732 | ||
@@ -710,17 +736,19 @@ channel_register_filter(int id, channel_infilter_fn *ifn, | |||
710 | } | 736 | } |
711 | c->input_filter = ifn; | 737 | c->input_filter = ifn; |
712 | c->output_filter = ofn; | 738 | c->output_filter = ofn; |
739 | c->filter_ctx = ctx; | ||
740 | c->filter_cleanup = cfn; | ||
713 | } | 741 | } |
714 | 742 | ||
715 | void | 743 | void |
716 | channel_set_fds(int id, int rfd, int wfd, int efd, | 744 | channel_set_fds(int id, int rfd, int wfd, int efd, |
717 | int extusage, int nonblock, u_int window_max) | 745 | int extusage, int nonblock, int is_tty, u_int window_max) |
718 | { | 746 | { |
719 | Channel *c = channel_lookup(id); | 747 | Channel *c = channel_lookup(id); |
720 | 748 | ||
721 | if (c == NULL || c->type != SSH_CHANNEL_LARVAL) | 749 | if (c == NULL || c->type != SSH_CHANNEL_LARVAL) |
722 | fatal("channel_activate for non-larval channel %d.", id); | 750 | fatal("channel_activate for non-larval channel %d.", id); |
723 | channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); | 751 | channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); |
724 | c->type = SSH_CHANNEL_OPEN; | 752 | c->type = SSH_CHANNEL_OPEN; |
725 | c->local_window = c->local_window_max = window_max; | 753 | c->local_window = c->local_window_max = window_max; |
726 | packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); | 754 | packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); |
@@ -788,7 +816,8 @@ channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) | |||
788 | } | 816 | } |
789 | } | 817 | } |
790 | /** XXX check close conditions, too */ | 818 | /** XXX check close conditions, too */ |
791 | if (compat20 && c->efd != -1) { | 819 | if (compat20 && c->efd != -1 && |
820 | !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { | ||
792 | if (c->extended_usage == CHAN_EXTENDED_WRITE && | 821 | if (c->extended_usage == CHAN_EXTENDED_WRITE && |
793 | buffer_len(&c->extended) > 0) | 822 | buffer_len(&c->extended) > 0) |
794 | FD_SET(c->efd, writeset); | 823 | FD_SET(c->efd, writeset); |
@@ -1181,7 +1210,7 @@ static void | |||
1181 | channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) | 1210 | channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) |
1182 | { | 1211 | { |
1183 | Channel *nc; | 1212 | Channel *nc; |
1184 | struct sockaddr addr; | 1213 | struct sockaddr_storage addr; |
1185 | int newsock; | 1214 | int newsock; |
1186 | socklen_t addrlen; | 1215 | socklen_t addrlen; |
1187 | char buf[16384], *remote_ipaddr; | 1216 | char buf[16384], *remote_ipaddr; |
@@ -1190,7 +1219,7 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) | |||
1190 | if (FD_ISSET(c->sock, readset)) { | 1219 | if (FD_ISSET(c->sock, readset)) { |
1191 | debug("X11 connection requested."); | 1220 | debug("X11 connection requested."); |
1192 | addrlen = sizeof(addr); | 1221 | addrlen = sizeof(addr); |
1193 | newsock = accept(c->sock, &addr, &addrlen); | 1222 | newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); |
1194 | if (c->single_connection) { | 1223 | if (c->single_connection) { |
1195 | debug2("single_connection: closing X11 listener."); | 1224 | debug2("single_connection: closing X11 listener."); |
1196 | channel_close_fd(&c->sock); | 1225 | channel_close_fd(&c->sock); |
@@ -1307,7 +1336,7 @@ static void | |||
1307 | channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) | 1336 | channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) |
1308 | { | 1337 | { |
1309 | Channel *nc; | 1338 | Channel *nc; |
1310 | struct sockaddr addr; | 1339 | struct sockaddr_storage addr; |
1311 | int newsock, nextstate; | 1340 | int newsock, nextstate; |
1312 | socklen_t addrlen; | 1341 | socklen_t addrlen; |
1313 | char *rtype; | 1342 | char *rtype; |
@@ -1331,7 +1360,7 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) | |||
1331 | } | 1360 | } |
1332 | 1361 | ||
1333 | addrlen = sizeof(addr); | 1362 | addrlen = sizeof(addr); |
1334 | newsock = accept(c->sock, &addr, &addrlen); | 1363 | newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); |
1335 | if (newsock < 0) { | 1364 | if (newsock < 0) { |
1336 | error("accept: %.100s", strerror(errno)); | 1365 | error("accept: %.100s", strerror(errno)); |
1337 | return; | 1366 | return; |
@@ -1366,12 +1395,12 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) | |||
1366 | { | 1395 | { |
1367 | Channel *nc; | 1396 | Channel *nc; |
1368 | int newsock; | 1397 | int newsock; |
1369 | struct sockaddr addr; | 1398 | struct sockaddr_storage addr; |
1370 | socklen_t addrlen; | 1399 | socklen_t addrlen; |
1371 | 1400 | ||
1372 | if (FD_ISSET(c->sock, readset)) { | 1401 | if (FD_ISSET(c->sock, readset)) { |
1373 | addrlen = sizeof(addr); | 1402 | addrlen = sizeof(addr); |
1374 | newsock = accept(c->sock, &addr, &addrlen); | 1403 | newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); |
1375 | if (newsock < 0) { | 1404 | if (newsock < 0) { |
1376 | error("accept from auth socket: %.100s", strerror(errno)); | 1405 | error("accept from auth socket: %.100s", strerror(errno)); |
1377 | return; | 1406 | return; |
@@ -1398,7 +1427,7 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) | |||
1398 | static void | 1427 | static void |
1399 | channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) | 1428 | channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) |
1400 | { | 1429 | { |
1401 | int err = 0; | 1430 | int err = 0, sock; |
1402 | socklen_t sz = sizeof(err); | 1431 | socklen_t sz = sizeof(err); |
1403 | 1432 | ||
1404 | if (FD_ISSET(c->sock, writeset)) { | 1433 | if (FD_ISSET(c->sock, writeset)) { |
@@ -1407,7 +1436,9 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) | |||
1407 | error("getsockopt SO_ERROR failed"); | 1436 | error("getsockopt SO_ERROR failed"); |
1408 | } | 1437 | } |
1409 | if (err == 0) { | 1438 | if (err == 0) { |
1410 | debug("channel %d: connected", c->self); | 1439 | debug("channel %d: connected to %s port %d", |
1440 | c->self, c->connect_ctx.host, c->connect_ctx.port); | ||
1441 | channel_connect_ctx_free(&c->connect_ctx); | ||
1411 | c->type = SSH_CHANNEL_OPEN; | 1442 | c->type = SSH_CHANNEL_OPEN; |
1412 | if (compat20) { | 1443 | if (compat20) { |
1413 | packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); | 1444 | packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); |
@@ -1421,8 +1452,19 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) | |||
1421 | packet_put_int(c->self); | 1452 | packet_put_int(c->self); |
1422 | } | 1453 | } |
1423 | } else { | 1454 | } else { |
1424 | debug("channel %d: not connected: %s", | 1455 | debug("channel %d: connection failed: %s", |
1425 | c->self, strerror(err)); | 1456 | c->self, strerror(err)); |
1457 | /* Try next address, if any */ | ||
1458 | if ((sock = connect_next(&c->connect_ctx)) > 0) { | ||
1459 | close(c->sock); | ||
1460 | c->sock = c->rfd = c->wfd = sock; | ||
1461 | channel_max_fd = channel_find_maxfd(); | ||
1462 | return; | ||
1463 | } | ||
1464 | /* Exhausted all addresses */ | ||
1465 | error("connect_to %.100s port %d: failed.", | ||
1466 | c->connect_ctx.host, c->connect_ctx.port); | ||
1467 | channel_connect_ctx_free(&c->connect_ctx); | ||
1426 | if (compat20) { | 1468 | if (compat20) { |
1427 | packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); | 1469 | packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); |
1428 | packet_put_int(c->remote_id); | 1470 | packet_put_int(c->remote_id); |
@@ -1452,7 +1494,8 @@ channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) | |||
1452 | if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { | 1494 | if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { |
1453 | errno = 0; | 1495 | errno = 0; |
1454 | len = read(c->rfd, buf, sizeof(buf)); | 1496 | len = read(c->rfd, buf, sizeof(buf)); |
1455 | if (len < 0 && (errno == EINTR || (errno == EAGAIN && !force))) | 1497 | if (len < 0 && (errno == EINTR || |
1498 | ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) | ||
1456 | return 1; | 1499 | return 1; |
1457 | #ifndef PTY_ZEROREAD | 1500 | #ifndef PTY_ZEROREAD |
1458 | if (len <= 0) { | 1501 | if (len <= 0) { |
@@ -1523,7 +1566,8 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) | |||
1523 | c->local_consumed += dlen + 4; | 1566 | c->local_consumed += dlen + 4; |
1524 | len = write(c->wfd, buf, dlen); | 1567 | len = write(c->wfd, buf, dlen); |
1525 | xfree(data); | 1568 | xfree(data); |
1526 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) | 1569 | if (len < 0 && (errno == EINTR || errno == EAGAIN || |
1570 | errno == EWOULDBLOCK)) | ||
1527 | return 1; | 1571 | return 1; |
1528 | if (len <= 0) { | 1572 | if (len <= 0) { |
1529 | if (c->type != SSH_CHANNEL_OPEN) | 1573 | if (c->type != SSH_CHANNEL_OPEN) |
@@ -1541,7 +1585,8 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) | |||
1541 | #endif | 1585 | #endif |
1542 | 1586 | ||
1543 | len = write(c->wfd, buf, dlen); | 1587 | len = write(c->wfd, buf, dlen); |
1544 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) | 1588 | if (len < 0 && |
1589 | (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) | ||
1545 | return 1; | 1590 | return 1; |
1546 | if (len <= 0) { | 1591 | if (len <= 0) { |
1547 | if (c->type != SSH_CHANNEL_OPEN) { | 1592 | if (c->type != SSH_CHANNEL_OPEN) { |
@@ -1593,7 +1638,8 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) | |||
1593 | buffer_len(&c->extended)); | 1638 | buffer_len(&c->extended)); |
1594 | debug2("channel %d: written %d to efd %d", | 1639 | debug2("channel %d: written %d to efd %d", |
1595 | c->self, len, c->efd); | 1640 | c->self, len, c->efd); |
1596 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) | 1641 | if (len < 0 && (errno == EINTR || errno == EAGAIN || |
1642 | errno == EWOULDBLOCK)) | ||
1597 | return 1; | 1643 | return 1; |
1598 | if (len <= 0) { | 1644 | if (len <= 0) { |
1599 | debug2("channel %d: closing write-efd %d", | 1645 | debug2("channel %d: closing write-efd %d", |
@@ -1608,8 +1654,8 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) | |||
1608 | len = read(c->efd, buf, sizeof(buf)); | 1654 | len = read(c->efd, buf, sizeof(buf)); |
1609 | debug2("channel %d: read %d from efd %d", | 1655 | debug2("channel %d: read %d from efd %d", |
1610 | c->self, len, c->efd); | 1656 | c->self, len, c->efd); |
1611 | if (len < 0 && (errno == EINTR || | 1657 | if (len < 0 && (errno == EINTR || ((errno == EAGAIN || |
1612 | (errno == EAGAIN && !c->detach_close))) | 1658 | errno == EWOULDBLOCK) && !c->detach_close))) |
1613 | return 1; | 1659 | return 1; |
1614 | if (len <= 0) { | 1660 | if (len <= 0) { |
1615 | debug2("channel %d: closing read-efd %d", | 1661 | debug2("channel %d: closing read-efd %d", |
@@ -1633,7 +1679,8 @@ channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) | |||
1633 | /* Monitor control fd to detect if the slave client exits */ | 1679 | /* Monitor control fd to detect if the slave client exits */ |
1634 | if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { | 1680 | if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { |
1635 | len = read(c->ctl_fd, buf, sizeof(buf)); | 1681 | len = read(c->ctl_fd, buf, sizeof(buf)); |
1636 | if (len < 0 && (errno == EINTR || errno == EAGAIN)) | 1682 | if (len < 0 && |
1683 | (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) | ||
1637 | return 1; | 1684 | return 1; |
1638 | if (len <= 0) { | 1685 | if (len <= 0) { |
1639 | debug2("channel %d: ctl read<=0", c->self); | 1686 | debug2("channel %d: ctl read<=0", c->self); |
@@ -2012,7 +2059,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) | |||
2012 | return; | 2059 | return; |
2013 | 2060 | ||
2014 | /* Get the data. */ | 2061 | /* Get the data. */ |
2015 | data = packet_get_string(&data_len); | 2062 | data = packet_get_string_ptr(&data_len); |
2016 | 2063 | ||
2017 | /* | 2064 | /* |
2018 | * Ignore data for protocol > 1.3 if output end is no longer open. | 2065 | * Ignore data for protocol > 1.3 if output end is no longer open. |
@@ -2026,7 +2073,6 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) | |||
2026 | c->local_window -= data_len; | 2073 | c->local_window -= data_len; |
2027 | c->local_consumed += data_len; | 2074 | c->local_consumed += data_len; |
2028 | } | 2075 | } |
2029 | xfree(data); | ||
2030 | return; | 2076 | return; |
2031 | } | 2077 | } |
2032 | 2078 | ||
@@ -2038,17 +2084,15 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) | |||
2038 | if (data_len > c->local_window) { | 2084 | if (data_len > c->local_window) { |
2039 | logit("channel %d: rcvd too much data %d, win %d", | 2085 | logit("channel %d: rcvd too much data %d, win %d", |
2040 | c->self, data_len, c->local_window); | 2086 | c->self, data_len, c->local_window); |
2041 | xfree(data); | ||
2042 | return; | 2087 | return; |
2043 | } | 2088 | } |
2044 | c->local_window -= data_len; | 2089 | c->local_window -= data_len; |
2045 | } | 2090 | } |
2046 | packet_check_eom(); | ||
2047 | if (c->datagram) | 2091 | if (c->datagram) |
2048 | buffer_put_string(&c->output, data, data_len); | 2092 | buffer_put_string(&c->output, data, data_len); |
2049 | else | 2093 | else |
2050 | buffer_append(&c->output, data, data_len); | 2094 | buffer_append(&c->output, data, data_len); |
2051 | xfree(data); | 2095 | packet_check_eom(); |
2052 | } | 2096 | } |
2053 | 2097 | ||
2054 | /* ARGSUSED */ | 2098 | /* ARGSUSED */ |
@@ -2212,9 +2256,9 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) | |||
2212 | if (compat20) { | 2256 | if (compat20) { |
2213 | c->remote_window = packet_get_int(); | 2257 | c->remote_window = packet_get_int(); |
2214 | c->remote_maxpacket = packet_get_int(); | 2258 | c->remote_maxpacket = packet_get_int(); |
2215 | if (c->confirm) { | 2259 | if (c->open_confirm) { |
2216 | debug2("callback start"); | 2260 | debug2("callback start"); |
2217 | c->confirm(c->self, c->confirm_ctx); | 2261 | c->open_confirm(c->self, c->open_confirm_ctx); |
2218 | debug2("callback done"); | 2262 | debug2("callback done"); |
2219 | } | 2263 | } |
2220 | debug2("channel %d: open confirm rwindow %u rmax %u", c->self, | 2264 | debug2("channel %d: open confirm rwindow %u rmax %u", c->self, |
@@ -2303,7 +2347,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) | |||
2303 | Channel *c = NULL; | 2347 | Channel *c = NULL; |
2304 | u_short host_port; | 2348 | u_short host_port; |
2305 | char *host, *originator_string; | 2349 | char *host, *originator_string; |
2306 | int remote_id, sock = -1; | 2350 | int remote_id; |
2307 | 2351 | ||
2308 | remote_id = packet_get_int(); | 2352 | remote_id = packet_get_int(); |
2309 | host = packet_get_string(NULL); | 2353 | host = packet_get_string(NULL); |
@@ -2315,22 +2359,46 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) | |||
2315 | originator_string = xstrdup("unknown (remote did not supply name)"); | 2359 | originator_string = xstrdup("unknown (remote did not supply name)"); |
2316 | } | 2360 | } |
2317 | packet_check_eom(); | 2361 | packet_check_eom(); |
2318 | sock = channel_connect_to(host, host_port); | 2362 | c = channel_connect_to(host, host_port, |
2319 | if (sock != -1) { | 2363 | "connected socket", originator_string); |
2320 | c = channel_new("connected socket", | ||
2321 | SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, | ||
2322 | originator_string, 1); | ||
2323 | c->remote_id = remote_id; | ||
2324 | } | ||
2325 | xfree(originator_string); | 2364 | xfree(originator_string); |
2365 | xfree(host); | ||
2326 | if (c == NULL) { | 2366 | if (c == NULL) { |
2327 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | 2367 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
2328 | packet_put_int(remote_id); | 2368 | packet_put_int(remote_id); |
2329 | packet_send(); | 2369 | packet_send(); |
2330 | } | 2370 | } else |
2331 | xfree(host); | 2371 | c->remote_id = remote_id; |
2332 | } | 2372 | } |
2333 | 2373 | ||
2374 | /* ARGSUSED */ | ||
2375 | void | ||
2376 | channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) | ||
2377 | { | ||
2378 | Channel *c; | ||
2379 | struct channel_confirm *cc; | ||
2380 | int remote_id; | ||
2381 | |||
2382 | /* Reset keepalive timeout */ | ||
2383 | keep_alive_timeouts = 0; | ||
2384 | |||
2385 | remote_id = packet_get_int(); | ||
2386 | packet_check_eom(); | ||
2387 | |||
2388 | debug2("channel_input_confirm: type %d id %d", type, remote_id); | ||
2389 | |||
2390 | if ((c = channel_lookup(remote_id)) == NULL) { | ||
2391 | logit("channel_input_success_failure: %d: unknown", remote_id); | ||
2392 | return; | ||
2393 | } | ||
2394 | ; | ||
2395 | if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) | ||
2396 | return; | ||
2397 | cc->cb(type, c, cc->ctx); | ||
2398 | TAILQ_REMOVE(&c->status_confirms, cc, entry); | ||
2399 | bzero(cc, sizeof(*cc)); | ||
2400 | xfree(cc); | ||
2401 | } | ||
2334 | 2402 | ||
2335 | /* -- tcp forwarding */ | 2403 | /* -- tcp forwarding */ |
2336 | 2404 | ||
@@ -2385,7 +2453,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por | |||
2385 | wildcard = 1; | 2453 | wildcard = 1; |
2386 | } else if (gateway_ports || is_client) { | 2454 | } else if (gateway_ports || is_client) { |
2387 | if (((datafellows & SSH_OLD_FORWARD_ADDR) && | 2455 | if (((datafellows & SSH_OLD_FORWARD_ADDR) && |
2388 | strcmp(listen_addr, "0.0.0.0") == 0) || | 2456 | strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || |
2389 | *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || | 2457 | *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || |
2390 | (!is_client && gateway_ports == 1)) | 2458 | (!is_client && gateway_ports == 1)) |
2391 | wildcard = 1; | 2459 | wildcard = 1; |
@@ -2409,10 +2477,11 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por | |||
2409 | if (addr == NULL) { | 2477 | if (addr == NULL) { |
2410 | /* This really shouldn't happen */ | 2478 | /* This really shouldn't happen */ |
2411 | packet_disconnect("getaddrinfo: fatal error: %s", | 2479 | packet_disconnect("getaddrinfo: fatal error: %s", |
2412 | gai_strerror(r)); | 2480 | ssh_gai_strerror(r)); |
2413 | } else { | 2481 | } else { |
2414 | error("channel_setup_fwd_listener: " | 2482 | error("channel_setup_fwd_listener: " |
2415 | "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); | 2483 | "getaddrinfo(%.64s): %s", addr, |
2484 | ssh_gai_strerror(r)); | ||
2416 | } | 2485 | } |
2417 | return 0; | 2486 | return 0; |
2418 | } | 2487 | } |
@@ -2717,35 +2786,37 @@ channel_clear_adm_permitted_opens(void) | |||
2717 | num_adm_permitted_opens = 0; | 2786 | num_adm_permitted_opens = 0; |
2718 | } | 2787 | } |
2719 | 2788 | ||
2720 | /* return socket to remote host, port */ | 2789 | void |
2790 | channel_print_adm_permitted_opens(void) | ||
2791 | { | ||
2792 | int i; | ||
2793 | |||
2794 | for (i = 0; i < num_adm_permitted_opens; i++) | ||
2795 | if (permitted_adm_opens[i].host_to_connect != NULL) | ||
2796 | printf(" %s:%d", permitted_adm_opens[i].host_to_connect, | ||
2797 | permitted_adm_opens[i].port_to_connect); | ||
2798 | } | ||
2799 | |||
2800 | /* Try to start non-blocking connect to next host in cctx list */ | ||
2721 | static int | 2801 | static int |
2722 | connect_to(const char *host, u_short port) | 2802 | connect_next(struct channel_connect *cctx) |
2723 | { | 2803 | { |
2724 | struct addrinfo hints, *ai, *aitop; | 2804 | int sock, saved_errno; |
2725 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | 2805 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
2726 | int gaierr; | ||
2727 | int sock = -1; | ||
2728 | 2806 | ||
2729 | memset(&hints, 0, sizeof(hints)); | 2807 | for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { |
2730 | hints.ai_family = IPv4or6; | 2808 | if (cctx->ai->ai_family != AF_INET && |
2731 | hints.ai_socktype = SOCK_STREAM; | 2809 | cctx->ai->ai_family != AF_INET6) |
2732 | snprintf(strport, sizeof strport, "%d", port); | ||
2733 | if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { | ||
2734 | error("connect_to %.100s: unknown host (%s)", host, | ||
2735 | gai_strerror(gaierr)); | ||
2736 | return -1; | ||
2737 | } | ||
2738 | for (ai = aitop; ai; ai = ai->ai_next) { | ||
2739 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | ||
2740 | continue; | 2810 | continue; |
2741 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), | 2811 | if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, |
2742 | strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { | 2812 | ntop, sizeof(ntop), strport, sizeof(strport), |
2743 | error("connect_to: getnameinfo failed"); | 2813 | NI_NUMERICHOST|NI_NUMERICSERV) != 0) { |
2814 | error("connect_next: getnameinfo failed"); | ||
2744 | continue; | 2815 | continue; |
2745 | } | 2816 | } |
2746 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | 2817 | if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, |
2747 | if (sock < 0) { | 2818 | cctx->ai->ai_protocol)) == -1) { |
2748 | if (ai->ai_next == NULL) | 2819 | if (cctx->ai->ai_next == NULL) |
2749 | error("socket: %.100s", strerror(errno)); | 2820 | error("socket: %.100s", strerror(errno)); |
2750 | else | 2821 | else |
2751 | verbose("socket: %.100s", strerror(errno)); | 2822 | verbose("socket: %.100s", strerror(errno)); |
@@ -2753,45 +2824,95 @@ connect_to(const char *host, u_short port) | |||
2753 | } | 2824 | } |
2754 | if (set_nonblock(sock) == -1) | 2825 | if (set_nonblock(sock) == -1) |
2755 | fatal("%s: set_nonblock(%d)", __func__, sock); | 2826 | fatal("%s: set_nonblock(%d)", __func__, sock); |
2756 | if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && | 2827 | if (connect(sock, cctx->ai->ai_addr, |
2757 | errno != EINPROGRESS) { | 2828 | cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { |
2758 | error("connect_to %.100s port %s: %.100s", ntop, strport, | 2829 | debug("connect_next: host %.100s ([%.100s]:%s): " |
2830 | "%.100s", cctx->host, ntop, strport, | ||
2759 | strerror(errno)); | 2831 | strerror(errno)); |
2832 | saved_errno = errno; | ||
2760 | close(sock); | 2833 | close(sock); |
2834 | errno = saved_errno; | ||
2761 | continue; /* fail -- try next */ | 2835 | continue; /* fail -- try next */ |
2762 | } | 2836 | } |
2763 | break; /* success */ | 2837 | debug("connect_next: host %.100s ([%.100s]:%s) " |
2838 | "in progress, fd=%d", cctx->host, ntop, strport, sock); | ||
2839 | cctx->ai = cctx->ai->ai_next; | ||
2840 | set_nodelay(sock); | ||
2841 | return sock; | ||
2842 | } | ||
2843 | return -1; | ||
2844 | } | ||
2764 | 2845 | ||
2846 | static void | ||
2847 | channel_connect_ctx_free(struct channel_connect *cctx) | ||
2848 | { | ||
2849 | xfree(cctx->host); | ||
2850 | if (cctx->aitop) | ||
2851 | freeaddrinfo(cctx->aitop); | ||
2852 | bzero(cctx, sizeof(*cctx)); | ||
2853 | cctx->host = NULL; | ||
2854 | cctx->ai = cctx->aitop = NULL; | ||
2855 | } | ||
2856 | |||
2857 | /* Return CONNECTING channel to remote host, port */ | ||
2858 | static Channel * | ||
2859 | connect_to(const char *host, u_short port, char *ctype, char *rname) | ||
2860 | { | ||
2861 | struct addrinfo hints; | ||
2862 | int gaierr; | ||
2863 | int sock = -1; | ||
2864 | char strport[NI_MAXSERV]; | ||
2865 | struct channel_connect cctx; | ||
2866 | Channel *c; | ||
2867 | |||
2868 | memset(&cctx, 0, sizeof(cctx)); | ||
2869 | memset(&hints, 0, sizeof(hints)); | ||
2870 | hints.ai_family = IPv4or6; | ||
2871 | hints.ai_socktype = SOCK_STREAM; | ||
2872 | snprintf(strport, sizeof strport, "%d", port); | ||
2873 | if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { | ||
2874 | error("connect_to %.100s: unknown host (%s)", host, | ||
2875 | ssh_gai_strerror(gaierr)); | ||
2876 | return NULL; | ||
2765 | } | 2877 | } |
2766 | freeaddrinfo(aitop); | 2878 | |
2767 | if (!ai) { | 2879 | cctx.host = xstrdup(host); |
2768 | error("connect_to %.100s port %d: failed.", host, port); | 2880 | cctx.port = port; |
2769 | return -1; | 2881 | cctx.ai = cctx.aitop; |
2882 | |||
2883 | if ((sock = connect_next(&cctx)) == -1) { | ||
2884 | error("connect to %.100s port %d failed: %s", | ||
2885 | host, port, strerror(errno)); | ||
2886 | channel_connect_ctx_free(&cctx); | ||
2887 | return NULL; | ||
2770 | } | 2888 | } |
2771 | /* success */ | 2889 | c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, |
2772 | set_nodelay(sock); | 2890 | CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); |
2773 | return sock; | 2891 | c->connect_ctx = cctx; |
2892 | return c; | ||
2774 | } | 2893 | } |
2775 | 2894 | ||
2776 | int | 2895 | Channel * |
2777 | channel_connect_by_listen_address(u_short listen_port) | 2896 | channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) |
2778 | { | 2897 | { |
2779 | int i; | 2898 | int i; |
2780 | 2899 | ||
2781 | for (i = 0; i < num_permitted_opens; i++) | 2900 | for (i = 0; i < num_permitted_opens; i++) { |
2782 | if (permitted_opens[i].host_to_connect != NULL && | 2901 | if (permitted_opens[i].host_to_connect != NULL && |
2783 | permitted_opens[i].listen_port == listen_port) | 2902 | permitted_opens[i].listen_port == listen_port) { |
2784 | return connect_to( | 2903 | return connect_to( |
2785 | permitted_opens[i].host_to_connect, | 2904 | permitted_opens[i].host_to_connect, |
2786 | permitted_opens[i].port_to_connect); | 2905 | permitted_opens[i].port_to_connect, ctype, rname); |
2906 | } | ||
2907 | } | ||
2787 | error("WARNING: Server requests forwarding for unknown listen_port %d", | 2908 | error("WARNING: Server requests forwarding for unknown listen_port %d", |
2788 | listen_port); | 2909 | listen_port); |
2789 | return -1; | 2910 | return NULL; |
2790 | } | 2911 | } |
2791 | 2912 | ||
2792 | /* Check if connecting to that port is permitted and connect. */ | 2913 | /* Check if connecting to that port is permitted and connect. */ |
2793 | int | 2914 | Channel * |
2794 | channel_connect_to(const char *host, u_short port) | 2915 | channel_connect_to(const char *host, u_short port, char *ctype, char *rname) |
2795 | { | 2916 | { |
2796 | int i, permit, permit_adm = 1; | 2917 | int i, permit, permit_adm = 1; |
2797 | 2918 | ||
@@ -2817,9 +2938,9 @@ channel_connect_to(const char *host, u_short port) | |||
2817 | if (!permit || !permit_adm) { | 2938 | if (!permit || !permit_adm) { |
2818 | logit("Received request to connect to host %.100s port %d, " | 2939 | logit("Received request to connect to host %.100s port %d, " |
2819 | "but the request was denied.", host, port); | 2940 | "but the request was denied.", host, port); |
2820 | return -1; | 2941 | return NULL; |
2821 | } | 2942 | } |
2822 | return connect_to(host, port); | 2943 | return connect_to(host, port, ctype, rname); |
2823 | } | 2944 | } |
2824 | 2945 | ||
2825 | void | 2946 | void |
@@ -2874,7 +2995,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2874 | hints.ai_socktype = SOCK_STREAM; | 2995 | hints.ai_socktype = SOCK_STREAM; |
2875 | snprintf(strport, sizeof strport, "%d", port); | 2996 | snprintf(strport, sizeof strport, "%d", port); |
2876 | if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { | 2997 | if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { |
2877 | error("getaddrinfo: %.100s", gai_strerror(gaierr)); | 2998 | error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); |
2878 | return -1; | 2999 | return -1; |
2879 | } | 3000 | } |
2880 | for (ai = aitop; ai; ai = ai->ai_next) { | 3001 | for (ai = aitop; ai; ai = ai->ai_next) { |
@@ -2904,7 +3025,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2904 | error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); | 3025 | error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); |
2905 | } | 3026 | } |
2906 | #endif | 3027 | #endif |
2907 | channel_set_reuseaddr(sock); | 3028 | if (x11_use_localhost) |
3029 | channel_set_reuseaddr(sock); | ||
2908 | if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { | 3030 | if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { |
2909 | debug2("bind port %d: %.100s", port, strerror(errno)); | 3031 | debug2("bind port %d: %.100s", port, strerror(errno)); |
2910 | close(sock); | 3032 | close(sock); |
@@ -2916,17 +3038,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, | |||
2916 | break; | 3038 | break; |
2917 | } | 3039 | } |
2918 | socks[num_socks++] = sock; | 3040 | socks[num_socks++] = sock; |
2919 | #ifndef DONT_TRY_OTHER_AF | ||
2920 | if (num_socks == NUM_SOCKS) | 3041 | if (num_socks == NUM_SOCKS) |
2921 | break; | 3042 | break; |
2922 | #else | ||
2923 | if (x11_use_localhost) { | ||
2924 | if (num_socks == NUM_SOCKS) | ||
2925 | break; | ||
2926 | } else { | ||
2927 | break; | ||
2928 | } | ||
2929 | #endif | ||
2930 | } | 3043 | } |
2931 | freeaddrinfo(aitop); | 3044 | freeaddrinfo(aitop); |
2932 | if (num_socks > 0) | 3045 | if (num_socks > 0) |
@@ -3048,7 +3161,8 @@ x11_connect_display(void) | |||
3048 | hints.ai_socktype = SOCK_STREAM; | 3161 | hints.ai_socktype = SOCK_STREAM; |
3049 | snprintf(strport, sizeof strport, "%u", 6000 + display_number); | 3162 | snprintf(strport, sizeof strport, "%u", 6000 + display_number); |
3050 | if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { | 3163 | if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { |
3051 | error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); | 3164 | error("%.100s: unknown host. (%s)", buf, |
3165 | ssh_gai_strerror(gaierr)); | ||
3052 | return -1; | 3166 | return -1; |
3053 | } | 3167 | } |
3054 | for (ai = aitop; ai; ai = ai->ai_next) { | 3168 | for (ai = aitop; ai; ai = ai->ai_next) { |