if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
fprintf(stderr, "%s\n", s); }
-#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, sk_close(s), \
- s = NULL, connection_fatal msg )
+#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
+ (s ? sk_close(s), s = NULL : (void)0), \
+ connection_fatal msg )
#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
#define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */
#define SSH1_MSG_CHANNEL_DATA 23 /* 0x17 */
#define SSH1_MSG_CHANNEL_CLOSE 24 /* 0x18 */
#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* 0x19 */
+#define SSH1_SMSG_X11_OPEN 27 /* 0x1b */
+#define SSH1_CMSG_PORT_FORWARD_REQUEST 28 /* 0x1c */
+#define SSH1_MSG_PORT_OPEN 29 /* 0x1d */
#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 /* 0x1e */
#define SSH1_SMSG_AGENT_OPEN 31 /* 0x1f */
-#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */
#define SSH1_MSG_IGNORE 32 /* 0x20 */
+#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */
+#define SSH1_CMSG_X11_REQUEST_FORWARDING 34 /* 0x22 */
+#define SSH1_CMSG_AUTH_RHOSTS_RSA 35 /* 0x23 */
#define SSH1_MSG_DEBUG 36 /* 0x24 */
#define SSH1_CMSG_REQUEST_COMPRESSION 37 /* 0x25 */
#define SSH1_CMSG_AUTH_TIS 39 /* 0x27 */
extern const struct ssh_cipher ssh_blowfish_ssh1;
extern const struct ssh_cipher ssh_blowfish_ssh2;
+extern char *x11_init (Socket *, char *, void *);
+extern void x11_close (Socket);
+extern void x11_send (Socket , char *, int);
+extern void x11_invent_auth(char *, int, char *, int);
+
/*
* Ciphers for SSH2. We miss out single-DES because it isn't
* supported; also 3DES and Blowfish are both done differently from
const static struct ssh_compress *compressions[] = {
&ssh_zlib, &ssh_comp_none };
+enum { /* channel types */
+ CHAN_MAINSESSION,
+ CHAN_X11,
+ CHAN_AGENT,
+};
+
/*
* 2-3-4 tree storing channels.
*/
unsigned remoteid, localid;
int type;
int closes;
+ struct ssh2_data_channel {
+ unsigned char *outbuffer;
+ unsigned outbuflen, outbufsize;
+ unsigned remwindow, remmaxpkt;
+ } v2;
union {
struct ssh_agent_channel {
unsigned char *message;
unsigned char msglen[4];
int lensofar, totallen;
} a;
- struct ssh2_data_channel {
- unsigned char *outbuffer;
- unsigned outbuflen, outbufsize;
- unsigned remwindow, remmaxpkt;
- } v2;
+ struct ssh_x11_channel {
+ Socket s;
+ } x11;
} u;
};
static unsigned char session_key[32];
static int ssh1_compressing;
static int ssh_agentfwd_enabled;
+static int ssh_X11_fwd_enabled;
static const struct ssh_cipher *cipher = NULL;
static const struct ssh_cipher *cscipher = NULL;
static const struct ssh_cipher *sccipher = NULL;
static char *savedhost;
static int savedport;
static int ssh_send_ok;
+static int ssh_echoing, ssh_editing;
static tree234 *ssh_channels; /* indexed by local id */
static struct ssh_channel *mainchan; /* primary session channel */
static void ssh2_protocol(unsigned char *in, int inlen, int ispkt);
static void ssh_size(void);
static void ssh_special (Telnet_Special);
+static void ssh2_try_send(struct ssh_channel *c);
+static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
static int (*s_rdpkt)(unsigned char **data, int *datalen);
/*
* SSH2 packet construction functions.
*/
-static void ssh2_pkt_adddata(void *data, int len) {
- pktout.length += len;
- if (pktout.maxlen < pktout.length) {
- pktout.maxlen = pktout.length + 256;
+static void ssh2_pkt_ensure(int length) {
+ if (pktout.maxlen < length) {
+ pktout.maxlen = length + 256;
pktout.data = (pktout.data == NULL ? smalloc(pktout.maxlen+APIEXTRA) :
srealloc(pktout.data, pktout.maxlen+APIEXTRA));
if (!pktout.data)
fatalbox("Out of memory");
}
+}
+static void ssh2_pkt_adddata(void *data, int len) {
+ pktout.length += len;
+ ssh2_pkt_ensure(pktout.length);
memcpy(pktout.data+pktout.length-len, data, len);
}
static void ssh2_pkt_addbyte(unsigned char byte) {
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
padding = 4;
padding += (cipherblk - (pktout.length + padding) % cipherblk) % cipherblk;
+ maclen = csmac ? csmac->len : 0;
+ ssh2_pkt_ensure(pktout.length + padding + maclen);
pktout.data[4] = padding;
for (i = 0; i < padding; i++)
pktout.data[pktout.length + i] = random_byte();
if (cscipher)
cscipher->encrypt(pktout.data, pktout.length + padding);
- maclen = csmac ? csmac->len : 0;
sk_write(s, pktout.data, pktout.length + padding + maclen);
}
}
static int ssh_receive(Socket skt, int urgent, char *data, int len) {
- if (!len) {
+ if (urgent==3) {
+ /* A socket error has occurred. */
+ ssh_state = SSH_STATE_CLOSED;
+ sk_close(s);
+ s = NULL;
+ connection_fatal(data);
+ return 0;
+ } else if (!len) {
/* Connection has closed. */
+ ssh_state = SSH_STATE_CLOSED;
sk_close(s);
s = NULL;
return 0;
/*
* Open socket.
*/
- s = sk_new(addr, port, ssh_receive);
+ s = sk_new(addr, port, 0, 1, ssh_receive);
if ( (err = sk_socket_error(s)) )
return err;
crFinish(1);
}
+void sshfwd_close(struct ssh_channel *c) {
+ if (c) {
+ if (ssh_version == 1) {
+ send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END);
+ } else {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_send();
+ }
+ c->closes = 1;
+ if (c->type == CHAN_X11) {
+ c->u.x11.s = NULL;
+ logevent("X11 connection terminated");
+ }
+ }
+}
+
+void sshfwd_write(struct ssh_channel *c, char *buf, int len) {
+ if (ssh_version == 1) {
+ send_packet(SSH1_MSG_CHANNEL_DATA,
+ PKT_INT, c->remoteid,
+ PKT_INT, len,
+ PKT_DATA, buf, len,
+ PKT_END);
+ } else {
+ ssh2_add_channel_data(c, buf, len);
+ ssh2_try_send(c);
+ }
+}
+
static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
crBegin;
}
}
+ if (cfg.x11_forward) {
+ char proto[20], data[64];
+ logevent("Requesting X11 forwarding");
+ x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
+ send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING,
+ PKT_STR, proto, PKT_STR, data,
+ PKT_INT, 0,
+ PKT_END);
+ do { crReturnV; } while (!ispkt);
+ if (pktin.type != SSH1_SMSG_SUCCESS && pktin.type != SSH1_SMSG_FAILURE) {
+ bombout(("Protocol confusion"));
+ crReturnV;
+ } else if (pktin.type == SSH1_SMSG_FAILURE) {
+ logevent("X11 forwarding refused");
+ } else {
+ logevent("X11 forwarding enabled");
+ ssh_X11_fwd_enabled = TRUE;
+ }
+ }
+
if (!cfg.nopty) {
send_packet(SSH1_CMSG_REQUEST_PTY,
PKT_STR, cfg.termtype,
crReturnV;
} else if (pktin.type == SSH1_SMSG_FAILURE) {
c_write("Server refused to allocate pty\r\n", 32);
+ ssh_editing = ssh_echoing = 1;
}
logevent("Allocated pty");
+ } else {
+ ssh_editing = ssh_echoing = 1;
}
if (cfg.compression) {
if (eof_needed)
ssh_special(TS_EOF);
+ ldisc_send(NULL, 0); /* cause ldisc to notice changes */
ssh_send_ok = 1;
ssh_channels = newtree234(ssh_channelcmp);
- begin_session();
while (1) {
crReturnV;
if (ispkt) {
ssh_state = SSH_STATE_CLOSED;
logevent("Received disconnect request");
crReturnV;
+ } else if (pktin.type == SSH1_SMSG_X11_OPEN) {
+ /* Remote side is trying to open a channel to talk to our
+ * X-Server. Give them back a local channel number. */
+ unsigned i;
+ struct ssh_channel *c, *d;
+ enum234 e;
+
+ logevent("Received X11 connect request");
+ /* Refuse if X11 forwarding is disabled. */
+ if (!ssh_X11_fwd_enabled) {
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body),
+ PKT_END);
+ logevent("Rejected X11 connect request");
+ } else {
+ c = smalloc(sizeof(struct ssh_channel));
+
+ if ( x11_init(&c->u.x11.s, cfg.x11_display, c) != NULL ) {
+ logevent("opening X11 forward connection failed");
+ sfree(c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body),
+ PKT_END);
+ } else {
+ logevent("opening X11 forward connection succeeded");
+ for (i=1, d = first234(ssh_channels, &e); d; d = next234(&e)) {
+ if (d->localid > i)
+ break; /* found a free number */
+ i = d->localid + 1;
+ }
+ c->remoteid = GET_32BIT(pktin.body);
+ c->localid = i;
+ c->closes = 0;
+ c->type = CHAN_X11; /* identify channel type */
+ add234(ssh_channels, c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_INT, c->localid,
+ PKT_END);
+ logevent("Opened X11 forward channel");
+ }
+ }
} else if (pktin.type == SSH1_SMSG_AGENT_OPEN) {
/* Remote side is trying to open a channel to talk to our
* agent. Give them back a local channel number. */
c->remoteid = GET_32BIT(pktin.body);
c->localid = i;
c->closes = 0;
- c->type = SSH1_SMSG_AGENT_OPEN;/* identify channel type */
+ c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
add234(ssh_channels, c);
send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
int closetype;
closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END);
+ if ((c->closes == 0) && (c->type == CHAN_X11)) {
+ logevent("X11 connection closed");
+ assert(c->u.x11.s != NULL);
+ x11_close(c->u.x11.s);
+ c->u.x11.s = NULL;
+ }
c->closes |= closetype;
if (c->closes == 3) {
del234(ssh_channels, c);
c = find234(ssh_channels, &i, ssh_channelfind);
if (c) {
switch(c->type) {
- case SSH1_SMSG_AGENT_OPEN:
+ case CHAN_X11:
+ x11_send(c->u.x11.s, p, len);
+ break;
+ case CHAN_AGENT:
/* Data for an agent message. Buffer it. */
while (len > 0) {
if (c->u.a.lensofar < 4) {
/*
* SSH2 key creation method.
*/
-static void ssh2_mkkey(Bignum K, char *H, char chr, char *keyspace) {
+static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr, char *keyspace) {
SHA_State s;
/* First 20 bytes. */
SHA_Init(&s);
sha_mpint(&s, K);
SHA_Bytes(&s, H, 20);
SHA_Bytes(&s, &chr, 1);
- SHA_Bytes(&s, H, 20);
+ SHA_Bytes(&s, sessid, 20);
SHA_Final(&s, keyspace);
/* Next 20 bytes. */
SHA_Init(&s);
static int hostkeylen, siglen;
static void *hkey; /* actual host key */
static unsigned char exchange_hash[20];
+ static unsigned char first_exchange_hash[20];
static unsigned char keyspace[40];
static const struct ssh_cipher *preferred_cipher;
static const struct ssh_compress *preferred_comp;
fingerprint = hostkey->fingerprint(hkey);
verify_ssh_host_key(savedhost, savedport, hostkey->keytype,
keystr, fingerprint);
- logevent("Host key fingerprint is:");
- logevent(fingerprint);
+ if (first_kex) { /* don't bother logging this in rekeys */
+ logevent("Host key fingerprint is:");
+ logevent(fingerprint);
+ }
sfree(fingerprint);
sfree(keystr);
hostkey->freekey(hkey);
cscomp->compress_init();
sccomp->decompress_init();
/*
- * Set IVs after keys.
+ * Set IVs after keys. Here we use the exchange hash from the
+ * _first_ key exchange.
*/
- ssh2_mkkey(K, exchange_hash, 'C', keyspace); cscipher->setcskey(keyspace);
- ssh2_mkkey(K, exchange_hash, 'D', keyspace); cscipher->setsckey(keyspace);
- ssh2_mkkey(K, exchange_hash, 'A', keyspace); cscipher->setcsiv(keyspace);
- ssh2_mkkey(K, exchange_hash, 'B', keyspace); sccipher->setsciv(keyspace);
- ssh2_mkkey(K, exchange_hash, 'E', keyspace); csmac->setcskey(keyspace);
- ssh2_mkkey(K, exchange_hash, 'F', keyspace); scmac->setsckey(keyspace);
+ if (first_kex)
+ memcpy(first_exchange_hash, exchange_hash, sizeof(exchange_hash));
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'C', keyspace);
+ cscipher->setcskey(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'D', keyspace);
+ sccipher->setsckey(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'A', keyspace);
+ cscipher->setcsiv(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'B', keyspace);
+ sccipher->setsciv(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'E', keyspace);
+ csmac->setcskey(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'F', keyspace);
+ scmac->setsckey(keyspace);
/*
* If this is the first key exchange phase, we must pass the
* transport. If we ever see a KEXINIT, we must go back to the
* start.
*/
- do {
+ while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT)) {
crReturn(1);
- } while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT));
+ }
+ logevent("Server initiated key re-exchange");
goto begin_key_exchange;
crFinish(1);
}
+/*
+ * Add data to an SSH2 channel output buffer.
+ */
+static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len) {
+ if (c->v2.outbufsize <
+ c->v2.outbuflen + len) {
+ c->v2.outbufsize =
+ c->v2.outbuflen + len + 1024;
+ c->v2.outbuffer = srealloc(c->v2.outbuffer,
+ c->v2.outbufsize);
+ }
+ memcpy(c->v2.outbuffer + c->v2.outbuflen,
+ buf, len);
+ c->v2.outbuflen += len;
+}
+
+/*
+ * Attempt to send data on an SSH2 channel.
+ */
+static void ssh2_try_send(struct ssh_channel *c) {
+ while (c->v2.remwindow > 0 &&
+ c->v2.outbuflen > 0) {
+ unsigned len = c->v2.remwindow;
+ if (len > c->v2.outbuflen)
+ len = c->v2.outbuflen;
+ if (len > c->v2.remmaxpkt)
+ len = c->v2.remmaxpkt;
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data(c->v2.outbuffer, len);
+ ssh2_pkt_send();
+ c->v2.outbuflen -= len;
+ memmove(c->v2.outbuffer, c->v2.outbuffer+len,
+ c->v2.outbuflen);
+ c->v2.remwindow -= len;
+ }
+}
+
/*
* Handle the SSH2 userauth and connection layers.
*/
crReturnV;
}
mainchan->remoteid = ssh2_pkt_getuint32();
- mainchan->u.v2.remwindow = ssh2_pkt_getuint32();
- mainchan->u.v2.remmaxpkt = ssh2_pkt_getuint32();
- mainchan->u.v2.outbuffer = NULL;
- mainchan->u.v2.outbuflen = mainchan->u.v2.outbufsize = 0;
+ mainchan->type = CHAN_MAINSESSION;
+ mainchan->closes = 0;
+ mainchan->v2.remwindow = ssh2_pkt_getuint32();
+ mainchan->v2.remmaxpkt = ssh2_pkt_getuint32();
+ mainchan->v2.outbuffer = NULL;
+ mainchan->v2.outbuflen = mainchan->v2.outbufsize = 0;
+ ssh_channels = newtree234(ssh_channelcmp);
+ add234(ssh_channels, mainchan);
logevent("Opened channel for session");
+ /*
+ * Potentially enable X11 forwarding.
+ */
+ if (cfg.x11_forward) {
+ char proto[20], data[64];
+ logevent("Requesting X11 forwarding");
+ x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_addstring("x11-req");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addbool(0); /* many connections */
+ ssh2_pkt_addstring(proto);
+ ssh2_pkt_addstring(data);
+ ssh2_pkt_adduint32(0); /* screen number */
+ ssh2_pkt_send();
+
+ do {
+ crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ c->v2.remwindow += ssh2_pkt_getuint32();
+ }
+ } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+
+ if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+ if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+ bombout(("Server got confused by X11 forwarding request"));
+ crReturnV;
+ }
+ logevent("X11 forwarding refused");
+ } else {
+ logevent("X11 forwarding enabled");
+ ssh_X11_fwd_enabled = TRUE;
+ }
+ }
+
/*
* Now allocate a pty for the session.
*/
do {
crWaitUntilV(ispkt);
if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- /* FIXME: be able to handle other channels here */
- if (ssh2_pkt_getuint32() != mainchan->localid)
- continue; /* wrong channel */
- mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ c->v2.remwindow += ssh2_pkt_getuint32();
}
} while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
crReturnV;
}
c_write("Server refused to allocate pty\r\n", 32);
+ ssh_editing = ssh_echoing = 1;
} else {
logevent("Allocated pty");
}
+ } else {
+ ssh_editing = ssh_echoing = 1;
}
/*
do {
crWaitUntilV(ispkt);
if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- /* FIXME: be able to handle other channels here */
- if (ssh2_pkt_getuint32() != mainchan->localid)
- continue; /* wrong channel */
- mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ c->v2.remwindow += ssh2_pkt_getuint32();
}
} while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
/*
* Transfer data!
*/
+ ldisc_send(NULL, 0); /* cause ldisc to notice changes */
ssh_send_ok = 1;
- begin_session();
while (1) {
static int try_send;
crReturnV;
pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
char *data;
int length;
- /* FIXME: be able to handle other channels here */
- if (ssh2_pkt_getuint32() != mainchan->localid)
- continue; /* wrong channel */
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
if (pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
ssh2_pkt_getuint32() != SSH2_EXTENDED_DATA_STDERR)
continue; /* extended but not stderr */
ssh2_pkt_getstring(&data, &length);
if (data) {
- from_backend(pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA,
- data, length);
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ from_backend(pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA,
+ data, length);
+ break;
+ case CHAN_X11:
+ x11_send(c->u.x11.s, data, length);
+ break;
+ }
/*
- * Enlarge the window again at the remote side,
- * just in case it ever runs down and they fail
- * to send us any more data.
+ * Enlarge the window again at the remote
+ * side, just in case it ever runs down and
+ * they fail to send us any more data.
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
- ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_adduint32(c->remoteid);
ssh2_pkt_adduint32(length);
ssh2_pkt_send();
}
} else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
continue; /* exit status et al; ignore (FIXME?) */
} else if (pktin.type == SSH2_MSG_CHANNEL_EOF) {
- continue; /* remote sends EOF; ignore */
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+
+ if (c->type == CHAN_X11) {
+ /*
+ * Remote EOF on an X11 channel means we should
+ * wrap up and close the channel ourselves.
+ */
+ x11_close(c->u.x11.s);
+ sshfwd_close(c);
+ }
} else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
- /* FIXME: be able to handle other channels here */
- if (ssh2_pkt_getuint32() != mainchan->localid)
- continue; /* wrong channel */
- ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(mainchan->remoteid);
- ssh2_pkt_send();
- /* FIXME: mark the channel as closed */
- if (1 /* FIXME: "all channels are closed" */) {
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ enum234 e;
+
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ if (c->closes == 0) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_send();
+ }
+ /* Do pre-close processing on the channel. */
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ break; /* nothing to see here, move along */
+ case CHAN_X11:
+ break;
+ }
+ del234(ssh_channels, c);
+ sfree(c->v2.outbuffer);
+ sfree(c);
+
+ /*
+ * See if that was the last channel left open.
+ */
+ c = first234(ssh_channels, &e);
+ if (!c) {
logevent("All channels closed. Disconnecting");
ssh2_pkt_init(SSH2_MSG_DISCONNECT);
ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
}
continue; /* remote sends close; ignore (FIXME) */
} else if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- /* FIXME: be able to handle other channels here */
- if (ssh2_pkt_getuint32() != mainchan->localid)
- continue; /* wrong channel */
- mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ mainchan->v2.remwindow += ssh2_pkt_getuint32();
try_send = TRUE;
+ } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
+ char *type;
+ int typelen;
+ char *error = NULL;
+ struct ssh_channel *c;
+ ssh2_pkt_getstring(&type, &typelen);
+ c = smalloc(sizeof(struct ssh_channel));
+
+ if (typelen == 3 && !memcmp(type, "x11", 3)) {
+ if (!ssh_X11_fwd_enabled)
+ error = "X11 forwarding is not enabled";
+ else if ( x11_init(&c->u.x11.s, cfg.x11_display, c) != NULL ) {
+ error = "Unable to open an X11 connection";
+ } else {
+ c->type = CHAN_X11;
+ }
+ } else {
+ error = "Unsupported channel type requested";
+ }
+
+ c->remoteid = ssh2_pkt_getuint32();
+ if (error) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_adduint32(SSH2_OPEN_CONNECT_FAILED);
+ ssh2_pkt_addstring(error);
+ ssh2_pkt_addstring("en"); /* language tag */
+ ssh2_pkt_send();
+ sfree(c);
+ } else {
+ struct ssh_channel *d;
+ unsigned i;
+ enum234 e;
+
+ for (i=1, d = first234(ssh_channels, &e); d;
+ d = next234(&e)) {
+ if (d->localid > i)
+ break; /* found a free number */
+ i = d->localid + 1;
+ }
+ c->localid = i;
+ c->closes = 0;
+ c->v2.remwindow = ssh2_pkt_getuint32();
+ c->v2.remmaxpkt = ssh2_pkt_getuint32();
+ c->v2.outbuffer = NULL;
+ c->v2.outbuflen = c->v2.outbufsize = 0;
+ add234(ssh_channels, c);
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_adduint32(c->localid);
+ ssh2_pkt_adduint32(0x8000UL); /* our window size */
+ ssh2_pkt_adduint32(0x4000UL); /* our max pkt size */
+ ssh2_pkt_send();
+ }
} else {
bombout(("Strange packet received: type %d", pktin.type));
crReturnV;
/*
* We have spare data. Add it to the channel buffer.
*/
- if (mainchan->u.v2.outbufsize <
- mainchan->u.v2.outbuflen + inlen) {
- mainchan->u.v2.outbufsize =
- mainchan->u.v2.outbuflen + inlen + 1024;
- mainchan->u.v2.outbuffer = srealloc(mainchan->u.v2.outbuffer,
- mainchan->u.v2.outbufsize);
- }
- memcpy(mainchan->u.v2.outbuffer + mainchan->u.v2.outbuflen,
- in, inlen);
- mainchan->u.v2.outbuflen += inlen;
+ ssh2_add_channel_data(mainchan, in, inlen);
try_send = TRUE;
}
if (try_send) {
+ enum234 e;
+ struct ssh_channel *c;
/*
- * Try to send data on the channel if we can. (FIXME:
- * on _all_ channels.)
+ * Try to send data on all channels if we can.
*/
- while (mainchan->u.v2.remwindow > 0 &&
- mainchan->u.v2.outbuflen > 0) {
- unsigned len = mainchan->u.v2.remwindow;
- if (len > mainchan->u.v2.outbuflen)
- len = mainchan->u.v2.outbuflen;
- if (len > mainchan->u.v2.remmaxpkt)
- len = mainchan->u.v2.remmaxpkt;
- ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
- ssh2_pkt_adduint32(mainchan->remoteid);
- ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data(mainchan->u.v2.outbuffer, len);
- ssh2_pkt_send();
- mainchan->u.v2.outbuflen -= len;
- memmove(mainchan->u.v2.outbuffer, mainchan->u.v2.outbuffer+len,
- mainchan->u.v2.outbuflen);
- mainchan->u.v2.remwindow -= len;
- }
+ for (c = first234(ssh_channels, &e); c; c = next234(&e))
+ ssh2_try_send(c);
}
}
#endif
ssh_send_ok = 0;
+ ssh_editing = 0;
+ ssh_echoing = 0;
p = connect_to_host(host, port, realhost);
if (p != NULL)
static int ssh_sendok(void) { return ssh_send_ok; }
+static int ssh_ldisc(int option) {
+ if (option == LD_ECHO) return ssh_echoing;
+ if (option == LD_EDIT) return ssh_editing;
+ return FALSE;
+}
+
Backend ssh_backend = {
ssh_init,
ssh_send,
ssh_special,
ssh_socket,
ssh_sendok,
+ ssh_ldisc,
22
};