summaryrefslogtreecommitdiff
path: root/channels.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2008-05-19 15:37:09 +1000
committerDamien Miller <djm@mindrot.org>2008-05-19 15:37:09 +1000
commitbd74025c7b08104828986d01e60f04372b3d5337 (patch)
treeba2d9b51b7e33e64f1c19b15f862b3929559506c /channels.c
parent5771ed7d1b5af26bc991151ab977e77bf1e87666 (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@
Diffstat (limited to 'channels.c')
-rw-r--r--channels.c167
1 files changed, 110 insertions, 57 deletions
diff --git a/channels.c b/channels.c
index b5e28dabf..1e57951dc 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: channels.c,v 1.275 2008/05/08 12:02:23 djm Exp $ */ 1/* $OpenBSD: channels.c,v 1.276 2008/05/09 04:55:56 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
@@ -165,6 +165,10 @@ static int IPv4or6 = AF_UNSPEC;
165/* helper */ 165/* helper */
166static void port_open_helper(Channel *c, char *rtype); 166static void port_open_helper(Channel *c, char *rtype);
167 167
168/* non-blocking connect helpers */
169static int connect_next(struct channel_connect *);
170static void channel_connect_ctx_free(struct channel_connect *);
171
168/* -- channel core */ 172/* -- channel core */
169 173
170Channel * 174Channel *
@@ -1425,7 +1429,7 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
1425static void 1429static void
1426channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 1430channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
1427{ 1431{
1428 int err = 0; 1432 int err = 0, sock;
1429 socklen_t sz = sizeof(err); 1433 socklen_t sz = sizeof(err);
1430 1434
1431 if (FD_ISSET(c->sock, writeset)) { 1435 if (FD_ISSET(c->sock, writeset)) {
@@ -1434,7 +1438,9 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
1434 error("getsockopt SO_ERROR failed"); 1438 error("getsockopt SO_ERROR failed");
1435 } 1439 }
1436 if (err == 0) { 1440 if (err == 0) {
1437 debug("channel %d: connected", c->self); 1441 debug("channel %d: connected to %s port %d",
1442 c->self, c->connect_ctx.host, c->connect_ctx.port);
1443 channel_connect_ctx_free(&c->connect_ctx);
1438 c->type = SSH_CHANNEL_OPEN; 1444 c->type = SSH_CHANNEL_OPEN;
1439 if (compat20) { 1445 if (compat20) {
1440 packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1446 packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
@@ -1448,8 +1454,19 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
1448 packet_put_int(c->self); 1454 packet_put_int(c->self);
1449 } 1455 }
1450 } else { 1456 } else {
1451 debug("channel %d: not connected: %s", 1457 debug("channel %d: connection failed: %s",
1452 c->self, strerror(err)); 1458 c->self, strerror(err));
1459 /* Try next address, if any */
1460 if ((sock = connect_next(&c->connect_ctx)) > 0) {
1461 close(c->sock);
1462 c->sock = c->rfd = c->wfd = sock;
1463 channel_max_fd = channel_find_maxfd();
1464 return;
1465 }
1466 /* Exhausted all addresses */
1467 error("connect_to %.100s port %d: failed.",
1468 c->connect_ctx.host, c->connect_ctx.port);
1469 channel_connect_ctx_free(&c->connect_ctx);
1453 if (compat20) { 1470 if (compat20) {
1454 packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1471 packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
1455 packet_put_int(c->remote_id); 1472 packet_put_int(c->remote_id);
@@ -2327,7 +2344,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
2327 Channel *c = NULL; 2344 Channel *c = NULL;
2328 u_short host_port; 2345 u_short host_port;
2329 char *host, *originator_string; 2346 char *host, *originator_string;
2330 int remote_id, sock = -1; 2347 int remote_id;
2331 2348
2332 remote_id = packet_get_int(); 2349 remote_id = packet_get_int();
2333 host = packet_get_string(NULL); 2350 host = packet_get_string(NULL);
@@ -2339,20 +2356,16 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
2339 originator_string = xstrdup("unknown (remote did not supply name)"); 2356 originator_string = xstrdup("unknown (remote did not supply name)");
2340 } 2357 }
2341 packet_check_eom(); 2358 packet_check_eom();
2342 sock = channel_connect_to(host, host_port); 2359 c = channel_connect_to(host, host_port,
2343 if (sock != -1) { 2360 "connected socket", originator_string);
2344 c = channel_new("connected socket",
2345 SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
2346 originator_string, 1);
2347 c->remote_id = remote_id;
2348 }
2349 xfree(originator_string); 2361 xfree(originator_string);
2362 xfree(host);
2350 if (c == NULL) { 2363 if (c == NULL) {
2351 packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2364 packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
2352 packet_put_int(remote_id); 2365 packet_put_int(remote_id);
2353 packet_send(); 2366 packet_send();
2354 } 2367 } else
2355 xfree(host); 2368 c->remote_id = remote_id;
2356} 2369}
2357 2370
2358/* ARGSUSED */ 2371/* ARGSUSED */
@@ -2770,35 +2783,26 @@ channel_clear_adm_permitted_opens(void)
2770 num_adm_permitted_opens = 0; 2783 num_adm_permitted_opens = 0;
2771} 2784}
2772 2785
2773/* return socket to remote host, port */ 2786/* Try to start non-blocking connect to next host in cctx list */
2774static int 2787static int
2775connect_to(const char *host, u_short port) 2788connect_next(struct channel_connect *cctx)
2776{ 2789{
2777 struct addrinfo hints, *ai, *aitop; 2790 int sock, saved_errno;
2778 char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2791 char ntop[NI_MAXHOST], strport[NI_MAXSERV];
2779 int gaierr;
2780 int sock = -1;
2781 2792
2782 memset(&hints, 0, sizeof(hints)); 2793 for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
2783 hints.ai_family = IPv4or6; 2794 if (cctx->ai->ai_family != AF_INET &&
2784 hints.ai_socktype = SOCK_STREAM; 2795 cctx->ai->ai_family != AF_INET6)
2785 snprintf(strport, sizeof strport, "%d", port);
2786 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
2787 error("connect_to %.100s: unknown host (%s)", host,
2788 ssh_gai_strerror(gaierr));
2789 return -1;
2790 }
2791 for (ai = aitop; ai; ai = ai->ai_next) {
2792 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
2793 continue; 2796 continue;
2794 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2797 if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
2795 strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2798 ntop, sizeof(ntop), strport, sizeof(strport),
2796 error("connect_to: getnameinfo failed"); 2799 NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
2800 error("connect_next: getnameinfo failed");
2797 continue; 2801 continue;
2798 } 2802 }
2799 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2803 if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
2800 if (sock < 0) { 2804 cctx->ai->ai_protocol)) == -1) {
2801 if (ai->ai_next == NULL) 2805 if (cctx->ai->ai_next == NULL)
2802 error("socket: %.100s", strerror(errno)); 2806 error("socket: %.100s", strerror(errno));
2803 else 2807 else
2804 verbose("socket: %.100s", strerror(errno)); 2808 verbose("socket: %.100s", strerror(errno));
@@ -2806,45 +2810,94 @@ connect_to(const char *host, u_short port)
2806 } 2810 }
2807 if (set_nonblock(sock) == -1) 2811 if (set_nonblock(sock) == -1)
2808 fatal("%s: set_nonblock(%d)", __func__, sock); 2812 fatal("%s: set_nonblock(%d)", __func__, sock);
2809 if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 2813 if (connect(sock, cctx->ai->ai_addr,
2810 errno != EINPROGRESS) { 2814 cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
2811 error("connect_to %.100s port %s: %.100s", ntop, strport, 2815 debug("connect_next: host %.100s ([%.100s]:%s): "
2816 "%.100s", cctx->host, ntop, strport,
2812 strerror(errno)); 2817 strerror(errno));
2818 saved_errno = errno;
2813 close(sock); 2819 close(sock);
2820 errno = saved_errno;
2814 continue; /* fail -- try next */ 2821 continue; /* fail -- try next */
2815 } 2822 }
2816 break; /* success */ 2823 debug("connect_next: host %.100s ([%.100s]:%s) "
2824 "in progress, fd=%d", cctx->host, ntop, strport, sock);
2825 cctx->ai = cctx->ai->ai_next;
2826 set_nodelay(sock);
2827 return sock;
2828 }
2829 return -1;
2830}
2831
2832static void
2833channel_connect_ctx_free(struct channel_connect *cctx)
2834{
2835 xfree(cctx->host);
2836 if (cctx->aitop)
2837 freeaddrinfo(cctx->aitop);
2838 bzero(cctx, sizeof(*cctx));
2839 cctx->host = NULL;
2840 cctx->ai = cctx->aitop = NULL;
2841}
2817 2842
2843/* Return CONNECTING channel to remote host, port */
2844static Channel *
2845connect_to(const char *host, u_short port, char *ctype, char *rname)
2846{
2847 struct addrinfo hints;
2848 int gaierr;
2849 int sock = -1;
2850 char strport[NI_MAXSERV];
2851 struct channel_connect cctx;
2852 Channel *c;
2853
2854 memset(&hints, 0, sizeof(hints));
2855 hints.ai_family = IPv4or6;
2856 hints.ai_socktype = SOCK_STREAM;
2857 snprintf(strport, sizeof strport, "%d", port);
2858 if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) {
2859 error("connect_to %.100s: unknown host (%s)", host,
2860 ssh_gai_strerror(gaierr));
2861 return NULL;
2818 } 2862 }
2819 freeaddrinfo(aitop); 2863
2820 if (!ai) { 2864 cctx.host = xstrdup(host);
2821 error("connect_to %.100s port %d: failed.", host, port); 2865 cctx.port = port;
2822 return -1; 2866 cctx.ai = cctx.aitop;
2867
2868 if ((sock = connect_next(&cctx)) == -1) {
2869 error("connect to %.100s port %d failed: %s",
2870 host, port, strerror(errno));
2871 channel_connect_ctx_free(&cctx);
2872 return NULL;
2823 } 2873 }
2824 /* success */ 2874 c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
2825 set_nodelay(sock); 2875 CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
2826 return sock; 2876 c->connect_ctx = cctx;
2877 return c;
2827} 2878}
2828 2879
2829int 2880Channel *
2830channel_connect_by_listen_address(u_short listen_port) 2881channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
2831{ 2882{
2832 int i; 2883 int i;
2833 2884
2834 for (i = 0; i < num_permitted_opens; i++) 2885 for (i = 0; i < num_permitted_opens; i++) {
2835 if (permitted_opens[i].host_to_connect != NULL && 2886 if (permitted_opens[i].host_to_connect != NULL &&
2836 permitted_opens[i].listen_port == listen_port) 2887 permitted_opens[i].listen_port == listen_port) {
2837 return connect_to( 2888 return connect_to(
2838 permitted_opens[i].host_to_connect, 2889 permitted_opens[i].host_to_connect,
2839 permitted_opens[i].port_to_connect); 2890 permitted_opens[i].port_to_connect, ctype, rname);
2891 }
2892 }
2840 error("WARNING: Server requests forwarding for unknown listen_port %d", 2893 error("WARNING: Server requests forwarding for unknown listen_port %d",
2841 listen_port); 2894 listen_port);
2842 return -1; 2895 return NULL;
2843} 2896}
2844 2897
2845/* Check if connecting to that port is permitted and connect. */ 2898/* Check if connecting to that port is permitted and connect. */
2846int 2899Channel *
2847channel_connect_to(const char *host, u_short port) 2900channel_connect_to(const char *host, u_short port, char *ctype, char *rname)
2848{ 2901{
2849 int i, permit, permit_adm = 1; 2902 int i, permit, permit_adm = 1;
2850 2903
@@ -2870,9 +2923,9 @@ channel_connect_to(const char *host, u_short port)
2870 if (!permit || !permit_adm) { 2923 if (!permit || !permit_adm) {
2871 logit("Received request to connect to host %.100s port %d, " 2924 logit("Received request to connect to host %.100s port %d, "
2872 "but the request was denied.", host, port); 2925 "but the request was denied.", host, port);
2873 return -1; 2926 return NULL;
2874 } 2927 }
2875 return connect_to(host, port); 2928 return connect_to(host, port, ctype, rname);
2876} 2929}
2877 2930
2878void 2931void