+ /*
+ * Compress packet payload.
+ */
+#if 0
+ debug(("Pre-compression payload:\r\n"));
+ for (i = 5; i < pktout.length; i++)
+ debug((" %02x", (unsigned char)pktout.data[i]));
+ debug(("\r\n"));
+#endif
+ {
+ unsigned char *newpayload;
+ int newlen;
+ if (cscomp && cscomp->compress(pktout.data+5, pktout.length-5,
+ &newpayload, &newlen)) {
+ pktout.length = 5;
+ ssh2_pkt_adddata(newpayload, newlen);
+ sfree(newpayload);
+ }
+ }
+
+ /*
+ * Add padding. At least four bytes, and must also bring total
+ * length (minus MAC) up to a multiple of the block size.
+ */
+ cipherblk = cipher ? cipher->blksize : 8; /* block size */
+ 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();
+ PUT_32BIT(pktout.data, pktout.length + padding - 4);
+ if (csmac)
+ csmac->generate(pktout.data, pktout.length + padding,
+ outgoing_sequence);
+ outgoing_sequence++; /* whether or not we MACed */
+
+#if 0
+ debug(("Sending packet len=%d\r\n", pktout.length+padding));
+ for (i = 0; i < pktout.length+padding; i++)
+ debug((" %02x", (unsigned char)pktout.data[i]));
+ debug(("\r\n"));
+#endif
+
+ if (cscipher)
+ cscipher->encrypt(pktout.data, pktout.length + padding);
+
+ /* Ready-to-send packet starts at pktout.data. We return length. */
+ return pktout.length + padding + maclen;
+}
+
+/*
+ * Construct and send an SSH2 packet immediately.
+ */
+static void ssh2_pkt_send(void) {
+ int len = ssh2_pkt_construct();
+ sk_write(s, pktout.data, len);
+}
+
+/*
+ * Construct an SSH2 packet and add it to a deferred data block.
+ * Useful for sending multiple packets in a single sk_write() call,
+ * to prevent a traffic-analysing listener from being able to work
+ * out the length of any particular packet (such as the password
+ * packet).
+ *
+ * Note that because SSH2 sequence-numbers its packets, this can
+ * NOT be used as an m4-style `defer' allowing packets to be
+ * constructed in one order and sent in another.
+ */
+static void ssh2_pkt_defer(void) {
+ int len = ssh2_pkt_construct();
+ if (deferred_len + len > deferred_size) {
+ deferred_size = deferred_len + len + 128;
+ deferred_send_data = srealloc(deferred_send_data, deferred_size);
+ }
+ memcpy(deferred_send_data+deferred_len, pktout.data, len);
+ deferred_len += len;
+}
+
+/*
+ * Send the whole deferred data block constructed by
+ * ssh2_pkt_defer().
+ */
+static void ssh2_pkt_defersend(void) {
+ sk_write(s, deferred_send_data, deferred_len);
+ deferred_len = deferred_size = 0;
+ sfree(deferred_send_data);
+ deferred_send_data = NULL;
+}
+
+#if 0
+void bndebug(char *string, Bignum b) {
+ unsigned char *p;
+ int i, len;
+ p = ssh2_mpint_fmt(b, &len);
+ debug(("%s", string));
+ for (i = 0; i < len; i++)
+ debug((" %02x", p[i]));
+ debug(("\r\n"));
+ sfree(p);
+}
+#endif
+
+static void sha_mpint(SHA_State *s, Bignum b) {
+ unsigned char *p;
+ int len;
+ p = ssh2_mpint_fmt(b, &len);
+ sha_string(s, p, len);
+ sfree(p);
+}
+
+/*
+ * SSH2 packet decode functions.
+ */
+static unsigned long ssh2_pkt_getuint32(void) {
+ unsigned long value;
+ if (pktin.length - pktin.savedpos < 4)
+ return 0; /* arrgh, no way to decline (FIXME?) */
+ value = GET_32BIT(pktin.data+pktin.savedpos);
+ pktin.savedpos += 4;
+ return value;
+}
+static void ssh2_pkt_getstring(char **p, int *length) {
+ *p = NULL;
+ if (pktin.length - pktin.savedpos < 4)
+ return;
+ *length = GET_32BIT(pktin.data+pktin.savedpos);
+ pktin.savedpos += 4;
+ if (pktin.length - pktin.savedpos < *length)
+ return;
+ *p = pktin.data+pktin.savedpos;
+ pktin.savedpos += *length;
+}
+static Bignum ssh2_pkt_getmp(void) {
+ char *p;
+ int length;
+ Bignum b;
+
+ ssh2_pkt_getstring(&p, &length);
+ if (!p)
+ return NULL;
+ if (p[0] & 0x80) {
+ bombout(("internal error: Can't handle negative mpints"));
+ return NULL;
+ }
+ b = bignum_from_bytes(p, length);
+ return b;
+}
+
+static int do_ssh_init(unsigned char c) {
+ static char *vsp;
+ static char version[10];
+ static char vstring[80];
+ static char vlog[sizeof(vstring)+20];
+ static int i;
+
+ crBegin;
+
+ /* Search for the string "SSH-" in the input. */
+ i = 0;
+ while (1) {
+ static const int transS[] = { 1, 2, 2, 1 };
+ static const int transH[] = { 0, 0, 3, 0 };
+ static const int transminus[] = { 0, 0, 0, -1 };
+ if (c == 'S') i = transS[i];
+ else if (c == 'H') i = transH[i];
+ else if (c == '-') i = transminus[i];
+ else i = 0;
+ if (i < 0)
+ break;
+ crReturn(1); /* get another character */
+ }
+
+ strcpy(vstring, "SSH-");
+ vsp = vstring+4;
+ i = 0;
+ while (1) {
+ crReturn(1); /* get another char */
+ if (vsp < vstring+sizeof(vstring)-1)
+ *vsp++ = c;
+ if (i >= 0) {
+ if (c == '-') {
+ version[i] = '\0';
+ i = -1;
+ } else if (i < sizeof(version)-1)
+ version[i++] = c;
+ }
+ else if (c == '\n')
+ break;
+ }
+
+ ssh_agentfwd_enabled = FALSE;
+ rdpkt2_state.incoming_sequence = 0;
+
+ *vsp = 0;
+ sprintf(vlog, "Server version: %s", vstring);
+ vlog[strcspn(vlog, "\r\n")] = '\0';
+ logevent(vlog);
+
+ /*
+ * Server version "1.99" means we can choose whether we use v1
+ * or v2 protocol. Choice is based on cfg.sshprot.
+ */
+ if (ssh_versioncmp(version, cfg.sshprot == 1 ? "2.0" : "1.99") >= 0) {