- djm@cvs.openbsd.org 2008/05/09 04:55:56
[channels.c channels.h clientloop.c serverloop.c]
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 first address and give up.
Reported by stig AT venaas.com in bz#343
great feedback and ok markus@
diff --git a/ChangeLog b/ChangeLog
index bd6b640..9e68314 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -104,6 +104,13 @@
client stderr (subject to LogLevel in the mux master) - better than
silently failing.
most bits ok markus@ (as part of a larger diff)
+ - djm@cvs.openbsd.org 2008/05/09 04:55:56
+ [channels.c channels.h clientloop.c serverloop.c]
+ 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 first address and give up.
+ Reported by stig AT venaas.com in bz#343
+ great feedback and ok markus@
20080403
- (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile-
@@ -3964,4 +3971,4 @@
OpenServer 6 and add osr5bigcrypt support so when someone migrates
passwords between UnixWare and OpenServer they will still work. OK dtucker@
-$Id: ChangeLog,v 1.4924 2008/05/19 05:36:08 djm Exp $
+$Id: ChangeLog,v 1.4925 2008/05/19 05:37:09 djm Exp $
diff --git a/channels.c b/channels.c
index b5e28da..1e57951 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.275 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.276 2008/05/09 04:55:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -165,6 +165,10 @@
/* helper */
static void port_open_helper(Channel *c, char *rtype);
+/* non-blocking connect helpers */
+static int connect_next(struct channel_connect *);
+static void channel_connect_ctx_free(struct channel_connect *);
+
/* -- channel core */
Channel *
@@ -1425,7 +1429,7 @@
static void
channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
{
- int err = 0;
+ int err = 0, sock;
socklen_t sz = sizeof(err);
if (FD_ISSET(c->sock, writeset)) {
@@ -1434,7 +1438,9 @@
error("getsockopt SO_ERROR failed");
}
if (err == 0) {
- debug("channel %d: connected", c->self);
+ debug("channel %d: connected to %s port %d",
+ c->self, c->connect_ctx.host, c->connect_ctx.port);
+ channel_connect_ctx_free(&c->connect_ctx);
c->type = SSH_CHANNEL_OPEN;
if (compat20) {
packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
@@ -1448,8 +1454,19 @@
packet_put_int(c->self);
}
} else {
- debug("channel %d: not connected: %s",
+ debug("channel %d: connection failed: %s",
c->self, strerror(err));
+ /* Try next address, if any */
+ if ((sock = connect_next(&c->connect_ctx)) > 0) {
+ close(c->sock);
+ c->sock = c->rfd = c->wfd = sock;
+ channel_max_fd = channel_find_maxfd();
+ return;
+ }
+ /* Exhausted all addresses */
+ error("connect_to %.100s port %d: failed.",
+ c->connect_ctx.host, c->connect_ctx.port);
+ channel_connect_ctx_free(&c->connect_ctx);
if (compat20) {
packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
packet_put_int(c->remote_id);
@@ -2327,7 +2344,7 @@
Channel *c = NULL;
u_short host_port;
char *host, *originator_string;
- int remote_id, sock = -1;
+ int remote_id;
remote_id = packet_get_int();
host = packet_get_string(NULL);
@@ -2339,20 +2356,16 @@
originator_string = xstrdup("unknown (remote did not supply name)");
}
packet_check_eom();
- sock = channel_connect_to(host, host_port);
- if (sock != -1) {
- c = channel_new("connected socket",
- SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
- originator_string, 1);
- c->remote_id = remote_id;
- }
+ c = channel_connect_to(host, host_port,
+ "connected socket", originator_string);
xfree(originator_string);
+ xfree(host);
if (c == NULL) {
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
packet_put_int(remote_id);
packet_send();
- }
- xfree(host);
+ } else
+ c->remote_id = remote_id;
}
/* ARGSUSED */
@@ -2770,35 +2783,26 @@
num_adm_permitted_opens = 0;
}
-/* return socket to remote host, port */
+/* Try to start non-blocking connect to next host in cctx list */
static int
-connect_to(const char *host, u_short port)
+connect_next(struct channel_connect *cctx)
{
- struct addrinfo hints, *ai, *aitop;
+ int sock, saved_errno;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
- int gaierr;
- int sock = -1;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = IPv4or6;
- hints.ai_socktype = SOCK_STREAM;
- snprintf(strport, sizeof strport, "%d", port);
- if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
- error("connect_to %.100s: unknown host (%s)", host,
- ssh_gai_strerror(gaierr));
- return -1;
- }
- for (ai = aitop; ai; ai = ai->ai_next) {
- if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+ for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
+ if (cctx->ai->ai_family != AF_INET &&
+ cctx->ai->ai_family != AF_INET6)
continue;
- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
- strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
- error("connect_to: getnameinfo failed");
+ if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+ error("connect_next: getnameinfo failed");
continue;
}
- sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if (sock < 0) {
- if (ai->ai_next == NULL)
+ if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
+ cctx->ai->ai_protocol)) == -1) {
+ if (cctx->ai->ai_next == NULL)
error("socket: %.100s", strerror(errno));
else
verbose("socket: %.100s", strerror(errno));
@@ -2806,45 +2810,94 @@
}
if (set_nonblock(sock) == -1)
fatal("%s: set_nonblock(%d)", __func__, sock);
- if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
- errno != EINPROGRESS) {
- error("connect_to %.100s port %s: %.100s", ntop, strport,
+ if (connect(sock, cctx->ai->ai_addr,
+ cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
+ debug("connect_next: host %.100s ([%.100s]:%s): "
+ "%.100s", cctx->host, ntop, strport,
strerror(errno));
+ saved_errno = errno;
close(sock);
+ errno = saved_errno;
continue; /* fail -- try next */
}
- break; /* success */
-
+ debug("connect_next: host %.100s ([%.100s]:%s) "
+ "in progress, fd=%d", cctx->host, ntop, strport, sock);
+ cctx->ai = cctx->ai->ai_next;
+ set_nodelay(sock);
+ return sock;
}
- freeaddrinfo(aitop);
- if (!ai) {
- error("connect_to %.100s port %d: failed.", host, port);
- return -1;
- }
- /* success */
- set_nodelay(sock);
- return sock;
-}
-
-int
-channel_connect_by_listen_address(u_short listen_port)
-{
- int i;
-
- for (i = 0; i < num_permitted_opens; i++)
- if (permitted_opens[i].host_to_connect != NULL &&
- permitted_opens[i].listen_port == listen_port)
- return connect_to(
- permitted_opens[i].host_to_connect,
- permitted_opens[i].port_to_connect);
- error("WARNING: Server requests forwarding for unknown listen_port %d",
- listen_port);
return -1;
}
+static void
+channel_connect_ctx_free(struct channel_connect *cctx)
+{
+ xfree(cctx->host);
+ if (cctx->aitop)
+ freeaddrinfo(cctx->aitop);
+ bzero(cctx, sizeof(*cctx));
+ cctx->host = NULL;
+ cctx->ai = cctx->aitop = NULL;
+}
+
+/* Return CONNECTING channel to remote host, port */
+static Channel *
+connect_to(const char *host, u_short port, char *ctype, char *rname)
+{
+ struct addrinfo hints;
+ int gaierr;
+ int sock = -1;
+ char strport[NI_MAXSERV];
+ struct channel_connect cctx;
+ Channel *c;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = IPv4or6;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf(strport, sizeof strport, "%d", port);
+ if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) {
+ error("connect_to %.100s: unknown host (%s)", host,
+ ssh_gai_strerror(gaierr));
+ return NULL;
+ }
+
+ cctx.host = xstrdup(host);
+ cctx.port = port;
+ cctx.ai = cctx.aitop;
+
+ if ((sock = connect_next(&cctx)) == -1) {
+ error("connect to %.100s port %d failed: %s",
+ host, port, strerror(errno));
+ channel_connect_ctx_free(&cctx);
+ return NULL;
+ }
+ c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
+ CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
+ c->connect_ctx = cctx;
+ return c;
+}
+
+Channel *
+channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
+{
+ int i;
+
+ for (i = 0; i < num_permitted_opens; i++) {
+ if (permitted_opens[i].host_to_connect != NULL &&
+ permitted_opens[i].listen_port == listen_port) {
+ return connect_to(
+ permitted_opens[i].host_to_connect,
+ permitted_opens[i].port_to_connect, ctype, rname);
+ }
+ }
+ error("WARNING: Server requests forwarding for unknown listen_port %d",
+ listen_port);
+ return NULL;
+}
+
/* Check if connecting to that port is permitted and connect. */
-int
-channel_connect_to(const char *host, u_short port)
+Channel *
+channel_connect_to(const char *host, u_short port, char *ctype, char *rname)
{
int i, permit, permit_adm = 1;
@@ -2870,9 +2923,9 @@
if (!permit || !permit_adm) {
logit("Received request to connect to host %.100s port %d, "
"but the request was denied.", host, port);
- return -1;
+ return NULL;
}
- return connect_to(host, port);
+ return connect_to(host, port, ctype, rname);
}
void
diff --git a/channels.h b/channels.h
index 46cde03..d4ac24a 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.90 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: channels.h,v 1.91 2008/05/09 04:55:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -75,6 +75,13 @@
};
TAILQ_HEAD(channel_confirms, channel_confirm);
+/* Context for non-blocking connects */
+struct channel_connect {
+ char *host;
+ int port;
+ struct addrinfo *ai, *aitop;
+};
+
struct Channel {
int type; /* channel type/state */
int self; /* my own channel identifier */
@@ -125,7 +132,11 @@
channel_infilter_fn *input_filter;
channel_outfilter_fn *output_filter;
- int datagram; /* keep boundaries */
+ /* keep boundaries */
+ int datagram;
+
+ /* non-blocking connect */
+ struct channel_connect connect_ctx;
};
#define CHAN_EXTENDED_IGNORE 0
@@ -225,8 +236,8 @@
void channel_clear_permitted_opens(void);
void channel_clear_adm_permitted_opens(void);
int channel_input_port_forward_request(int, int);
-int channel_connect_to(const char *, u_short);
-int channel_connect_by_listen_address(u_short);
+Channel *channel_connect_to(const char *, u_short, char *, char *);
+Channel *channel_connect_by_listen_address(u_short, char *, char *);
int channel_request_remote_forwarding(const char *, u_short,
const char *, u_short);
int channel_setup_local_fwd_listener(const char *, u_short,
@@ -241,7 +252,7 @@
int x11_create_display_inet(int, int, int, u_int *, int **);
void x11_input_open(int, u_int32_t, void *);
void x11_request_forwarding_with_spoofing(int, const char *, const char *,
- const char *);
+ const char *);
void deny_input_open(int, u_int32_t, void *);
/* agent forwarding */
diff --git a/clientloop.c b/clientloop.c
index c40f2c3..7bd1af6 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.190 2008/05/08 13:06:10 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.191 2008/05/09 04:55:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1762,7 +1762,6 @@
Channel *c = NULL;
char *listen_address, *originator_address;
int listen_port, originator_port;
- int sock;
/* Get rest of the packet */
listen_address = packet_get_string(NULL);
@@ -1771,19 +1770,13 @@
originator_port = packet_get_int();
packet_check_eom();
- debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d",
- listen_address, listen_port, originator_address, originator_port);
+ debug("client_request_forwarded_tcpip: listen %s port %d, "
+ "originator %s port %d", listen_address, listen_port,
+ originator_address, originator_port);
- sock = channel_connect_by_listen_address(listen_port);
- if (sock < 0) {
- xfree(originator_address);
- xfree(listen_address);
- return NULL;
- }
- c = channel_new("forwarded-tcpip",
- SSH_CHANNEL_CONNECTING, sock, sock, -1,
- CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
- originator_address, 1);
+ c = channel_connect_by_listen_address(listen_port,
+ "forwarded-tcpip", originator_address);
+
xfree(originator_address);
xfree(listen_address);
return c;
diff --git a/serverloop.c b/serverloop.c
index 20991c3..2142f38 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: serverloop.c,v 1.149 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: serverloop.c,v 1.150 2008/05/09 04:55:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -938,7 +938,6 @@
server_request_direct_tcpip(void)
{
Channel *c;
- int sock;
char *target, *originator;
int target_port, originator_port;
@@ -948,18 +947,16 @@
originator_port = packet_get_int();
packet_check_eom();
- debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
- originator, originator_port, target, target_port);
+ debug("server_request_direct_tcpip: originator %s port %d, target %s "
+ "port %d", originator, originator_port, target, target_port);
/* XXX check permission */
- sock = channel_connect_to(target, target_port);
- xfree(target);
+ c = channel_connect_to(target, target_port,
+ "direct-tcpip", "direct-tcpip");
+
xfree(originator);
- if (sock < 0)
- return NULL;
- c = channel_new("direct-tcpip", SSH_CHANNEL_CONNECTING,
- sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT,
- CHAN_TCP_PACKET_DEFAULT, 0, "direct-tcpip", 1);
+ xfree(target);
+
return c;
}