version 1.425, 2023/01/06 02:42:34 |
version 1.426, 2023/01/06 02:47:18 |
|
|
int all_permitted; |
int all_permitted; |
}; |
}; |
|
|
|
/* Used to record timeouts per channel type */ |
|
struct ssh_channel_timeout { |
|
char *type_pattern; |
|
u_int timeout_secs; |
|
}; |
|
|
/* Master structure for channels state */ |
/* Master structure for channels state */ |
struct ssh_channels { |
struct ssh_channels { |
/* |
/* |
|
|
|
|
/* AF_UNSPEC or AF_INET or AF_INET6 */ |
/* AF_UNSPEC or AF_INET or AF_INET6 */ |
int IPv4or6; |
int IPv4or6; |
|
|
|
/* Channel timeouts by type */ |
|
struct ssh_channel_timeout *timeouts; |
|
size_t ntimeouts; |
}; |
}; |
|
|
/* helper */ |
/* helper */ |
|
|
} |
} |
|
|
/* |
/* |
|
* Add a timeout for open channels whose c->ctype (or c->xctype if it is set) |
|
* match type_pattern. |
|
*/ |
|
void |
|
channel_add_timeout(struct ssh *ssh, const char *type_pattern, |
|
u_int timeout_secs) |
|
{ |
|
struct ssh_channels *sc = ssh->chanctxt; |
|
|
|
debug2_f("channel type \"%s\" timeout %u seconds", |
|
type_pattern, timeout_secs); |
|
sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts, |
|
sc->ntimeouts + 1, sizeof(*sc->timeouts)); |
|
sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern); |
|
sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs; |
|
sc->ntimeouts++; |
|
} |
|
|
|
/* Clears all previously-added channel timeouts */ |
|
void |
|
channel_clear_timeouts(struct ssh *ssh) |
|
{ |
|
struct ssh_channels *sc = ssh->chanctxt; |
|
size_t i; |
|
|
|
debug3_f("clearing"); |
|
for (i = 0; i < sc->ntimeouts; i++) |
|
free(sc->timeouts[i].type_pattern); |
|
free(sc->timeouts); |
|
sc->timeouts = NULL; |
|
sc->ntimeouts = 0; |
|
} |
|
|
|
static u_int |
|
lookup_timeout(struct ssh *ssh, const char *type) |
|
{ |
|
struct ssh_channels *sc = ssh->chanctxt; |
|
size_t i; |
|
|
|
for (i = 0; i < sc->ntimeouts; i++) { |
|
if (match_pattern(type, sc->timeouts[i].type_pattern)) |
|
return sc->timeouts[i].timeout_secs; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/* |
* Sets "extended type" of a channel; used by session layer to add additional |
* Sets "extended type" of a channel; used by session layer to add additional |
* information about channel types (e.g. shell, login, subsystem) that can then |
* information about channel types (e.g. shell, login, subsystem) that can then |
* be used to select timeouts. |
* be used to select timeouts. |
|
* Will reset c->inactive_deadline as a side-effect. |
*/ |
*/ |
void |
void |
channel_set_xtype(struct ssh *ssh, int id, const char *xctype) |
channel_set_xtype(struct ssh *ssh, int id, const char *xctype) |
|
|
if (c->xctype != NULL) |
if (c->xctype != NULL) |
free(c->xctype); |
free(c->xctype); |
c->xctype = xstrdup(xctype); |
c->xctype = xstrdup(xctype); |
debug2_f("labeled channel %d as %s", id, xctype); |
/* Type has changed, so look up inactivity deadline again */ |
|
c->inactive_deadline = lookup_timeout(ssh, c->xctype); |
|
debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype, |
|
c->inactive_deadline); |
} |
} |
|
|
/* |
/* |
|
|
c->remote_name = xstrdup(remote_name); |
c->remote_name = xstrdup(remote_name); |
c->ctl_chan = -1; |
c->ctl_chan = -1; |
c->delayed = 1; /* prevent call to channel_post handler */ |
c->delayed = 1; /* prevent call to channel_post handler */ |
|
c->inactive_deadline = lookup_timeout(ssh, c->ctype); |
TAILQ_INIT(&c->status_confirms); |
TAILQ_INIT(&c->status_confirms); |
debug("channel %d: new [%s]", found, remote_name); |
debug("channel %d: new %s [%s] (inactive timeout: %u)", |
|
found, c->ctype, remote_name, c->inactive_deadline); |
return c; |
return c; |
} |
} |
|
|
|
|
|
|
channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); |
channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); |
c->type = SSH_CHANNEL_OPEN; |
c->type = SSH_CHANNEL_OPEN; |
|
c->lastused = monotime(); |
c->local_window = c->local_window_max = window_max; |
c->local_window = c->local_window_max = window_max; |
|
|
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || |
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || |
|
|
channel_close_fd(ssh, c, &c->efd); |
channel_close_fd(ssh, c, &c->efd); |
if (abandon) |
if (abandon) |
c->type = SSH_CHANNEL_ABANDONED; |
c->type = SSH_CHANNEL_ABANDONED; |
|
/* exempt from inactivity timeouts */ |
|
c->inactive_deadline = 0; |
|
c->lastused = 0; |
} |
} |
|
|
static void |
static void |
|
|
|
|
if (ret == 1) { |
if (ret == 1) { |
c->type = SSH_CHANNEL_OPEN; |
c->type = SSH_CHANNEL_OPEN; |
|
c->lastused = monotime(); |
channel_pre_open(ssh, c); |
channel_pre_open(ssh, c); |
} else if (ret == -1) { |
} else if (ret == -1) { |
logit("X11 connection rejected because of wrong " |
logit("X11 connection rejected because of wrong " |
|
|
c->self, c->connect_ctx.host, c->connect_ctx.port); |
c->self, c->connect_ctx.host, c->connect_ctx.port); |
channel_connect_ctx_free(&c->connect_ctx); |
channel_connect_ctx_free(&c->connect_ctx); |
c->type = SSH_CHANNEL_OPEN; |
c->type = SSH_CHANNEL_OPEN; |
|
c->lastused = monotime(); |
if (isopen) { |
if (isopen) { |
/* no message necessary */ |
/* no message necessary */ |
} else { |
} else { |
|
|
char buf[CHAN_RBUF]; |
char buf[CHAN_RBUF]; |
ssize_t len; |
ssize_t len; |
int r; |
int r; |
size_t have, avail, maxlen = CHANNEL_MAX_READ; |
size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ; |
|
|
if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) |
if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) |
return 1; /* Shouldn't happen */ |
return 1; /* Shouldn't happen */ |
|
|
} |
} |
if (maxlen > avail) |
if (maxlen > avail) |
maxlen = avail; |
maxlen = avail; |
if ((r = sshbuf_read(c->rfd, c->input, maxlen, NULL)) != 0) { |
if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) { |
if (errno == EINTR || errno == EAGAIN) |
if (errno == EINTR || errno == EAGAIN) |
return 1; |
return 1; |
debug2("channel %d: read failed rfd %d maxlen %zu: %s", |
debug2("channel %d: read failed rfd %d maxlen %zu: %s", |
c->self, c->rfd, maxlen, ssh_err(r)); |
c->self, c->rfd, maxlen, ssh_err(r)); |
goto rfail; |
goto rfail; |
} |
} |
|
if (nr != 0) |
|
c->lastused = monotime(); |
return 1; |
return 1; |
} |
} |
|
|
|
|
} |
} |
return -1; |
return -1; |
} |
} |
|
c->lastused = monotime(); |
if (c->input_filter != NULL) { |
if (c->input_filter != NULL) { |
if (c->input_filter(ssh, c, buf, len) == -1) { |
if (c->input_filter(ssh, c, buf, len) == -1) { |
debug2("channel %d: filter stops", c->self); |
debug2("channel %d: filter stops", c->self); |
|
|
} |
} |
return -1; |
return -1; |
} |
} |
|
c->lastused = monotime(); |
if (c->isatty && dlen >= 1 && buf[0] != '\r') { |
if (c->isatty && dlen >= 1 && buf[0] != '\r') { |
if (tcgetattr(c->wfd, &tio) == 0 && |
if (tcgetattr(c->wfd, &tio) == 0 && |
!(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { |
!(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { |
|
|
if ((r = sshbuf_consume(c->extended, len)) != 0) |
if ((r = sshbuf_consume(c->extended, len)) != 0) |
fatal_fr(r, "channel %i: consume", c->self); |
fatal_fr(r, "channel %i: consume", c->self); |
c->local_consumed += len; |
c->local_consumed += len; |
|
c->lastused = monotime(); |
} |
} |
return 1; |
return 1; |
} |
} |
|
|
if (len <= 0) { |
if (len <= 0) { |
debug2("channel %d: closing read-efd %d", c->self, c->efd); |
debug2("channel %d: closing read-efd %d", c->self, c->efd); |
channel_close_fd(ssh, c, &c->efd); |
channel_close_fd(ssh, c, &c->efd); |
} else if (c->extended_usage == CHAN_EXTENDED_IGNORE) |
return 1; |
|
} |
|
c->lastused = monotime(); |
|
if (c->extended_usage == CHAN_EXTENDED_IGNORE) |
debug3("channel %d: discard efd", c->self); |
debug3("channel %d: discard efd", c->self); |
else if ((r = sshbuf_put(c->extended, buf, len)) != 0) |
else if ((r = sshbuf_put(c->extended, buf, len)) != 0) |
fatal_fr(r, "channel %i: append", c->self); |
fatal_fr(r, "channel %i: append", c->self); |
|
|
continue; |
continue; |
} |
} |
if (ftab[c->type] != NULL) { |
if (ftab[c->type] != NULL) { |
/* |
if (table == CHAN_PRE && |
* Run handlers that are not paused. |
c->type == SSH_CHANNEL_OPEN && |
*/ |
c->inactive_deadline != 0 && c->lastused != 0 && |
if (c->notbefore <= now) |
now >= c->lastused + c->inactive_deadline) { |
|
/* channel closed for inactivity */ |
|
verbose("channel %d: closing after %u seconds " |
|
"of inactivity", c->self, |
|
c->inactive_deadline); |
|
channel_force_close(ssh, c, 1); |
|
} else if (c->notbefore <= now) { |
|
/* Run handlers that are not paused. */ |
(*ftab[c->type])(ssh, c); |
(*ftab[c->type])(ssh, c); |
else if (timeout != NULL) { |
/* inactivity timeouts must interrupt poll() */ |
|
if (timeout != NULL && |
|
c->type == SSH_CHANNEL_OPEN && |
|
c->lastused != 0 && |
|
c->inactive_deadline != 0) { |
|
ptimeout_deadline_monotime(timeout, |
|
c->lastused + c->inactive_deadline); |
|
} |
|
} else if (timeout != NULL) { |
/* |
/* |
* Arrange for poll wakeup when channel pause |
* Arrange for poll() wakeup when channel pause |
* timer expires. |
* timer expires. |
*/ |
*/ |
ptimeout_deadline_monotime(timeout, |
ptimeout_deadline_monotime(timeout, |
|
|
c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); |
c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); |
debug2_f("channel %d: callback done", c->self); |
debug2_f("channel %d: callback done", c->self); |
} |
} |
|
c->lastused = monotime(); |
debug2("channel %d: open confirm rwindow %u rmax %u", c->self, |
debug2("channel %d: open confirm rwindow %u rmax %u", c->self, |
c->remote_window, c->remote_maxpacket); |
c->remote_window, c->remote_maxpacket); |
return 0; |
return 0; |