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 ssh_channel_got_eof(struct ssh_channel *c);
static void ssh2_channel_check_close(struct ssh_channel *c);
+static void ssh_channel_close_local(struct ssh_channel *c, char const *reason);
static void ssh_channel_destroy(struct ssh_channel *c);
static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize);
static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin);
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. */
*/
if (ssh->channels) {
while (NULL != (c = index234(ssh->channels, 0))) {
- switch (c->type) {
- case CHAN_X11:
- x11_close(c->u.x11.xconn);
- break;
- case CHAN_SOCKDATA:
- case CHAN_SOCKDATA_DORMANT:
- pfd_close(c->u.pfd.pf);
- break;
- }
+ ssh_channel_close_local(c, NULL);
del234(ssh->channels, c); /* moving next one to index 0 */
if (ssh->version == 2)
bufchain_clear(&c->v.v2.outbuffer);
void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
{
Ssh ssh = c->ssh;
+ char *reason;
if (ssh->state == SSH_STATE_CLOSED)
return;
- switch (c->type) {
- case CHAN_X11:
- x11_close(c->u.x11.xconn);
- logeventf(ssh, "Forwarded X11 connection terminated due to local "
- "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;
- }
- c->type = CHAN_ZOMBIE;
+ reason = dupprintf("due to local error: %s", err);
+ ssh_channel_close_local(c, reason);
+ sfree(reason);
c->pending_eof = FALSE; /* this will confuse a zombie channel */
ssh2_channel_check_close(c);
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) {
- if (pktin->type == SSH1_MSG_CHANNEL_CLOSE &&
- !(c->closes & CLOSES_RCVD_EOF)) {
+ c = ssh_channel_msg(ssh, pktin);
+ if (c) {
+
+ if (pktin->type == SSH1_MSG_CHANNEL_CLOSE) {
/*
* Received CHANNEL_CLOSE, which we translate into
* outgoing EOF.
*/
- int send_close = FALSE;
-
- 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;
- break;
- case CHAN_SOCKDATA:
- if (c->u.pfd.pf)
- pfd_send_eof(c->u.pfd.pf);
- else
- send_close = TRUE;
- break;
- case CHAN_AGENT:
- send_close = TRUE;
- break;
- }
-
- if (send_close && !(c->closes & CLOSES_SENT_EOF)) {
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
- PKT_END);
- c->closes |= CLOSES_SENT_EOF;
- }
+ ssh_channel_got_eof(c);
}
if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION &&
!(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) {
s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
logevent("Storing additional host key for this host:");
logevent(s->fingerprint);
+ sfree(s->fingerprint);
store_host_key(ssh->savedhost, ssh->savedport,
ssh->hostkey->keytype, s->keystr);
ssh->cross_certifying = FALSE;
/*
* 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,
sfree(buf);
}
-static void ssh_channel_destroy(struct ssh_channel *c)
+/*
+ * Close any local socket and free any local resources associated with
+ * a channel. This converts the channel into a CHAN_ZOMBIE.
+ */
+static void ssh_channel_close_local(struct ssh_channel *c, char const *reason)
{
Ssh ssh = c->ssh;
+ char const *msg = NULL;
switch (c->type) {
case CHAN_MAINSESSION:
update_specials_menu(ssh->frontend);
break;
case CHAN_X11:
- if (c->u.x11.xconn != NULL)
- x11_close(c->u.x11.xconn);
- logevent("Forwarded X11 connection terminated");
+ assert(c->u.x11.xconn != NULL);
+ x11_close(c->u.x11.xconn);
+ msg = "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);
- logevent("Forwarded port closed");
+ assert(c->u.pfd.pf != NULL);
+ pfd_close(c->u.pfd.pf);
+ msg = "Forwarded port closed";
break;
}
+ c->type = CHAN_ZOMBIE;
+ if (msg != NULL) {
+ if (reason != NULL)
+ logeventf(ssh, "%s %s", msg, reason);
+ else
+ logevent(msg);
+ }
+}
+
+static void ssh_channel_destroy(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+
+ ssh_channel_close_local(c, NULL);
del234(ssh->channels, c);
if (ssh->version == 2) {
}
}
-static void ssh2_channel_got_eof(struct ssh_channel *c)
+static void ssh_channel_got_eof(struct ssh_channel *c)
{
if (c->closes & CLOSES_RCVD_EOF)
return; /* already seen EOF */
c->closes |= CLOSES_RCVD_EOF;
if (c->type == CHAN_X11) {
+ assert(c->u.x11.xconn != NULL);
x11_send_eof(c->u.x11.xconn);
} else if (c->type == CHAN_AGENT) {
if (c->u.a.outstanding_requests == 0) {
sshfwd_write_eof(c);
}
} else if (c->type == CHAN_SOCKDATA) {
+ assert(c->u.pfd.pf != NULL);
pfd_send_eof(c->u.pfd.pf);
} else if (c->type == CHAN_MAINSESSION) {
Ssh ssh = c->ssh;
}
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);
+ ssh_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
* implied EOF if we haven't seen EOF yet.
*/
- ssh2_channel_got_eof(c);
+ ssh_channel_got_eof(c);
if (!(ssh->remote_bugs & BUG_SENDS_LATE_REQUEST_REPLY)) {
/*
{
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);
if (ssh->channels) {
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);
- break;
- case CHAN_SOCKDATA:
- case CHAN_SOCKDATA_DORMANT:
- if (c->u.pfd.pf != NULL)
- pfd_close(c->u.pfd.pf);
- break;
- }
+ ssh_channel_close_local(c, NULL);
if (ssh->version == 2) {
struct outstanding_channel_request *ocr, *nocr;
ocr = c->v.v2.chanreq_head;
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;
}