- 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@
This commit is contained in:
Damien Miller 2008-05-19 15:37:09 +10:00
parent 5771ed7d1b
commit bd74025c7b
5 changed files with 160 additions and 99 deletions

View File

@ -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 $

View File

@ -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 */
}
freeaddrinfo(aitop);
if (!ai) {
error("connect_to %.100s port %d: failed.", host, port);
return -1;
}
/* 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;
}
int
channel_connect_by_listen_address(u_short listen_port)
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++)
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

View File

@ -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,

View File

@ -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;

View File

@ -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;
}