summaryrefslogtreecommitdiff
path: root/channels.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels.c')
-rw-r--r--channels.c215
1 files changed, 136 insertions, 79 deletions
diff --git a/channels.c b/channels.c
index f6e9b4d8..e741f29b 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.315 2011/09/23 07:45:05 markus Exp $ */
+/* $OpenBSD: channels.c,v 1.328 2013/12/19 01:04:36 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -213,6 +213,7 @@ channel_lookup(int id)
case SSH_CHANNEL_OPEN:
case SSH_CHANNEL_INPUT_DRAINING:
case SSH_CHANNEL_OUTPUT_DRAINING:
+ case SSH_CHANNEL_ABANDONED:
return (c);
}
logit("Non-public channel %d, type %d.", id, c->type);
@@ -247,7 +248,10 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd,
if ((c->isatty = is_tty) != 0)
debug2("channel %d: rfd %d isatty", c->self, c->rfd);
+#ifdef _AIX
+ /* XXX: Later AIX versions can't push as much data to tty */
c->wfd_isatty = is_tty || isatty(c->wfd);
+#endif
/* enable nonblocking mode */
if (nonblock) {
@@ -311,6 +315,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
c->istate = CHAN_INPUT_OPEN;
c->flags = 0;
channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0);
+ c->notbefore = 0;
c->self = found;
c->type = type;
c->ctype = ctype;
@@ -400,7 +405,7 @@ channel_free(Channel *c)
s = channel_open_message();
debug3("channel %d: status: %s", c->self, s);
- xfree(s);
+ free(s);
if (c->sock != -1)
shutdown(c->sock, SHUT_RDWR);
@@ -408,29 +413,23 @@ channel_free(Channel *c)
buffer_free(&c->input);
buffer_free(&c->output);
buffer_free(&c->extended);
- if (c->remote_name) {
- xfree(c->remote_name);
- c->remote_name = NULL;
- }
- if (c->path) {
- xfree(c->path);
- c->path = NULL;
- }
- if (c->listening_addr) {
- xfree(c->listening_addr);
- c->listening_addr = NULL;
- }
+ free(c->remote_name);
+ c->remote_name = NULL;
+ free(c->path);
+ c->path = NULL;
+ free(c->listening_addr);
+ c->listening_addr = NULL;
while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
if (cc->abandon_cb != NULL)
cc->abandon_cb(c, cc->ctx);
TAILQ_REMOVE(&c->status_confirms, cc, entry);
bzero(cc, sizeof(*cc));
- xfree(cc);
+ free(cc);
}
if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
c->filter_cleanup(c->self, c->filter_ctx);
channels[c->self] = NULL;
- xfree(c);
+ free(c);
}
void
@@ -535,6 +534,7 @@ channel_still_open(void)
case SSH_CHANNEL_DYNAMIC:
case SSH_CHANNEL_CONNECTING:
case SSH_CHANNEL_ZOMBIE:
+ case SSH_CHANNEL_ABANDONED:
continue;
case SSH_CHANNEL_LARVAL:
if (!compat20)
@@ -580,6 +580,7 @@ channel_find_open(void)
case SSH_CHANNEL_OPENING:
case SSH_CHANNEL_CONNECTING:
case SSH_CHANNEL_ZOMBIE:
+ case SSH_CHANNEL_ABANDONED:
continue;
case SSH_CHANNEL_LARVAL:
case SSH_CHANNEL_AUTH_SOCKET:
@@ -627,6 +628,7 @@ channel_open_message(void)
case SSH_CHANNEL_CLOSED:
case SSH_CHANNEL_AUTH_SOCKET:
case SSH_CHANNEL_ZOMBIE:
+ case SSH_CHANNEL_ABANDONED:
case SSH_CHANNEL_MUX_CLIENT:
case SSH_CHANNEL_MUX_LISTENER:
continue;
@@ -702,7 +704,7 @@ channel_register_status_confirm(int id, channel_confirm_cb *cb,
if ((c = channel_lookup(id)) == NULL)
fatal("channel_register_expect: %d: bad id", id);
- cc = xmalloc(sizeof(*cc));
+ cc = xcalloc(1, sizeof(*cc));
cc->cb = cb;
cc->abandon_cb = abandon_cb;
cc->ctx = ctx;
@@ -1079,10 +1081,8 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset)
strlcpy(username, p, sizeof(username));
buffer_consume(&c->input, len);
- if (c->path != NULL) {
- xfree(c->path);
- c->path = NULL;
- }
+ free(c->path);
+ c->path = NULL;
if (need == 1) { /* SOCKS4: one string */
host = inet_ntoa(s4_req.dest_addr);
c->path = xstrdup(host);
@@ -1142,7 +1142,8 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
u_int8_t atyp;
} s5_req, s5_rsp;
u_int16_t dest_port;
- u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
+ char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
+ u_char *p;
u_int have, need, i, found, nmethods, addrlen, af;
debug2("channel %d: decode socks5", c->self);
@@ -1212,13 +1213,11 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
buffer_consume(&c->input, sizeof(s5_req));
if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
buffer_consume(&c->input, 1); /* host string length */
- buffer_get(&c->input, (char *)&dest_addr, addrlen);
+ buffer_get(&c->input, &dest_addr, addrlen);
buffer_get(&c->input, (char *)&dest_port, 2);
dest_addr[addrlen] = '\0';
- if (c->path != NULL) {
- xfree(c->path);
- c->path = NULL;
- }
+ free(c->path);
+ c->path = NULL;
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
if (addrlen >= NI_MAXHOST) {
error("channel %d: dynamic request: socks5 hostname "
@@ -1240,11 +1239,10 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
s5_rsp.command = SSH_SOCKS5_SUCCESS;
s5_rsp.reserved = 0; /* ignored */
s5_rsp.atyp = SSH_SOCKS5_IPV4;
- ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY;
dest_port = 0; /* ignored */
buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
- buffer_append(&c->output, &dest_addr, sizeof(struct in_addr));
+ buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */
buffer_append(&c->output, &dest_port, sizeof(dest_port));
return 1;
}
@@ -1323,7 +1321,7 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
{
Channel *nc;
struct sockaddr_storage addr;
- int newsock;
+ int newsock, oerrno;
socklen_t addrlen;
char buf[16384], *remote_ipaddr;
int remote_port;
@@ -1333,12 +1331,18 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
addrlen = sizeof(addr);
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
if (c->single_connection) {
+ oerrno = errno;
debug2("single_connection: closing X11 listener.");
channel_close_fd(&c->sock);
chan_mark_dead(c);
+ errno = oerrno;
}
if (newsock < 0) {
- error("accept: %.100s", strerror(errno));
+ if (errno != EINTR && errno != EWOULDBLOCK &&
+ errno != ECONNABORTED)
+ error("accept: %.100s", strerror(errno));
+ if (errno == EMFILE || errno == ENFILE)
+ c->notbefore = monotime() + 1;
return;
}
set_nodelay(newsock);
@@ -1372,7 +1376,7 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
packet_put_cstring(buf);
packet_send();
}
- xfree(remote_ipaddr);
+ free(remote_ipaddr);
}
}
@@ -1381,12 +1385,14 @@ port_open_helper(Channel *c, char *rtype)
{
int direct;
char buf[1024];
+ char *local_ipaddr = get_local_ipaddr(c->sock);
+ int local_port = get_sock_port(c->sock, 1);
char *remote_ipaddr = get_peer_ipaddr(c->sock);
int remote_port = get_peer_port(c->sock);
if (remote_port == -1) {
/* Fake addr/port to appease peers that validate it (Tectia) */
- xfree(remote_ipaddr);
+ free(remote_ipaddr);
remote_ipaddr = xstrdup("127.0.0.1");
remote_port = 65535;
}
@@ -1395,11 +1401,11 @@ port_open_helper(Channel *c, char *rtype)
snprintf(buf, sizeof buf,
"%s: listening port %d for %.100s port %d, "
- "connect from %.200s port %d",
+ "connect from %.200s port %d to %.100s port %d",
rtype, c->listening_port, c->path, c->host_port,
- remote_ipaddr, remote_port);
+ remote_ipaddr, remote_port, local_ipaddr, local_port);
- xfree(c->remote_name);
+ free(c->remote_name);
c->remote_name = xstrdup(buf);
if (compat20) {
@@ -1415,7 +1421,7 @@ port_open_helper(Channel *c, char *rtype)
} else {
/* listen address, port */
packet_put_cstring(c->path);
- packet_put_int(c->listening_port);
+ packet_put_int(local_port);
}
/* originator host and port */
packet_put_cstring(remote_ipaddr);
@@ -1431,7 +1437,8 @@ port_open_helper(Channel *c, char *rtype)
packet_put_cstring(c->remote_name);
packet_send();
}
- xfree(remote_ipaddr);
+ free(remote_ipaddr);
+ free(local_ipaddr);
}
static void
@@ -1481,7 +1488,11 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
addrlen = sizeof(addr);
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
if (newsock < 0) {
- error("accept: %.100s", strerror(errno));
+ if (errno != EINTR && errno != EWOULDBLOCK &&
+ errno != ECONNABORTED)
+ error("accept: %.100s", strerror(errno));
+ if (errno == EMFILE || errno == ENFILE)
+ c->notbefore = monotime() + 1;
return;
}
set_nodelay(newsock);
@@ -1514,7 +1525,10 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
addrlen = sizeof(addr);
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
if (newsock < 0) {
- error("accept from auth socket: %.100s", strerror(errno));
+ error("accept from auth socket: %.100s",
+ strerror(errno));
+ if (errno == EMFILE || errno == ENFILE)
+ c->notbefore = monotime() + 1;
return;
}
nc = channel_new("accepted auth socket",
@@ -1677,7 +1691,7 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
if (c->datagram) {
/* ignore truncated writes, datagrams might get lost */
len = write(c->wfd, buf, dlen);
- xfree(data);
+ free(data);
if (len < 0 && (errno == EINTR || errno == EAGAIN ||
errno == EWOULDBLOCK))
return 1;
@@ -1917,6 +1931,8 @@ channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset)
if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
&addrlen)) == -1) {
error("%s accept: %s", __func__, strerror(errno));
+ if (errno == EMFILE || errno == ENFILE)
+ c->notbefore = monotime() + 1;
return;
}
@@ -2067,16 +2083,21 @@ channel_garbage_collect(Channel *c)
}
static void
-channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
+channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset,
+ time_t *unpause_secs)
{
static int did_init = 0;
u_int i, oalloc;
Channel *c;
+ time_t now;
if (!did_init) {
channel_handler_init();
did_init = 1;
}
+ now = monotime();
+ if (unpause_secs != NULL)
+ *unpause_secs = 0;
for (i = 0, oalloc = channels_alloc; i < oalloc; i++) {
c = channels[i];
if (c == NULL)
@@ -2087,10 +2108,30 @@ channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
else
continue;
}
- if (ftab[c->type] != NULL)
- (*ftab[c->type])(c, readset, writeset);
+ if (ftab[c->type] != NULL) {
+ /*
+ * Run handlers that are not paused.
+ */
+ if (c->notbefore <= now)
+ (*ftab[c->type])(c, readset, writeset);
+ else if (unpause_secs != NULL) {
+ /*
+ * Collect the time that the earliest
+ * channel comes off pause.
+ */
+ debug3("%s: chan %d: skip for %d more seconds",
+ __func__, c->self,
+ (int)(c->notbefore - now));
+ if (*unpause_secs == 0 ||
+ (c->notbefore - now) < *unpause_secs)
+ *unpause_secs = c->notbefore - now;
+ }
+ }
channel_garbage_collect(c);
}
+ if (unpause_secs != NULL && *unpause_secs != 0)
+ debug3("%s: first channel unpauses in %d seconds",
+ __func__, (int)*unpause_secs);
}
/*
@@ -2099,7 +2140,7 @@ channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
*/
void
channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
- u_int *nallocp, int rekeying)
+ u_int *nallocp, time_t *minwait_secs, int rekeying)
{
u_int n, sz, nfdset;
@@ -2122,7 +2163,8 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
memset(*writesetp, 0, sz);
if (!rekeying)
- channel_handler(channel_pre, *readsetp, *writesetp);
+ channel_handler(channel_pre, *readsetp, *writesetp,
+ minwait_secs);
}
/*
@@ -2132,7 +2174,7 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
void
channel_after_select(fd_set *readset, fd_set *writeset)
{
- channel_handler(channel_post, readset, writeset);
+ channel_handler(channel_post, readset, writeset, NULL);
}
@@ -2183,7 +2225,7 @@ channel_output_poll(void)
debug("channel %d: datagram "
"too big for channel",
c->self);
- xfree(data);
+ free(data);
continue;
}
packet_start(SSH2_MSG_CHANNEL_DATA);
@@ -2191,7 +2233,7 @@ channel_output_poll(void)
packet_put_string(data, dlen);
packet_send();
c->remote_window -= dlen + 4;
- xfree(data);
+ free(data);
}
continue;
}
@@ -2363,13 +2405,13 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt)
if (data_len > c->local_window) {
logit("channel %d: rcvd too much extended_data %d, win %d",
c->self, data_len, c->local_window);
- xfree(data);
+ free(data);
return;
}
debug2("channel %d: rcvd ext data %d", c->self, data_len);
c->local_window -= data_len;
buffer_append(&c->extended, data, data_len);
- xfree(data);
+ free(data);
}
/* ARGSUSED */
@@ -2459,7 +2501,7 @@ channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
if (c == NULL)
packet_disconnect("Received close confirmation for "
"out-of-range channel %d.", id);
- if (c->type != SSH_CHANNEL_CLOSED)
+ if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED)
packet_disconnect("Received close confirmation for "
"non-closed channel %d (type %d).", id, c->type);
channel_free(c);
@@ -2535,10 +2577,8 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt)
}
logit("channel %d: open failed: %s%s%s", id,
reason2txt(reason), msg ? ": ": "", msg ? msg : "");
- if (msg != NULL)
- xfree(msg);
- if (lang != NULL)
- xfree(lang);
+ free(msg);
+ free(lang);
if (c->open_confirm) {
debug2("callback start");
c->open_confirm(c->self, 0, c->open_confirm_ctx);
@@ -2596,8 +2636,8 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
packet_check_eom();
c = channel_connect_to(host, host_port,
"connected socket", originator_string);
- xfree(originator_string);
- xfree(host);
+ free(originator_string);
+ free(host);
if (c == NULL) {
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
packet_put_int(remote_id);
@@ -2632,7 +2672,7 @@ channel_input_status_confirm(int type, u_int32_t seq, void *ctxt)
cc->cb(type, c, cc->ctx);
TAILQ_REMOVE(&c->status_confirms, cc, entry);
bzero(cc, sizeof(*cc));
- xfree(cc);
+ free(cc);
}
/* -- tcp forwarding */
@@ -2673,8 +2713,20 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp,
if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
*listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
- (!is_client && gateway_ports == 1))
+ (!is_client && gateway_ports == 1)) {
wildcard = 1;
+ /*
+ * Notify client if they requested a specific listen
+ * address and it was overridden.
+ */
+ if (*listen_addr != '\0' &&
+ strcmp(listen_addr, "0.0.0.0") != 0 &&
+ strcmp(listen_addr, "*") != 0) {
+ packet_send_debug("Forwarding listen address "
+ "\"%s\" overridden by server "
+ "GatewayPorts", listen_addr);
+ }
+ }
else if (strcmp(listen_addr, "localhost") != 0)
addr = listen_addr;
}
@@ -3012,7 +3064,7 @@ channel_request_rforward_cancel(const char *host, u_short port)
permitted_opens[i].listen_port = 0;
permitted_opens[i].port_to_connect = 0;
- xfree(permitted_opens[i].host_to_connect);
+ free(permitted_opens[i].host_to_connect);
permitted_opens[i].host_to_connect = NULL;
return 0;
@@ -3053,7 +3105,7 @@ channel_input_port_forward_request(int is_root, int gateway_ports)
host_port, gateway_ports);
/* Free the argument string. */
- xfree(hostname);
+ free(hostname);
return (success ? 0 : -1);
}
@@ -3108,7 +3160,7 @@ channel_update_permitted_opens(int idx, int newport)
} else {
permitted_opens[idx].listen_port = 0;
permitted_opens[idx].port_to_connect = 0;
- xfree(permitted_opens[idx].host_to_connect);
+ free(permitted_opens[idx].host_to_connect);
permitted_opens[idx].host_to_connect = NULL;
}
}
@@ -3127,17 +3179,23 @@ channel_add_adm_permitted_opens(char *host, int port)
}
void
+channel_disable_adm_local_opens(void)
+{
+ channel_clear_adm_permitted_opens();
+ permitted_adm_opens = xmalloc(sizeof(*permitted_adm_opens));
+ permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL;
+ num_adm_permitted_opens = 1;
+}
+
+void
channel_clear_permitted_opens(void)
{
int i;
for (i = 0; i < num_permitted_opens; i++)
- if (permitted_opens[i].host_to_connect != NULL)
- xfree(permitted_opens[i].host_to_connect);
- if (num_permitted_opens > 0) {
- xfree(permitted_opens);
- permitted_opens = NULL;
- }
+ free(permitted_opens[i].host_to_connect);
+ free(permitted_opens);
+ permitted_opens = NULL;
num_permitted_opens = 0;
}
@@ -3147,12 +3205,9 @@ channel_clear_adm_permitted_opens(void)
int i;
for (i = 0; i < num_adm_permitted_opens; i++)
- if (permitted_adm_opens[i].host_to_connect != NULL)
- xfree(permitted_adm_opens[i].host_to_connect);
- if (num_adm_permitted_opens > 0) {
- xfree(permitted_adm_opens);
- permitted_adm_opens = NULL;
- }
+ free(permitted_adm_opens[i].host_to_connect);
+ free(permitted_adm_opens);
+ permitted_adm_opens = NULL;
num_adm_permitted_opens = 0;
}
@@ -3167,7 +3222,9 @@ channel_print_adm_permitted_opens(void)
return;
}
for (i = 0; i < num_adm_permitted_opens; i++)
- if (permitted_adm_opens[i].host_to_connect != NULL)
+ if (permitted_adm_opens[i].host_to_connect == NULL)
+ printf(" none");
+ else
printf(" %s:%d", permitted_adm_opens[i].host_to_connect,
permitted_adm_opens[i].port_to_connect);
printf("\n");
@@ -3244,7 +3301,7 @@ connect_next(struct channel_connect *cctx)
static void
channel_connect_ctx_free(struct channel_connect *cctx)
{
- xfree(cctx->host);
+ free(cctx->host);
if (cctx->aitop)
freeaddrinfo(cctx->aitop);
bzero(cctx, sizeof(*cctx));
@@ -3639,7 +3696,7 @@ x11_input_open(int type, u_int32_t seq, void *ctxt)
c->remote_id = remote_id;
c->force_drain = 1;
}
- xfree(remote_host);
+ free(remote_host);
if (c == NULL) {
/* Send refusal to the remote host. */
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
@@ -3747,7 +3804,7 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
packet_put_int(screen_number);
packet_send();
packet_write_wait();
- xfree(new_data);
+ free(new_data);
}