static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin);
static void ssh_channel_init(struct ssh_channel *c);
+static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin);
static void ssh2_channel_check_close(struct ssh_channel *c);
static void ssh_channel_destroy(struct ssh_channel *c);
static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize);
CHAN_X11,
CHAN_AGENT,
CHAN_SOCKDATA,
- CHAN_SOCKDATA_DORMANT, /* one the remote hasn't confirmed */
/*
* CHAN_SHARING indicates a channel which is tracked here on
* behalf of a connection-sharing downstream. We do almost nothing
int cross_certifying;
};
+static const char *ssh_pkt_type(Ssh ssh, int type)
+{
+ if (ssh->version == 1)
+ return ssh1_pkt_type(type);
+ else
+ return ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, type);
+}
+
#define logevent(s) logevent(ssh->frontend, s)
/* logevent, only printf-formatted. */
x11_close(c->u.x11.xconn);
break;
case CHAN_SOCKDATA:
- case CHAN_SOCKDATA_DORMANT:
pfd_close(c->u.pfd.pf);
break;
}
"error: %s", err);
break;
case CHAN_SOCKDATA:
- case CHAN_SOCKDATA_DORMANT:
pfd_close(c->u.pfd.pf);
logeventf(ssh, "Forwarded port closed due to local error: %s", err);
break;
static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
{
- unsigned int remoteid = ssh_pkt_getuint32(pktin);
- unsigned int localid = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
- c = find234(ssh->channels, &remoteid, ssh_channelfind);
- if (c && c->type == CHAN_SOCKDATA_DORMANT) {
- c->remoteid = localid;
+ c = ssh_channel_msg(ssh, pktin);
+ if (c && c->type == CHAN_SOCKDATA) {
+ c->remoteid = ssh_pkt_getuint32(pktin);
c->halfopen = FALSE;
- c->type = CHAN_SOCKDATA;
c->throttling_conn = 0;
pfd_confirm(c->u.pfd.pf);
}
static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
{
- unsigned int remoteid = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
- c = find234(ssh->channels, &remoteid, ssh_channelfind);
- if (c && c->type == CHAN_SOCKDATA_DORMANT) {
+ c = ssh_channel_msg(ssh, pktin);
+ if (c && c->type == CHAN_SOCKDATA) {
logevent("Forwarded connection refused by server");
pfd_close(c->u.pfd.pf);
del234(ssh->channels, c);
static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
{
/* Remote side closes a channel. */
- unsigned i = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (c && !c->halfopen) {
+
+ c = ssh_channel_msg(ssh, pktin);
+ if (c) {
if (pktin->type == SSH1_MSG_CHANNEL_CLOSE &&
!(c->closes & CLOSES_RCVD_EOF)) {
switch (c->type) {
case CHAN_X11:
- if (c->u.x11.xconn)
- x11_send_eof(c->u.x11.xconn);
- else
- send_close = TRUE;
+ assert(c->u.x11.xconn != NULL);
+ x11_send_eof(c->u.x11.xconn);
break;
case CHAN_SOCKDATA:
- if (c->u.pfd.pf)
- pfd_send_eof(c->u.pfd.pf);
- else
- send_close = TRUE;
+ assert(c->u.pfd.pf != NULL);
+ pfd_send_eof(c->u.pfd.pf);
break;
case CHAN_AGENT:
send_close = TRUE;
!(c->closes & CLOSES_RCVD_CLOSE)) {
if (!(c->closes & CLOSES_SENT_EOF)) {
- bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d"
- " for which we never sent CHANNEL_CLOSE\n", i));
+ bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %u"
+ " for which we never sent CHANNEL_CLOSE\n",
+ c->localid));
}
c->closes |= CLOSES_RCVD_CLOSE;
if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes))
ssh_channel_destroy(c);
- } else {
- bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
- pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" :
- "_CONFIRMATION", c ? "half-open" : "nonexistent",
- i));
}
}
static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
{
/* Data sent down one of our channels. */
- int i = ssh_pkt_getuint32(pktin);
char *p;
int len;
struct ssh_channel *c;
+ c = ssh_channel_msg(ssh, pktin);
ssh_pkt_getstring(pktin, &p, &len);
- c = find234(ssh->channels, &i, ssh_channelfind);
if (c) {
int bufsize = ssh_channel_data(c, FALSE, p, len);
if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
/*
* Find the channel associated with a message. If there's no channel,
* or it's not properly open, make a noise about it and return NULL.
+ * If the channel is shared, pass the message on to downstream and
+ * also return NULL (meaning the caller should ignore this message).
*/
-static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)
+static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin)
{
unsigned localid = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
+ int halfopen_ok;
+ /* Is this message OK on a half-open connection? */
+ if (ssh->version == 1)
+ halfopen_ok = (pktin->type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION ||
+ pktin->type == SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ else
+ halfopen_ok = (pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION ||
+ pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE);
c = find234(ssh->channels, &localid, ssh_channelfind);
- if (!c ||
- (c->type != CHAN_SHARING && c->halfopen &&
- pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
- pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) {
+ if (!c || (c->type != CHAN_SHARING && (c->halfopen != halfopen_ok))) {
char *buf = dupprintf("Received %s for %s channel %u",
- ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
- pktin->type),
- c ? "half-open" : "nonexistent", localid);
+ ssh_pkt_type(ssh, pktin->type),
+ !c ? "nonexistent" :
+ c->halfopen ? "half-open" : "open",
+ localid);
ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
sfree(buf);
return NULL;
}
+ if (c->type == CHAN_SHARING) {
+ share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
+ pktin->body, pktin->length);
+ return NULL;
+ }
return c;
}
static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin)
{
- struct ssh_channel *c = ssh2_channel_msg(ssh, pktin);
+ struct ssh_channel *c = ssh_channel_msg(ssh, pktin);
struct outstanding_channel_request *ocr;
if (!c) return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
ocr = c->v.v2.chanreq_head;
if (!ocr) {
ssh2_msg_unexpected(ssh, pktin);
static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
if (!(c->closes & CLOSES_SENT_EOF)) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
ssh2_try_send_and_unthrottle(ssh, c);
{
char *data;
int length;
+ unsigned ext_type = 0; /* 0 means not extended */
struct ssh_channel *c;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
- if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
- ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)
- return; /* extended but not stderr */
+ if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
+ ext_type = ssh_pkt_getuint32(pktin);
ssh_pkt_getstring(pktin, &data, &length);
if (data) {
int bufsize;
c->v.v2.locwindow -= length;
c->v.v2.remlocwin -= length;
- bufsize = ssh_channel_data(c, pktin->type ==
- SSH2_MSG_CHANNEL_EXTENDED_DATA,
+ if (ext_type != 0 && ext_type != SSH2_EXTENDED_DATA_STDERR)
+ length = 0; /* Don't do anything with unknown extended data. */
+ bufsize = ssh_channel_data(c, ext_type == SSH2_EXTENDED_DATA_STDERR,
data, length);
/*
* If it looks like the remote end hit the end of its window,
update_specials_menu(ssh->frontend);
break;
case CHAN_X11:
- if (c->u.x11.xconn != NULL)
- x11_close(c->u.x11.xconn);
+ assert(c->u.x11.xconn != NULL);
+ x11_close(c->u.x11.xconn);
logevent("Forwarded X11 connection terminated");
break;
case CHAN_AGENT:
sfree(c->u.a.message);
break;
case CHAN_SOCKDATA:
- if (c->u.pfd.pf != NULL)
- pfd_close(c->u.pfd.pf);
+ assert(c->u.pfd.pf != NULL);
+ pfd_close(c->u.pfd.pf);
logevent("Forwarded port closed");
break;
}
}
ssh->sent_console_eof = TRUE;
}
-
- ssh2_channel_check_close(c);
}
static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
ssh2_channel_got_eof(c);
+ ssh2_channel_check_close(c);
}
static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
/*
* When we receive CLOSE on a channel, we assume it comes with an
{
struct ssh_channel *c;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
- assert(c->halfopen); /* ssh2_channel_msg will have enforced this */
+ assert(c->halfopen); /* ssh_channel_msg will have enforced this */
c->remoteid = ssh_pkt_getuint32(pktin);
c->halfopen = FALSE;
c->v.v2.remwindow = ssh_pkt_getuint32(pktin);
c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
- if (c->type == CHAN_SOCKDATA_DORMANT) {
- c->type = CHAN_SOCKDATA;
- if (c->u.pfd.pf)
- pfd_confirm(c->u.pfd.pf);
+ if (c->type == CHAN_SOCKDATA) {
+ assert(c->u.pfd.pf != NULL);
+ pfd_confirm(c->u.pfd.pf);
} else if (c->type == CHAN_ZOMBIE) {
/*
* This case can occur if a local socket error occurred
int reason_length;
struct ssh_channel *c;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
- assert(c->halfopen); /* ssh2_channel_msg will have enforced this */
+ assert(c->halfopen); /* ssh_channel_msg will have enforced this */
- if (c->type == CHAN_SOCKDATA_DORMANT) {
+ if (c->type == CHAN_SOCKDATA) {
reason_code = ssh_pkt_getuint32(pktin);
if (reason_code >= lenof(reasons))
reason_code = 0; /* ensure reasons[reason_code] in range */
struct ssh_channel *c;
struct Packet *pktout;
- c = ssh2_channel_msg(ssh, pktin);
+ c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
- return;
- }
ssh_pkt_getstring(pktin, &type, &typelen);
want_reply = ssh2_pkt_getbool(pktin);
while ((c = delpos234(ssh->channels, 0)) != NULL) {
switch (c->type) {
case CHAN_X11:
- if (c->u.x11.xconn != NULL)
- x11_close(c->u.x11.xconn);
+ assert(c->u.x11.xconn != NULL);
+ x11_close(c->u.x11.xconn);
break;
case CHAN_SOCKDATA:
- case CHAN_SOCKDATA_DORMANT:
- if (c->u.pfd.pf != NULL)
- pfd_close(c->u.pfd.pf);
+ assert(c->u.pfd.pf != NULL);
+ pfd_close(c->u.pfd.pf);
break;
}
if (ssh->version == 2) {
c->ssh = ssh;
ssh_channel_init(c);
c->halfopen = TRUE;
- c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
+ c->type = CHAN_SOCKDATA;/* identify channel type */
c->u.pfd.pf = pf;
return c;
}