+
+ /*
+ * 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 = smalloc(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();
+ ssh_state = SSH_STATE_INTERMED;
+
+ 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");
+ }
+
+ ssh_state = SSH_STATE_SESSION;
+ if (size_needed)
+ ssh_size();
+ if (eof_needed)
+ ssh_special(TS_EOF);
+
+ /*
+ * 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) {
+ from_backend(pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA,
+ 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");
+ crReturnV;
+ } 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_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
+ ssh2_pkt_addstring("All open channels closed");
+ ssh2_pkt_addstring("en"); /* language tag */
+ ssh2_pkt_send();
+ ssh_state = SSH_STATE_CLOSED;
+ crReturnV;
+ }
+ 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.
+ *
+ * Returns an error message, or NULL on success.
+ */
+static char *ssh_init (char *host, int port, char **realhost) {
+ char *p;
+
+#ifdef MSCRYPTOAPI
+ if(crypto_startup() == 0)
+ return "Microsoft high encryption pack not installed!";
+#endif
+
+ ssh_send_ok = 0;
+
+ p = connect_to_host(host, port, realhost);
+ if (p != NULL)
+ return p;
+
+ return NULL;