diff options
author | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
commit | 137d76ba65883aa8143af1fcad83b57e7badef0c (patch) | |
tree | f426e804bb5248ceafedfab7bb78ae6e6752942c /channels.c | |
parent | dac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff) | |
parent | ef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff) |
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1:
- 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9):
+ Added chroot(2) support for sshd(8), controlled by a new option
"ChrootDirectory" (closes: #139047, LP: #24777).
+ Linked sftp-server(8) into sshd(8). The internal sftp server is used
when the command "internal-sftp" is specified in a Subsystem or
ForceCommand declaration. When used with ChrootDirectory, the
internal sftp server requires no special configuration of files
inside the chroot environment.
+ Added a protocol extension method "posix-rename@openssh.com" for
sftp-server(8) to perform POSIX atomic rename() operations; sftp(1)
prefers this if available (closes: #308561).
+ Removed the fixed limit of 100 file handles in sftp-server(8).
+ ssh(8) will now skip generation of SSH protocol 1 ephemeral server
keys when in inetd mode and protocol 2 connections are negotiated.
This speeds up protocol 2 connections to inetd-mode servers that
also allow Protocol 1.
+ Accept the PermitRootLogin directive in a sshd_config(5) Match
block. Allows for, e.g. permitting root only from the local network.
+ Reworked sftp(1) argument splitting and escaping to be more
internally consistent (i.e. between sftp commands) and more
consistent with sh(1). Please note that this will change the
interpretation of some quoted strings, especially those with
embedded backslash escape sequences.
+ Support "Banner=none" in sshd_config(5) to disable sending of a
pre-login banner (e.g. in a Match block).
+ ssh(1) ProxyCommands are now executed with $SHELL rather than
/bin/sh.
+ ssh(1)'s ConnectTimeout option is now applied to both the TCP
connection and the SSH banner exchange (previously it just covered
the TCP connection). This allows callers of ssh(1) to better detect
and deal with stuck servers that accept a TCP connection but don't
progress the protocol, and also makes ConnectTimeout useful for
connections via a ProxyCommand.
+ scp(1) incorrectly reported "stalled" on slow copies (closes:
#140828).
+ scp(1) date underflow for timestamps before epoch.
+ ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS,
instead of the current standard RRSIG.
+ Correctly drain ACKs when a sftp(1) upload write fails midway,
avoids a fatal() exit from what should be a recoverable condition.
+ Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF
hostname") to not include any IP address in the data to be hashed.
+ Make ssh(1) skip listening on the IPv6 wildcard address when a
binding address of 0.0.0.0 is used against an old SSH server that
does not support the RFC4254 syntax for wildcard bind addresses.
+ Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is
already done for X11/TCP forwarding sockets (closes: #439661).
+ Fix FD leak that could hang a ssh(1) connection multiplexing master.
+ Make ssh(1) -q option documentation consistent with reality.
+ Fixed sshd(8) PAM support not calling pam_session_close(), or
failing to call it with root privileges (closes: #372680).
+ Fix activation of OpenSSL engine support when requested in configure
(LP: #119295).
- 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1):
+ Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1)
and ssh-keygen(1). Visual fingerprint display is controlled by a new
ssh_config(5) option "VisualHostKey". The intent is to render SSH
host keys in a visual form that is amenable to easy recall and
rejection of changed host keys.
+ sshd_config(5) now supports CIDR address/masklen matching in "Match
address" blocks, with a fallback to classic wildcard matching.
+ sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys
from="..." restrictions, also with a fallback to classic wildcard
matching.
+ Added an extended test mode (-T) to sshd(8) to request that it write
its effective configuration to stdout and exit. Extended test mode
also supports the specification of connection parameters (username,
source address and hostname) to test the application of
sshd_config(5) Match rules.
+ ssh(1) now prints the number of bytes transferred and the overall
connection throughput for SSH protocol 2 sessions when in verbose
mode (previously these statistics were displayed for protocol 1
connections only).
+ sftp-server(8) now supports extension methods statvfs@openssh.com
and fstatvfs@openssh.com that implement statvfs(2)-like operations.
+ sftp(1) now has a "df" command to the sftp client that uses the
statvfs@openssh.com to produce a df(1)-like display of filesystem
space and inode utilisation (requires statvfs@openssh.com support on
the server).
+ Added a MaxSessions option to sshd_config(5) to allow control of the
number of multiplexed sessions supported over a single TCP
connection. This allows increasing the number of allowed sessions
above the previous default of 10, disabling connection multiplexing
(MaxSessions=1) or disallowing login/shell/subsystem sessions
entirely (MaxSessions=0).
+ Added a no-more-sessions@openssh.com global request extension that
is sent from ssh(1) to sshd(8) when the client knows that it will
never request another session (i.e. when session multiplexing is
disabled). This allows a server to disallow further session requests
and terminate the session in cases where the client has been
hijacked.
+ ssh-keygen(1) now supports the use of the -l option in combination
with -F to search for a host in ~/.ssh/known_hosts and display its
fingerprint.
+ ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of
"rsa1".
+ Added an AllowAgentForwarding option to sshd_config(8) to control
whether authentication agent forwarding is permitted. Note that this
is a loose control, as a client may install their own unofficial
forwarder.
+ ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when
receiving network data, resulting in a ~10% speedup.
+ ssh(1) and sshd(8) will now try additional addresses when connecting
to a port forward destination whose DNS name resolves to more than
one address. The previous behaviour was to try the only first
address and give up if that failed.
+ ssh(1) and sshd(8) now support signalling that channels are
half-closed for writing, through a channel protocol extension
notification "eow@openssh.com". This allows propagation of closed
file descriptors, so that commands such as "ssh -2 localhost od
/bin/ls | true" do not send unnecessary data over the wire.
+ sshd(8): increased the default size of ssh protocol 1 ephemeral keys
from 768 to 1024 bits.
+ When ssh(1) has been requested to fork after authentication ("ssh
-f") with ExitOnForwardFailure enabled, delay the fork until after
replies for any -R forwards have been seen. Allows for robust
detection of -R forward failure when using -f.
+ "Match group" blocks in sshd_config(5) now support negation of
groups. E.g. "Match group staff,!guests".
+ sftp(1) and sftp-server(8) now allow chmod-like operations to set
set[ug]id/sticky bits.
+ The MaxAuthTries option is now permitted in sshd_config(5) match
blocks.
+ Multiplexed ssh(1) sessions now support a subset of the ~ escapes
that are available to a primary connection.
+ ssh(1) connection multiplexing will now fall back to creating a new
connection in most error cases (closes: #352830).
+ Make ssh(1) deal more gracefully with channel requests that fail.
Previously it would optimistically assume that requests would always
succeed, which could cause hangs if they did not (e.g. when the
server runs out of file descriptors).
+ ssh(1) now reports multiplexing errors via the multiplex slave's
stderr where possible (subject to LogLevel in the mux master).
+ Prevent sshd(8) from erroneously applying public key restrictions
leaned from ~/.ssh/authorized_keys to other authentication methods
when public key authentication subsequently fails (LP: #161047).
+ Fixed an UMAC alignment problem that manifested on Itanium
platforms.
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) { |