summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--channels.c167
-rw-r--r--channels.h21
-rw-r--r--clientloop.c21
-rw-r--r--serverloop.c19
5 files changed, 149 insertions, 88 deletions
diff --git a/ChangeLog b/ChangeLog
index bd6b640b..9e683143 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 b5e28dab..1e57951d 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 @@ static int IPv4or6 = AF_UNSPEC;
/* 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 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
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 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
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 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
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_input_port_open(int type, u_int32_t seq, void *ctxt)
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 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
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 @@ channel_clear_adm_permitted_opens(void)
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 @@ connect_to(const char *host, u_short port)
}
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;
+ }
+ 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;
}
- freeaddrinfo(aitop);
- if (!ai) {
- error("connect_to %.100s port %d: failed.", host, port);
- return -1;
+
+ 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;
}
- /* success */
- set_nodelay(sock);
- return sock;
+ 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;
}
-int
-channel_connect_by_listen_address(u_short listen_port)
+Channel *
+channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
{
int i;
- for (i = 0; i < num_permitted_opens; i++)
+ for (i = 0; i < num_permitted_opens; i++) {
if (permitted_opens[i].host_to_connect != NULL &&
- permitted_opens[i].listen_port == listen_port)
+ permitted_opens[i].listen_port == listen_port) {
return connect_to(
permitted_opens[i].host_to_connect,
- permitted_opens[i].port_to_connect);
+ permitted_opens[i].port_to_connect, ctype, rname);
+ }
+ }
error("WARNING: Server requests forwarding for unknown listen_port %d",
listen_port);
- return -1;
+ 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 @@ channel_connect_to(const char *host, u_short port)
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 46cde030..d4ac24a5 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 @@ struct channel_confirm {
};
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 @@ struct Channel {
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 @@ int channel_add_adm_permitted_opens(char *, int);
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_connect_display(void);
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 c40f2c30..7bd1af60 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 @@ client_request_forwarded_tcpip(const char *request_type, int rchan)
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 @@ client_request_forwarded_tcpip(const char *request_type, int rchan)
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);
+
+ c = channel_connect_by_listen_address(listen_port,
+ "forwarded-tcpip", originator_address);
- 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);
xfree(originator_address);
xfree(listen_address);
return c;
diff --git a/serverloop.c b/serverloop.c
index 20991c3c..2142f380 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 @@ static Channel *
server_request_direct_tcpip(void)
{
Channel *c;
- int sock;
char *target, *originator;
int target_port, originator_port;
@@ -948,18 +947,16 @@ server_request_direct_tcpip(void)
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;
}