-/* $OpenBSD: channels.c,v 1.294 2009/01/22 09:49:57 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.299 2009/11/11 21:37:03 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#include <arpa/inet.h>
#include <errno.h>
+#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
/* AF_UNSPEC or AF_INET or AF_INET6 */
static int IPv4or6 = AF_UNSPEC;
+/* Set the routing domain a.k.a. VRF */
+static int channel_rdomain = -1;
+
/* helper */
static void port_open_helper(Channel *c, char *rtype);
channel_max_fd = MAX(channel_max_fd, wfd);
channel_max_fd = MAX(channel_max_fd, efd);
- /* XXX set close-on-exec -markus */
+ if (rfd != -1)
+ fcntl(rfd, F_SETFD, FD_CLOEXEC);
+ if (wfd != -1 && wfd != rfd)
+ fcntl(wfd, F_SETFD, FD_CLOEXEC);
+ if (efd != -1 && efd != rfd && efd != wfd)
+ fcntl(efd, F_SETFD, FD_CLOEXEC);
c->rfd = rfd;
c->wfd = wfd;
c->output_filter = NULL;
c->filter_ctx = NULL;
c->filter_cleanup = NULL;
+ c->delayed = 1; /* prevent call to channel_post handler */
TAILQ_INIT(&c->status_confirms);
debug("channel %d: new [%s]", found, remote_name);
return c;
int ret;
have = buffer_len(&c->input);
- c->delayed = 0;
debug2("channel %d: pre_dynamic: have %d", c->self, have);
/* buffer_dump(&c->input); */
/* check if the fixed size part of the packet is in buffer. */
if (c->path != NULL)
nc->path = xstrdup(c->path);
- if (nextstate == SSH_CHANNEL_DYNAMIC) {
- /*
- * do not call the channel_post handler until
- * this flag has been reset by a pre-handler.
- * otherwise the FD_ISSET calls might overflow
- */
- nc->delayed = 1;
- } else {
+ if (nextstate != SSH_CHANNEL_DYNAMIC)
port_open_helper(nc, rtype);
- }
}
}
}
return -1;
}
+#ifndef BROKEN_TCGETATTR_ICANON
if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') {
if (tcgetattr(c->wfd, &tio) == 0 &&
!(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
packet_send();
}
}
+#endif
buffer_consume(&c->output, len);
if (compat20 && len > 0) {
c->local_consumed += len;
static void
channel_post_open(Channel *c, fd_set *readset, fd_set *writeset)
{
- if (c->delayed)
- return;
channel_handle_rfd(c, readset, writeset);
channel_handle_wfd(c, readset, writeset);
if (!compat20)
channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
{
static int did_init = 0;
- u_int i;
+ u_int i, oalloc;
Channel *c;
if (!did_init) {
channel_handler_init();
did_init = 1;
}
- for (i = 0; i < channels_alloc; i++) {
+ for (i = 0, oalloc = channels_alloc; i < oalloc; i++) {
c = channels[i];
if (c == NULL)
continue;
+ if (c->delayed) {
+ if (ftab == channel_pre)
+ c->delayed = 0;
+ else
+ continue;
+ }
if (ftab[c->type] != NULL)
(*ftab[c->type])(c, readset, writeset);
channel_garbage_collect(c);
int id;
/* Reset keepalive timeout */
- keep_alive_timeouts = 0;
+ packet_set_alive_timeouts(0);
id = packet_get_int();
packet_check_eom();
IPv4or6 = af;
}
+void
+channel_set_rdomain(int rdomain)
+{
+ channel_rdomain = rdomain;
+}
+
static int
-channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
+channel_setup_fwd_listener(int type, const char *listen_addr,
+ u_short listen_port, int *allocated_listen_port,
const char *host_to_connect, u_short port_to_connect, int gateway_ports)
{
Channel *c;
struct addrinfo hints, *ai, *aitop;
const char *host, *addr;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+ in_port_t *lport_p;
host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
listen_addr : host_to_connect;
}
return 0;
}
-
+ if (allocated_listen_port != NULL)
+ *allocated_listen_port = 0;
for (ai = aitop; ai; ai = ai->ai_next) {
- if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+ switch (ai->ai_family) {
+ case AF_INET:
+ lport_p = &((struct sockaddr_in *)ai->ai_addr)->
+ sin_port;
+ break;
+ case AF_INET6:
+ lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
+ sin6_port;
+ break;
+ default:
continue;
+ }
+ /*
+ * If allocating a port for -R forwards, then use the
+ * same port for all address families.
+ */
+ if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
+ allocated_listen_port != NULL && *allocated_listen_port > 0)
+ *lport_p = htons(*allocated_listen_port);
+
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
error("channel_setup_fwd_listener: getnameinfo failed");
continue;
}
/* Create a port to listen for the host. */
- sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ sock = socket_rdomain(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol, channel_rdomain);
if (sock < 0) {
/* this is no error since kernel may not support ipv6 */
verbose("socket: %.100s", strerror(errno));
}
channel_set_reuseaddr(sock);
+ if (ai->ai_family == AF_INET6)
+ sock_set_v6only(sock);
- debug("Local forwarding listening on %s port %s.", ntop, strport);
+ debug("Local forwarding listening on %s port %s.",
+ ntop, strport);
/* Bind the socket to the address. */
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
close(sock);
continue;
}
+
+ /*
+ * listen_port == 0 requests a dynamically allocated port -
+ * record what we got.
+ */
+ if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
+ allocated_listen_port != NULL &&
+ *allocated_listen_port == 0) {
+ *allocated_listen_port = get_sock_port(sock, 1);
+ debug("Allocated listen port %d",
+ *allocated_listen_port);
+ }
+
/* Allocate a channel number for the socket. */
c = channel_new("port listener", type, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
const char *host_to_connect, u_short port_to_connect, int gateway_ports)
{
return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
- listen_host, listen_port, host_to_connect, port_to_connect,
+ listen_host, listen_port, NULL, host_to_connect, port_to_connect,
gateway_ports);
}
/* protocol v2 remote port fwd, used by sshd */
int
channel_setup_remote_fwd_listener(const char *listen_address,
- u_short listen_port, int gateway_ports)
+ u_short listen_port, int *allocated_listen_port, int gateway_ports)
{
return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
- listen_address, listen_port, NULL, 0, gateway_ports);
+ listen_address, listen_port, allocated_listen_port,
+ NULL, 0, gateway_ports);
}
/*
error("connect_next: getnameinfo failed");
continue;
}
- if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
- cctx->ai->ai_protocol)) == -1) {
+ if ((sock = socket_rdomain(cctx->ai->ai_family,
+ cctx->ai->ai_socktype, cctx->ai->ai_protocol,
+ channel_rdomain)) == -1) {
if (cctx->ai->ai_next == NULL)
error("socket: %.100s", strerror(errno));
else
for (ai = aitop; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
- sock = socket(ai->ai_family, ai->ai_socktype,
- ai->ai_protocol);
+ sock = socket_rdomain(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol, channel_rdomain);
if (sock < 0) {
if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) {
error("socket: %.100s", strerror(errno));
continue;
}
}
-#ifdef IPV6_V6ONLY
- if (ai->ai_family == AF_INET6) {
- int on = 1;
- if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
- error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
- }
-#endif
+ if (ai->ai_family == AF_INET6)
+ sock_set_v6only(sock);
if (x11_use_localhost)
channel_set_reuseaddr(sock);
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
}
for (ai = aitop; ai; ai = ai->ai_next) {
/* Create a socket. */
- sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ sock = socket_rdomain(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol, channel_rdomain);
if (sock < 0) {
debug2("socket: %.100s", strerror(errno));
continue;