+static int in_commasep_string(char *needle, char *haystack, int haylen) {
+ int needlen = strlen(needle);
+ while (1) {
+ /*
+ * Is it at the start of the string?
+ */
+ if (haylen >= needlen && /* haystack is long enough */
+ !memcmp(needle, haystack, needlen) && /* initial match */
+ (haylen == needlen || haystack[needlen] == ',')
+ /* either , or EOS follows */
+ )
+ return 1;
+ /*
+ * If not, search for the next comma and resume after that.
+ * If no comma found, terminate.
+ */
+ while (haylen > 0 && *haystack != ',')
+ haylen--, haystack++;
+ if (haylen == 0)
+ return 0;
+ haylen--, haystack++; /* skip over comma itself */
+ }
+}
+
+/*
+ * SSH2 key creation method.
+ */
+static void ssh2_mkkey(Bignum K, char *H, 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_Final(&s, keyspace);
+ /* Next 20 bytes. */
+ SHA_Init(&s);
+ sha_mpint(&s, K);
+ SHA_Bytes(&s, H, 20);
+ SHA_Bytes(&s, keyspace, 20);
+ SHA_Final(&s, keyspace+20);
+}
+
+/*
+ * Handle the SSH2 transport layer.
+ */
+static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
+{
+ static int i, len;
+ static char *str;
+ static Bignum e, f, K;
+ static const struct ssh_mac **maclist;
+ static int nmacs;
+ static const struct ssh_cipher *cscipher_tobe = NULL;
+ static const struct ssh_cipher *sccipher_tobe = NULL;
+ static const struct ssh_mac *csmac_tobe = NULL;
+ static const struct ssh_mac *scmac_tobe = NULL;
+ static const struct ssh_compress *cscomp_tobe = NULL;
+ static const struct ssh_compress *sccomp_tobe = NULL;
+ static char *hostkeydata, *sigdata, *keystr, *fingerprint;
+ static int hostkeylen, siglen;
+ static unsigned char exchange_hash[20];
+ static unsigned char keyspace[40];
+ static const struct ssh_cipher *preferred_cipher;
+
+ crBegin;
+ random_init();
+
+ /*
+ * Set up the preferred cipher.
+ */
+ if (cfg.cipher == CIPHER_BLOWFISH) {
+ preferred_cipher = &ssh_blowfish_ssh2;
+ } else if (cfg.cipher == CIPHER_DES) {
+ logevent("Single DES not supported in SSH2; using 3DES");
+ preferred_cipher = &ssh_3des_ssh2;
+ } else if (cfg.cipher == CIPHER_3DES) {
+ preferred_cipher = &ssh_3des_ssh2;
+ } else {
+ /* Shouldn't happen, but we do want to initialise to _something_. */
+ preferred_cipher = &ssh_3des_ssh2;
+ }
+
+ /*
+ * Be prepared to work around the buggy MAC problem.
+ */
+ if (cfg.buggymac)
+ maclist = buggymacs, nmacs = lenof(buggymacs);
+ else
+ maclist = macs, nmacs = lenof(macs);
+
+ begin_key_exchange:
+ /*
+ * Construct and send our key exchange packet.
+ */
+ ssh2_pkt_init(SSH2_MSG_KEXINIT);
+ for (i = 0; i < 16; i++)
+ ssh2_pkt_addbyte((unsigned char)random_byte());
+ /* List key exchange algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(kex_algs); i++) {
+ ssh2_pkt_addstring_str(kex_algs[i]->name);
+ if (i < lenof(kex_algs)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server host key algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ ssh2_pkt_addstring_str(hostkey_algs[i]->name);
+ if (i < lenof(hostkey_algs)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server encryption algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(ciphers)+1; i++) {
+ const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(ciphers))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client encryption algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(ciphers)+1; i++) {
+ const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(ciphers))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server MAC algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < nmacs; i++) {
+ ssh2_pkt_addstring_str(maclist[i]->name);
+ if (i < nmacs-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client MAC algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < nmacs; i++) {
+ ssh2_pkt_addstring_str(maclist[i]->name);
+ if (i < nmacs-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server compression algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(compressions); i++) {
+ ssh2_pkt_addstring_str(compressions[i]->name);
+ if (i < lenof(compressions)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client compression algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(compressions); i++) {
+ ssh2_pkt_addstring_str(compressions[i]->name);
+ if (i < lenof(compressions)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server languages. Empty list. */
+ ssh2_pkt_addstring_start();
+ /* List server->client languages. Empty list. */
+ ssh2_pkt_addstring_start();
+ /* First KEX packet does _not_ follow, because we're not that brave. */
+ ssh2_pkt_addbool(FALSE);
+ /* Reserved. */
+ ssh2_pkt_adduint32(0);
+ sha_string(&exhash, pktout.data+5, pktout.length-5);
+ ssh2_pkt_send();
+
+ if (!ispkt) crWaitUntil(ispkt);
+ sha_string(&exhash, pktin.data+5, pktin.length-5);
+
+ /*
+ * Now examine the other side's KEXINIT to see what we're up
+ * to.
+ */
+ if (pktin.type != SSH2_MSG_KEXINIT) {
+ bombout(("expected key exchange packet from server"));
+ crReturn(0);
+ }
+ kex = NULL; hostkey = NULL; cscipher_tobe = NULL; sccipher_tobe = NULL;
+ csmac_tobe = NULL; scmac_tobe = NULL; cscomp_tobe = NULL; sccomp_tobe = NULL;
+ pktin.savedpos += 16; /* skip garbage cookie */
+ ssh2_pkt_getstring(&str, &len); /* key exchange algorithms */
+ for (i = 0; i < lenof(kex_algs); i++) {
+ if (in_commasep_string(kex_algs[i]->name, str, len)) {
+ kex = kex_algs[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* host key algorithms */
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
+ hostkey = hostkey_algs[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* client->server cipher */
+ for (i = 0; i < lenof(ciphers)+1; i++) {
+ const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
+ if (in_commasep_string(c->name, str, len)) {
+ cscipher_tobe = c;
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* server->client cipher */
+ for (i = 0; i < lenof(ciphers)+1; i++) {
+ const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
+ if (in_commasep_string(c->name, str, len)) {
+ sccipher_tobe = c;
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* client->server mac */
+ for (i = 0; i < nmacs; i++) {
+ if (in_commasep_string(maclist[i]->name, str, len)) {
+ csmac_tobe = maclist[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* server->client mac */
+ for (i = 0; i < nmacs; i++) {
+ if (in_commasep_string(maclist[i]->name, str, len)) {
+ scmac_tobe = maclist[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* client->server compression */
+ for (i = 0; i < lenof(compressions); i++) {
+ if (in_commasep_string(compressions[i]->name, str, len)) {
+ cscomp_tobe = compressions[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* server->client compression */
+ for (i = 0; i < lenof(compressions); i++) {
+ if (in_commasep_string(compressions[i]->name, str, len)) {
+ sccomp_tobe = compressions[i];
+ break;
+ }
+ }
+
+ /*
+ * Currently we only support Diffie-Hellman and DSS, so let's
+ * bomb out if those aren't selected.
+ */
+ if (kex != &ssh_diffiehellman || hostkey != &ssh_dss) {
+ bombout(("internal fault: chaos in SSH 2 transport layer"));
+ crReturn(0);
+ }
+
+ /*
+ * Now we begin the fun. Generate and send e for Diffie-Hellman.
+ */
+ e = dh_create_e();
+ ssh2_pkt_init(SSH2_MSG_KEXDH_INIT);
+ ssh2_pkt_addmp(e);
+ ssh2_pkt_send();
+
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH2_MSG_KEXDH_REPLY) {
+ bombout(("expected key exchange packet from server"));
+ crReturn(0);
+ }
+ ssh2_pkt_getstring(&hostkeydata, &hostkeylen);
+ f = ssh2_pkt_getmp();
+ ssh2_pkt_getstring(&sigdata, &siglen);
+
+ K = dh_find_K(f);
+
+ sha_string(&exhash, hostkeydata, hostkeylen);
+ sha_mpint(&exhash, e);
+ sha_mpint(&exhash, f);
+ sha_mpint(&exhash, K);
+ SHA_Final(&exhash, exchange_hash);
+
+#if 0
+ debug(("Exchange hash is:\r\n"));
+ for (i = 0; i < 20; i++)
+ debug((" %02x", exchange_hash[i]));
+ debug(("\r\n"));
+#endif
+
+ hostkey->setkey(hostkeydata, hostkeylen);
+ if (!hostkey->verifysig(sigdata, siglen, exchange_hash, 20)) {
+ bombout(("Server failed host key check"));
+ crReturn(0);
+ }
+
+ /*
+ * Expect SSH2_MSG_NEWKEYS from server.
+ */
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH2_MSG_NEWKEYS) {
+ bombout(("expected new-keys packet from server"));
+ crReturn(0);
+ }
+
+ /*
+ * Authenticate remote host: verify host key. (We've already
+ * checked the signature of the exchange hash.)
+ */
+ keystr = hostkey->fmtkey();
+ fingerprint = hostkey->fingerprint();
+ verify_ssh_host_key(savedhost, savedport, hostkey->keytype,
+ keystr, fingerprint);
+ logevent("Host key fingerprint is:");
+ logevent(fingerprint);
+ free(fingerprint);
+ free(keystr);
+
+ /*
+ * Send SSH2_MSG_NEWKEYS.
+ */
+ ssh2_pkt_init(SSH2_MSG_NEWKEYS);
+ ssh2_pkt_send();
+
+ /*
+ * Create and initialise session keys.
+ */
+ cscipher = cscipher_tobe;
+ sccipher = sccipher_tobe;
+ csmac = csmac_tobe;
+ scmac = scmac_tobe;
+ cscomp = cscomp_tobe;
+ sccomp = sccomp_tobe;
+ /*
+ * Set IVs after keys.
+ */
+ 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);
+
+ /*
+ * Now we're encrypting. Begin returning 1 to the protocol main
+ * function so that other things can run on top of the
+ * transport. If we ever see a KEXINIT, we must go back to the
+ * start.
+ */
+ do {
+ crReturn(1);
+ } while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT));
+ goto begin_key_exchange;
+
+ crFinish(1);
+}
+
+/*
+ * Handle the SSH2 userauth and connection layers.
+ */
+static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
+{
+ static unsigned long remote_winsize;
+ static unsigned long remote_maxpkt;
+
+ crBegin;
+
+ /*
+ * Request userauth protocol, and await a response to it.
+ */
+ ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ ssh2_pkt_addstring("ssh-userauth");
+ ssh2_pkt_send();
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_SERVICE_ACCEPT) {
+ bombout(("Server refused user authentication protocol"));
+ crReturnV;
+ }
+
+ /*
+ * FIXME: currently we support only password authentication.
+ * (This places us technically in violation of the SSH2 spec.
+ * We must fix this.)
+ */
+ while (1) {
+ /*
+ * Get a username and a password.
+ */
+ static char username[100];
+ static char password[100];
+ static int pos = 0;
+ static char c;
+
+ if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
+ c_write("login as: ", 10);
+ ssh_send_ok = 1;
+ while (pos >= 0) {
+ crWaitUntilV(!ispkt);
+ while (inlen--) switch (c = *in++) {
+ case 10: case 13:
+ username[pos] = 0;
+ pos = -1;
+ break;
+ case 8: case 127:
+ if (pos > 0) {
+ c_write("\b \b", 3);
+ pos--;
+ }
+ break;
+ case 21: case 27:
+ while (pos > 0) {
+ c_write("\b \b", 3);
+ pos--;
+ }
+ break;
+ case 3: case 4:
+ random_save_seed();
+ exit(0);
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char)c >= 160)) && pos < 40) {
+ username[pos++] = c;
+ c_write(&c, 1);
+ }
+ break;
+ }
+ }
+ c_write("\r\n", 2);
+ username[strcspn(username, "\n\r")] = '\0';
+ } else {
+ char stuff[200];
+ strncpy(username, cfg.username, 99);
+ username[99] = '\0';
+ if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
+ sprintf(stuff, "Using username \"%s\".\r\n", username);
+ c_write(stuff, strlen(stuff));
+ }
+ }
+
+ if (ssh_get_password) {
+ char prompt[200];
+ sprintf(prompt, "%.90s@%.90s's password: ", username, savedhost);
+ if (!ssh_get_password(prompt, password, sizeof(password))) {
+ /*
+ * get_password failed to get a password (for
+ * example because one was supplied on the command
+ * line which has already failed to work).
+ * Terminate.
+ */
+ logevent("No more passwords to try");
+ ssh_state = SSH_STATE_CLOSED;
+ crReturnV;
+ }
+ } else {
+ c_write("password: ", 10);
+ ssh_send_ok = 1;
+
+ pos = 0;
+ while (pos >= 0) {
+ crWaitUntilV(!ispkt);
+ while (inlen--) switch (c = *in++) {
+ case 10: case 13:
+ password[pos] = 0;
+ pos = -1;
+ break;
+ case 8: case 127:
+ if (pos > 0)
+ pos--;
+ break;
+ case 21: case 27:
+ pos = 0;
+ break;
+ case 3: case 4:
+ random_save_seed();
+ exit(0);
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char)c >= 160)) && pos < 40)
+ password[pos++] = c;
+ break;
+ }
+ }
+ c_write("\r\n", 2);
+ }
+
+ ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(username);
+ ssh2_pkt_addstring("ssh-connection"); /* service requested */
+ ssh2_pkt_addstring("password");
+ ssh2_pkt_addbool(FALSE);
+ ssh2_pkt_addstring(password);
+ ssh2_pkt_send();
+
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_USERAUTH_SUCCESS) {
+ c_write("Access denied\r\n", 15);
+ logevent("Authentication refused");
+ } else
+ break;
+ }
+
+ /*
+ * Now we're authenticated for the connection protocol. The
+ * connection protocol will automatically have started at this
+ * point; there's no need to send SERVICE_REQUEST.
+ */
+
+ /*
+ * So now create a channel with a session in it.
+ */
+ mainchan = malloc(sizeof(struct ssh_channel));
+ mainchan->localid = 100; /* as good as any */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+ ssh2_pkt_addstring("session");
+ ssh2_pkt_adduint32(mainchan->localid);
+ ssh2_pkt_adduint32(0x8000UL); /* our window size */
+ ssh2_pkt_adduint32(0x4000UL); /* our max pkt size */
+ ssh2_pkt_send();
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ bombout(("Server refused to open a session"));
+ crReturnV;
+ /* FIXME: error data comes back in FAILURE packet */
+ }
+ if (ssh2_pkt_getuint32() != mainchan->localid) {
+ bombout(("Server's channel confirmation cited wrong channel"));
+ 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;
+ logevent("Opened channel for session");
+
+ /*
+ * Now allocate a pty for the session.
+ */
+ if (!cfg.nopty) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ ssh2_pkt_addstring("pty-req");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.termtype);
+ ssh2_pkt_adduint32(cols);
+ ssh2_pkt_adduint32(rows);
+ ssh2_pkt_adduint32(0); /* pixel width */
+ ssh2_pkt_adduint32(0); /* pixel height */
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data("\0", 1);/* TTY_OP_END, no special options */
+ ssh2_pkt_send();
+
+ 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();
+ }
+ } 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 pty request"));
+ crReturnV;
+ }
+ c_write("Server refused to allocate pty\r\n", 32);
+ } else {
+ logevent("Allocated pty");
+ }
+ }
+
+ /*
+ * Start a shell or a remote command.
+ */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ if (*cfg.remote_cmd) {
+ ssh2_pkt_addstring("exec");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.remote_cmd);
+ } else {
+ ssh2_pkt_addstring("shell");
+ ssh2_pkt_addbool(1); /* want reply */
+ }
+ ssh2_pkt_send();
+ 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();
+ }
+ } 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 shell/command request"));
+ crReturnV;
+ }
+ bombout(("Server refused to start a shell/command"));
+ crReturnV;
+ } else {
+ logevent("Started a shell/command");
+ }
+
+ /*
+ * Transfer data!
+ */
+ ssh_send_ok = 1;
+ begin_session();
+ while (1) {
+ static int try_send;
+ crReturnV;
+ try_send = FALSE;
+ if (ispkt) {
+ if (pktin.type == SSH2_MSG_CHANNEL_DATA ||
+ 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 */
+ 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) {
+ c_writedata(data, length);
+ /*
+ * 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(length);
+ ssh2_pkt_send();
+ }
+ } else if (pktin.type == SSH2_MSG_DISCONNECT) {
+ ssh_state = SSH_STATE_CLOSED;
+ logevent("Received disconnect message");
+ } 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 */
+ } 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" */) {
+ logevent("All channels closed. Disconnecting");
+ ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_send();
+ ssh_state = SSH_STATE_CLOSED;
+ closesocket(s);
+ s = INVALID_SOCKET;
+ }
+ 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();
+ try_send = TRUE;
+ } else {
+ bombout(("Strange packet received: type %d", pktin.type));
+ crReturnV;
+ }
+ } else {
+ /*
+ * 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;
+ try_send = TRUE;
+ }
+ if (try_send) {
+ /*
+ * Try to send data on the channel if we can. (FIXME:
+ * on _all_ channels.)
+ */
+ 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;
+ }
+ }
+ }
+
+ crFinishV;
+}
+
+/*
+ * Handle the top-level SSH2 protocol.
+ */
+static void ssh2_protocol(unsigned char *in, int inlen, int ispkt)
+{
+ if (do_ssh2_transport(in, inlen, ispkt) == 0)
+ return;
+ do_ssh2_authconn(in, inlen, ispkt);
+}
+
+/*
+ * Called to set up the connection. Will arrange for WM_NETEVENT
+ * messages to be passed to the specified window, whose window
+ * procedure should then call telnet_msg().
+ *
+ * Returns an error message, or NULL on success.
+ */
+static char *ssh_init (HWND hwnd, char *host, int port, char **realhost) {
+ char *p;
+
+#ifdef MSCRYPTOAPI
+ if(crypto_startup() == 0)
+ return "Microsoft high encryption pack not installed!";
+#endif
+
+ p = connect_to_host(host, port, realhost);
+ if (p != NULL)
+ return p;
+