[commit] r2100 - in trunk/Tools/GMEplink: . Windows

GMESRC Repository Notifications gme-commit at list.isis.vanderbilt.edu
Wed Nov 7 10:27:00 CST 2012


Author: ksmyth
Date: Wed Nov  7 10:26:59 2012
New Revision: 2100

Log:
Upgrade to putty-0.62

Modified:
   trunk/Tools/GMEplink/BE_ALL_S.C
   trunk/Tools/GMEplink/CMDLINE.C
   trunk/Tools/GMEplink/LDISC.C
   trunk/Tools/GMEplink/LICENCE
   trunk/Tools/GMEplink/LOGGING.C
   trunk/Tools/GMEplink/MISC.C
   trunk/Tools/GMEplink/NETWORK.H
   trunk/Tools/GMEplink/PORTFWD.C
   trunk/Tools/GMEplink/PUTTY.H
   trunk/Tools/GMEplink/PUTTYPS.H
   trunk/Tools/GMEplink/RAW.C
   trunk/Tools/GMEplink/RLOGIN.C
   trunk/Tools/GMEplink/SETTINGS.C
   trunk/Tools/GMEplink/SSH.C
   trunk/Tools/GMEplink/SSH.H
   trunk/Tools/GMEplink/SSHAES.C
   trunk/Tools/GMEplink/SSHBN.C
   trunk/Tools/GMEplink/SSHDES.C
   trunk/Tools/GMEplink/SSHDH.C
   trunk/Tools/GMEplink/SSHDSS.C
   trunk/Tools/GMEplink/SSHMD5.C
   trunk/Tools/GMEplink/SSHPUBK.C
   trunk/Tools/GMEplink/SSHRAND.C
   trunk/Tools/GMEplink/SSHRSA.C
   trunk/Tools/GMEplink/SSHSH512.C
   trunk/Tools/GMEplink/SSHSHA.C
   trunk/Tools/GMEplink/SSHZLIB.C
   trunk/Tools/GMEplink/TELNET.C
   trunk/Tools/GMEplink/TERMINAL.H
   trunk/Tools/GMEplink/VERSION.C
   trunk/Tools/GMEplink/Windows/WINCONS.C
   trunk/Tools/GMEplink/Windows/WINHANDL.C
   trunk/Tools/GMEplink/Windows/WINHELP.H
   trunk/Tools/GMEplink/Windows/WINMISC.C
   trunk/Tools/GMEplink/Windows/WINNET.C
   trunk/Tools/GMEplink/Windows/WINPGNTC.C
   trunk/Tools/GMEplink/Windows/WINPROXY.C
   trunk/Tools/GMEplink/Windows/WINSER.C
   trunk/Tools/GMEplink/Windows/WINSTORE.C
   trunk/Tools/GMEplink/Windows/WINSTUFF.H
   trunk/Tools/GMEplink/Windows/winplink.c
   trunk/Tools/GMEplink/X11FWD.C

Modified: trunk/Tools/GMEplink/BE_ALL_S.C
==============================================================================
--- trunk/Tools/GMEplink/BE_ALL_S.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/BE_ALL_S.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -22,11 +22,11 @@
 const int be_default_protocol = PROT_SSH;
 #endif
 
-struct backend_list backends[] = {
-    {PROT_SSH, "ssh", &ssh_backend},
-    {PROT_TELNET, "telnet", &telnet_backend},
-    {PROT_RLOGIN, "rlogin", &rlogin_backend},
-    {PROT_RAW, "raw", &raw_backend},
-    {PROT_SERIAL, "serial", &serial_backend},
-    {0, NULL}
+Backend *backends[] = {
+    &ssh_backend,
+    &telnet_backend,
+    &rlogin_backend,
+    &raw_backend,
+    &serial_backend,
+    NULL
 };

Modified: trunk/Tools/GMEplink/CMDLINE.C
==============================================================================
--- trunk/Tools/GMEplink/CMDLINE.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/CMDLINE.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -56,20 +56,30 @@
     saves[pri].nsaved++;
 }
 
+static char *cmdline_password = NULL;
+
 void cmdline_cleanup(void)
 {
     int pri;
 
-    for (pri = 0; pri < NPRIORITIES; pri++)
+    if (cmdline_password) {
+	memset(cmdline_password, 0, strlen(cmdline_password));
+	sfree(cmdline_password);
+	cmdline_password = NULL;
+    }
+    
+    for (pri = 0; pri < NPRIORITIES; pri++) {
 	sfree(saves[pri].params);
+	saves[pri].params = NULL;
+	saves[pri].savesize = 0;
+	saves[pri].nsaved = 0;
+    }
 }
 
 #define SAVEABLE(pri) do { \
     if (need_save) { cmdline_save_param(p, value, pri); return ret; } \
 } while (0)
 
-static char *cmdline_password = NULL;
-
 /*
  * Similar interface to get_userpass_input(), except that here a -1
  * return means that we aren't capable of processing the prompt and
@@ -99,6 +109,8 @@
 	    p->prompts[0]->result_len);
     p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0';
     memset(cmdline_password, 0, strlen(cmdline_password));
+    sfree(cmdline_password);
+    cmdline_password = NULL;
     tried_once = 1;
     return 1;
 
@@ -160,6 +172,7 @@
 	 * saved. */
 	do_defaults(value, cfg);
 	loaded_session = TRUE;
+	cmdline_session_name = dupstr(value);
 	return 2;
     }
     if (!strcmp(p, "-ssh")) {
@@ -192,6 +205,16 @@
 	SAVEABLE(0);
 	default_protocol = cfg->protocol = PROT_RAW;
     }
+    if (!strcmp(p, "-serial")) {
+	RETURN(1);
+	/* Serial is not NONNETWORK in an odd sense of the word */
+	UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
+	SAVEABLE(0);
+	default_protocol = cfg->protocol = PROT_SERIAL;
+	/* The host parameter will already be loaded into cfg->host, so copy it across */
+	strncpy(cfg->serline, cfg->host, sizeof(cfg->serline) - 1);
+	cfg->serline[sizeof(cfg->serline) - 1] = '\0';
+    }
     if (!strcmp(p, "-v")) {
 	RETURN(1);
 	flags |= FLAG_VERBOSE;
@@ -203,6 +226,13 @@
 	strncpy(cfg->username, value, sizeof(cfg->username));
 	cfg->username[sizeof(cfg->username) - 1] = '\0';
     }
+    if (!strcmp(p, "-loghost")) {
+	RETURN(2);
+	UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
+	SAVEABLE(0);
+	strncpy(cfg->loghost, value, sizeof(cfg->loghost));
+	cfg->loghost[sizeof(cfg->loghost) - 1] = '\0';
+    }
     if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {
 	char *fwd, *ptr, *q, *qq;
 	int dynamic, i=0;
@@ -263,8 +293,8 @@
 	    unsigned len = portp - host;
 	    if (len >= sizeof(cfg->ssh_nc_host))
 		len = sizeof(cfg->ssh_nc_host) - 1;
-	    strncpy(cfg->ssh_nc_host, value, len);
-	    cfg->ssh_nc_host[sizeof(cfg->ssh_nc_host) - 1] = '\0';
+	    memcpy(cfg->ssh_nc_host, value, len);
+	    cfg->ssh_nc_host[len] = '\0';
 	    cfg->ssh_nc_port = atoi(portp+1);
 	} else {
 	    cmdline_error("-nc expects argument of form 'host:port'");
@@ -305,6 +335,7 @@
 	cfg->remote_cmd_ptr = command;
 	cfg->remote_cmd_ptr2 = NULL;
 	cfg->nopty = TRUE;      /* command => no terminal */
+	fclose(fp);
     }
     if (!strcmp(p, "-P")) {
 	RETURN(2);
@@ -428,7 +459,100 @@
 	SAVEABLE(1);
 	cfg->addressfamily = ADDRTYPE_IPV6;
     }
-
+    if (!strcmp(p, "-sercfg")) {
+	char* nextitem;
+	RETURN(2);
+	UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
+	SAVEABLE(1);
+	if (cfg->protocol != PROT_SERIAL)
+	    cmdline_error("the -sercfg option can only be used with the "
+			  "serial protocol");
+	/* Value[0] contains one or more , separated values, like 19200,8,n,1,X */
+	nextitem = value;
+	while (nextitem[0] != '\0') {
+	    int length, skip;
+	    char *end = strchr(nextitem, ',');
+	    if (!end) {
+		length = strlen(nextitem);
+		skip = 0;
+	    } else {
+		length = end - nextitem;
+		nextitem[length] = '\0';
+		skip = 1;
+	    }
+	    if (length == 1) {
+		switch (*nextitem) {
+		  case '1':
+		    cfg->serstopbits = 2;
+		    break;
+		  case '2':
+		    cfg->serstopbits = 4;
+		    break;
+
+		  case '5':
+		    cfg->serdatabits = 5;
+		    break;
+		  case '6':
+		    cfg->serdatabits = 6;
+		    break;
+		  case '7':
+		    cfg->serdatabits = 7;
+		    break;
+		  case '8':
+		    cfg->serdatabits = 8;
+		    break;
+		  case '9':
+		    cfg->serdatabits = 9;
+		    break;
+
+		  case 'n':
+		    cfg->serparity = SER_PAR_NONE;
+		    break;
+		  case 'o':
+		    cfg->serparity = SER_PAR_ODD;
+		    break;
+		  case 'e':
+		    cfg->serparity = SER_PAR_EVEN;
+		    break;
+		  case 'm':
+		    cfg->serparity = SER_PAR_MARK;
+		    break;
+		  case 's':
+		    cfg->serparity = SER_PAR_SPACE;
+		    break;
+
+		  case 'N':
+		    cfg->serflow = SER_FLOW_NONE;
+		    break;
+		  case 'X':
+		    cfg->serflow = SER_FLOW_XONXOFF;
+		    break;
+		  case 'R':
+		    cfg->serflow = SER_FLOW_RTSCTS;
+		    break;
+		  case 'D':
+		    cfg->serflow = SER_FLOW_DSRDTR;
+		    break;
+
+		  default:
+		    cmdline_error("Unrecognised suboption \"-sercfg %c\"",
+				  *nextitem);
+		}
+	    } else if (length == 3 && !strncmp(nextitem,"1.5",3)) {
+		/* Messy special case */
+		cfg->serstopbits = 3;
+	    } else {
+		int serspeed = atoi(nextitem);
+		if (serspeed != 0) {
+		    cfg->serspeed = serspeed;
+		} else {
+		    cmdline_error("Unrecognised suboption \"-sercfg %s\"",
+				  nextitem);
+		}
+	    }
+	    nextitem += length + skip;
+	}
+    }
     return ret;			       /* unrecognised */
 }
 

Modified: trunk/Tools/GMEplink/LDISC.C
==============================================================================
--- trunk/Tools/GMEplink/LDISC.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/LDISC.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -147,7 +147,7 @@
     if (EDITING) {
 	while (len--) {
 	    int c;
-	    c = *buf++ + keyflag;
+	    c = (unsigned char)(*buf++) + keyflag;
 	    if (!interactive && c == '\r')
 		c += KCTRL('@');
 	    switch (ldisc->quotenext ? ' ' : c) {

Modified: trunk/Tools/GMEplink/LICENCE
==============================================================================
--- trunk/Tools/GMEplink/LICENCE	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/LICENCE	Wed Nov  7 10:26:59 2012	(r2100)
@@ -1,9 +1,9 @@
-PuTTY is copyright 1997-2007 Simon Tatham.
+PuTTY is copyright 1997-2011 Simon Tatham.
 
 Portions copyright Robert de Bath, Joris van Rantwijk, Delian
 Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
 Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
-Kuhn, and CORE SDI S.A.
+Kuhn, Colin Watson, and CORE SDI S.A.
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation files

Modified: trunk/Tools/GMEplink/LOGGING.C
==============================================================================
--- trunk/Tools/GMEplink/LOGGING.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/LOGGING.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -43,7 +43,13 @@
 	bufchain_add(&ctx->queue, data, len);
     } else if (ctx->state == L_OPEN) {
 	assert(ctx->lgfp);
-	fwrite(data, 1, len, ctx->lgfp);
+	if (fwrite(data, 1, len, ctx->lgfp) < (size_t)len) {
+	    logfclose(ctx);
+	    ctx->state = L_ERROR;
+	    /* Log state is L_ERROR so this won't cause a loop */
+	    logevent(ctx->frontend,
+		     "Disabled writing session log due to error while writing");
+	}
     }				       /* else L_ERROR, so ignore the write */
 }
 
@@ -85,7 +91,7 @@
 	ctx->state = L_ERROR;	       /* disable logging */
     } else {
 	fmode = (mode == 1 ? "ab" : "wb");
-	ctx->lgfp = f_open(ctx->currlogfilename, fmode, TRUE);
+	ctx->lgfp = f_open(ctx->currlogfilename, fmode, FALSE);
 	if (ctx->lgfp)
 	    ctx->state = L_OPEN;
 	else
@@ -101,8 +107,9 @@
     }
 
     event = dupprintf("%s session log (%s mode) to file: %s",
-		      (mode == 0 ? "Disabled writing" :
-                       mode == 1 ? "Appending" : "Writing new"),
+		      ctx->state == L_ERROR ?
+		      (mode == 0 ? "Disabled writing" : "Error writing") :
+		      (mode == 1 ? "Appending" : "Writing new"),
 		      (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :
 		       ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :
 		       ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :
@@ -220,8 +227,9 @@
  * Set of blanking areas must be in increasing order.
  */
 void log_packet(void *handle, int direction, int type,
-		char *texttype, void *data, int len,
-		int n_blanks, const struct logblank_t *blanks)
+		char *texttype, const void *data, int len,
+		int n_blanks, const struct logblank_t *blanks,
+		const unsigned long *seq)
 {
     struct LogContext *ctx = (struct LogContext *)handle;
     char dumpdata[80], smalldata[5];
@@ -233,13 +241,20 @@
 	return;
 
     /* Packet header. */
-    if (texttype)
-        logprintf(ctx, "%s packet type %d / 0x%02x (%s)\r\n",
-                  direction == PKT_INCOMING ? "Incoming" : "Outgoing",
-                  type, type, texttype);
-    else
+    if (texttype) {
+	if (seq) {
+	    logprintf(ctx, "%s packet #0x%lx, type %d / 0x%02x (%s)\r\n",
+		      direction == PKT_INCOMING ? "Incoming" : "Outgoing",
+		      *seq, type, type, texttype);
+	} else {
+	    logprintf(ctx, "%s packet type %d / 0x%02x (%s)\r\n",
+		      direction == PKT_INCOMING ? "Incoming" : "Outgoing",
+		      type, type, texttype);
+	}
+    } else {
         logprintf(ctx, "%s raw data\r\n",
                   direction == PKT_INCOMING ? "Incoming" : "Outgoing");
+    }
 
     /*
      * Output a hex/ASCII dump of the packet body, blanking/omitting
@@ -376,7 +391,7 @@
 	    char c;
 	    s++;
 	    size = 0;
-	    if (*s) switch (c = *s++, tolower(c)) {
+	    if (*s) switch (c = *s++, tolower((unsigned char)c)) {
 	      case 'y':
 		size = strftime(buf, sizeof(buf), "%Y", tm);
 		break;

Modified: trunk/Tools/GMEplink/MISC.C
==============================================================================
--- trunk/Tools/GMEplink/MISC.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/MISC.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -605,7 +605,7 @@
     if (L) {
 	int delta;
 	debug_printf("\t%d (0x%x) bytes:\n", len, len);
-	delta = 15 & (int) p;
+	delta = 15 & (unsigned long int) p;
 	p -= delta;
 	len += delta;
     }

Modified: trunk/Tools/GMEplink/NETWORK.H
==============================================================================
--- trunk/Tools/GMEplink/NETWORK.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/NETWORK.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -120,6 +120,12 @@
 int sk_addrtype(SockAddr addr);
 void sk_addrcopy(SockAddr addr, char *buf);
 void sk_addr_free(SockAddr addr);
+/* sk_addr_dup generates another SockAddr which contains the same data
+ * as the original one and can be freed independently. May not actually
+ * physically _duplicate_ it: incrementing a reference count so that
+ * one more free is required before it disappears is an acceptable
+ * implementation. */
+SockAddr sk_addr_dup(SockAddr addr);
 
 /* NB, control of 'addr' is passed via sk_new, which takes responsibility
  * for freeing it, as for new_connection() */
@@ -196,6 +202,12 @@
  */
 int net_service_lookup(char *service);
 
+/*
+ * Look up the local hostname; return value needs freeing.
+ * May return NULL.
+ */
+char *get_hostname(void);
+
 /********** SSL stuff **********/
 
 /*

Modified: trunk/Tools/GMEplink/PORTFWD.C
==============================================================================
--- trunk/Tools/GMEplink/PORTFWD.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/PORTFWD.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -278,6 +278,12 @@
 	 */
 	connect:
 
+	/*
+	 * Freeze the socket until the SSH server confirms the
+	 * connection.
+	 */
+	sk_set_frozen(pr->s, 1);
+
 	pr->c = new_sock_channel(pr->backhandle, pr->s);
 	if (pr->c == NULL) {
 	    pfd_close(pr->s);
@@ -289,11 +295,6 @@
 	pr->dynamic = 0;
 
 	/*
-	 * Now freeze the socket until the SSH server confirms the
-	 * connection.
-	 */
-	sk_set_frozen(pr->s, 1);
-	/*
 	 * If there's any data remaining in our current buffer,
 	 * save it to be sent on pfd_confirm().
 	 */

Modified: trunk/Tools/GMEplink/PUTTY.H
==============================================================================
--- trunk/Tools/GMEplink/PUTTY.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/PUTTY.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -252,6 +252,7 @@
     KEX_DHGROUP1,
     KEX_DHGROUP14,
     KEX_DHGEX,
+    KEX_RSA,
     KEX_MAX
 };
 
@@ -347,6 +348,59 @@
     SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR
 };
 
+/*
+ * Tables of string <-> enum value mappings used in settings.c.
+ * Defined here so that backends can export their GSS library tables
+ * to the cross-platform settings code.
+ */
+struct keyvalwhere {
+    /*
+     * Two fields which define a string and enum value to be
+     * equivalent to each other.
+     */
+    char *s;
+    int v;
+
+    /*
+     * The next pair of fields are used by gprefs() in settings.c to
+     * arrange that when it reads a list of strings representing a
+     * preference list and translates it into the corresponding list
+     * of integers, strings not appearing in the list are entered in a
+     * configurable position rather than uniformly at the end.
+     */
+
+    /*
+     * 'vrel' indicates which other value in the list to place this
+     * element relative to. It should be a value that has occurred in
+     * a 'v' field of some other element of the array, or -1 to
+     * indicate that we simply place relative to one or other end of
+     * the list.
+     *
+     * gprefs will try to process the elements in an order which makes
+     * this field work (i.e. so that the element referenced has been
+     * added before processing this one).
+     */
+    int vrel;
+
+    /*
+     * 'where' indicates whether to place the new value before or
+     * after the one referred to by vrel. -1 means before; +1 means
+     * after.
+     *
+     * When vrel is -1, this also implicitly indicates which end of
+     * the array to use. So vrel=-1, where=-1 means to place _before_
+     * some end of the list (hence, at the last element); vrel=-1,
+     * where=+1 means to place _after_ an end (hence, at the first).
+     */
+    int where;
+};
+
+#ifndef NO_GSSAPI
+extern const int ngsslibs;
+extern const char *const gsslibnames[]; /* for displaying in configuration */
+extern const struct keyvalwhere gsslibkeywords[]; /* for settings.c */
+#endif
+
 extern const char *const ttymodes[];
 
 enum {
@@ -388,14 +442,12 @@
      */
     void (*unthrottle) (void *handle, int);
     int (*cfg_info) (void *handle);
+    char *name;
+    int protocol;
     int default_port;
 };
 
-extern struct backend_list {
-    int protocol;
-    char *name;
-    Backend *backend;
-} backends[];
+extern Backend *backends[];
 
 /*
  * Suggested default protocol provided by the backend link module.
@@ -428,6 +480,7 @@
     int ping_interval;		       /* in seconds */
     int tcp_nodelay;
     int tcp_keepalives;
+    char loghost[512];  /* logical host being contacted, for host key check */
     /* Proxy options */
     char proxy_exclude_list[512];
     int proxy_dns;
@@ -457,8 +510,13 @@
     int sshprot;		       /* use v1 or v2 when both available */
     int ssh2_des_cbc;		       /* "des-cbc" unrecommended SSH-2 cipher */
     int ssh_no_userauth;	       /* bypass "ssh-userauth" (SSH-2 only) */
+    int ssh_show_banner;	       /* show USERAUTH_BANNERs (SSH-2 only) */
     int try_tis_auth;
     int try_ki_auth;
+    int try_gssapi_auth;               /* attempt gssapi auth */
+    int gssapifwd;                     /* forward tgt via gss */
+    int ssh_gsslist[4];		       /* preference order for local GSS libs */
+    Filename ssh_gss_custom;
     int ssh_subsys;		       /* run a subsystem rather than a command */
     int ssh_subsys2;		       /* fallback to go with remote_cmd_ptr2 */
     int ssh_no_shell;		       /* avoid running a shell */
@@ -470,6 +528,7 @@
     char ttymodes[768];		       /* MODE\tVvalue\0MODE\tA\0\0 */
     char environmt[1024];	       /* VAR\tvalue\0VAR\tvalue\0\0 */
     char username[100];
+    int username_from_env;
     char localusername[100];
     int rfc_environ;
     int passive_telnet;
@@ -570,6 +629,7 @@
     int x11_forward;
     char x11_display[128];
     int x11_auth;
+    Filename xauthfile;
     /* port forwarding */
     int lport_acceptall; /* accept conns from hosts other than localhost */
     int rport_acceptall; /* same for remote forwarded ports (SSH-2 only) */
@@ -588,7 +648,14 @@
     /* SSH bug compatibility modes */
     int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
 	sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
-	sshbug_pksessid2, sshbug_rekey2;
+	sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2,
+	sshbug_ignore2;
+    /*
+     * ssh_simple means that we promise never to open any channel other
+     * than the main one, which means it can safely use a very large
+     * window in SSH-2.
+     */
+    int ssh_simple;
     /* Options for pterm. Should split out into platform-dependent part. */
     int stamp_utmp;
     int login_shell;
@@ -598,6 +665,8 @@
     FontSpec widefont;
     FontSpec wideboldfont;
     int shadowboldoffset;
+    int crhaslf;
+    char winclass[256];
 };
 
 /*
@@ -639,6 +708,10 @@
  * This is set TRUE by cmdline.c iff a session is loaded with "-load".
  */
 GLOBAL int loaded_session;
+/*
+ * This is set to the name of the loaded session.
+ */
+GLOBAL char *cmdline_session_name;
 
 struct RSAKey;			       /* be a little careful of scope */
 
@@ -677,7 +750,8 @@
     int name_reqd;	/* Display of `name' required or optional? */
     char *instruction;	/* Long description, maybe with embedded newlines */
     int instr_reqd;	/* Display of `instruction' required or optional? */
-    size_t n_prompts;
+    size_t n_prompts;   /* May be zero (in which case display the foregoing,
+                         * if any, and return success) */
     prompt_t **prompts;
     void *frontend;
     void *data;		/* slot for housekeeping data, managed by
@@ -777,6 +851,9 @@
 /*
  * Exports from settings.c.
  */
+Backend *backend_from_name(const char *name);
+Backend *backend_from_proto(int proto);
+int get_remote_username(Config *cfg, char *user, size_t len);
 char *save_settings(char *section, Config * cfg);
 void save_open_settings(void *sesskey, Config *cfg);
 void load_settings(char *section, Config * cfg);
@@ -810,6 +887,7 @@
 void term_size(Terminal *, int, int, int);
 void term_paint(Terminal *, Context, int, int, int, int, int);
 void term_scroll(Terminal *, int, int);
+void term_scroll_to_selection(Terminal *, int);
 void term_pwron(Terminal *, int);
 void term_clrsb(Terminal *);
 void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,
@@ -839,6 +917,8 @@
 int term_get_userpass_input(Terminal *term, prompts_t *p,
 			    unsigned char *in, int inlen);
 
+int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
+
 /*
  * Exports from logging.c.
  */
@@ -858,8 +938,9 @@
     int type;
 };
 void log_packet(void *logctx, int direction, int type,
-		char *texttype, void *data, int len,
-		int n_blanks, const struct logblank_t *blanks);
+		char *texttype, const void *data, int len,
+		int n_blanks, const struct logblank_t *blanks,
+		const unsigned long *sequence);
 
 /*
  * Exports from testback.c
@@ -1211,4 +1292,15 @@
 int run_timers(long now, long *next);
 void timer_change_notify(long next);
 
+/*
+ * Define no-op macros for the jump list functions, on platforms that
+ * don't support them. (This is a bit of a hack, and it'd be nicer to
+ * localise even the calls to those functions into the Windows front
+ * end, but it'll do for the moment.)
+ */
+#ifndef JUMPLIST_SUPPORTED
+#define add_session_to_jumplist(x) ((void)0)
+#define remove_session_from_jumplist(x) ((void)0)
+#endif
+
 #endif

Modified: trunk/Tools/GMEplink/PUTTYPS.H
==============================================================================
--- trunk/Tools/GMEplink/PUTTYPS.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/PUTTYPS.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -9,10 +9,6 @@
 
 #include "winstuff.h"
 
-#elif defined(macintosh)
-
-#include "macstuff.h"
-
 #elif defined(MACOSX)
 
 #include "osx.h"

Modified: trunk/Tools/GMEplink/RAW.C
==============================================================================
--- trunk/Tools/GMEplink/RAW.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/RAW.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -139,6 +139,22 @@
     if ((err = sk_socket_error(raw->s)) != NULL)
 	return err;
 
+    if (*cfg->loghost) {
+	char *colon;
+
+	sfree(*realhost);
+	*realhost = dupstr(cfg->loghost);
+	colon = strrchr(*realhost, ':');
+	if (colon) {
+	    /*
+	     * FIXME: if we ever update this aspect of ssh.c for
+	     * IPv6 literal management, this should change in line
+	     * with it.
+	     */
+	    *colon++ = '\0';
+	}
+    }
+
     return NULL;
 }
 
@@ -278,5 +294,7 @@
     raw_provide_logctx,
     raw_unthrottle,
     raw_cfg_info,
-    1
+    "raw",
+    PROT_RAW,
+    0
 };

Modified: trunk/Tools/GMEplink/RLOGIN.C
==============================================================================
--- trunk/Tools/GMEplink/RLOGIN.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/RLOGIN.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -27,6 +27,11 @@
     int cansize;
     int term_width, term_height;
     void *frontend;
+
+    Config cfg;
+
+    /* In case we need to read a username from the terminal before starting */
+    prompts_t *prompt;
 } *Rlogin;
 
 static void rlogin_size(void *handle, int width, int height);
@@ -113,6 +118,27 @@
     rlogin->bufsize = bufsize;
 }
 
+static void rlogin_startup(Rlogin rlogin, const char *ruser)
+{
+    char z = 0;
+    char *p;
+    sk_write(rlogin->s, &z, 1);
+    sk_write(rlogin->s, rlogin->cfg.localusername,
+             strlen(rlogin->cfg.localusername));
+    sk_write(rlogin->s, &z, 1);
+    sk_write(rlogin->s, ruser,
+             strlen(ruser));
+    sk_write(rlogin->s, &z, 1);
+    sk_write(rlogin->s, rlogin->cfg.termtype,
+             strlen(rlogin->cfg.termtype));
+    sk_write(rlogin->s, "/", 1);
+    for (p = rlogin->cfg.termspeed; isdigit((unsigned char)*p); p++) continue;
+    sk_write(rlogin->s, rlogin->cfg.termspeed, p - rlogin->cfg.termspeed);
+    rlogin->bufsize = sk_write(rlogin->s, &z, 1);
+
+    rlogin->prompt = NULL;
+}
+
 /*
  * Called to set up the rlogin connection.
  * 
@@ -135,6 +161,7 @@
     SockAddr addr;
     const char *err;
     Rlogin rlogin;
+    char ruser[sizeof(cfg->username)];
 
     rlogin = snew(struct rlogin_tag);
     rlogin->fn = &fn_table;
@@ -144,6 +171,8 @@
     rlogin->term_height = cfg->height;
     rlogin->firstbyte = 1;
     rlogin->cansize = 0;
+    rlogin->prompt = NULL;
+    rlogin->cfg = *cfg;                /* STRUCTURE COPY */
     *backend_handle = rlogin;
 
     /*
@@ -175,26 +204,42 @@
     if ((err = sk_socket_error(rlogin->s)) != NULL)
 	return err;
 
+    if (*cfg->loghost) {
+	char *colon;
+
+	sfree(*realhost);
+	*realhost = dupstr(cfg->loghost);
+	colon = strrchr(*realhost, ':');
+	if (colon) {
+	    /*
+	     * FIXME: if we ever update this aspect of ssh.c for
+	     * IPv6 literal management, this should change in line
+	     * with it.
+	     */
+	    *colon++ = '\0';
+	}
+    }
+
     /*
-     * Send local username, remote username, terminal/speed
+     * Send local username, remote username, terminal type and
+     * terminal speed - unless we don't have the remote username yet,
+     * in which case we prompt for it and may end up deferring doing
+     * anything else until the local prompt mechanism returns.
      */
+    if (get_remote_username(cfg, ruser, sizeof(ruser))) {
+        rlogin_startup(rlogin, ruser);
+    } else {
+        int ret;
 
-    {
-	char z = 0;
-	char *p;
-	sk_write(rlogin->s, &z, 1);
-	sk_write(rlogin->s, cfg->localusername,
-		 strlen(cfg->localusername));
-	sk_write(rlogin->s, &z, 1);
-	sk_write(rlogin->s, cfg->username,
-		 strlen(cfg->username));
-	sk_write(rlogin->s, &z, 1);
-	sk_write(rlogin->s, cfg->termtype,
-		 strlen(cfg->termtype));
-	sk_write(rlogin->s, "/", 1);
-	for (p = cfg->termspeed; isdigit((unsigned char)*p); p++) continue;
-	sk_write(rlogin->s, cfg->termspeed, p - cfg->termspeed);
-	rlogin->bufsize = sk_write(rlogin->s, &z, 1);
+        rlogin->prompt = new_prompts(rlogin->frontend);
+        rlogin->prompt->to_server = TRUE;
+        rlogin->prompt->name = dupstr("Rlogin login name");
+        add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE,
+                   sizeof(cfg->username)); 
+        ret = get_userpass_input(rlogin->prompt, NULL, 0);
+        if (ret >= 0) {
+            rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
+        }
     }
 
     return NULL;
@@ -204,6 +249,8 @@
 {
     Rlogin rlogin = (Rlogin) handle;
 
+    if (rlogin->prompt)
+        free_prompts(rlogin->prompt);
     if (rlogin->s)
 	sk_close(rlogin->s);
     sfree(rlogin);
@@ -226,7 +273,21 @@
     if (rlogin->s == NULL)
 	return 0;
 
-    rlogin->bufsize = sk_write(rlogin->s, buf, len);
+    if (rlogin->prompt) {
+        /*
+         * We're still prompting for a username, and aren't talking
+         * directly to the network connection yet.
+         */
+        int ret = get_userpass_input(rlogin->prompt,
+                                     (unsigned char *)buf, len);
+        if (ret >= 0) {
+            rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
+            /* that nulls out rlogin->prompt, so then we'll start sending
+             * data down the wire in the obvious way */
+        }
+    } else {
+        rlogin->bufsize = sk_write(rlogin->s, buf, len);
+    }
 
     return rlogin->bufsize;
 }
@@ -349,5 +410,7 @@
     rlogin_provide_logctx,
     rlogin_unthrottle,
     rlogin_cfg_info,
-    1
+    "rlogin",
+    PROT_RLOGIN,
+    513
 };

Modified: trunk/Tools/GMEplink/SETTINGS.C
==============================================================================
--- trunk/Tools/GMEplink/SETTINGS.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SETTINGS.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -8,26 +8,22 @@
 #include "putty.h"
 #include "storage.h"
 
-/*
- * Tables of string <-> enum value mappings
- */
-struct keyval { char *s; int v; };
-
 /* The cipher order given here is the default order. */
-static const struct keyval ciphernames[] = {
-    { "aes",	    CIPHER_AES },
-    { "blowfish",   CIPHER_BLOWFISH },
-    { "3des",	    CIPHER_3DES },
-    { "WARN",	    CIPHER_WARN },
-    { "arcfour",    CIPHER_ARCFOUR },
-    { "des",	    CIPHER_DES }
+static const struct keyvalwhere ciphernames[] = {
+    { "aes",        CIPHER_AES,             -1, -1 },
+    { "blowfish",   CIPHER_BLOWFISH,        -1, -1 },
+    { "3des",       CIPHER_3DES,            -1, -1 },
+    { "WARN",       CIPHER_WARN,            -1, -1 },
+    { "arcfour",    CIPHER_ARCFOUR,         -1, -1 },
+    { "des",        CIPHER_DES,             -1, -1 }
 };
 
-static const struct keyval kexnames[] = {
-    { "dh-gex-sha1",	    KEX_DHGEX },
-    { "dh-group14-sha1",    KEX_DHGROUP14 },
-    { "dh-group1-sha1",	    KEX_DHGROUP1 },
-    { "WARN",		    KEX_WARN }
+static const struct keyvalwhere kexnames[] = {
+    { "dh-gex-sha1",        KEX_DHGEX,      -1, -1 },
+    { "dh-group14-sha1",    KEX_DHGROUP14,  -1, -1 },
+    { "dh-group1-sha1",     KEX_DHGROUP1,   -1, -1 },
+    { "rsa",                KEX_RSA,        KEX_WARN, -1 },
+    { "WARN",               KEX_WARN,       -1, -1 }
 };
 
 /*
@@ -51,6 +47,52 @@
     "CS8",	"PARENB",   "PARODD",	NULL
 };
 
+/*
+ * Convenience functions to access the backends[] array
+ * (which is only present in tools that manage settings).
+ */
+
+Backend *backend_from_name(const char *name)
+{
+    Backend **p;
+    for (p = backends; *p != NULL; p++)
+	if (!strcmp((*p)->name, name))
+	    return *p;
+    return NULL;
+}
+
+Backend *backend_from_proto(int proto)
+{
+    Backend **p;
+    for (p = backends; *p != NULL; p++)
+	if ((*p)->protocol == proto)
+	    return *p;
+    return NULL;
+}
+
+int get_remote_username(Config *cfg, char *user, size_t len)
+{
+    if (*cfg->username) {
+	strncpy(user, cfg->username, len);
+	user[len-1] = '\0';
+    } else {
+	if (cfg->username_from_env) {
+	    /* Use local username. */
+	    char *luser = get_username();
+	    if (luser) {
+		strncpy(user, luser, len);
+		user[len-1] = '\0';
+		sfree(luser);
+	    } else {
+		*user = '\0';
+	    }
+	} else {
+	    *user = '\0';
+	}
+    }
+    return (*user != '\0');
+}
+
 static void gpps(void *handle, const char *name, const char *def,
 		 char *val, int len)
 {
@@ -146,7 +188,8 @@
     sfree(buf);
 }
 
-static int key2val(const struct keyval *mapping, int nmaps, char *key)
+static int key2val(const struct keyvalwhere *mapping,
+                   int nmaps, char *key)
 {
     int i;
     for (i = 0; i < nmaps; i++)
@@ -154,7 +197,8 @@
     return -1;
 }
 
-static const char *val2key(const struct keyval *mapping, int nmaps, int val)
+static const char *val2key(const struct keyvalwhere *mapping,
+                           int nmaps, int val)
 {
     int i;
     for (i = 0; i < nmaps; i++)
@@ -169,40 +213,80 @@
  * XXX: assumes vals in 'mapping' are small +ve integers
  */
 static void gprefs(void *sesskey, char *name, char *def,
-		   const struct keyval *mapping, int nvals,
+		   const struct keyvalwhere *mapping, int nvals,
 		   int *array)
 {
-    char commalist[80];
-    char *tokarg = commalist;
-    int n;
+    char commalist[256];
+    char *p, *q;
+    int i, j, n, v, pos;
     unsigned long seen = 0;	       /* bitmap for weeding dups etc */
+
+    /*
+     * Fetch the string which we'll parse as a comma-separated list.
+     */
     gpps(sesskey, name, def, commalist, sizeof(commalist));
 
-    /* Grotty parsing of commalist. */
+    /*
+     * Go through that list and convert it into values.
+     */
     n = 0;
-    do {
-	int v;
-	char *key;
-	key = strtok(tokarg, ","); /* sorry */
-	tokarg = NULL;
-	if (!key) break;
-	if (((v = key2val(mapping, nvals, key)) != -1) &&
-	    !(seen & 1<<v)) {
-	    array[n] = v;
-	    n++;
-	    seen |= 1<<v;
+    p = commalist;
+    while (1) {
+        while (*p && *p == ',') p++;
+        if (!*p)
+            break;                     /* no more words */
+
+        q = p;
+        while (*p && *p != ',') p++;
+        if (*p) *p++ = '\0';
+
+        v = key2val(mapping, nvals, q);
+        if (v != -1 && !(seen & (1 << v))) {
+	    seen |= (1 << v);
+	    array[n++] = v;
 	}
-    } while (n < nvals);
-    /* Add any missing values (backward compatibility ect). */
-    {
-	int i;
-	for (i = 0; i < nvals; i++) {
+    }
+
+    /*
+     * Now go through 'mapping' and add values that weren't mentioned
+     * in the list we fetched. We may have to loop over it multiple
+     * times so that we add values before other values whose default
+     * positions depend on them.
+     */
+    while (n < nvals) {
+        for (i = 0; i < nvals; i++) {
 	    assert(mapping[i].v < 32);
-	    if (!(seen & 1<<mapping[i].v)) {
-		array[n] = mapping[i].v;
-		n++;
-	    }
-	}
+
+	    if (!(seen & (1 << mapping[i].v))) {
+                /*
+                 * This element needs adding. But can we add it yet?
+                 */
+                if (mapping[i].vrel != -1 && !(seen & (1 << mapping[i].vrel)))
+                    continue;          /* nope */
+
+                /*
+                 * OK, we can work out where to add this element, so
+                 * do so.
+                 */
+                if (mapping[i].vrel == -1) {
+                    pos = (mapping[i].where < 0 ? n : 0);
+                } else {
+                    for (j = 0; j < n; j++)
+                        if (array[j] == mapping[i].vrel)
+                            break;
+                    assert(j < n);     /* implied by (seen & (1<<vrel)) */
+                    pos = (mapping[i].where < 0 ? j : j+1);
+                }
+
+                /*
+                 * And add it.
+                 */
+                for (j = n-1; j >= pos; j--)
+                    array[j+1] = array[j];
+                array[pos] = mapping[i].v;
+                n++;
+            }
+        }
     }
 }
 
@@ -210,25 +294,35 @@
  * Write out a preference list.
  */
 static void wprefs(void *sesskey, char *name,
-		   const struct keyval *mapping, int nvals,
+		   const struct keyvalwhere *mapping, int nvals,
 		   int *array)
 {
-    char buf[80] = "";	/* XXX assumed big enough */
-    int l = sizeof(buf)-1, i;
-    buf[l] = '\0';
-    for (i = 0; l > 0 && i < nvals; i++) {
+    char *buf, *p;
+    int i, maxlen;
+
+    for (maxlen = i = 0; i < nvals; i++) {
 	const char *s = val2key(mapping, nvals, array[i]);
 	if (s) {
-	    int sl = strlen(s);
-	    if (i > 0) {
-		strncat(buf, ",", l);
-		l--;
-	    }
-	    strncat(buf, s, l);
-	    l -= sl;
+            maxlen += (maxlen > 0 ? 1 : 0) + strlen(s);
+        }
+    }
+
+    buf = snewn(maxlen + 1, char);
+    p = buf;
+
+    for (i = 0; i < nvals; i++) {
+	const char *s = val2key(mapping, nvals, array[i]);
+	if (s) {
+            p += sprintf(p, "%s%s", (p > buf ? "," : ""), s);
 	}
     }
+
+    assert(p - buf == maxlen);
+    *p = '\0';
+
     write_setting_s(sesskey, name, buf);
+
+    sfree(buf);
 }
 
 char *save_settings(char *section, Config * cfg)
@@ -258,11 +352,11 @@
     write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass);
     write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata);
     p = "raw";
-    for (i = 0; backends[i].name != NULL; i++)
-	if (backends[i].protocol == cfg->protocol) {
-	    p = backends[i].name;
-	    break;
-	}
+    {
+	const Backend *b = backend_from_proto(cfg->protocol);
+	if (b)
+	    p = b->name;
+    }
     write_setting_s(sesskey, "Protocol", p);
     write_setting_i(sesskey, "PortNumber", cfg->port);
     /* The CloseOnExit numbers are arranged in a different order from
@@ -292,11 +386,13 @@
     write_setting_s(sesskey, "ProxyTelnetCommand", cfg->proxy_telnet_command);
     wmap(sesskey, "Environment", cfg->environmt, lenof(cfg->environmt));
     write_setting_s(sesskey, "UserName", cfg->username);
+    write_setting_i(sesskey, "UserNameFromEnvironment", cfg->username_from_env);
     write_setting_s(sesskey, "LocalUserName", cfg->localusername);
     write_setting_i(sesskey, "NoPTY", cfg->nopty);
     write_setting_i(sesskey, "Compression", cfg->compression);
     write_setting_i(sesskey, "TryAgent", cfg->tryagent);
     write_setting_i(sesskey, "AgentFwd", cfg->agentfwd);
+    write_setting_i(sesskey, "GssapiFwd", cfg->gssapifwd);
     write_setting_i(sesskey, "ChangeUsername", cfg->change_username);
     wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX,
 	   cfg->ssh_cipherlist);
@@ -304,10 +400,18 @@
     write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time);
     write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data);
     write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth);
+    write_setting_i(sesskey, "SshBanner", cfg->ssh_show_banner);
     write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth);
     write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth);
+    write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth);
+#ifndef NO_GSSAPI
+    wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs,
+	   cfg->ssh_gsslist);
+    write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom);
+#endif
     write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);
     write_setting_i(sesskey, "SshProt", cfg->sshprot);
+    write_setting_s(sesskey, "LogHost", cfg->loghost);
     write_setting_i(sesskey, "SSH2DES", cfg->ssh2_des_cbc);
     write_setting_filename(sesskey, "PublicKeyFile", cfg->keyfile);
     write_setting_s(sesskey, "RemoteCommand", cfg->remote_cmd);
@@ -364,6 +468,7 @@
     write_setting_i(sesskey, "DECOriginMode", cfg->dec_om);
     write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode);
     write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr);
+    write_setting_i(sesskey, "CRImpliesLF", cfg->crhaslf);
     write_setting_i(sesskey, "DisableArabicShaping", cfg->arabicshaping);
     write_setting_i(sesskey, "DisableBidi", cfg->bidi);
     write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always);
@@ -418,17 +523,20 @@
     write_setting_i(sesskey, "X11Forward", cfg->x11_forward);
     write_setting_s(sesskey, "X11Display", cfg->x11_display);
     write_setting_i(sesskey, "X11AuthType", cfg->x11_auth);
+    write_setting_filename(sesskey, "X11AuthFile", cfg->xauthfile);
     write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall);
     write_setting_i(sesskey, "RemotePortAcceptAll", cfg->rport_acceptall);
     wmap(sesskey, "PortForwardings", cfg->portfwd, lenof(cfg->portfwd));
     write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1);
     write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1);
     write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1);
+    write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2);
     write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2);
     write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2);
     write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);
     write_setting_i(sesskey, "BugPKSessID2", 2-cfg->sshbug_pksessid2);
     write_setting_i(sesskey, "BugRekey2", 2-cfg->sshbug_rekey2);
+    write_setting_i(sesskey, "BugMaxPkt2", 2-cfg->sshbug_maxpkt2);
     write_setting_i(sesskey, "StampUtmp", cfg->stamp_utmp);
     write_setting_i(sesskey, "LoginShell", cfg->login_shell);
     write_setting_i(sesskey, "ScrollbarOnLeft", cfg->scrollbar_on_left);
@@ -443,6 +551,7 @@
     write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits);
     write_setting_i(sesskey, "SerialParity", cfg->serparity);
     write_setting_i(sesskey, "SerialFlowControl", cfg->serflow);
+    write_setting_s(sesskey, "WindowClass", cfg->winclass);
 }
 
 void load_settings(char *section, Config * cfg)
@@ -452,6 +561,9 @@
     sesskey = open_settings_r(section);
     load_open_settings(sesskey, cfg);
     close_settings_r(sesskey);
+
+    if (cfg_launchable(cfg))
+        add_session_to_jumplist(section);
 }
 
 void load_open_settings(void *sesskey, Config *cfg)
@@ -475,12 +587,13 @@
     gpps(sesskey, "Protocol", "default", prot, 10);
     cfg->protocol = default_protocol;
     cfg->port = default_port;
-    for (i = 0; backends[i].name != NULL; i++)
-	if (!strcmp(prot, backends[i].name)) {
-	    cfg->protocol = backends[i].protocol;
+    {
+	const Backend *b = backend_from_name(prot);
+	if (b) {
+	    cfg->protocol = b->protocol;
 	    gppi(sesskey, "PortNumber", default_port, &cfg->port);
-	    break;
 	}
+    }
 
     /* Address family selection */
     gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily);
@@ -554,6 +667,7 @@
 	 cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command));
     gppmap(sesskey, "Environment", "", cfg->environmt, lenof(cfg->environmt));
     gpps(sesskey, "UserName", "", cfg->username, sizeof(cfg->username));
+    gppi(sesskey, "UserNameFromEnvironment", 0, &cfg->username_from_env);
     gpps(sesskey, "LocalUserName", "", cfg->localusername,
 	 sizeof(cfg->localusername));
     gppi(sesskey, "NoPTY", 0, &cfg->nopty);
@@ -561,6 +675,7 @@
     gppi(sesskey, "TryAgent", 1, &cfg->tryagent);
     gppi(sesskey, "AgentFwd", 0, &cfg->agentfwd);
     gppi(sesskey, "ChangeUsername", 0, &cfg->change_username);
+    gppi(sesskey, "GssapiFwd", 0, &cfg->gssapifwd);
     gprefs(sesskey, "Cipher", "\0",
 	   ciphernames, CIPHER_MAX, cfg->ssh_cipherlist);
     {
@@ -571,9 +686,9 @@
 	char *default_kexes;
 	gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;
 	if (i == FORCE_ON)
-	    default_kexes = "dh-group14-sha1,dh-group1-sha1,WARN,dh-gex-sha1";
+	    default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";
 	else
-	    default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,WARN";
+	    default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";
 	gprefs(sesskey, "KEX", default_kexes,
 	       kexnames, KEX_MAX, cfg->ssh_kexlist);
     }
@@ -581,10 +696,18 @@
     gpps(sesskey, "RekeyBytes", "1G", cfg->ssh_rekey_data,
 	 sizeof(cfg->ssh_rekey_data));
     gppi(sesskey, "SshProt", 2, &cfg->sshprot);
+    gpps(sesskey, "LogHost", "", cfg->loghost, sizeof(cfg->loghost));
     gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc);
     gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth);
+    gppi(sesskey, "SshBanner", 1, &cfg->ssh_show_banner);
     gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);
     gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth);
+    gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth);
+#ifndef NO_GSSAPI
+    gprefs(sesskey, "GSSLibs", "\0",
+	   gsslibkeywords, ngsslibs, cfg->ssh_gsslist);
+    gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom);
+#endif
     gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell);
     gppfile(sesskey, "PublicKeyFile", &cfg->keyfile);
     gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd,
@@ -640,13 +763,21 @@
     gppfile(sesskey, "BellWaveFile", &cfg->bell_wavefile);
     gppi(sesskey, "BellOverload", 1, &cfg->bellovl);
     gppi(sesskey, "BellOverloadN", 5, &cfg->bellovl_n);
-    gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC, &i);
+    gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC
+#ifdef PUTTY_UNIX_H
+				   *1000
+#endif
+				   , &i);
     cfg->bellovl_t = i
 #ifdef PUTTY_UNIX_H
 		    / 1000
 #endif
 	;
-    gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC, &i);
+    gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC
+#ifdef PUTTY_UNIX_H
+				   *1000
+#endif
+				   , &i);
     cfg->bellovl_s = i
 #ifdef PUTTY_UNIX_H
 		    / 1000
@@ -656,6 +787,7 @@
     gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om);
     gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);
     gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr);
+    gppi(sesskey, "CRImpliesLF", 0, &cfg->crhaslf);
     gppi(sesskey, "DisableArabicShaping", 0, &cfg->arabicshaping);
     gppi(sesskey, "DisableBidi", 0, &cfg->bidi);
     gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always);
@@ -741,6 +873,7 @@
     gpps(sesskey, "X11Display", "", cfg->x11_display,
 	 sizeof(cfg->x11_display));
     gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth);
+    gppfile(sesskey, "X11AuthFile", &cfg->xauthfile);
 
     gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall);
     gppi(sesskey, "RemotePortAcceptAll", 0, &cfg->rport_acceptall);
@@ -748,6 +881,7 @@
     gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;
     gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;
     gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;
+    gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i;
     {
 	int i;
 	gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
@@ -761,6 +895,8 @@
     gppi(sesskey, "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i;
     gppi(sesskey, "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i;
     gppi(sesskey, "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i;
+    gppi(sesskey, "BugMaxPkt2", 0, &i); cfg->sshbug_maxpkt2 = 2-i;
+    cfg->ssh_simple = FALSE;
     gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp);
     gppi(sesskey, "LoginShell", 1, &cfg->login_shell);
     gppi(sesskey, "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left);
@@ -775,6 +911,7 @@
     gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits);
     gppi(sesskey, "SerialParity", SER_PAR_NONE, &cfg->serparity);
     gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow);
+    gpps(sesskey, "WindowClass", "", cfg->winclass, sizeof(cfg->winclass));
 }
 
 void do_defaults(char *session, Config * cfg)

Modified: trunk/Tools/GMEplink/SSH.C
==============================================================================
--- trunk/Tools/GMEplink/SSH.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSH.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -12,6 +12,10 @@
 #include "putty.h"
 #include "tree234.h"
 #include "ssh.h"
+#ifndef NO_GSSAPI
+#include "sshgssc.h"
+#include "sshgss.h"
+#endif
 
 #ifndef FALSE
 #define FALSE 0
@@ -62,6 +66,10 @@
 #define SSH1_SMSG_AUTH_CCARD_CHALLENGE            71	/* 0x47 */
 #define SSH1_CMSG_AUTH_CCARD_RESPONSE             72	/* 0x48 */
 
+#define SSH1_AUTH_RHOSTS                          1	/* 0x1 */
+#define SSH1_AUTH_RSA                             2	/* 0x2 */
+#define SSH1_AUTH_PASSWORD                        3	/* 0x3 */
+#define SSH1_AUTH_RHOSTS_RSA                      4	/* 0x4 */
 #define SSH1_AUTH_TIS                             5	/* 0x5 */
 #define SSH1_AUTH_CCARD                           16	/* 0x10 */
 
@@ -83,6 +91,9 @@
 #define SSH2_MSG_KEX_DH_GEX_GROUP                 31	/* 0x1f */
 #define SSH2_MSG_KEX_DH_GEX_INIT                  32	/* 0x20 */
 #define SSH2_MSG_KEX_DH_GEX_REPLY                 33	/* 0x21 */
+#define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
+#define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
+#define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
 #define SSH2_MSG_USERAUTH_REQUEST                 50	/* 0x32 */
 #define SSH2_MSG_USERAUTH_FAILURE                 51	/* 0x33 */
 #define SSH2_MSG_USERAUTH_SUCCESS                 52	/* 0x34 */
@@ -105,18 +116,30 @@
 #define SSH2_MSG_CHANNEL_REQUEST                  98	/* 0x62 */
 #define SSH2_MSG_CHANNEL_SUCCESS                  99	/* 0x63 */
 #define SSH2_MSG_CHANNEL_FAILURE                  100	/* 0x64 */
+#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE               60
+#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN                  61
+#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE      63
+#define SSH2_MSG_USERAUTH_GSSAPI_ERROR                  64
+#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK                 65
+#define SSH2_MSG_USERAUTH_GSSAPI_MIC                    66
 
 /*
  * Packet type contexts, so that ssh2_pkt_type can correctly decode
  * the ambiguous type numbers back into the correct type strings.
  */
-#define SSH2_PKTCTX_DHGROUP          0x0001
-#define SSH2_PKTCTX_DHGEX            0x0002
-#define SSH2_PKTCTX_KEX_MASK         0x000F
-#define SSH2_PKTCTX_PUBLICKEY        0x0010
-#define SSH2_PKTCTX_PASSWORD         0x0020
-#define SSH2_PKTCTX_KBDINTER         0x0040
-#define SSH2_PKTCTX_AUTH_MASK        0x00F0
+typedef enum {
+    SSH2_PKTCTX_NOKEX,
+    SSH2_PKTCTX_DHGROUP,
+    SSH2_PKTCTX_DHGEX,
+    SSH2_PKTCTX_RSAKEX
+} Pkt_KCtx;
+typedef enum {
+    SSH2_PKTCTX_NOAUTH,
+    SSH2_PKTCTX_PUBLICKEY,
+    SSH2_PKTCTX_PASSWORD,
+    SSH2_PKTCTX_GSSAPI,
+    SSH2_PKTCTX_KBDINTER
+} Pkt_ACtx;
 
 #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1	/* 0x1 */
 #define SSH2_DISCONNECT_PROTOCOL_ERROR            2	/* 0x2 */
@@ -171,11 +194,13 @@
 #define BUG_SSH2_DERIVEKEY                       32
 #define BUG_SSH2_REKEY                           64
 #define BUG_SSH2_PK_SESSIONID                   128
+#define BUG_SSH2_MAXPKT				256
+#define BUG_CHOKES_ON_SSH2_IGNORE               512
 
 /*
  * Codes for terminal modes.
  * Most of these are the same in SSH-1 and SSH-2.
- * This list is derived from draft-ietf-secsh-connect-25 and
+ * This list is derived from RFC 4254 and
  * SSH-1 RFC-1.2.31.
  */
 static const struct {
@@ -277,7 +302,8 @@
 }
 
 #define translate(x) if (type == x) return #x
-#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
+#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
+#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
 static char *ssh1_pkt_type(int type)
 {
     translate(SSH1_MSG_DISCONNECT);
@@ -323,8 +349,14 @@
     translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
     return "unknown";
 }
-static char *ssh2_pkt_type(int pkt_ctx, int type)
+static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
 {
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);
     translate(SSH2_MSG_DISCONNECT);
     translate(SSH2_MSG_IGNORE);
     translate(SSH2_MSG_UNIMPLEMENTED);
@@ -333,20 +365,23 @@
     translate(SSH2_MSG_SERVICE_ACCEPT);
     translate(SSH2_MSG_KEXINIT);
     translate(SSH2_MSG_NEWKEYS);
-    translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
-    translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
-    translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
-    translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
-    translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
-    translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
+    translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
+    translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
     translate(SSH2_MSG_USERAUTH_REQUEST);
     translate(SSH2_MSG_USERAUTH_FAILURE);
     translate(SSH2_MSG_USERAUTH_SUCCESS);
     translate(SSH2_MSG_USERAUTH_BANNER);
-    translatec(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
-    translatec(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
-    translatec(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
-    translatec(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
+    translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
+    translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
+    translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
+    translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
     translate(SSH2_MSG_GLOBAL_REQUEST);
     translate(SSH2_MSG_REQUEST_SUCCESS);
     translate(SSH2_MSG_REQUEST_FAILURE);
@@ -458,12 +493,27 @@
  * 
  *  - OUR_V2_WINSIZE is the maximum window size we present on SSH-2
  *    channels.
+ *
+ *  - OUR_V2_BIGWIN is the window size we advertise for the only
+ *    channel in a simple connection.  It must be <= INT_MAX.
+ *
+ *  - OUR_V2_MAXPKT is the official "maximum packet size" we send
+ *    to the remote side. This actually has nothing to do with the
+ *    size of the _packet_, but is instead a limit on the amount
+ *    of data we're willing to receive in a single SSH2 channel
+ *    data message.
+ *
+ *  - OUR_V2_PACKETLIMIT is actually the maximum size of SSH
+ *    _packet_ we're prepared to cope with.  It must be a multiple
+ *    of the cipher block size, and must be at least 35000.
  */
 
 #define SSH1_BUFFER_LIMIT 32768
 #define SSH_MAX_BACKLOG 32768
 #define OUR_V2_WINSIZE 16384
+#define OUR_V2_BIGWIN 0x7fffffff
 #define OUR_V2_MAXPKT 0x4000UL
+#define OUR_V2_PACKETLIMIT 0x9000UL
 
 /* Maximum length of passwords/passphrases (arbitrary) */
 #define SSH_MAX_PASSWORD_LEN 100
@@ -494,7 +544,7 @@
     return 0;
 }
 const static struct ssh_compress ssh_comp_none = {
-    "none",
+    "none", NULL,
     ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
     ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
     ssh_comp_none_disable, NULL
@@ -513,6 +563,14 @@
 };
 
 /*
+ * little structure to keep track of outstanding WINDOW_ADJUSTs
+ */
+struct winadj {
+    struct winadj *next;
+    unsigned size;
+};
+
+/*
  * 2-3-4 tree storing channels.
  */
 struct ssh_channel {
@@ -532,14 +590,40 @@
      * A channel is completely finished with when all four bits are set.
      */
     int closes;
+
+    /*
+     * This flag indicates that a close is pending on the outgoing
+     * side of the channel: that is, wherever we're getting the data
+     * for this channel has sent us some data followed by EOF. We
+     * can't actually close the channel until we've finished sending
+     * the data, so we set this flag instead to remind us to
+     * initiate the closing process once our buffer is clear.
+     */
+    int pending_close;
+
+    /*
+     * True if this channel is causing the underlying connection to be
+     * throttled.
+     */
+    int throttling_conn;
     union {
-	struct ssh1_data_channel {
-	    int throttling;
-	} v1;
 	struct ssh2_data_channel {
 	    bufchain outbuffer;
 	    unsigned remwindow, remmaxpkt;
-	    unsigned locwindow;
+	    /* locwindow is signed so we can cope with excess data. */
+	    int locwindow, locmaxwin;
+	    /*
+	     * remlocwin is the amount of local window that we think
+	     * the remote end had available to it after it sent the
+	     * last data packet or window adjust ack.
+	     */
+	    int remlocwin;
+	    /*
+	     * These store the list of window adjusts that haven't
+	     * been acked.
+	     */
+	    struct winadj *winadj_head, *winadj_tail;
+	    enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;
 	} v2;
     } v;
     union {
@@ -646,7 +730,7 @@
 static int ssh2_try_send(struct ssh_channel *c);
 static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
 static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
-static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
+static void ssh2_set_window(struct ssh_channel *c, int newwin);
 static int ssh_sendbuffer(void *handle);
 static int ssh_do_close(Ssh ssh, int notify_exit);
 static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
@@ -763,12 +847,13 @@
 
     bufchain banner;	/* accumulates banners during do_ssh2_authconn */
 
-    int pkt_ctx;
+    Pkt_KCtx pkt_kctx;
+    Pkt_ACtx pkt_actx;
 
-    void *x11auth;
+    struct X11Display *x11disp;
 
     int version;
-    int v1_throttle_count;
+    int conn_throttle_count;
     int overall_bufsize;
     int throttled_all;
     int v1_stdout_throttling;
@@ -851,6 +936,18 @@
     int kex_in_progress;
     long next_rekey, last_rekey;
     char *deferred_rekey_reason;    /* points to STATIC string; don't free */
+
+    /*
+     * Fully qualified host name, which we need if doing GSSAPI.
+     */
+    char *fullhostname;
+
+#ifndef NO_GSSAPI
+    /*
+     * GSSAPI libraries for this session.
+     */
+    struct ssh_gss_liblist *gsslibs;
+#endif
 };
 
 #define logevent(s) logevent(ssh->frontend, s)
@@ -1208,9 +1305,9 @@
 	    /* "Session data" packets - omit the data field */
 	    if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||
 		(st->pktin->type == SSH1_SMSG_STDERR_DATA)) {
-		do_blank = TRUE; blank_prefix = 0;
-	    } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {
 		do_blank = TRUE; blank_prefix = 4;
+	    } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {
+		do_blank = TRUE; blank_prefix = 8;
 	    }
 	    if (do_blank) {
 		blank.offset = blank_prefix;
@@ -1223,7 +1320,7 @@
 		   PKT_INCOMING, st->pktin->type,
 		   ssh1_pkt_type(st->pktin->type),
 		   st->pktin->body, st->pktin->length,
-		   nblanks, &blank);
+		   nblanks, &blank, NULL);
     }
 
     crFinish(st->pktin);
@@ -1245,91 +1342,163 @@
 	st->cipherblk = 8;
     if (st->cipherblk < 8)
 	st->cipherblk = 8;
+    st->maclen = ssh->scmac ? ssh->scmac->len : 0;
 
-    st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
+    if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) &&
+	ssh->scmac) {
+	/*
+	 * When dealing with a CBC-mode cipher, we want to avoid the
+	 * possibility of an attacker's tweaking the ciphertext stream
+	 * so as to cause us to feed the same block to the block
+	 * cipher more than once and thus leak information
+	 * (VU#958563).  The way we do this is not to take any
+	 * decisions on the basis of anything we've decrypted until
+	 * we've verified it with a MAC.  That includes the packet
+	 * length, so we just read data and check the MAC repeatedly,
+	 * and when the MAC passes, see if the length we've got is
+	 * plausible.
+	 */
+
+	/* May as well allocate the whole lot now. */
+	st->pktin->data = snewn(OUR_V2_PACKETLIMIT + st->maclen + APIEXTRA,
+				unsigned char);
+
+	/* Read an amount corresponding to the MAC. */
+	for (st->i = 0; st->i < st->maclen; st->i++) {
+	    while ((*datalen) == 0)
+		crReturn(NULL);
+	    st->pktin->data[st->i] = *(*data)++;
+	    (*datalen)--;
+	}
 
-    /*
-     * Acquire and decrypt the first block of the packet. This will
-     * contain the length and padding details.
-     */
-    for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
-	while ((*datalen) == 0)
-	    crReturn(NULL);
-	st->pktin->data[st->i] = *(*data)++;
-	(*datalen)--;
-    }
+	st->packetlen = 0;
+	{
+	    unsigned char seq[4];
+	    ssh->scmac->start(ssh->sc_mac_ctx);
+	    PUT_32BIT(seq, st->incoming_sequence);
+	    ssh->scmac->bytes(ssh->sc_mac_ctx, seq, 4);
+	}
+
+	for (;;) { /* Once around this loop per cipher block. */
+	    /* Read another cipher-block's worth, and tack it onto the end. */
+	    for (st->i = 0; st->i < st->cipherblk; st->i++) {
+		while ((*datalen) == 0)
+		    crReturn(NULL);
+		st->pktin->data[st->packetlen+st->maclen+st->i] = *(*data)++;
+		(*datalen)--;
+	    }
+	    /* Decrypt one more block (a little further back in the stream). */
+	    ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+				   st->pktin->data + st->packetlen,
+				   st->cipherblk);
+	    /* Feed that block to the MAC. */
+	    ssh->scmac->bytes(ssh->sc_mac_ctx,
+			      st->pktin->data + st->packetlen, st->cipherblk);
+	    st->packetlen += st->cipherblk;
+	    /* See if that gives us a valid packet. */
+	    if (ssh->scmac->verresult(ssh->sc_mac_ctx,
+				      st->pktin->data + st->packetlen) &&
+		(st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen)
+		    break;
+	    if (st->packetlen >= OUR_V2_PACKETLIMIT) {
+		bombout(("No valid incoming packet found"));
+		ssh_free_packet(st->pktin);
+		crStop(NULL);
+	    }	    
+	}
+	st->pktin->maxlen = st->packetlen + st->maclen;
+	st->pktin->data = sresize(st->pktin->data,
+				  st->pktin->maxlen + APIEXTRA,
+				  unsigned char);
+    } else {
+	st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
 
-    if (ssh->sccipher)
-	ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
-			       st->pktin->data, st->cipherblk);
+	/*
+	 * Acquire and decrypt the first block of the packet. This will
+	 * contain the length and padding details.
+	 */
+	for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
+	    while ((*datalen) == 0)
+		crReturn(NULL);
+	    st->pktin->data[st->i] = *(*data)++;
+	    (*datalen)--;
+	}
 
-    /*
-     * Now get the length and padding figures.
-     */
-    st->len = GET_32BIT(st->pktin->data);
-    st->pad = st->pktin->data[4];
+	if (ssh->sccipher)
+	    ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+				   st->pktin->data, st->cipherblk);
 
-    /*
-     * _Completely_ silly lengths should be stomped on before they
-     * do us any more damage.
-     */
-    if (st->len < 0 || st->len > 35000 || st->pad < 4 ||
-	st->len - st->pad < 1 || (st->len + 4) % st->cipherblk != 0) {
-	bombout(("Incoming packet was garbled on decryption"));
+	/*
+	 * Now get the length figure.
+	 */
+	st->len = GET_32BIT(st->pktin->data);
+
+	/*
+	 * _Completely_ silly lengths should be stomped on before they
+	 * do us any more damage.
+	 */
+	if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
+	    (st->len + 4) % st->cipherblk != 0) {
+	    bombout(("Incoming packet was garbled on decryption"));
+	    ssh_free_packet(st->pktin);
+	    crStop(NULL);
+	}
+
+	/*
+	 * So now we can work out the total packet length.
+	 */
+	st->packetlen = st->len + 4;
+
+	/*
+	 * Allocate memory for the rest of the packet.
+	 */
+	st->pktin->maxlen = st->packetlen + st->maclen;
+	st->pktin->data = sresize(st->pktin->data,
+				  st->pktin->maxlen + APIEXTRA,
+				  unsigned char);
+
+	/*
+	 * Read and decrypt the remainder of the packet.
+	 */
+	for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
+	     st->i++) {
+	    while ((*datalen) == 0)
+		crReturn(NULL);
+	    st->pktin->data[st->i] = *(*data)++;
+	    (*datalen)--;
+	}
+	/* Decrypt everything _except_ the MAC. */
+	if (ssh->sccipher)
+	    ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+				   st->pktin->data + st->cipherblk,
+				   st->packetlen - st->cipherblk);
+
+	/*
+	 * Check the MAC.
+	 */
+	if (ssh->scmac
+	    && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,
+				   st->len + 4, st->incoming_sequence)) {
+	    bombout(("Incorrect MAC received on packet"));
+	    ssh_free_packet(st->pktin);
+	    crStop(NULL);
+	}
+    }
+    /* Get and sanity-check the amount of random padding. */
+    st->pad = st->pktin->data[4];
+    if (st->pad < 4 || st->len - st->pad < 1) {
+	bombout(("Invalid padding length on received packet"));
 	ssh_free_packet(st->pktin);
 	crStop(NULL);
     }
-
     /*
      * This enables us to deduce the payload length.
      */
     st->payload = st->len - st->pad - 1;
 
     st->pktin->length = st->payload + 5;
-
-    /*
-     * So now we can work out the total packet length.
-     */
-    st->packetlen = st->len + 4;
-    st->maclen = ssh->scmac ? ssh->scmac->len : 0;
-
-    /*
-     * Allocate memory for the rest of the packet.
-     */
-    st->pktin->maxlen = st->packetlen + st->maclen;
-    st->pktin->data = sresize(st->pktin->data,
-			      st->pktin->maxlen + APIEXTRA,
-			      unsigned char);
-
-    /*
-     * Read and decrypt the remainder of the packet.
-     */
-    for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
-	 st->i++) {
-	while ((*datalen) == 0)
-	    crReturn(NULL);
-	st->pktin->data[st->i] = *(*data)++;
-	(*datalen)--;
-    }
-    /* Decrypt everything _except_ the MAC. */
-    if (ssh->sccipher)
-	ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
-			       st->pktin->data + st->cipherblk,
-			       st->packetlen - st->cipherblk);
-
     st->pktin->encrypted_len = st->packetlen;
 
-    /*
-     * Check the MAC.
-     */
-    if (ssh->scmac
-	&& !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data, st->len + 4,
-			       st->incoming_sequence)) {
-	bombout(("Incorrect MAC received on packet"));
-	ssh_free_packet(st->pktin);
-	crStop(NULL);
-    }
-
     st->pktin->sequence = st->incoming_sequence++;
 
     /*
@@ -1368,9 +1537,9 @@
 	    int do_blank = FALSE, blank_prefix = 0;
 	    /* "Session data" packets - omit the data field */
 	    if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) {
-		do_blank = TRUE; blank_prefix = 4;
-	    } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
 		do_blank = TRUE; blank_prefix = 8;
+	    } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
+		do_blank = TRUE; blank_prefix = 12;
 	    }
 	    if (do_blank) {
 		blank.offset = blank_prefix;
@@ -1380,9 +1549,10 @@
 	    }
 	}
 	log_packet(ssh->logctx, PKT_INCOMING, st->pktin->type,
-		   ssh2_pkt_type(ssh->pkt_ctx, st->pktin->type),
+		   ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
+				 st->pktin->type),
 		   st->pktin->data+6, st->pktin->length-6,
-		   nblanks, &blank);
+		   nblanks, &blank, &st->pktin->sequence);
     }
 
     crFinish(st->pktin);
@@ -1407,7 +1577,7 @@
 	log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
 		   ssh1_pkt_type(pkt->data[12]),
 		   pkt->body, pkt->length - (pkt->body - pkt->data),
-		   pkt->nblanks, pkt->blanks);
+		   pkt->nblanks, pkt->blanks, NULL);
     sfree(pkt->blanks); pkt->blanks = NULL;
     pkt->nblanks = 0;
 
@@ -1417,6 +1587,7 @@
 	zlib_compress_block(ssh->cs_comp_ctx,
 			    pkt->data + 12, pkt->length - 12,
 			    &compblk, &complen);
+	ssh_pkt_ensure(pkt, complen + 2);   /* just in case it's got bigger */
 	memcpy(pkt->data + 12, compblk, complen);
 	sfree(compblk);
 	pkt->length = complen + 12;
@@ -1445,7 +1616,9 @@
 
 static int s_write(Ssh ssh, void *data, int len)
 {
-    log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
+    if (ssh->logctx)
+	log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len,
+		   0, NULL, NULL);
     return sk_write(ssh->s, (char *)data, len);
 }
 
@@ -1726,9 +1899,9 @@
 
     if (ssh->logctx)
 	log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
-		   ssh2_pkt_type(ssh->pkt_ctx, pkt->data[5]),
+		   ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
 		   pkt->body, pkt->length - (pkt->body - pkt->data),
-		   pkt->nblanks, pkt->blanks);
+		   pkt->nblanks, pkt->blanks, &ssh->v2_outgoing_sequence);
     sfree(pkt->blanks); pkt->blanks = NULL;
     pkt->nblanks = 0;
 
@@ -1858,7 +2031,8 @@
 {
     int len;
     if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
-	ssh->deferred_len == 0 && !noignore) {
+	ssh->deferred_len == 0 && !noignore &&
+	!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
 	/*
 	 * Interpose an SSH_MSG_IGNORE to ensure that user data don't
 	 * get encrypted with a known IV.
@@ -1988,7 +2162,8 @@
 	 * unavailable, we don't do this trick at all, because we
 	 * gain nothing by it.)
 	 */
-	if (ssh->cscipher) {
+	if (ssh->cscipher &&
+	    !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
 	    int stringlen, i;
 
 	    stringlen = (256 - ssh->deferred_len);
@@ -2344,6 +2519,26 @@
 	ssh->remote_bugs |= BUG_SSH2_REKEY;
 	logevent("We believe remote version has SSH-2 rekey bug");
     }
+
+    if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON ||
+	(ssh->cfg.sshbug_maxpkt2 == AUTO &&
+	 (wc_match("1.36_sshlib GlobalSCAPE", imp) ||
+          wc_match("1.36 sshlib: GlobalScape", imp)))) {
+	/*
+	 * This version ignores our makpkt and needs to be throttled.
+	 */
+	ssh->remote_bugs |= BUG_SSH2_MAXPKT;
+	logevent("We believe remote version ignores SSH-2 maximum packet size");
+    }
+
+    if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+	/*
+	 * Servers that don't support SSH2_MSG_IGNORE. Currently,
+	 * none detected automatically.
+	 */
+	ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
+	logevent("We believe remote version has SSH-2 ignore bug");
+    }
 }
 
 /*
@@ -2369,6 +2564,47 @@
     }
 }
 
+/*
+ * Send an appropriate SSH version string.
+ */
+static void ssh_send_verstring(Ssh ssh, char *svers)
+{
+    char *verstring;
+
+    if (ssh->version == 2) {
+	/*
+	 * Construct a v2 version string.
+	 */
+	verstring = dupprintf("SSH-2.0-%s\015\012", sshver);
+    } else {
+	/*
+	 * Construct a v1 version string.
+	 */
+	verstring = dupprintf("SSH-%s-%s\012",
+			      (ssh_versioncmp(svers, "1.5") <= 0 ?
+			       svers : "1.5"),
+			      sshver);
+    }
+
+    ssh_fix_verstring(verstring);
+
+    if (ssh->version == 2) {
+	size_t len;
+	/*
+	 * Record our version string.
+	 */
+	len = strcspn(verstring, "\015\012");
+	ssh->v_c = snewn(len + 1, char);
+	memcpy(ssh->v_c, verstring, len);
+	ssh->v_c[len] = 0;
+    }
+
+    logeventf(ssh, "We claim version: %.*s",
+	      strcspn(verstring, "\015\012"), verstring);
+    s_write(ssh, verstring, strlen(verstring));
+    sfree(verstring);
+}
+
 static int do_ssh_init(Ssh ssh, unsigned char c)
 {
     struct do_ssh_init_state {
@@ -2447,65 +2683,43 @@
 	crStop(0);
     }
 
-    {
-        char *verstring;
+    if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1))
+	ssh->version = 2;
+    else
+	ssh->version = 1;
 
-        if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) {
-            /*
-             * Construct a v2 version string.
-             */
-            verstring = dupprintf("SSH-2.0-%s\015\012", sshver);
-            ssh->version = 2;
-        } else {
-            /*
-             * Construct a v1 version string.
-             */
-            verstring = dupprintf("SSH-%s-%s\012",
-                                  (ssh_versioncmp(s->version, "1.5") <= 0 ?
-                                   s->version : "1.5"),
-                                  sshver);
-            ssh->version = 1;
-        }
+    logeventf(ssh, "Using SSH protocol version %d", ssh->version);
 
-        ssh_fix_verstring(verstring);
+    /* Send the version string, if we haven't already */
+    if (ssh->cfg.sshprot != 3)
+	ssh_send_verstring(ssh, s->version);
 
-        if (ssh->version == 2) {
-	    size_t len;
-            /*
-             * Hash our version string and their version string.
-             */
-	    len = strcspn(verstring, "\015\012");
-	    ssh->v_c = snewn(len + 1, char);
-	    memcpy(ssh->v_c, verstring, len);
-	    ssh->v_c[len] = 0;
-	    len = strcspn(s->vstring, "\015\012");
-	    ssh->v_s = snewn(len + 1, char);
-	    memcpy(ssh->v_s, s->vstring, len);
-	    ssh->v_s[len] = 0;
+    if (ssh->version == 2) {
+	size_t len;
+	/*
+	 * Record their version string.
+	 */
+	len = strcspn(s->vstring, "\015\012");
+	ssh->v_s = snewn(len + 1, char);
+	memcpy(ssh->v_s, s->vstring, len);
+	ssh->v_s[len] = 0;
 	    
-            /*
-             * Initialise SSH-2 protocol.
-             */
-            ssh->protocol = ssh2_protocol;
-            ssh2_protocol_setup(ssh);
-            ssh->s_rdpkt = ssh2_rdpkt;
-        } else {
-            /*
-             * Initialise SSH-1 protocol.
-             */
-            ssh->protocol = ssh1_protocol;
-            ssh1_protocol_setup(ssh);
-            ssh->s_rdpkt = ssh1_rdpkt;
-        }
-        logeventf(ssh, "We claim version: %.*s",
-                  strcspn(verstring, "\015\012"), verstring);
-	s_write(ssh, verstring, strlen(verstring));
-        sfree(verstring);
-	if (ssh->version == 2)
-	    do_ssh2_transport(ssh, NULL, -1, NULL);
+	/*
+	 * Initialise SSH-2 protocol.
+	 */
+	ssh->protocol = ssh2_protocol;
+	ssh2_protocol_setup(ssh);
+	ssh->s_rdpkt = ssh2_rdpkt;
+    } else {
+	/*
+	 * Initialise SSH-1 protocol.
+	 */
+	ssh->protocol = ssh1_protocol;
+	ssh1_protocol_setup(ssh);
+	ssh->s_rdpkt = ssh1_rdpkt;
     }
-
-    logeventf(ssh, "Using SSH protocol version %d", ssh->version);
+    if (ssh->version == 2)
+	do_ssh2_transport(ssh, NULL, -1, NULL);
 
     update_specials_menu(ssh->frontend);
     ssh->state = SSH_STATE_BEFORE_SIZE;
@@ -2565,7 +2779,9 @@
 static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
 {
     /* Log raw data, if we're in that mode. */
-    log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL);
+    if (ssh->logctx)
+	log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen,
+		   0, NULL, NULL);
 
     crBegin(ssh->ssh_gotdata_crstate);
 
@@ -2646,6 +2862,7 @@
 		x11_close(c->u.x11.s);
 		break;
 	      case CHAN_SOCKDATA:
+	      case CHAN_SOCKDATA_DORMANT:
 		pfd_close(c->u.pfd.s);
 		break;
 	    }
@@ -2668,6 +2885,8 @@
 	    del234(ssh->portfwds, pf); /* moving next one to index 0 */
 	    free_portfwd(pf);
 	}
+	freetree234(ssh->portfwds);
+	ssh->portfwds = NULL;
     }
 
     return ret;
@@ -2758,12 +2977,30 @@
     SockAddr addr;
     const char *err;
 
-    ssh->savedhost = snewn(1 + strlen(host), char);
-    strcpy(ssh->savedhost, host);
+    if (*ssh->cfg.loghost) {
+	char *colon;
 
-    if (port < 0)
-	port = 22;		       /* default ssh port */
-    ssh->savedport = port;
+	ssh->savedhost = dupstr(ssh->cfg.loghost);
+	ssh->savedport = 22;	       /* default ssh port */
+
+	/*
+	 * A colon suffix on savedhost also lets us affect
+	 * savedport.
+	 * 
+	 * (FIXME: do something about IPv6 address literals here.)
+	 */
+	colon = strrchr(ssh->savedhost, ':');
+	if (colon) {
+	    *colon++ = '\0';
+	    if (*colon)
+		ssh->savedport = atoi(colon);
+	}
+    } else {
+	ssh->savedhost = dupstr(host);
+	if (port < 0)
+	    port = 22;		       /* default ssh port */
+	ssh->savedport = port;
+    }
 
     /*
      * Try to find host.
@@ -2777,6 +3014,7 @@
 	sk_addr_free(addr);
 	return err;
     }
+    ssh->fullhostname = dupstr(*realhost);   /* save in case of GSSAPI */
 
     /*
      * Open socket.
@@ -2790,20 +3028,39 @@
 	return err;
     }
 
+    /*
+     * If the SSH version number's fixed, set it now, and if it's SSH-2,
+     * send the version string too.
+     */
+    if (ssh->cfg.sshprot == 0)
+	ssh->version = 1;
+    if (ssh->cfg.sshprot == 3) {
+	ssh->version = 2;
+	ssh_send_verstring(ssh, NULL);
+    }
+
+    /*
+     * loghost, if configured, overrides realhost.
+     */
+    if (*ssh->cfg.loghost) {
+	sfree(*realhost);
+	*realhost = dupstr(ssh->cfg.loghost);
+    }
+
     return NULL;
 }
 
 /*
  * Throttle or unthrottle the SSH connection.
  */
-static void ssh1_throttle(Ssh ssh, int adjust)
+static void ssh_throttle_conn(Ssh ssh, int adjust)
 {
-    int old_count = ssh->v1_throttle_count;
-    ssh->v1_throttle_count += adjust;
-    assert(ssh->v1_throttle_count >= 0);
-    if (ssh->v1_throttle_count && !old_count) {
+    int old_count = ssh->conn_throttle_count;
+    ssh->conn_throttle_count += adjust;
+    assert(ssh->conn_throttle_count >= 0);
+    if (ssh->conn_throttle_count && !old_count) {
 	ssh_set_frozen(ssh, 1);
-    } else if (!ssh->v1_throttle_count && old_count) {
+    } else if (!ssh->conn_throttle_count && old_count) {
 	ssh_set_frozen(ssh, 0);
     }
 }
@@ -2891,8 +3148,8 @@
     } else {
 	send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
 		    PKT_INT, c->remoteid,
-		    PKTT_DATA,
 		    PKT_INT, replylen,
+		    PKTT_DATA,
 		    PKT_DATA, sentreply, replylen,
 		    PKTT_OTHER,
 		    PKT_END);
@@ -3013,6 +3270,8 @@
     ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin);
     s->supported_ciphers_mask = ssh_pkt_getuint32(pktin);
     s->supported_auths_mask = ssh_pkt_getuint32(pktin);
+    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA))
+	s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA);
 
     ssh->v1_local_protoflags =
 	ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
@@ -3221,7 +3480,8 @@
 
     fflush(stdout); /* FIXME eh? */
     {
-	if (!*ssh->cfg.username) {
+	if (!get_remote_username(&ssh->cfg, s->username,
+				 sizeof(s->username))) {
 	    int ret; /* need not be kept over crReturn */
 	    s->cur_prompt = new_prompts(ssh->frontend);
 	    s->cur_prompt->to_server = TRUE;
@@ -3246,9 +3506,6 @@
 	    memcpy(s->username, s->cur_prompt->prompts[0]->result,
 		   lenof(s->username));
 	    free_prompts(s->cur_prompt);
-	} else {
-	    strncpy(s->username, ssh->cfg.username, sizeof(s->username));
-	    s->username[sizeof(s->username)-1] = '\0';
 	}
 
 	send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);
@@ -3266,7 +3523,7 @@
 
     crWaitUntil(pktin);
 
-    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) {
+    if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) {
 	/* We must not attempt PK auth. Pretend we've already tried it. */
 	s->tried_publickey = s->tried_agent = 1;
     } else {
@@ -3487,7 +3744,9 @@
 		sfree(s->response);
 		if (s->publickey_blob && !s->tried_publickey)
 		    logevent("Configured key file not in Pageant");
-	    }
+	    } else {
+                logevent("Failed to get reply from Pageant");
+            }
 	    if (s->authed)
 		break;
 	}
@@ -3723,6 +3982,10 @@
 	    }
 	}
 	if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+	    if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) {
+		bombout(("No supported authentication methods available"));
+		crStop(0);
+	    }
 	    s->cur_prompt->to_server = TRUE;
 	    s->cur_prompt->name = dupstr("SSH password");
 	    add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",
@@ -3911,7 +4174,7 @@
     if (ssh->state == SSH_STATE_CLOSED)
 	return;
 
-    if (c && !c->closes) {
+    if (!c->closes) {
 	/*
 	 * If halfopen is true, we have sent
 	 * CHANNEL_OPEN for this channel, but it hasn't even been
@@ -3923,14 +4186,42 @@
 	    if (ssh->version == 1) {
 		send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
 			    PKT_END);
+		c->closes = 1;		       /* sent MSG_CLOSE */
 	    } else {
-		struct Packet *pktout;
-		pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
-		ssh2_pkt_adduint32(pktout, c->remoteid);
-		ssh2_pkt_send(ssh, pktout);
+		int bytes_to_send = bufchain_size(&c->v.v2.outbuffer);
+		if (bytes_to_send > 0) {
+		    /*
+		     * If we still have unsent data in our outgoing
+		     * buffer for this channel, we can't actually
+		     * initiate a close operation yet or that data
+		     * will be lost. Instead, set the pending_close
+		     * flag so that when we do clear the buffer
+		     * we'll start closing the channel.
+		     */
+		    char logmsg[160] = {'\0'};
+		    sprintf(
+			    logmsg,
+			    "Forwarded port pending to be closed : "
+			    "%d bytes remaining",
+			    bytes_to_send);
+		    logevent(logmsg);
+
+		    c->pending_close = TRUE;
+		} else {
+		    /*
+		     * No locally buffered data, so we can send the
+		     * close message immediately.
+		     */
+		    struct Packet *pktout;
+		    pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+		    ssh2_pkt_adduint32(pktout, c->remoteid);
+		    ssh2_pkt_send(ssh, pktout);
+		    c->closes = 1;		       /* sent MSG_CLOSE */
+		    logevent("Nothing left to send, closing channel");
+		}
 	    }
 	}
-	c->closes = 1;		       /* sent MSG_CLOSE */
+
 	if (c->type == CHAN_X11) {
 	    c->u.x11.s = NULL;
 	    logevent("Forwarded X11 connection terminated");
@@ -3952,8 +4243,7 @@
     if (ssh->version == 1) {
 	send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
 		    PKT_INT, c->remoteid,
-		    PKTT_DATA,
-		    PKT_INT, len, PKT_DATA, buf, len,
+		    PKT_INT, len, PKTT_DATA, PKT_DATA, buf, len,
 		    PKTT_OTHER, PKT_END);
 	/*
 	 * In SSH-1 we can return 0 here - implying that forwarded
@@ -3972,17 +4262,20 @@
 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)
 {
     Ssh ssh = c->ssh;
+    int buflimit;
 
     if (ssh->state == SSH_STATE_CLOSED)
 	return;
 
     if (ssh->version == 1) {
-	if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) {
-	    c->v.v1.throttling = 0;
-	    ssh1_throttle(ssh, -1);
-	}
+	buflimit = SSH1_BUFFER_LIMIT;
     } else {
-	ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
+	buflimit = c->v.v2.locmaxwin;
+	ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0);
+    }
+    if (c->throttling_conn && bufsize <= buflimit) {
+	c->throttling_conn = 0;
+	ssh_throttle_conn(ssh, -1);
     }
 }
 
@@ -4067,6 +4360,7 @@
 
 	rpf = del234(ssh->rportfwds, pf);
 	assert(rpf == pf);
+	pf->pfrec->remote = NULL;
 	free_rportfwd(pf);
     }
 }
@@ -4198,12 +4492,19 @@
 
 	    epfrec = add234(ssh->portfwds, pfrec);
 	    if (epfrec != pfrec) {
+		if (epfrec->status == DESTROY) {
+		    /*
+		     * We already have a port forwarding up and running
+		     * with precisely these parameters. Hence, no need
+		     * to do anything; simply re-tag the existing one
+		     * as KEEP.
+		     */
+		    epfrec->status = KEEP;
+		}
 		/*
-		 * We already have a port forwarding with precisely
-		 * these parameters. Hence, no need to do anything;
-		 * simply tag the existing one as KEEP.
+		 * Anything else indicates that there was a duplicate
+		 * in our input, which we'll silently ignore.
 		 */
-		epfrec->status = KEEP;
 		free_portfwd(pfrec);
 	    } else {
 		pfrec->status = CREATE;
@@ -4236,6 +4537,8 @@
 	    logeventf(ssh, "Cancelling %s", message);
 	    sfree(message);
 
+	    /* epf->remote or epf->local may be NULL if setting up a
+	     * forwarding failed. */
 	    if (epf->remote) {
 		struct ssh_rportfwd *rpf = epf->remote;
 		struct Packet *pktout;
@@ -4411,7 +4714,7 @@
 			   string, stringlen);
     if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
 	ssh->v1_stdout_throttling = 1;
-	ssh1_throttle(ssh, +1);
+	ssh_throttle_conn(ssh, +1);
     }
 }
 
@@ -4432,8 +4735,8 @@
 	c = snew(struct ssh_channel);
 	c->ssh = ssh;
 
-	if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
-		     ssh->x11auth, NULL, -1, &ssh->cfg) != NULL) {
+	if (x11_init(&c->u.x11.s, ssh->x11disp, c,
+		     NULL, -1, &ssh->cfg) != NULL) {
 	    logevent("Opening X11 forward connection failed");
 	    sfree(c);
 	    send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
@@ -4445,7 +4748,8 @@
 	    c->halfopen = FALSE;
 	    c->localid = alloc_channel_id(ssh);
 	    c->closes = 0;
-	    c->v.v1.throttling = 0;
+	    c->pending_close = FALSE;
+	    c->throttling_conn = 0;
 	    c->type = CHAN_X11;	/* identify channel type */
 	    add234(ssh->channels, c);
 	    send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
@@ -4474,7 +4778,8 @@
 	c->halfopen = FALSE;
 	c->localid = alloc_channel_id(ssh);
 	c->closes = 0;
-	c->v.v1.throttling = 0;
+	c->pending_close = FALSE;
+	c->throttling_conn = 0;
 	c->type = CHAN_AGENT;	/* identify channel type */
 	c->u.a.lensofar = 0;
 	add234(ssh->channels, c);
@@ -4528,7 +4833,8 @@
 	    c->halfopen = FALSE;
 	    c->localid = alloc_channel_id(ssh);
 	    c->closes = 0;
-	    c->v.v1.throttling = 0;
+	    c->pending_close = FALSE;
+	    c->throttling_conn = 0;
 	    c->type = CHAN_SOCKDATA;	/* identify channel type */
 	    add234(ssh->channels, c);
 	    send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
@@ -4550,7 +4856,7 @@
 	c->remoteid = localid;
 	c->halfopen = FALSE;
 	c->type = CHAN_SOCKDATA;
-	c->v.v1.throttling = 0;
+	c->throttling_conn = 0;
 	pfd_confirm(c->u.pfd.s);
     }
 
@@ -4686,9 +4992,9 @@
 	    bufsize = 0;   /* agent channels never back up */
 	    break;
 	}
-	if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) {
-	    c->v.v1.throttling = 1;
-	    ssh1_throttle(ssh, +1);
+	if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
+	    c->throttling_conn = 1;
+	    ssh_throttle_conn(ssh, +1);
 	}
     }
 }
@@ -4767,12 +5073,10 @@
 	}
     }
 
-    if (ssh->cfg.x11_forward) {
-	char proto[20], data[64];
+    if (ssh->cfg.x11_forward &&
+	(ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
+					  ssh->cfg.x11_auth, &ssh->cfg))) {
 	logevent("Requesting X11 forwarding");
-	ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
-				       data, sizeof(data), ssh->cfg.x11_auth);
-        x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
 	/*
 	 * Note that while we blank the X authentication data here, we don't
 	 * take any special action to blank the start of an X11 channel,
@@ -4782,14 +5086,19 @@
 	 */
 	if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
 	    send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
-			PKT_STR, proto,
-			PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER,
-			PKT_INT, x11_get_screen_number(ssh->cfg.x11_display),
+			PKT_STR, ssh->x11disp->remoteauthprotoname,
+			PKTT_PASSWORD,
+			PKT_STR, ssh->x11disp->remoteauthdatastring,
+			PKTT_OTHER,
+			PKT_INT, ssh->x11disp->screennum,
 			PKT_END);
 	} else {
 	    send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
-			PKT_STR, proto,
-			PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END);
+			PKT_STR, ssh->x11disp->remoteauthprotoname,
+			PKTT_PASSWORD,
+			PKT_STR, ssh->x11disp->remoteauthdatastring,
+			PKTT_OTHER,
+			PKT_END);
 	}
 	do {
 	    crReturnV;
@@ -4924,8 +5233,8 @@
 	} else {
 	    while (inlen > 0) {
 		int len = min(inlen, 512);
-		send_packet(ssh, SSH1_CMSG_STDIN_DATA, PKTT_DATA,
-			    PKT_INT, len, PKT_DATA, in, len,
+		send_packet(ssh, SSH1_CMSG_STDIN_DATA,
+			    PKT_INT, len,  PKTT_DATA, PKT_DATA, in, len,
 			    PKTT_OTHER, PKT_END);
 		in += len;
 		inlen -= len;
@@ -5105,15 +5414,18 @@
 	const struct ssh_mac *scmac_tobe;
 	const struct ssh_compress *cscomp_tobe;
 	const struct ssh_compress *sccomp_tobe;
-	char *hostkeydata, *sigdata, *keystr, *fingerprint;
-	int hostkeylen, siglen;
+	char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
+	int hostkeylen, siglen, rsakeylen;
 	void *hkey;		       /* actual host key */
+	void *rsakey;		       /* for RSA kex */
 	unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
 	int n_preferred_kex;
 	const struct ssh_kexes *preferred_kex[KEX_MAX];
 	int n_preferred_ciphers;
 	const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
 	const struct ssh_compress *preferred_comp;
+	int userauth_succeeded;	    /* for delayed compression */
+	int pending_compression;
 	int got_session_id, activated_authconn;
 	struct Packet *pktout;
         int dlgret;
@@ -5129,6 +5441,8 @@
     s->cscomp_tobe = s->sccomp_tobe = NULL;
 
     s->got_session_id = s->activated_authconn = FALSE;
+    s->userauth_succeeded = FALSE;
+    s->pending_compression = FALSE;
 
     /*
      * Be prepared to work around the buggy MAC problem.
@@ -5139,7 +5453,7 @@
 	s->maclist = macs, s->nmacs = lenof(macs);
 
   begin_key_exchange:
-    ssh->pkt_ctx &= ~SSH2_PKTCTX_KEX_MASK;
+    ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
     {
 	int i, j, commalist_started;
 
@@ -5161,6 +5475,10 @@
 		s->preferred_kex[s->n_preferred_kex++] =
 		    &ssh_diffiehellman_group1;
 		break;
+	      case KEX_RSA:
+		s->preferred_kex[s->n_preferred_kex++] =
+		    &ssh_rsa_kex;
+		break;
 	      case KEX_WARN:
 		/* Flag for later. Don't bother if it's the last in
 		 * the list. */
@@ -5289,26 +5607,32 @@
 	    if (i < s->nmacs - 1)
 		ssh2_pkt_addstring_str(s->pktout, ",");
 	}
-	/* List client->server compression algorithms. */
-	ssh2_pkt_addstring_start(s->pktout);
-	assert(lenof(compressions) > 1);
-	ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
-	for (i = 0; i < lenof(compressions); i++) {
-	    const struct ssh_compress *c = compressions[i];
-	    if (c != s->preferred_comp) {
+	/* List client->server compression algorithms,
+	 * then server->client compression algorithms. (We use the
+	 * same set twice.) */
+	for (j = 0; j < 2; j++) {
+	    ssh2_pkt_addstring_start(s->pktout);
+	    assert(lenof(compressions) > 1);
+	    /* Prefer non-delayed versions */
+	    ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
+	    /* We don't even list delayed versions of algorithms until
+	     * they're allowed to be used, to avoid a race. See the end of
+	     * this function. */
+	    if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
 		ssh2_pkt_addstring_str(s->pktout, ",");
-		ssh2_pkt_addstring_str(s->pktout, c->name);
+		ssh2_pkt_addstring_str(s->pktout,
+				       s->preferred_comp->delayed_name);
 	    }
-	}
-	/* List server->client compression algorithms. */
-	ssh2_pkt_addstring_start(s->pktout);
-	assert(lenof(compressions) > 1);
-	ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
-	for (i = 0; i < lenof(compressions); i++) {
-	    const struct ssh_compress *c = compressions[i];
-	    if (c != s->preferred_comp) {
-		ssh2_pkt_addstring_str(s->pktout, ",");
-		ssh2_pkt_addstring_str(s->pktout, c->name);
+	    for (i = 0; i < lenof(compressions); i++) {
+		const struct ssh_compress *c = compressions[i];
+		if (c != s->preferred_comp) {
+		    ssh2_pkt_addstring_str(s->pktout, ",");
+		    ssh2_pkt_addstring_str(s->pktout, c->name);
+		    if (s->userauth_succeeded && c->delayed_name) {
+			ssh2_pkt_addstring_str(s->pktout, ",");
+			ssh2_pkt_addstring_str(s->pktout, c->delayed_name);
+		    }
+		}
 	    }
 	}
 	/* List client->server languages. Empty list. */
@@ -5457,6 +5781,13 @@
 	    if (in_commasep_string(c->name, str, len)) {
 		s->cscomp_tobe = c;
 		break;
+	    } else if (in_commasep_string(c->delayed_name, str, len)) {
+		if (s->userauth_succeeded) {
+		    s->cscomp_tobe = c;
+		    break;
+		} else {
+		    s->pending_compression = TRUE;  /* try this later */
+		}
 	    }
 	}
 	ssh_pkt_getstring(pktin, &str, &len);  /* server->client compression */
@@ -5466,8 +5797,19 @@
 	    if (in_commasep_string(c->name, str, len)) {
 		s->sccomp_tobe = c;
 		break;
+	    } else if (in_commasep_string(c->delayed_name, str, len)) {
+		if (s->userauth_succeeded) {
+		    s->sccomp_tobe = c;
+		    break;
+		} else {
+		    s->pending_compression = TRUE;  /* try this later */
+		}
 	    }
 	}
+	if (s->pending_compression) {
+	    logevent("Server supports delayed compression; "
+		     "will try this later");
+	}
 	ssh_pkt_getstring(pktin, &str, &len);  /* client->server language */
 	ssh_pkt_getstring(pktin, &str, &len);  /* server->client language */
 	s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
@@ -5560,107 +5902,217 @@
 	    crWaitUntil(pktin);                /* Ignore packet */
     }
 
-    /*
-     * Work out the number of bits of key we will need from the key
-     * exchange. We start with the maximum key length of either
-     * cipher...
-     */
-    {
-	int csbits, scbits;
+    if (ssh->kex->main_type == KEXTYPE_DH) {
+        /*
+         * Work out the number of bits of key we will need from the
+         * key exchange. We start with the maximum key length of
+         * either cipher...
+         */
+        {
+            int csbits, scbits;
 
-	csbits = s->cscipher_tobe->keylen;
-	scbits = s->sccipher_tobe->keylen;
-	s->nbits = (csbits > scbits ? csbits : scbits);
-    }
-    /* The keys only have hlen-bit entropy, since they're based on
-     * a hash. So cap the key size at hlen bits. */
-    if (s->nbits > ssh->kex->hash->hlen * 8)
-	s->nbits = ssh->kex->hash->hlen * 8;
+            csbits = s->cscipher_tobe->keylen;
+            scbits = s->sccipher_tobe->keylen;
+            s->nbits = (csbits > scbits ? csbits : scbits);
+        }
+        /* The keys only have hlen-bit entropy, since they're based on
+         * a hash. So cap the key size at hlen bits. */
+        if (s->nbits > ssh->kex->hash->hlen * 8)
+            s->nbits = ssh->kex->hash->hlen * 8;
 
-    /*
-     * If we're doing Diffie-Hellman group exchange, start by
-     * requesting a group.
-     */
-    if (!ssh->kex->pdata) {
-	logevent("Doing Diffie-Hellman group exchange");
-	ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX;
-	/*
-	 * Work out how big a DH group we will need to allow that
-	 * much data.
-	 */
-	s->pbits = 512 << ((s->nbits - 1) / 64);
-	s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
-	ssh2_pkt_adduint32(s->pktout, s->pbits);
-	ssh2_pkt_send_noqueue(ssh, s->pktout);
+        /*
+         * If we're doing Diffie-Hellman group exchange, start by
+         * requesting a group.
+         */
+        if (!ssh->kex->pdata) {
+            logevent("Doing Diffie-Hellman group exchange");
+            ssh->pkt_kctx = SSH2_PKTCTX_DHGEX;
+            /*
+             * Work out how big a DH group we will need to allow that
+             * much data.
+             */
+            s->pbits = 512 << ((s->nbits - 1) / 64);
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
+            ssh2_pkt_adduint32(s->pktout, s->pbits);
+            ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+            crWaitUntil(pktin);
+            if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
+                bombout(("expected key exchange group packet from server"));
+                crStop(0);
+            }
+            s->p = ssh2_pkt_getmp(pktin);
+            s->g = ssh2_pkt_getmp(pktin);
+            if (!s->p || !s->g) {
+                bombout(("unable to read mp-ints from incoming group packet"));
+                crStop(0);
+            }
+            ssh->kex_ctx = dh_setup_gex(s->p, s->g);
+            s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
+            s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+        } else {
+            ssh->pkt_kctx = SSH2_PKTCTX_DHGROUP;
+            ssh->kex_ctx = dh_setup_group(ssh->kex);
+            s->kex_init_value = SSH2_MSG_KEXDH_INIT;
+            s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
+            logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
+                      ssh->kex->groupname);
+        }
 
-	crWaitUntil(pktin);
-	if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
-	    bombout(("expected key exchange group packet from server"));
-	    crStop(0);
-	}
-	s->p = ssh2_pkt_getmp(pktin);
-	s->g = ssh2_pkt_getmp(pktin);
-	if (!s->p || !s->g) {
-	    bombout(("unable to read mp-ints from incoming group packet"));
-	    crStop(0);
-	}
-	ssh->kex_ctx = dh_setup_gex(s->p, s->g);
-	s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
-	s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+        logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
+                  ssh->kex->hash->text_name);
+        /*
+         * Now generate and send e for Diffie-Hellman.
+         */
+        set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
+        s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
+        s->pktout = ssh2_pkt_init(s->kex_init_value);
+        ssh2_pkt_addmp(s->pktout, s->e);
+        ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+        set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
+        crWaitUntil(pktin);
+        if (pktin->type != s->kex_reply_value) {
+            bombout(("expected key exchange reply packet from server"));
+            crStop(0);
+        }
+        set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+        s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+        s->f = ssh2_pkt_getmp(pktin);
+        if (!s->f) {
+            bombout(("unable to parse key exchange reply packet"));
+            crStop(0);
+        }
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        s->K = dh_find_K(ssh->kex_ctx, s->f);
+
+        /* We assume everything from now on will be quick, and it might
+         * involve user interaction. */
+        set_busy_status(ssh->frontend, BUSY_NOT);
+
+        hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
+        if (!ssh->kex->pdata) {
+            hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
+            hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
+            hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
+        }
+        hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
+        hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
+        dh_cleanup(ssh->kex_ctx);
+        freebn(s->f);
+        if (!ssh->kex->pdata) {
+            freebn(s->g);
+            freebn(s->p);
+        }
     } else {
-	ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP;
-	ssh->kex_ctx = dh_setup_group(ssh->kex);
-	s->kex_init_value = SSH2_MSG_KEXDH_INIT;
-	s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
-	logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
-		  ssh->kex->groupname);
-    }
-
-    logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
-             ssh->kex->hash->text_name);
-    /*
-     * Now generate and send e for Diffie-Hellman.
-     */
-    set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
-    s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
-    s->pktout = ssh2_pkt_init(s->kex_init_value);
-    ssh2_pkt_addmp(s->pktout, s->e);
-    ssh2_pkt_send_noqueue(ssh, s->pktout);
+	logeventf(ssh, "Doing RSA key exchange with hash %s",
+		  ssh->kex->hash->text_name);
+	ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX;
+        /*
+         * RSA key exchange. First expect a KEXRSA_PUBKEY packet
+         * from the server.
+         */
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
+            bombout(("expected RSA public key packet from server"));
+            crStop(0);
+        }
 
-    set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
-    crWaitUntil(pktin);
-    if (pktin->type != s->kex_reply_value) {
-	bombout(("expected key exchange reply packet from server"));
-	crStop(0);
-    }
-    set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
-    ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
-    s->f = ssh2_pkt_getmp(pktin);
-    if (!s->f) {
-	bombout(("unable to parse key exchange reply packet"));
-	crStop(0);
-    }
-    ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+        hash_string(ssh->kex->hash, ssh->exhash,
+		    s->hostkeydata, s->hostkeylen);
+	s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+
+        {
+            char *keydata;
+            ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+            s->rsakeydata = snewn(s->rsakeylen, char);
+            memcpy(s->rsakeydata, keydata, s->rsakeylen);
+        }
 
-    s->K = dh_find_K(ssh->kex_ctx, s->f);
+        s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
+        if (!s->rsakey) {
+            sfree(s->rsakeydata);
+            bombout(("unable to parse RSA public key from server"));
+            crStop(0);
+        }
 
-    /* We assume everything from now on will be quick, and it might
-     * involve user interaction. */
-    set_busy_status(ssh->frontend, BUSY_NOT);
-
-    hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
-    if (!ssh->kex->pdata) {
-	hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
-	hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
-	hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
+        hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
+
+        /*
+         * Next, set up a shared secret K, of precisely KLEN -
+         * 2*HLEN - 49 bits, where KLEN is the bit length of the
+         * RSA key modulus and HLEN is the bit length of the hash
+         * we're using.
+         */
+        {
+            int klen = ssh_rsakex_klen(s->rsakey);
+            int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
+            int i, byte = 0;
+            unsigned char *kstr1, *kstr2, *outstr;
+            int kstr1len, kstr2len, outstrlen;
+
+            s->K = bn_power_2(nbits - 1);
+
+            for (i = 0; i < nbits; i++) {
+                if ((i & 7) == 0) {
+                    byte = random_byte();
+                }
+                bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
+            }
+
+            /*
+             * Encode this as an mpint.
+             */
+            kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);
+            kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);
+            PUT_32BIT(kstr2, kstr1len);
+            memcpy(kstr2 + 4, kstr1, kstr1len);
+
+            /*
+             * Encrypt it with the given RSA key.
+             */
+            outstrlen = (klen + 7) / 8;
+            outstr = snewn(outstrlen, unsigned char);
+            ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,
+			       outstr, outstrlen, s->rsakey);
+
+            /*
+             * And send it off in a return packet.
+             */
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
+            ssh2_pkt_addstring_start(s->pktout);
+            ssh2_pkt_addstring_data(s->pktout, (char *)outstr, outstrlen);
+            ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+	    hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);
+
+            sfree(kstr2);
+            sfree(kstr1);
+            sfree(outstr);
+        }
+
+        ssh_rsakex_freekey(s->rsakey);
+
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
+            sfree(s->rsakeydata);
+            bombout(("expected signature packet from server"));
+            crStop(0);
+        }
+
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        sfree(s->rsakeydata);
     }
-    hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
-    hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
     hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
     assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
     ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
 
-    dh_cleanup(ssh->kex_ctx);
     ssh->kex_ctx = NULL;
 
 #if 0
@@ -5668,7 +6120,6 @@
     dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
 #endif
 
-    s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
     if (!s->hkey ||
 	!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
 				 (char *)s->exchange_hash,
@@ -5850,14 +6301,9 @@
 		  ssh->sccomp->text_name);
 
     /*
-     * Free key exchange data.
+     * Free shared secret.
      */
-    freebn(s->f);
     freebn(s->K);
-    if (!ssh->kex->pdata) {
-	freebn(s->g);
-	freebn(s->p);
-    }
 
     /*
      * Key exchange is over. Loop straight back round if we have a
@@ -5899,19 +6345,52 @@
      * start.
      * 
      * We _also_ go back to the start if we see pktin==NULL and
-     * inlen==-1, because this is a special signal meaning
+     * inlen negative, because this is a special signal meaning
      * `initiate client-driven rekey', and `in' contains a message
      * giving the reason for the rekey.
+     *
+     * inlen==-1 means always initiate a rekey;
+     * inlen==-2 means that userauth has completed successfully and
+     *   we should consider rekeying (for delayed compression).
      */
     while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||
-	     (!pktin && inlen == -1))) {
+	     (!pktin && inlen < 0))) {
         wait_for_rekey:
 	crReturn(1);
     }
     if (pktin) {
 	logevent("Server initiated key re-exchange");
     } else {
+	if (inlen == -2) {
+	    /* 
+	     * authconn has seen a USERAUTH_SUCCEEDED. Time to enable
+	     * delayed compression, if it's available.
+	     *
+	     * draft-miller-secsh-compression-delayed-00 says that you
+	     * negotiate delayed compression in the first key exchange, and
+	     * both sides start compressing when the server has sent
+	     * USERAUTH_SUCCESS. This has a race condition -- the server
+	     * can't know when the client has seen it, and thus which incoming
+	     * packets it should treat as compressed.
+	     *
+	     * Instead, we do the initial key exchange without offering the
+	     * delayed methods, but note if the server offers them; when we
+	     * get here, if a delayed method was available that was higher
+	     * on our list than what we got, we initiate a rekey in which we
+	     * _do_ list the delayed methods (and hopefully get it as a
+	     * result). Subsequent rekeys will do the same.
+	     */
+	    assert(!s->userauth_succeeded); /* should only happen once */
+	    s->userauth_succeeded = TRUE;
+	    if (!s->pending_compression)
+		/* Can't see any point rekeying. */
+		goto wait_for_rekey;       /* this is utterly horrid */
+	    /* else fall through to rekey... */
+	    s->pending_compression = FALSE;
+	}
         /*
+	 * Now we've decided to rekey.
+	 *
          * Special case: if the server bug is set that doesn't
          * allow rekeying, we give a different log message and
          * continue waiting. (If such a server _initiates_ a rekey,
@@ -5929,7 +6408,7 @@
                     schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
                                    ssh2_timer, ssh);
             }
-            goto wait_for_rekey;       /* this is utterly horrid */
+            goto wait_for_rekey;       /* this is still utterly horrid */
         } else {
             logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in);
         }
@@ -5966,8 +6445,8 @@
 	    len = c->v.v2.remmaxpkt;
 	pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
 	ssh2_pkt_adduint32(pktout, c->remoteid);
-	dont_log_data(ssh, pktout, PKTLOG_OMIT);
 	ssh2_pkt_addstring_start(pktout);
+	dont_log_data(ssh, pktout, PKTLOG_OMIT);
 	ssh2_pkt_addstring_data(pktout, data, len);
 	end_log_omission(ssh, pktout);
 	ssh2_pkt_send(ssh, pktout);
@@ -5982,7 +6461,7 @@
     return bufchain_size(&c->v.v2.outbuffer);
 }
 
-static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
+static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
 {
     int bufsize;
     if (c->closes)
@@ -6006,12 +6485,42 @@
 	    break;
 	}
     }
+
+    /*
+     * If we've emptied the channel's output buffer and there's a
+     * pending close event, start the channel-closing procedure.
+     */
+    if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) {
+	struct Packet *pktout;
+	pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+	ssh2_pkt_adduint32(pktout, c->remoteid);
+	ssh2_pkt_send(ssh, pktout);
+	c->closes = 1;
+	c->pending_close = FALSE;
+    }
+}
+
+/*
+ * Set up most of a new ssh_channel for SSH-2.
+ */
+static void ssh2_channel_init(struct ssh_channel *c)
+{
+    Ssh ssh = c->ssh;
+    c->localid = alloc_channel_id(ssh);
+    c->closes = 0;
+    c->pending_close = FALSE;
+    c->throttling_conn = FALSE;
+    c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
+	ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+    c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL;
+    c->v.v2.throttle_state = UNTHROTTLED;
+    bufchain_init(&c->v.v2.outbuffer);
 }
 
 /*
  * Potentially enlarge the window on an SSH-2 channel.
  */
-static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
+static void ssh2_set_window(struct ssh_channel *c, int newwin)
 {
     Ssh ssh = c->ssh;
 
@@ -6024,15 +6533,68 @@
 	return;
 
     /*
+     * If the remote end has a habit of ignoring maxpkt, limit the
+     * window so that it has no choice (assuming it doesn't ignore the
+     * window as well).
+     */
+    if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)
+	newwin = OUR_V2_MAXPKT;
+	
+
+    /*
      * Only send a WINDOW_ADJUST if there's significantly more window
      * available than the other end thinks there is.  This saves us
      * sending a WINDOW_ADJUST for every character in a shell session.
      *
      * "Significant" is arbitrarily defined as half the window size.
      */
-    if (newwin > c->v.v2.locwindow * 2) {
+    if (newwin / 2 >= c->v.v2.locwindow) {
 	struct Packet *pktout;
+	struct winadj *wa;
+
+	/*
+	 * In order to keep track of how much window the client
+	 * actually has available, we'd like it to acknowledge each
+	 * WINDOW_ADJUST.  We can't do that directly, so we accompany
+	 * it with a CHANNEL_REQUEST that has to be acknowledged.
+	 *
+	 * This is only necessary if we're opening the window wide.
+	 * If we're not, then throughput is being constrained by
+	 * something other than the maximum window size anyway.
+	 *
+	 * We also only send this if the main channel has finished its
+	 * initial CHANNEL_REQUESTs and installed the default
+	 * CHANNEL_FAILURE handler, so as not to risk giving it
+	 * unexpected CHANNEL_FAILUREs.
+	 */
+	if (newwin == c->v.v2.locmaxwin &&
+	    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) {
+	    pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+	    ssh2_pkt_adduint32(pktout, c->remoteid);
+	    ssh2_pkt_addstring(pktout, "winadj at putty.projects.tartarus.org");
+	    ssh2_pkt_addbool(pktout, TRUE);
+	    ssh2_pkt_send(ssh, pktout);
 
+	    /*
+	     * CHANNEL_FAILURE doesn't come with any indication of
+	     * what message caused it, so we have to keep track of the
+	     * outstanding CHANNEL_REQUESTs ourselves.
+	     */
+	    wa = snew(struct winadj);
+	    wa->size = newwin - c->v.v2.locwindow;
+	    wa->next = NULL;
+	    if (!c->v.v2.winadj_head)
+		c->v.v2.winadj_head = wa;
+	    else
+		c->v.v2.winadj_tail->next = wa;
+	    c->v.v2.winadj_tail = wa;
+	    if (c->v.v2.throttle_state != UNTHROTTLED)
+		c->v.v2.throttle_state = UNTHROTTLING;
+	} else {
+	    /* Pretend the WINDOW_ADJUST was acked immediately. */
+	    c->v.v2.remlocwin = newwin;
+	    c->v.v2.throttle_state = THROTTLED;
+	}
 	pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
 	ssh2_pkt_adduint32(pktout, c->remoteid);
 	ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow);
@@ -6041,14 +6603,101 @@
     }
 }
 
+/*
+ * 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.
+ */
+static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)
+{
+    unsigned localid = ssh_pkt_getuint32(pktin);
+    struct ssh_channel *c;
+
+    c = find234(ssh->channels, &localid, ssh_channelfind);
+    if (!c ||
+	(c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
+	 pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) {
+	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_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+	sfree(buf);
+	return NULL;
+    }
+    return c;
+}
+
+static int ssh2_handle_winadj_response(struct ssh_channel *c)
+{
+    struct winadj *wa = c->v.v2.winadj_head;
+    if (!wa)
+	return FALSE;
+    c->v.v2.winadj_head = wa->next;
+    c->v.v2.remlocwin += wa->size;
+    sfree(wa);
+    /*
+     * winadj messages are only sent when the window is fully open, so
+     * if we get an ack of one, we know any pending unthrottle is
+     * complete.
+     */
+    if (c->v.v2.throttle_state == UNTHROTTLING)
+	c->v.v2.throttle_state = UNTHROTTLED;
+    return TRUE;
+}
+
+static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin)
+{
+    /*
+     * This should never get called.  All channel requests are either
+     * sent with want_reply false, are sent before this handler gets
+     * installed, or are "winadj at putty" requests, which servers should
+     * never respond to with success.
+     *
+     * However, at least one server ("boks_sshd") is known to return
+     * SUCCESS for channel requests it's never heard of, such as
+     * "winadj at putty". Raised with foxt.com as bug 090916-090424, but
+     * for the sake of a quiet life, we handle it just the same as the
+     * expected FAILURE.
+     */
+    struct ssh_channel *c;
+
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+	return;
+    if (!ssh2_handle_winadj_response(c))
+	ssh_disconnect(ssh, NULL,
+		       "Received unsolicited SSH_MSG_CHANNEL_SUCCESS",
+		       SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+}
+
+static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin)
+{
+    /*
+     * The only time this should get called is for "winadj at putty"
+     * messages sent above.  All other channel requests are either
+     * sent with want_reply false or are sent before this handler gets
+     * installed.
+     */
+    struct ssh_channel *c;
+
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+	return;
+    if (!ssh2_handle_winadj_response(c))
+	ssh_disconnect(ssh, NULL,
+		       "Received unsolicited SSH_MSG_CHANNEL_FAILURE",
+		       SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+}
+
 static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
-    if (c && !c->closes) {
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+	return;
+    if (!c->closes) {
 	c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
-	ssh2_try_send_and_unthrottle(c);
+	ssh2_try_send_and_unthrottle(ssh, c);
     }
 }
 
@@ -6056,11 +6705,10 @@
 {
     char *data;
     int length;
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-	return;			       /* nonexistent channel */
+	return;
     if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
 	ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)
 	return;			       /* extended but not stderr */
@@ -6068,6 +6716,7 @@
     if (data) {
 	int bufsize = 0;
 	c->v.v2.locwindow -= length;
+	c->v.v2.remlocwin -= length;
 	switch (c->type) {
 	  case CHAN_MAINSESSION:
 	    bufsize =
@@ -6125,22 +6774,43 @@
 	    break;
 	}
 	/*
+	 * If it looks like the remote end hit the end of its window,
+	 * and we didn't want it to do that, think about using a
+	 * larger window.
+	 */
+	if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED &&
+	    c->v.v2.locmaxwin < 0x40000000)
+	    c->v.v2.locmaxwin += OUR_V2_WINSIZE;
+	/*
 	 * If we are not buffering too much data,
 	 * enlarge the window again at the remote side.
-	 */
-	if (bufsize < OUR_V2_WINSIZE)
-	    ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
+	 * If we are buffering too much, we may still
+	 * need to adjust the window if the server's
+	 * sent excess data.
+	 */
+	ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ?
+			c->v.v2.locmaxwin - bufsize : 0);
+	/*
+	 * If we're either buffering way too much data, or if we're
+	 * buffering anything at all and we're in "simple" mode,
+	 * throttle the whole channel.
+	 */
+	if ((bufsize > c->v.v2.locmaxwin ||
+	     (ssh->cfg.ssh_simple && bufsize > 0)) &&
+	    !c->throttling_conn) {
+	    c->throttling_conn = 1;
+	    ssh_throttle_conn(ssh, +1);
+	}
     }
 }
 
 static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-	return;			       /* nonexistent channel */
+	return;
 
     if (c->type == CHAN_X11) {
 	/*
@@ -6148,27 +6818,25 @@
 	 * wrap up and close the channel ourselves.
 	 */
 	x11_close(c->u.x11.s);
+	c->u.x11.s = NULL;
 	sshfwd_close(c);
     } else if (c->type == CHAN_AGENT) {
 	sshfwd_close(c);
     } else if (c->type == CHAN_SOCKDATA) {
 	pfd_close(c->u.pfd.s);
+	c->u.pfd.s = NULL;
 	sshfwd_close(c);
     }
 }
 
 static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
-    if (!c || c->halfopen) {
-	bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
-		 c ? "half-open" : "nonexistent", i));
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
 	return;
-    }
     /* Do pre-close processing on the channel. */
     switch (c->type) {
       case CHAN_MAINSESSION:
@@ -6221,13 +6889,12 @@
 
 static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-	return;			       /* nonexistent channel */
+	return;
     if (c->type != CHAN_SOCKDATA_DORMANT)
 	return;			       /* dunno why they're confirming this */
     c->remoteid = ssh_pkt_getuint32(pktin);
@@ -6259,14 +6926,13 @@
 	    "Unknown channel type",
 	    "Resource shortage",
     };
-    unsigned i = ssh_pkt_getuint32(pktin);
     unsigned reason_code;
     char *reason_string;
     int reason_length;
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-	return;			       /* nonexistent channel */
+	return;
     if (c->type != CHAN_SOCKDATA_DORMANT)
 	return;			       /* dunno why they're failing this */
 
@@ -6285,31 +6951,19 @@
 
 static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
 {
-    unsigned localid;
     char *type;
     int typelen, want_reply;
     int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    localid = ssh_pkt_getuint32(pktin);
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+	return;
     ssh_pkt_getstring(pktin, &type, &typelen);
     want_reply = ssh2_pkt_getbool(pktin);
 
     /*
-     * First, check that the channel exists. Otherwise,
-     * we can instantly disconnect with a rude message.
-     */
-    c = find234(ssh->channels, &localid, ssh_channelfind);
-    if (!c) {
-	char *buf = dupprintf("Received channel request for nonexistent"
-			      " channel %d", localid);
-	ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
-	sfree(buf);
-	return;
-    }
-
-    /*
      * Having got the channel number, we now look at
      * the request type string to see if it's something
      * we recognise.
@@ -6336,7 +6990,7 @@
 	    int msglen = 0, core = FALSE;
 	    /* ICK: older versions of OpenSSH (e.g. 3.4p1)
 	     * provide an `int' for the signal, despite its
-	     * having been a `string' in the drafts since at
+	     * having been a `string' in the drafts of RFC 4254 since at
 	     * least 2001. (Fixed in session.c 1.147.) Try to
 	     * infer which we can safely parse it as. */
 	    {
@@ -6379,7 +7033,7 @@
 		    fmt_sig = dupprintf(" %d", signum);
 		    ssh->exitcode = 128 + signum;
 		} else {
-		    /* As per the drafts. */
+		    /* As per RFC 4254. */
 		    char *sig;
 		    int siglen;
 		    ssh_pkt_getstring(pktin, &sig, &siglen);
@@ -6518,6 +7172,7 @@
 
     if (typelen == 3 && !memcmp(type, "x11", 3)) {
 	char *addrstr;
+	const char *x11err;
 
 	ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
 	addrstr = snewn(peeraddrlen+1, char);
@@ -6530,9 +7185,9 @@
 
 	if (!ssh->X11_fwd_enabled)
 	    error = "X11 forwarding is not enabled";
-	else if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
-			  ssh->x11auth, addrstr, peerport,
-			  &ssh->cfg) != NULL) {
+	else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,
+				    addrstr, peerport, &ssh->cfg)) != NULL) {
+	    logeventf(ssh, "Local X11 connection failed: %s", x11err);
 	    error = "Unable to open an X11 connection";
 	} else {
 	    logevent("Opening X11 forward connection succeeded");
@@ -6594,12 +7249,9 @@
 	logeventf(ssh, "Rejected channel open: %s", error);
 	sfree(c);
     } else {
-	c->localid = alloc_channel_id(ssh);
-	c->closes = 0;
-	c->v.v2.locwindow = OUR_V2_WINSIZE;
+	ssh2_channel_init(c);
 	c->v.v2.remwindow = winsize;
 	c->v.v2.remmaxpkt = pktsize;
-	bufchain_init(&c->v.v2.outbuffer);
 	add234(ssh->channels, c);
 	pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
 	ssh2_pkt_adduint32(pktout, c->remoteid);
@@ -6611,12 +7263,14 @@
 }
 
 /*
- * Buffer banner messages for later display at some convenient point.
+ * Buffer banner messages for later display at some convenient point,
+ * if we're going to display them.
  */
 static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
 {
     /* Arbitrary limit to prevent unbounded inflation of buffer */
-    if (bufchain_size(&ssh->banner) <= 131072) {
+    if (ssh->cfg.ssh_show_banner &&
+	bufchain_size(&ssh->banner) <= 131072) {
 	char *banner = NULL;
 	int size = 0;
 	ssh_pkt_getstring(pktin, &banner, &size);
@@ -6658,14 +7312,19 @@
 		AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
 		AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
 		AUTH_TYPE_PASSWORD,
+	        AUTH_TYPE_GSSAPI,      /* always QUIET */
 		AUTH_TYPE_KEYBOARD_INTERACTIVE,
 		AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
 	} type;
 	int done_service_req;
 	int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
 	int tried_pubkey_config, done_agent;
+#ifndef NO_GSSAPI
+	int can_gssapi;
+	int tried_gssapi;
+#endif
 	int kbd_inter_refused;
-	int we_are_in;
+	int we_are_in, userauth_success;
 	prompts_t *cur_prompt;
 	int num_prompts;
 	char username[100];
@@ -6687,13 +7346,25 @@
 	int try_send;
 	int num_env, env_left, env_ok;
 	struct Packet *pktout;
+#ifndef NO_GSSAPI
+	struct ssh_gss_library *gsslib;
+	Ssh_gss_ctx gss_ctx;
+	Ssh_gss_buf gss_buf;
+	Ssh_gss_buf gss_rcvtok, gss_sndtok;
+	Ssh_gss_name gss_srv_name;
+	Ssh_gss_stat gss_stat;
+#endif
     };
     crState(do_ssh2_authconn_state);
 
     crBegin(ssh->do_ssh2_authconn_crstate);
 
     s->done_service_req = FALSE;
-    s->we_are_in = FALSE;
+    s->we_are_in = s->userauth_success = FALSE;
+#ifndef NO_GSSAPI
+    s->tried_gssapi = FALSE;
+#endif
+
     if (!ssh->cfg.ssh_no_userauth) {
 	/*
 	 * Request userauth protocol, and await a response to it.
@@ -6836,6 +7507,8 @@
 			s->nkeys = 0;
 		    }
 		}
+	    } else {
+                logevent("Failed to get reply from Pageant");
 	    }
 	}
 
@@ -6877,7 +7550,8 @@
 	     * with change_username turned off we don't try to get
 	     * it again.
 	     */
-	} else if (!*ssh->cfg.username) {
+	} else if (!get_remote_username(&ssh->cfg, s->username,
+					sizeof(s->username))) {
 	    int ret; /* need not be kept over crReturn */
 	    s->cur_prompt = new_prompts(ssh->frontend);
 	    s->cur_prompt->to_server = TRUE;
@@ -6905,8 +7579,6 @@
 	    free_prompts(s->cur_prompt);
 	} else {
 	    char *stuff;
-	    strncpy(s->username, ssh->cfg.username, sizeof(s->username));
-	    s->username[sizeof(s->username)-1] = '\0';
 	    if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
 		stuff = dupprintf("Using username \"%s\".\r\n", s->username);
 		c_write_str(ssh, stuff);
@@ -6920,7 +7592,7 @@
 	 * just in case it succeeds, and (b) so that we know what
 	 * authentication methods we can usefully try next.
 	 */
-	ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+	ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
 
 	s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
 	ssh2_pkt_addstring(s->pktout, s->username);
@@ -6946,6 +7618,9 @@
 	}
 
 	while (1) {
+	    char *methods = NULL;
+	    int methlen = 0;
+
 	    /*
 	     * Wait for the result of the last authentication request.
 	     */
@@ -6977,11 +7652,11 @@
 	    }
 	    if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
 		logevent("Access granted");
-		s->we_are_in = TRUE;
+		s->we_are_in = s->userauth_success = TRUE;
 		break;
 	    }
 
-	    if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
+	    if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) {
 		bombout(("Strange packet received during authentication: "
 			 "type %d", pktin->type));
 		crStopV;
@@ -6995,26 +7670,25 @@
 	     * helpfully try next.
 	     */
 	    if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
-		char *methods;
-		int methlen;
 		ssh_pkt_getstring(pktin, &methods, &methlen);
 		if (!ssh2_pkt_getbool(pktin)) {
 		    /*
 		     * We have received an unequivocal Access
 		     * Denied. This can translate to a variety of
-		     * messages:
-		     * 
-		     *  - if we'd just tried "none" authentication,
-		     *    it's not worth printing anything at all
-		     * 
-		     *  - if we'd just tried a public key _offer_,
-		     *    the message should be "Server refused our
-		     *    key" (or no message at all if the key
-		     *    came from Pageant)
-		     * 
-		     *  - if we'd just tried anything else, the
-		     *    message really should be "Access denied".
-		     * 
+		     * messages, or no message at all.
+                     *
+                     * For forms of authentication which are attempted
+                     * implicitly, by which I mean without printing
+                     * anything in the window indicating that we're
+                     * trying them, we should never print 'Access
+                     * denied'.
+                     *
+                     * If we do print a message saying that we're
+                     * attempting some kind of authentication, it's OK
+                     * to print a followup message saying it failed -
+                     * but the message may sometimes be more specific
+                     * than simply 'Access denied'.
+                     *
 		     * Additionally, if we'd just tried password
 		     * authentication, we should break out of this
 		     * whole loop so as to go back to the username
@@ -7027,14 +7701,31 @@
 			       s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
 			if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
 			    c_write_str(ssh, "Server refused our key\r\n");
-			logevent("Server refused public key");
+			logevent("Server refused our key");
+                    } else if (s->type == AUTH_TYPE_PUBLICKEY) {
+                        /* This _shouldn't_ happen except by a
+                         * protocol bug causing client and server to
+                         * disagree on what is a correct signature. */
+                        c_write_str(ssh, "Server refused public-key signature"
+                                    " despite accepting key!\r\n");
+                        logevent("Server refused public-key signature"
+                                 " despite accepting key!");
 		    } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
-			/* server declined keyboard-interactive; ignore */
-		    } else {
+                        /* quiet, so no c_write */
+                        logevent("Server refused keyboard-interactive authentication");
+		    } else if (s->type==AUTH_TYPE_GSSAPI) {
+			/* always quiet, so no c_write */
+                        /* also, the code down in the GSSAPI block has
+                         * already logged this in the Event Log */
+		    } else if (s->type == AUTH_TYPE_KEYBOARD_INTERACTIVE) {
+                        logevent("Keyboard-interactive authentication failed");
 			c_write_str(ssh, "Access denied\r\n");
-			logevent("Access denied");
-			if (s->type == AUTH_TYPE_PASSWORD &&
-			    ssh->cfg.change_username) {
+                    } else {
+                        assert(s->type == AUTH_TYPE_PASSWORD);
+                        logevent("Password authentication failed");
+			c_write_str(ssh, "Access denied\r\n");
+
+			if (ssh->cfg.change_username) {
 			    /* XXX perhaps we should allow
 			     * keyboard-interactive to do this too? */
 			    s->we_are_in = FALSE;
@@ -7052,9 +7743,16 @@
 		    in_commasep_string("password", methods, methlen);
 		s->can_keyb_inter = ssh->cfg.try_ki_auth &&
 		    in_commasep_string("keyboard-interactive", methods, methlen);
+#ifndef NO_GSSAPI
+		if (!ssh->gsslibs)
+		    ssh->gsslibs = ssh_gss_setup(&ssh->cfg);
+		s->can_gssapi = ssh->cfg.try_gssapi_auth &&
+		    in_commasep_string("gssapi-with-mic", methods, methlen) &&
+		    ssh->gsslibs->nlibraries > 0;
+#endif
 	    }
 
-	    ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+	    ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
 
 	    if (s->can_pubkey && !s->done_agent && s->nkeys) {
 
@@ -7062,8 +7760,7 @@
 		 * Attempt public-key authentication using a key from Pageant.
 		 */
 
-		ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-		ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+		ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
 
 		logeventf(ssh, "Trying Pageant key #%d", s->keyi);
 
@@ -7210,8 +7907,7 @@
 		struct ssh2_userkey *key;   /* not live over crReturn */
 		char *passphrase;	    /* not live over crReturn */
 
-		ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-		ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+		ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
 
 		s->tried_pubkey_config = TRUE;
 
@@ -7378,10 +8074,203 @@
 		    sfree(sigdata);
 
 		    ssh2_pkt_send(ssh, s->pktout);
+                    logevent("Sent public key signature");
 		    s->type = AUTH_TYPE_PUBLICKEY;
 		    key->alg->freekey(key->data);
 		}
 
+#ifndef NO_GSSAPI
+	    } else if (s->can_gssapi && !s->tried_gssapi) {
+
+		/* GSSAPI Authentication */
+
+		int micoffset, len;
+		char *data;
+		Ssh_gss_buf mic;
+		s->type = AUTH_TYPE_GSSAPI;
+		s->tried_gssapi = TRUE;
+		s->gotit = TRUE;
+		ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+
+		/*
+		 * Pick the highest GSS library on the preference
+		 * list.
+		 */
+		{
+		    int i, j;
+		    s->gsslib = NULL;
+		    for (i = 0; i < ngsslibs; i++) {
+			int want_id = ssh->cfg.ssh_gsslist[i];
+			for (j = 0; j < ssh->gsslibs->nlibraries; j++)
+			    if (ssh->gsslibs->libraries[j].id == want_id) {
+				s->gsslib = &ssh->gsslibs->libraries[j];
+				goto got_gsslib;   /* double break */
+			    }
+		    }
+		    got_gsslib:
+		    /*
+		     * We always expect to have found something in
+		     * the above loop: we only came here if there
+		     * was at least one viable GSS library, and the
+		     * preference list should always mention
+		     * everything and only change the order.
+		     */
+		    assert(s->gsslib);
+		}
+
+		if (s->gsslib->gsslogmsg)
+		    logevent(s->gsslib->gsslogmsg);
+
+		/* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
+		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+		ssh2_pkt_addstring(s->pktout, s->username);
+		ssh2_pkt_addstring(s->pktout, "ssh-connection");
+		ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
+                logevent("Attempting GSSAPI authentication");
+
+		/* add mechanism info */
+		s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);
+
+		/* number of GSSAPI mechanisms */
+		ssh2_pkt_adduint32(s->pktout,1);
+
+		/* length of OID + 2 */
+		ssh2_pkt_adduint32(s->pktout, s->gss_buf.length + 2);
+		ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE);
+
+		/* length of OID */
+		ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.length);
+
+		ssh_pkt_adddata(s->pktout, s->gss_buf.value,
+				s->gss_buf.length);
+		ssh2_pkt_send(ssh, s->pktout);
+		crWaitUntilV(pktin);
+		if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
+		    logevent("GSSAPI authentication request refused");
+		    continue;
+		}
+
+		/* check returned packet ... */
+
+		ssh_pkt_getstring(pktin, &data, &len);
+		s->gss_rcvtok.value = data;
+		s->gss_rcvtok.length = len;
+		if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
+		    ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
+		    ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
+		    memcmp((char *)s->gss_rcvtok.value + 2,
+			   s->gss_buf.value,s->gss_buf.length) ) {
+		    logevent("GSSAPI authentication - wrong response from server");
+		    continue;
+		}
+
+		/* now start running */
+		s->gss_stat = s->gsslib->import_name(s->gsslib,
+						     ssh->fullhostname,
+						     &s->gss_srv_name);
+		if (s->gss_stat != SSH_GSS_OK) {
+		    if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
+			logevent("GSSAPI import name failed - Bad service name");
+		    else
+			logevent("GSSAPI import name failed");
+		    continue;
+		}
+
+		/* fetch TGT into GSS engine */
+		s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx);
+
+		if (s->gss_stat != SSH_GSS_OK) {
+		    logevent("GSSAPI authentication failed to get credentials");
+		    s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+		    continue;
+		}
+
+		/* initial tokens are empty */
+		SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
+		SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
+
+		/* now enter the loop */
+		do {
+		    s->gss_stat = s->gsslib->init_sec_context
+			(s->gsslib,
+			 &s->gss_ctx,
+			 s->gss_srv_name,
+			 ssh->cfg.gssapifwd,
+			 &s->gss_rcvtok,
+			 &s->gss_sndtok);
+
+		    if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
+			s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
+			logevent("GSSAPI authentication initialisation failed");
+
+			if (s->gsslib->display_status(s->gsslib, s->gss_ctx,
+						      &s->gss_buf) == SSH_GSS_OK) {
+			    logevent(s->gss_buf.value);
+			    sfree(s->gss_buf.value);
+			}
+
+			break;
+		    }
+		    logevent("GSSAPI authentication initialised");
+
+		    /* Client and server now exchange tokens until GSSAPI
+		     * no longer says CONTINUE_NEEDED */
+
+		    if (s->gss_sndtok.length != 0) {
+			s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+			ssh_pkt_addstring_start(s->pktout);
+			ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length);
+			ssh2_pkt_send(ssh, s->pktout);
+			s->gsslib->free_tok(s->gsslib, &s->gss_sndtok);
+		    }
+
+		    if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
+			crWaitUntilV(pktin);
+			if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {
+			    logevent("GSSAPI authentication - bad server response");
+			    s->gss_stat = SSH_GSS_FAILURE;
+			    break;
+			}
+			ssh_pkt_getstring(pktin, &data, &len);
+			s->gss_rcvtok.value = data;
+			s->gss_rcvtok.length = len;
+		    }
+		} while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
+
+		if (s->gss_stat != SSH_GSS_OK) {
+		    s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+		    s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
+		    continue;
+		}
+		logevent("GSSAPI authentication loop finished OK");
+
+		/* Now send the MIC */
+
+		s->pktout = ssh2_pkt_init(0);
+		micoffset = s->pktout->length;
+		ssh_pkt_addstring_start(s->pktout);
+		ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len);
+		ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST);
+		ssh_pkt_addstring(s->pktout, s->username);
+		ssh_pkt_addstring(s->pktout, "ssh-connection");
+		ssh_pkt_addstring(s->pktout, "gssapi-with-mic");
+
+		s->gss_buf.value = (char *)s->pktout->data + micoffset;
+		s->gss_buf.length = s->pktout->length - micoffset;
+
+		s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic);
+		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);
+		ssh_pkt_addstring_start(s->pktout);
+		ssh_pkt_addstring_data(s->pktout, mic.value, mic.length);
+		ssh2_pkt_send(ssh, s->pktout);
+		s->gsslib->free_mic(s->gsslib, &mic);
+
+		s->gotit = FALSE;
+
+		s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+		s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
+		continue;
+#endif
 	    } else if (s->can_keyb_inter && !s->kbd_inter_refused) {
 
 		/*
@@ -7390,8 +8279,7 @@
 
 		s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
 
-		ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-		ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;
+		ssh->pkt_actx = SSH2_PKTCTX_KBDINTER;
 
 		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
 		ssh2_pkt_addstring(s->pktout, s->username);
@@ -7402,6 +8290,8 @@
 		ssh2_pkt_addstring(s->pktout, "");	/* lang */
 		ssh2_pkt_addstring(s->pktout, "");	/* submethods */
 		ssh2_pkt_send(ssh, s->pktout);
+                
+                logevent("Attempting keyboard-interactive authentication");
 
 		crWaitUntilV(pktin);
 		if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
@@ -7410,8 +8300,6 @@
 		     * user without actually issuing any prompts).
 		     * Give up on it entirely. */
 		    s->gotit = TRUE;
-		    if (pktin->type == SSH2_MSG_USERAUTH_FAILURE)
-			logevent("Keyboard-interactive authentication refused");
 		    s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
 		    s->kbd_inter_refused = TRUE; /* don't try it again */
 		    continue;
@@ -7435,26 +8323,9 @@
 		    ssh_pkt_getstring(pktin, &lang, &lang_len);
 		    s->cur_prompt = new_prompts(ssh->frontend);
 		    s->cur_prompt->to_server = TRUE;
-		    if (name_len) {
-			/* FIXME: better prefix to distinguish from
-			 * local prompts? */
-			s->cur_prompt->name =
-			    dupprintf("SSH server: %.*s", name_len, name);
-			s->cur_prompt->name_reqd = TRUE;
-		    } else {
-			s->cur_prompt->name =
-			    dupstr("SSH server authentication");
-			s->cur_prompt->name_reqd = FALSE;
-		    }
-		    /* FIXME: ugly to print "Using..." in prompt _every_
-		     * time round. Can this be done more subtly? */
-		    s->cur_prompt->instruction =
-			dupprintf("Using keyboard-interactive authentication.%s%.*s",
-				  inst_len ? "\n" : "", inst_len, inst);
-		    s->cur_prompt->instr_reqd = TRUE;
 
 		    /*
-		     * Get the prompts from the packet.
+		     * Get any prompt(s) from the packet.
 		     */
 		    s->num_prompts = ssh_pkt_getuint32(pktin);
 		    for (i = 0; i < s->num_prompts; i++) {
@@ -7475,10 +8346,38 @@
 				   echo, SSH_MAX_PASSWORD_LEN);
 		    }
 
+		    if (name_len) {
+			/* FIXME: better prefix to distinguish from
+			 * local prompts? */
+			s->cur_prompt->name =
+			    dupprintf("SSH server: %.*s", name_len, name);
+			s->cur_prompt->name_reqd = TRUE;
+		    } else {
+			s->cur_prompt->name =
+			    dupstr("SSH server authentication");
+			s->cur_prompt->name_reqd = FALSE;
+		    }
+		    /* We add a prefix to try to make it clear that a prompt
+		     * has come from the server.
+		     * FIXME: ugly to print "Using..." in prompt _every_
+		     * time round. Can this be done more subtly? */
+		    /* Special case: for reasons best known to themselves,
+		     * some servers send k-i requests with no prompts and
+		     * nothing to display. Keep quiet in this case. */
+		    if (s->num_prompts || name_len || inst_len) {
+			s->cur_prompt->instruction =
+			    dupprintf("Using keyboard-interactive authentication.%s%.*s",
+				      inst_len ? "\n" : "", inst_len, inst);
+			s->cur_prompt->instr_reqd = TRUE;
+		    } else {
+			s->cur_prompt->instr_reqd = FALSE;
+		    }
+
 		    /*
-		     * Get the user's responses.
+                     * Display any instructions, and get the user's
+                     * response(s).
 		     */
-		    if (s->num_prompts) {
+		    {
 			int ret; /* not live over crReturn */
 			ret = get_userpass_input(s->cur_prompt, NULL, 0);
 			while (ret < 0) {
@@ -7500,7 +8399,7 @@
 		    }
 
 		    /*
-		     * Send the responses to the server.
+		     * Send the response(s) to the server.
 		     */
 		    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
 		    ssh2_pkt_adduint32(s->pktout, s->num_prompts);
@@ -7512,6 +8411,13 @@
 		    }
 		    ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
 
+                    /*
+                     * Free the prompts structure from this iteration.
+                     * If there's another, a new one will be allocated
+                     * when we return to the top of this while loop.
+                     */
+                    free_prompts(s->cur_prompt);
+
 		    /*
 		     * Get the next packet in case it's another
 		     * INFO_REQUEST.
@@ -7533,8 +8439,7 @@
 		int ret; /* not live over crReturn */
 		int changereq_first_time; /* not live over crReturn */
 
-		ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-		ssh->pkt_ctx |= SSH2_PKTCTX_PASSWORD;
+		ssh->pkt_actx = SSH2_PKTCTX_PASSWORD;
 
 		s->cur_prompt = new_prompts(ssh->frontend);
 		s->cur_prompt->to_server = TRUE;
@@ -7756,11 +8661,16 @@
 		sfree(s->password);
 
 	    } else {
+		char *str = dupprintf("No supported authentication methods available"
+				      " (server sent: %.*s)",
+				      methlen, methods);
 
-		ssh_disconnect(ssh, NULL,
+		ssh_disconnect(ssh, str,
 			       "No supported authentication methods available",
 			       SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
 			       FALSE);
+		sfree(str);
+
 		crStopV;
 
 	    }
@@ -7777,6 +8687,20 @@
     if (s->agent_response)
 	sfree(s->agent_response);
 
+    if (s->userauth_success) {
+	/*
+	 * We've just received USERAUTH_SUCCESS, and we haven't sent any
+	 * packets since. Signal the transport layer to consider enacting
+	 * delayed compression.
+	 *
+	 * (Relying on we_are_in is not sufficient, as
+	 * draft-miller-secsh-compression-delayed is quite clear that it
+	 * triggers on USERAUTH_SUCCESS specifically, and we_are_in can
+	 * become set for other reasons.)
+	 */
+	do_ssh2_transport(ssh, "enabling delayed compression", -2, NULL);
+    }
+
     /*
      * Now the connection protocol has started, one way or another.
      */
@@ -7804,14 +8728,13 @@
 	 */
 	ssh->mainchan = snew(struct ssh_channel);
 	ssh->mainchan->ssh = ssh;
-	ssh->mainchan->localid = alloc_channel_id(ssh);
+	ssh2_channel_init(ssh->mainchan);
 	logeventf(ssh,
 		  "Opening direct-tcpip channel to %s:%d in place of session",
 		  ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port);
 	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
 	ssh2_pkt_addstring(s->pktout, "direct-tcpip");
 	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-	ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
 	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
 	ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */
 	ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);
@@ -7838,10 +8761,8 @@
 	ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
 	ssh->mainchan->halfopen = FALSE;
 	ssh->mainchan->type = CHAN_MAINSESSION;
-	ssh->mainchan->closes = 0;
 	ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
 	ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
-	bufchain_init(&ssh->mainchan->v.v2.outbuffer);
 	add234(ssh->channels, ssh->mainchan);
 	update_specials_menu(ssh->frontend);
 	logevent("Opened direct-tcpip channel");
@@ -7849,11 +8770,10 @@
     } else {
 	ssh->mainchan = snew(struct ssh_channel);
 	ssh->mainchan->ssh = ssh;
-	ssh->mainchan->localid = alloc_channel_id(ssh);
+	ssh2_channel_init(ssh->mainchan);
 	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
 	ssh2_pkt_addstring(s->pktout, "session");
 	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-	ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
 	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
 	ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);    /* our max pkt size */
 	ssh2_pkt_send(ssh, s->pktout);
@@ -7870,10 +8790,8 @@
 	ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
 	ssh->mainchan->halfopen = FALSE;
 	ssh->mainchan->type = CHAN_MAINSESSION;
-	ssh->mainchan->closes = 0;
 	ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
 	ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
-	bufchain_init(&ssh->mainchan->v.v2.outbuffer);
 	add234(ssh->channels, ssh->mainchan);
 	update_specials_menu(ssh->frontend);
 	logevent("Opened channel for session");
@@ -7898,21 +8816,33 @@
     ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =
 	ssh2_msg_channel_open;
 
+    if (ssh->mainchan && ssh->cfg.ssh_simple) {
+	/*
+	 * This message indicates to the server that we promise
+	 * not to try to run any other channel in parallel with
+	 * this one, so it's safe for it to advertise a very large
+	 * window and leave the flow control to TCP.
+	 */
+	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
+	ssh2_pkt_addstring(s->pktout, "simple at putty.projects.tartarus.org");
+	ssh2_pkt_addbool(s->pktout, 0); /* no reply */
+	ssh2_pkt_send(ssh, s->pktout);
+    }
+
     /*
      * Potentially enable X11 forwarding.
      */
-    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
-	char proto[20], data[64];
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward &&
+	(ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
+					  ssh->cfg.x11_auth, &ssh->cfg))) {
 	logevent("Requesting X11 forwarding");
-	ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
-				       data, sizeof(data), ssh->cfg.x11_auth);
-        x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
 	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
 	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
 	ssh2_pkt_addstring(s->pktout, "x11-req");
 	ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */
 	ssh2_pkt_addbool(s->pktout, 0);	       /* many connections */
-	ssh2_pkt_addstring(s->pktout, proto);
+	ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname);
 	/*
 	 * Note that while we blank the X authentication data here, we don't
 	 * take any special action to blank the start of an X11 channel,
@@ -7921,9 +8851,9 @@
 	 * cookie into the log.
 	 */
 	dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
-	ssh2_pkt_addstring(s->pktout, data);
+	ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring);
 	end_log_omission(ssh, s->pktout);
-	ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display));
+	ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum);
 	ssh2_pkt_send(ssh, s->pktout);
 
 	crWaitUntilV(pktin);
@@ -8153,6 +9083,13 @@
 	ssh_special(ssh, TS_EOF);
 
     /*
+     * All the initial channel requests are done, so install the default
+     * failure handler.
+     */
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success;
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure;
+
+    /*
      * Transfer data!
      */
     if (ssh->ldisc)
@@ -8186,7 +9123,7 @@
 	     * Try to send data on all channels if we can.
 	     */
 	    for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
-		ssh2_try_send_and_unthrottle(c);
+		ssh2_try_send_and_unthrottle(ssh, c);
 	}
     }
 
@@ -8200,7 +9137,7 @@
 {
     /* log reason code in disconnect message */
     char *buf, *msg;
-    int nowlen, reason, msglen;
+    int reason, msglen;
 
     reason = ssh_pkt_getuint32(pktin);
     ssh_pkt_getstring(pktin, &msg, &msglen);
@@ -8214,14 +9151,14 @@
     }
     logevent(buf);
     sfree(buf);
-    buf = dupprintf("Disconnection message text: %n%.*s",
-		    &nowlen, msglen, msg);
+    buf = dupprintf("Disconnection message text: %.*s",
+		    msglen, msg);
     logevent(buf);
-    bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
+    bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"",
 	     reason,
 	     (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
 	     ssh2_disconnect_reasons[reason] : "unknown",
-	     buf+nowlen));
+	     msglen, msg));
     sfree(buf);
 }
 
@@ -8230,10 +9167,9 @@
     /* log the debug message */
     char *msg;
     int msglen;
-    int always_display;
 
-    /* XXX maybe we should actually take notice of this */
-    always_display = ssh2_pkt_getbool(pktin);
+    /* XXX maybe we should actually take notice of the return value */
+    ssh2_pkt_getbool(pktin);
     ssh_pkt_getstring(pktin, &msg, &msglen);
 
     logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
@@ -8405,8 +9341,9 @@
     ssh->deferred_len = 0;
     ssh->deferred_size = 0;
     ssh->fallback_cmd = 0;
-    ssh->pkt_ctx = 0;
-    ssh->x11auth = NULL;
+    ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
+    ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
+    ssh->x11disp = NULL;
     ssh->v1_compressing = FALSE;
     ssh->v2_outgoing_sequence = 0;
     ssh->ssh1_rdpkt_crstate = 0;
@@ -8452,7 +9389,7 @@
     ssh->send_ok = 0;
     ssh->editing = 0;
     ssh->echoing = 0;
-    ssh->v1_throttle_count = 0;
+    ssh->conn_throttle_count = 0;
     ssh->overall_bufsize = 0;
     ssh->fallback_cmd = 0;
 
@@ -8467,6 +9404,10 @@
     ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
     ssh->kex_in_progress = FALSE;
 
+#ifndef NO_GSSAPI
+    ssh->gsslibs = NULL;
+#endif
+
     p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
     if (p != NULL)
 	return p;
@@ -8527,6 +9468,7 @@
 		    x11_close(c->u.x11.s);
 		break;
 	      case CHAN_SOCKDATA:
+	      case CHAN_SOCKDATA_DORMANT:
 		if (c->u.pfd.s != NULL)
 		    pfd_close(c->u.pfd.s);
 		break;
@@ -8539,19 +9481,20 @@
 
     if (ssh->rportfwds) {
 	while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
-	    sfree(pf);
+	    free_rportfwd(pf);
 	freetree234(ssh->rportfwds);
 	ssh->rportfwds = NULL;
     }
     sfree(ssh->deferred_send_data);
-    if (ssh->x11auth)
-	x11_free_auth(ssh->x11auth);
+    if (ssh->x11disp)
+	x11_free_display(ssh->x11disp);
     sfree(ssh->do_ssh_init_state);
     sfree(ssh->do_ssh1_login_state);
     sfree(ssh->do_ssh2_transport_state);
     sfree(ssh->do_ssh2_authconn_state);
     sfree(ssh->v_c);
     sfree(ssh->v_s);
+    sfree(ssh->fullhostname);
     if (ssh->crcda_ctx) {
 	crcda_free_context(ssh->crcda_ctx);
 	ssh->crcda_ctx = NULL;
@@ -8562,6 +9505,10 @@
     if (ssh->pinger)
 	pinger_free(ssh->pinger);
     bufchain_clear(&ssh->queued_incoming_data);
+#ifndef NO_GSSAPI
+    if (ssh->gsslibs)
+	ssh_gss_cleanup(ssh->gsslibs);
+#endif
     sfree(ssh);
 
     random_unref();
@@ -8722,14 +9669,16 @@
     static const struct telnet_special ssh1_ignore_special[] = {
 	{"IGNORE message", TS_NOP}
     };
-    static const struct telnet_special ssh2_transport_specials[] = {
+    static const struct telnet_special ssh2_ignore_special[] = {
 	{"IGNORE message", TS_NOP},
+    };
+    static const struct telnet_special ssh2_rekey_special[] = {
 	{"Repeat key exchange", TS_REKEY},
     };
     static const struct telnet_special ssh2_session_specials[] = {
 	{NULL, TS_SEP},
 	{"Break", TS_BRK},
-	/* These are the signal names defined by draft-ietf-secsh-connect-23.
+	/* These are the signal names defined by RFC 4254.
 	 * They include all the ISO C signals, but are a subset of the POSIX
 	 * required signals. */
 	{"SIGINT (Interrupt)", TS_SIGINT},
@@ -8748,7 +9697,8 @@
 	{NULL, TS_EXITMENU}
     };
     /* XXX review this length for any changes: */
-    static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) +
+    static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) +
+					      lenof(ssh2_rekey_special) +
 					      lenof(ssh2_session_specials) +
 					      lenof(specials_end)];
     Ssh ssh = (Ssh) handle;
@@ -8767,7 +9717,10 @@
 	if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
 	    ADD_SPECIALS(ssh1_ignore_special);
     } else if (ssh->version == 2) {
-	ADD_SPECIALS(ssh2_transport_specials);
+	if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))
+	    ADD_SPECIALS(ssh2_ignore_special);
+	if (!(ssh->remote_bugs & BUG_SSH2_REKEY))
+	    ADD_SPECIALS(ssh2_rekey_special);
 	if (ssh->mainchan)
 	    ADD_SPECIALS(ssh2_session_specials);
     } /* else we're not ready yet */
@@ -8817,9 +9770,11 @@
 	    if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
 		send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
 	} else {
-	    pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
-	    ssh2_pkt_addstring_start(pktout);
-	    ssh2_pkt_send_noqueue(ssh, pktout);
+	    if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+		pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
+		ssh2_pkt_addstring_start(pktout);
+		ssh2_pkt_send_noqueue(ssh, pktout);
+	    }
 	}
     } else if (code == TS_REKEY) {
 	if (!ssh->kex_in_progress && ssh->version == 2) {
@@ -8878,17 +9833,13 @@
     Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
     c = snew(struct ssh_channel);
-    c->ssh = ssh;
 
-    if (c) {
-	c->halfopen = TRUE;
-	c->localid = alloc_channel_id(ssh);
-	c->closes = 0;
-	c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
-	c->u.pfd.s = s;
-	bufchain_init(&c->v.v2.outbuffer);
-	add234(ssh->channels, c);
-    }
+    c->ssh = ssh;
+    ssh2_channel_init(c);
+    c->halfopen = TRUE;
+    c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
+    c->u.pfd.s = s;
+    add234(ssh->channels, c);
     return c;
 }
 
@@ -8899,13 +9850,27 @@
 static void ssh_unthrottle(void *handle, int bufsize)
 {
     Ssh ssh = (Ssh) handle;
+    int buflimit;
+
     if (ssh->version == 1) {
 	if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
 	    ssh->v1_stdout_throttling = 0;
-	    ssh1_throttle(ssh, -1);
+	    ssh_throttle_conn(ssh, -1);
 	}
     } else {
-	ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
+	if (ssh->mainchan) {
+	    ssh2_set_window(ssh->mainchan,
+			    bufsize < ssh->mainchan->v.v2.locmaxwin ?
+			    ssh->mainchan->v.v2.locmaxwin - bufsize : 0);
+	    if (ssh->cfg.ssh_simple)
+		buflimit = 0;
+	    else
+		buflimit = ssh->mainchan->v.v2.locmaxwin;
+	    if (ssh->mainchan->throttling_conn && bufsize <= buflimit) {
+		ssh->mainchan->throttling_conn = 0;
+		ssh_throttle_conn(ssh, -1);
+	    }
+	}
     }
 }
 
@@ -8928,7 +9893,6 @@
 	pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
 	ssh2_pkt_addstring(pktout, "direct-tcpip");
 	ssh2_pkt_adduint32(pktout, c->localid);
-	c->v.v2.locwindow = OUR_V2_WINSIZE;
 	ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
 	ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
 	ssh2_pkt_addstring(pktout, hostname);
@@ -9029,5 +9993,7 @@
     ssh_provide_logctx,
     ssh_unthrottle,
     ssh_cfg_info,
+    "ssh",
+    PROT_SSH,
     22
 };

Modified: trunk/Tools/GMEplink/SSH.H
==============================================================================
--- trunk/Tools/GMEplink/SSH.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSH.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -2,6 +2,7 @@
 #include <string.h>
 
 #include "puttymem.h"
+#include "tree234.h"
 #include "network.h"
 #include "int64.h"
 #include "misc.h"
@@ -70,8 +71,12 @@
 int rsa_public_blob_len(void *data, int maxlen);
 void freersakey(struct RSAKey *key);
 
-typedef unsigned int word32;
+#ifndef PUTTY_UINT32_DEFINED
+/* This makes assumptions about the int type. */
 typedef unsigned int uint32;
+#define PUTTY_UINT32_DEFINED
+#endif
+typedef uint32 word32;
 
 unsigned long crc32_compute(const void *s, size_t len);
 unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);
@@ -82,6 +87,17 @@
 int detect_attack(void *handle, unsigned char *buf, uint32 len,
 		  unsigned char *IV);
 
+/*
+ * SSH2 RSA key exchange functions
+ */
+struct ssh_hash;
+void *ssh_rsakex_newkey(char *data, int len);
+void ssh_rsakex_freekey(void *key);
+int ssh_rsakex_klen(void *key);
+void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+                        unsigned char *out, int outlen,
+                        void *key);
+
 typedef struct {
     uint32 h[4];
 } MD5_Core_State;
@@ -178,8 +194,14 @@
     void *(*make_context)(void);
     void (*free_context)(void *);
     void (*setkey) (void *, unsigned char *key);
+    /* whole-packet operations */
     void (*generate) (void *, unsigned char *blk, int len, unsigned long seq);
     int (*verify) (void *, unsigned char *blk, int len, unsigned long seq);
+    /* partial-packet operations */
+    void (*start) (void *);
+    void (*bytes) (void *, unsigned char const *, int);
+    void (*genresult) (void *, unsigned char *);
+    int (*verresult) (void *, unsigned char const *);
     char *name;
     int len;
     char *text_name;
@@ -194,15 +216,10 @@
 };   
 
 struct ssh_kex {
-    /*
-     * Plugging in another KEX algorithm requires structural chaos,
-     * so it's hard to abstract them into nice little structures
-     * like this. Fortunately, all our KEXes are basically
-     * Diffie-Hellman at the moment, so in this structure I simply
-     * parametrise the DH exchange a bit.
-     */
     char *name, *groupname;
-    const unsigned char *pdata, *gdata;/* NULL means use group exchange */
+    enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
+    /* For DH */
+    const unsigned char *pdata, *gdata; /* NULL means group exchange */
     int plen, glen;
     const struct ssh_hash *hash;
 };
@@ -234,6 +251,9 @@
 
 struct ssh_compress {
     char *name;
+    /* For zlib at openssh.com: if non-NULL, this name will be considered once
+     * userauth has completed successfully. */
+    char *delayed_name;
     void *(*compress_init) (void);
     void (*compress_cleanup) (void *);
     int (*compress) (void *, unsigned char *block, int len,
@@ -268,6 +288,7 @@
 extern const struct ssh_kexes ssh_diffiehellman_group1;
 extern const struct ssh_kexes ssh_diffiehellman_group14;
 extern const struct ssh_kexes ssh_diffiehellman_gex;
+extern const struct ssh_kexes ssh_rsa_kex;
 extern const struct ssh_signkey ssh_dss;
 extern const struct ssh_signkey ssh_rsa;
 extern const struct ssh_mac ssh_hmac_md5;
@@ -276,6 +297,14 @@
 extern const struct ssh_mac ssh_hmac_sha1_96;
 extern const struct ssh_mac ssh_hmac_sha1_96_buggy;
 
+void *aes_make_context(void);
+void aes_free_context(void *handle);
+void aes128_key(void *handle, unsigned char *key);
+void aes192_key(void *handle, unsigned char *key);
+void aes256_key(void *handle, unsigned char *key);
+void aes_iv(void *handle, unsigned char *iv);
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len);
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len);
 
 /*
  * PuTTY version number formatted as an SSH version string. 
@@ -320,27 +349,85 @@
 extern void pfd_override_throttle(Socket s, int enable);
 
 /* Exports from x11fwd.c */
-extern const char *x11_init(Socket *, char *, void *, void *, const char *,
-			    int, const Config *);
+enum {
+    X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256
+};
+struct X11Display {
+    /* Broken-down components of the display name itself */
+    int unixdomain;
+    char *hostname;
+    int displaynum;
+    int screennum;
+    /* OSX sometimes replaces all the above with a full Unix-socket pathname */
+    char *unixsocketpath;
+
+    /* PuTTY networking SockAddr to connect to the display, and associated
+     * gubbins */
+    SockAddr addr;
+    int port;
+    char *realhost;
+
+    /* Auth details we invented for the virtual display on the SSH server. */
+    int remoteauthproto;
+    unsigned char *remoteauthdata;
+    int remoteauthdatalen;
+    char *remoteauthprotoname;
+    char *remoteauthdatastring;
+
+    /* Our local auth details for talking to the real X display. */
+    int localauthproto;
+    unsigned char *localauthdata;
+    int localauthdatalen;
+
+    /*
+     * Used inside x11fwd.c to remember recently seen
+     * XDM-AUTHORIZATION-1 strings, to avoid replay attacks.
+     */
+    tree234 *xdmseen;
+};
+/*
+ * x11_setup_display() parses the display variable and fills in an
+ * X11Display structure. Some remote auth details are invented;
+ * the supplied authtype parameter configures the preferred
+ * authorisation protocol to use at the remote end. The local auth
+ * details are looked up by calling platform_get_x11_auth.
+ */
+extern struct X11Display *x11_setup_display(char *display, int authtype,
+					    const Config *);
+void x11_free_display(struct X11Display *disp);
+extern const char *x11_init(Socket *, struct X11Display *, void *,
+			    const char *, int, const Config *);
 extern void x11_close(Socket);
 extern int x11_send(Socket, char *, int);
-extern void *x11_invent_auth(char *, int, char *, int, int);
-extern void x11_free_auth(void *);
 extern void x11_unthrottle(Socket s);
 extern void x11_override_throttle(Socket s, int enable);
-extern int x11_get_screen_number(char *display);
-void x11_get_real_auth(void *authv, char *display);
 char *x11_display(const char *display);
-
 /* Platform-dependent X11 functions */
-extern void platform_get_x11_auth(char *display, int *proto,
-                                  unsigned char *data, int *datalen);
-extern const char platform_x11_best_transport[];
-/* best X11 hostname for this platform if none specified */
-SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname);
-/* make up a SockAddr naming the address for displaynum */
+extern void platform_get_x11_auth(struct X11Display *display,
+				  const Config *);
+    /* examine a mostly-filled-in X11Display and fill in localauth* */
+extern const int platform_uses_x11_unix_by_default;
+    /* choose default X transport in the absence of a specified one */
+SockAddr platform_get_x11_unix_address(const char *path, int displaynum);
+    /* make up a SockAddr naming the address for displaynum */
 char *platform_get_x_display(void);
-/* allocated local X display string, if any */
+    /* allocated local X display string, if any */
+/* Callbacks in x11.c usable _by_ platform X11 functions */
+/*
+ * This function does the job of platform_get_x11_auth, provided
+ * it is told where to find a normally formatted .Xauthority file:
+ * it opens that file, parses it to find an auth record which
+ * matches the display details in "display", and fills in the
+ * localauth fields.
+ *
+ * It is expected that most implementations of
+ * platform_get_x11_auth() will work by finding their system's
+ * .Xauthority file, adjusting the display details if necessary
+ * for local oddities like Unix-domain socket transport, and
+ * calling this function to do the rest of the work.
+ */
+void x11_get_auth_from_authfile(struct X11Display *display,
+				const char *authfilename);
 
 Bignum copybn(Bignum b);
 Bignum bn_power_2(int n);
@@ -363,6 +450,8 @@
 Bignum biggcd(Bignum a, Bignum b);
 unsigned short bignum_mod_short(Bignum number, unsigned short modulus);
 Bignum bignum_add_long(Bignum number, unsigned long addend);
+Bignum bigadd(Bignum a, Bignum b);
+Bignum bigsub(Bignum a, Bignum b);
 Bignum bigmul(Bignum a, Bignum b);
 Bignum bigmuladd(Bignum a, Bignum b, Bignum addend);
 Bignum bigdiv(Bignum a, Bignum b);

Modified: trunk/Tools/GMEplink/SSHAES.C
==============================================================================
--- trunk/Tools/GMEplink/SSHAES.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHAES.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -1097,35 +1097,35 @@
     memcpy(ctx->iv, iv, sizeof(iv));
 }
 
-static void *aes_make_context(void)
+void *aes_make_context(void)
 {
     return snew(AESContext);
 }
 
-static void aes_free_context(void *handle)
+void aes_free_context(void *handle)
 {
     sfree(handle);
 }
 
-static void aes128_key(void *handle, unsigned char *key)
+void aes128_key(void *handle, unsigned char *key)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, 16, key, 16);
 }
 
-static void aes192_key(void *handle, unsigned char *key)
+void aes192_key(void *handle, unsigned char *key)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, 16, key, 24);
 }
 
-static void aes256_key(void *handle, unsigned char *key)
+void aes256_key(void *handle, unsigned char *key)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, 16, key, 32);
 }
 
-static void aes_iv(void *handle, unsigned char *iv)
+void aes_iv(void *handle, unsigned char *iv)
 {
     AESContext *ctx = (AESContext *)handle;
     int i;
@@ -1133,13 +1133,13 @@
 	ctx->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);
 }
 
-static void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_encrypt_cbc(blk, len, ctx);
 }
 
-static void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_decrypt_cbc(blk, len, ctx);

Modified: trunk/Tools/GMEplink/SSHBN.C
==============================================================================
--- trunk/Tools/GMEplink/SSHBN.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHBN.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -51,7 +51,34 @@
     __asm mov r, edx \
     __asm mov q, eax \
 } while(0)
+#elif defined _LP64
+/* 64-bit architectures can do 32x32->64 chunks at a time */
+typedef unsigned int BignumInt;
+typedef unsigned long BignumDblInt;
+#define BIGNUM_INT_MASK  0xFFFFFFFFU
+#define BIGNUM_TOP_BIT   0x80000000U
+#define BIGNUM_INT_BITS  32
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \
+    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
+    q = n / w; \
+    r = n % w; \
+} while (0)
+#elif defined _LLP64
+/* 64-bit architectures in which unsigned long is 32 bits, not 64 */
+typedef unsigned long BignumInt;
+typedef unsigned long long BignumDblInt;
+#define BIGNUM_INT_MASK  0xFFFFFFFFUL
+#define BIGNUM_TOP_BIT   0x80000000UL
+#define BIGNUM_INT_BITS  32
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \
+    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
+    q = n / w; \
+    r = n % w; \
+} while (0)
 #else
+/* Fallback for all other cases */
 typedef unsigned short BignumInt;
 typedef unsigned long BignumDblInt;
 #define BIGNUM_INT_MASK  0xFFFFU
@@ -133,29 +160,432 @@
 }
 
 /*
+ * Internal addition. Sets c = a - b, where 'a', 'b' and 'c' are all
+ * big-endian arrays of 'len' BignumInts. Returns a BignumInt carried
+ * off the top.
+ */
+static BignumInt internal_add(const BignumInt *a, const BignumInt *b,
+                              BignumInt *c, int len)
+{
+    int i;
+    BignumDblInt carry = 0;
+
+    for (i = len-1; i >= 0; i--) {
+        carry += (BignumDblInt)a[i] + b[i];
+        c[i] = (BignumInt)carry;
+        carry >>= BIGNUM_INT_BITS;
+    }
+
+    return (BignumInt)carry;
+}
+
+/*
+ * Internal subtraction. Sets c = a - b, where 'a', 'b' and 'c' are
+ * all big-endian arrays of 'len' BignumInts. Any borrow from the top
+ * is ignored.
+ */
+static void internal_sub(const BignumInt *a, const BignumInt *b,
+                         BignumInt *c, int len)
+{
+    int i;
+    BignumDblInt carry = 1;
+
+    for (i = len-1; i >= 0; i--) {
+        carry += (BignumDblInt)a[i] + (b[i] ^ BIGNUM_INT_MASK);
+        c[i] = (BignumInt)carry;
+        carry >>= BIGNUM_INT_BITS;
+    }
+}
+
+/*
  * Compute c = a * b.
  * Input is in the first len words of a and b.
  * Result is returned in the first 2*len words of c.
+ *
+ * 'scratch' must point to an array of BignumInt of size at least
+ * mul_compute_scratch(len). (This covers the needs of internal_mul
+ * and all its recursive calls to itself.)
+ */
+#define KARATSUBA_THRESHOLD 50
+static int mul_compute_scratch(int len)
+{
+    int ret = 0;
+    while (len > KARATSUBA_THRESHOLD) {
+        int toplen = len/2, botlen = len - toplen; /* botlen is the bigger */
+        int midlen = botlen + 1;
+        ret += 4*midlen;
+        len = midlen;
+    }
+    return ret;
+}
+static void internal_mul(const BignumInt *a, const BignumInt *b,
+			 BignumInt *c, int len, BignumInt *scratch)
+{
+    if (len > KARATSUBA_THRESHOLD) {
+        int i;
+
+        /*
+         * Karatsuba divide-and-conquer algorithm. Cut each input in
+         * half, so that it's expressed as two big 'digits' in a giant
+         * base D:
+         *
+         *   a = a_1 D + a_0
+         *   b = b_1 D + b_0
+         *
+         * Then the product is of course
+         *
+         *  ab = a_1 b_1 D^2 + (a_1 b_0 + a_0 b_1) D + a_0 b_0
+         *
+         * and we compute the three coefficients by recursively
+         * calling ourself to do half-length multiplications.
+         *
+         * The clever bit that makes this worth doing is that we only
+         * need _one_ half-length multiplication for the central
+         * coefficient rather than the two that it obviouly looks
+         * like, because we can use a single multiplication to compute
+         *
+         *   (a_1 + a_0) (b_1 + b_0) = a_1 b_1 + a_1 b_0 + a_0 b_1 + a_0 b_0
+         *
+         * and then we subtract the other two coefficients (a_1 b_1
+         * and a_0 b_0) which we were computing anyway.
+         *
+         * Hence we get to multiply two numbers of length N in about
+         * three times as much work as it takes to multiply numbers of
+         * length N/2, which is obviously better than the four times
+         * as much work it would take if we just did a long
+         * conventional multiply.
+         */
+
+        int toplen = len/2, botlen = len - toplen; /* botlen is the bigger */
+        int midlen = botlen + 1;
+        BignumDblInt carry;
+#ifdef KARA_DEBUG
+        int i;
+#endif
+
+        /*
+         * The coefficients a_1 b_1 and a_0 b_0 just avoid overlapping
+         * in the output array, so we can compute them immediately in
+         * place.
+         */
+
+#ifdef KARA_DEBUG
+        printf("a1,a0 = 0x");
+        for (i = 0; i < len; i++) {
+            if (i == toplen) printf(", 0x");
+            printf("%0*x", BIGNUM_INT_BITS/4, a[i]);
+        }
+        printf("\n");
+        printf("b1,b0 = 0x");
+        for (i = 0; i < len; i++) {
+            if (i == toplen) printf(", 0x");
+            printf("%0*x", BIGNUM_INT_BITS/4, b[i]);
+        }
+        printf("\n");
+#endif
+
+        /* a_1 b_1 */
+        internal_mul(a, b, c, toplen, scratch);
+#ifdef KARA_DEBUG
+        printf("a1b1 = 0x");
+        for (i = 0; i < 2*toplen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, c[i]);
+        }
+        printf("\n");
+#endif
+
+        /* a_0 b_0 */
+        internal_mul(a + toplen, b + toplen, c + 2*toplen, botlen, scratch);
+#ifdef KARA_DEBUG
+        printf("a0b0 = 0x");
+        for (i = 0; i < 2*botlen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, c[2*toplen+i]);
+        }
+        printf("\n");
+#endif
+
+        /* Zero padding. midlen exceeds toplen by at most 2, so just
+         * zero the first two words of each input and the rest will be
+         * copied over. */
+        scratch[0] = scratch[1] = scratch[midlen] = scratch[midlen+1] = 0;
+
+        for (i = 0; i < toplen; i++) {
+            scratch[midlen - toplen + i] = a[i]; /* a_1 */
+            scratch[2*midlen - toplen + i] = b[i]; /* b_1 */
+        }
+
+        /* compute a_1 + a_0 */
+        scratch[0] = internal_add(scratch+1, a+toplen, scratch+1, botlen);
+#ifdef KARA_DEBUG
+        printf("a1plusa0 = 0x");
+        for (i = 0; i < midlen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[i]);
+        }
+        printf("\n");
+#endif
+        /* compute b_1 + b_0 */
+        scratch[midlen] = internal_add(scratch+midlen+1, b+toplen,
+                                       scratch+midlen+1, botlen);
+#ifdef KARA_DEBUG
+        printf("b1plusb0 = 0x");
+        for (i = 0; i < midlen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[midlen+i]);
+        }
+        printf("\n");
+#endif
+
+        /*
+         * Now we can do the third multiplication.
+         */
+        internal_mul(scratch, scratch + midlen, scratch + 2*midlen, midlen,
+                     scratch + 4*midlen);
+#ifdef KARA_DEBUG
+        printf("a1plusa0timesb1plusb0 = 0x");
+        for (i = 0; i < 2*midlen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[2*midlen+i]);
+        }
+        printf("\n");
+#endif
+
+        /*
+         * Now we can reuse the first half of 'scratch' to compute the
+         * sum of the outer two coefficients, to subtract from that
+         * product to obtain the middle one.
+         */
+        scratch[0] = scratch[1] = scratch[2] = scratch[3] = 0;
+        for (i = 0; i < 2*toplen; i++)
+            scratch[2*midlen - 2*toplen + i] = c[i];
+        scratch[1] = internal_add(scratch+2, c + 2*toplen,
+                                  scratch+2, 2*botlen);
+#ifdef KARA_DEBUG
+        printf("a1b1plusa0b0 = 0x");
+        for (i = 0; i < 2*midlen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[i]);
+        }
+        printf("\n");
+#endif
+
+        internal_sub(scratch + 2*midlen, scratch,
+                     scratch + 2*midlen, 2*midlen);
+#ifdef KARA_DEBUG
+        printf("a1b0plusa0b1 = 0x");
+        for (i = 0; i < 2*midlen; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[2*midlen+i]);
+        }
+        printf("\n");
+#endif
+
+        /*
+         * And now all we need to do is to add that middle coefficient
+         * back into the output. We may have to propagate a carry
+         * further up the output, but we can be sure it won't
+         * propagate right the way off the top.
+         */
+        carry = internal_add(c + 2*len - botlen - 2*midlen,
+                             scratch + 2*midlen,
+                             c + 2*len - botlen - 2*midlen, 2*midlen);
+        i = 2*len - botlen - 2*midlen - 1;
+        while (carry) {
+            assert(i >= 0);
+            carry += c[i];
+            c[i] = (BignumInt)carry;
+            carry >>= BIGNUM_INT_BITS;
+            i--;
+        }
+#ifdef KARA_DEBUG
+        printf("ab = 0x");
+        for (i = 0; i < 2*len; i++) {
+            printf("%0*x", BIGNUM_INT_BITS/4, c[i]);
+        }
+        printf("\n");
+#endif
+
+    } else {
+        int i;
+        BignumInt carry;
+        BignumDblInt t;
+        const BignumInt *ap, *bp;
+        BignumInt *cp, *cps;
+
+        /*
+         * Multiply in the ordinary O(N^2) way.
+         */
+
+        for (i = 0; i < 2 * len; i++)
+            c[i] = 0;
+
+        for (cps = c + 2*len, ap = a + len; ap-- > a; cps--) {
+            carry = 0;
+            for (cp = cps, bp = b + len; cp--, bp-- > b ;) {
+                t = (MUL_WORD(*ap, *bp) + carry) + *cp;
+                *cp = (BignumInt) t;
+                carry = (BignumInt)(t >> BIGNUM_INT_BITS);
+            }
+            *cp = carry;
+        }
+    }
+}
+
+/*
+ * Variant form of internal_mul used for the initial step of
+ * Montgomery reduction. Only bothers outputting 'len' words
+ * (everything above that is thrown away).
+ */
+static void internal_mul_low(const BignumInt *a, const BignumInt *b,
+                             BignumInt *c, int len, BignumInt *scratch)
+{
+    if (len > KARATSUBA_THRESHOLD) {
+        int i;
+
+        /*
+         * Karatsuba-aware version of internal_mul_low. As before, we
+         * express each input value as a shifted combination of two
+         * halves:
+         *
+         *   a = a_1 D + a_0
+         *   b = b_1 D + b_0
+         *
+         * Then the full product is, as before,
+         *
+         *  ab = a_1 b_1 D^2 + (a_1 b_0 + a_0 b_1) D + a_0 b_0
+         *
+         * Provided we choose D on the large side (so that a_0 and b_0
+         * are _at least_ as long as a_1 and b_1), we don't need the
+         * topmost term at all, and we only need half of the middle
+         * term. So there's no point in doing the proper Karatsuba
+         * optimisation which computes the middle term using the top
+         * one, because we'd take as long computing the top one as
+         * just computing the middle one directly.
+         *
+         * So instead, we do a much more obvious thing: we call the
+         * fully optimised internal_mul to compute a_0 b_0, and we
+         * recursively call ourself to compute the _bottom halves_ of
+         * a_1 b_0 and a_0 b_1, each of which we add into the result
+         * in the obvious way.
+         *
+         * In other words, there's no actual Karatsuba _optimisation_
+         * in this function; the only benefit in doing it this way is
+         * that we call internal_mul proper for a large part of the
+         * work, and _that_ can optimise its operation.
+         */
+
+        int toplen = len/2, botlen = len - toplen; /* botlen is the bigger */
+
+        /*
+         * Scratch space for the various bits and pieces we're going
+         * to be adding together: we need botlen*2 words for a_0 b_0
+         * (though we may end up throwing away its topmost word), and
+         * toplen words for each of a_1 b_0 and a_0 b_1. That adds up
+         * to exactly 2*len.
+         */
+
+        /* a_0 b_0 */
+        internal_mul(a + toplen, b + toplen, scratch + 2*toplen, botlen,
+                     scratch + 2*len);
+
+        /* a_1 b_0 */
+        internal_mul_low(a, b + len - toplen, scratch + toplen, toplen,
+                         scratch + 2*len);
+
+        /* a_0 b_1 */
+        internal_mul_low(a + len - toplen, b, scratch, toplen,
+                         scratch + 2*len);
+
+        /* Copy the bottom half of the big coefficient into place */
+        for (i = 0; i < botlen; i++)
+            c[toplen + i] = scratch[2*toplen + botlen + i];
+
+        /* Add the two small coefficients, throwing away the returned carry */
+        internal_add(scratch, scratch + toplen, scratch, toplen);
+
+        /* And add that to the large coefficient, leaving the result in c. */
+        internal_add(scratch, scratch + 2*toplen + botlen - toplen,
+                     c, toplen);
+
+    } else {
+        int i;
+        BignumInt carry;
+        BignumDblInt t;
+        const BignumInt *ap, *bp;
+        BignumInt *cp, *cps;
+
+        /*
+         * Multiply in the ordinary O(N^2) way.
+         */
+
+        for (i = 0; i < len; i++)
+            c[i] = 0;
+
+        for (cps = c + len, ap = a + len; ap-- > a; cps--) {
+            carry = 0;
+            for (cp = cps, bp = b + len; bp--, cp-- > c ;) {
+                t = (MUL_WORD(*ap, *bp) + carry) + *cp;
+                *cp = (BignumInt) t;
+                carry = (BignumInt)(t >> BIGNUM_INT_BITS);
+            }
+        }
+    }
+}
+
+/*
+ * Montgomery reduction. Expects x to be a big-endian array of 2*len
+ * BignumInts whose value satisfies 0 <= x < rn (where r = 2^(len *
+ * BIGNUM_INT_BITS) is the Montgomery base). Returns in the same array
+ * a value x' which is congruent to xr^{-1} mod n, and satisfies 0 <=
+ * x' < n.
+ *
+ * 'n' and 'mninv' should be big-endian arrays of 'len' BignumInts
+ * each, containing respectively n and the multiplicative inverse of
+ * -n mod r.
+ *
+ * 'tmp' is an array of BignumInt used as scratch space, of length at
+ * least 3*len + mul_compute_scratch(len).
  */
-static void internal_mul(BignumInt *a, BignumInt *b,
-			 BignumInt *c, int len)
+static void monty_reduce(BignumInt *x, const BignumInt *n,
+                         const BignumInt *mninv, BignumInt *tmp, int len)
 {
-    int i, j;
-    BignumDblInt t;
+    int i;
+    BignumInt carry;
 
-    for (j = 0; j < 2 * len; j++)
-	c[j] = 0;
+    /*
+     * Multiply x by (-n)^{-1} mod r. This gives us a value m such
+     * that mn is congruent to -x mod r. Hence, mn+x is an exact
+     * multiple of r, and is also (obviously) congruent to x mod n.
+     */
+    internal_mul_low(x + len, mninv, tmp, len, tmp + 3*len);
 
-    for (i = len - 1; i >= 0; i--) {
-	t = 0;
-	for (j = len - 1; j >= 0; j--) {
-	    t += MUL_WORD(a[i], (BignumDblInt) b[j]);
-	    t += (BignumDblInt) c[i + j + 1];
-	    c[i + j + 1] = (BignumInt) t;
-	    t = t >> BIGNUM_INT_BITS;
-	}
-	c[i] = (BignumInt) t;
+    /*
+     * Compute t = (mn+x)/r in ordinary, non-modular, integer
+     * arithmetic. By construction this is exact, and is congruent mod
+     * n to x * r^{-1}, i.e. the answer we want.
+     *
+     * The following multiply leaves that answer in the _most_
+     * significant half of the 'x' array, so then we must shift it
+     * down.
+     */
+    internal_mul(tmp, n, tmp+len, len, tmp + 3*len);
+    carry = internal_add(x, tmp+len, x, 2*len);
+    for (i = 0; i < len; i++)
+        x[len + i] = x[i], x[i] = 0;
+
+    /*
+     * Reduce t mod n. This doesn't require a full-on division by n,
+     * but merely a test and single optional subtraction, since we can
+     * show that 0 <= t < 2n.
+     *
+     * Proof:
+     *  + we computed m mod r, so 0 <= m < r.
+     *  + so 0 <= mn < rn, obviously
+     *  + hence we only need 0 <= x < rn to guarantee that 0 <= mn+x < 2rn
+     *  + yielding 0 <= (mn+x)/r < 2n as required.
+     */
+    if (!carry) {
+        for (i = 0; i < len; i++)
+            if (x[len + i] != n[i])
+                break;
     }
+    if (carry || i >= len || x[len + i] > n[i])
+        internal_sub(x+len, n, x+len, len);
 }
 
 static void internal_add_shifted(BignumInt *number,
@@ -279,13 +709,13 @@
 }
 
 /*
- * Compute (base ^ exp) % mod.
+ * Compute (base ^ exp) % mod, the pedestrian way.
  */
-Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)
+Bignum modpow_simple(Bignum base_in, Bignum exp, Bignum mod)
 {
-    BignumInt *a, *b, *n, *m;
+    BignumInt *a, *b, *n, *m, *scratch;
     int mshift;
-    int mlen, i, j;
+    int mlen, scratchlen, i, j;
     Bignum base, result;
 
     /*
@@ -332,6 +762,10 @@
 	a[i] = 0;
     a[2 * mlen - 1] = 1;
 
+    /* Scratch space for multiplies */
+    scratchlen = mul_compute_scratch(mlen);
+    scratch = snewn(scratchlen, BignumInt);
+
     /* Skip leading zero bits of exp. */
     i = 0;
     j = BIGNUM_INT_BITS-1;
@@ -346,10 +780,10 @@
     /* Main computation */
     while (i < (int)exp[0]) {
 	while (j >= 0) {
-	    internal_mul(a + mlen, a + mlen, b, mlen);
+	    internal_mul(a + mlen, a + mlen, b, mlen, scratch);
 	    internal_mod(b, mlen * 2, m, mlen, NULL, 0);
 	    if ((exp[exp[0] - i] & (1 << j)) != 0) {
-		internal_mul(b + mlen, n, a, mlen);
+		internal_mul(b + mlen, n, a, mlen, scratch);
 		internal_mod(a, mlen * 2, m, mlen, NULL, 0);
 	    } else {
 		BignumInt *t;
@@ -384,6 +818,9 @@
     for (i = 0; i < 2 * mlen; i++)
 	a[i] = 0;
     sfree(a);
+    for (i = 0; i < scratchlen; i++)
+	scratch[i] = 0;
+    sfree(scratch);
     for (i = 0; i < 2 * mlen; i++)
 	b[i] = 0;
     sfree(b);
@@ -400,14 +837,165 @@
 }
 
 /*
+ * Compute (base ^ exp) % mod. Uses the Montgomery multiplication
+ * technique where possible, falling back to modpow_simple otherwise.
+ */
+Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)
+{
+    BignumInt *a, *b, *x, *n, *mninv, *scratch;
+    int len, scratchlen, i, j;
+    Bignum base, base2, r, rn, inv, result;
+
+    /*
+     * The most significant word of mod needs to be non-zero. It
+     * should already be, but let's make sure.
+     */
+    assert(mod[mod[0]] != 0);
+
+    /*
+     * mod had better be odd, or we can't do Montgomery multiplication
+     * using a power of two at all.
+     */
+    if (!(mod[1] & 1))
+        return modpow_simple(base_in, exp, mod);
+
+    /*
+     * Make sure the base is smaller than the modulus, by reducing
+     * it modulo the modulus if not.
+     */
+    base = bigmod(base_in, mod);
+
+    /*
+     * Compute the inverse of n mod r, for monty_reduce. (In fact we
+     * want the inverse of _minus_ n mod r, but we'll sort that out
+     * below.)
+     */
+    len = mod[0];
+    r = bn_power_2(BIGNUM_INT_BITS * len);
+    inv = modinv(mod, r);
+
+    /*
+     * Multiply the base by r mod n, to get it into Montgomery
+     * representation.
+     */
+    base2 = modmul(base, r, mod);
+    freebn(base);
+    base = base2;
+
+    rn = bigmod(r, mod);               /* r mod n, i.e. Montgomerified 1 */
+
+    freebn(r);                         /* won't need this any more */
+
+    /*
+     * Set up internal arrays of the right lengths, in big-endian
+     * format, containing the base, the modulus, and the modulus's
+     * inverse.
+     */
+    n = snewn(len, BignumInt);
+    for (j = 0; j < len; j++)
+	n[len - 1 - j] = mod[j + 1];
+
+    mninv = snewn(len, BignumInt);
+    for (j = 0; j < len; j++)
+	mninv[len - 1 - j] = (j < (int)inv[0] ? inv[j + 1] : 0);
+    freebn(inv);         /* we don't need this copy of it any more */
+    /* Now negate mninv mod r, so it's the inverse of -n rather than +n. */
+    x = snewn(len, BignumInt);
+    for (j = 0; j < len; j++)
+        x[j] = 0;
+    internal_sub(x, mninv, mninv, len);
+
+    /* x = snewn(len, BignumInt); */ /* already done above */
+    for (j = 0; j < len; j++)
+	x[len - 1 - j] = (j < (int)base[0] ? base[j + 1] : 0);
+    freebn(base);        /* we don't need this copy of it any more */
+
+    a = snewn(2*len, BignumInt);
+    b = snewn(2*len, BignumInt);
+    for (j = 0; j < len; j++)
+	a[2*len - 1 - j] = (j < (int)rn[0] ? rn[j + 1] : 0);
+    freebn(rn);
+
+    /* Scratch space for multiplies */
+    scratchlen = 3*len + mul_compute_scratch(len);
+    scratch = snewn(scratchlen, BignumInt);
+
+    /* Skip leading zero bits of exp. */
+    i = 0;
+    j = BIGNUM_INT_BITS-1;
+    while (i < (int)exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) {
+	j--;
+	if (j < 0) {
+	    i++;
+	    j = BIGNUM_INT_BITS-1;
+	}
+    }
+
+    /* Main computation */
+    while (i < (int)exp[0]) {
+	while (j >= 0) {
+	    internal_mul(a + len, a + len, b, len, scratch);
+            monty_reduce(b, n, mninv, scratch, len);
+	    if ((exp[exp[0] - i] & (1 << j)) != 0) {
+                internal_mul(b + len, x, a, len,  scratch);
+                monty_reduce(a, n, mninv, scratch, len);
+	    } else {
+		BignumInt *t;
+		t = a;
+		a = b;
+		b = t;
+	    }
+	    j--;
+	}
+	i++;
+	j = BIGNUM_INT_BITS-1;
+    }
+
+    /*
+     * Final monty_reduce to get back from the adjusted Montgomery
+     * representation.
+     */
+    monty_reduce(a, n, mninv, scratch, len);
+
+    /* Copy result to buffer */
+    result = newbn(mod[0]);
+    for (i = 0; i < len; i++)
+	result[result[0] - i] = a[i + len];
+    while (result[0] > 1 && result[result[0]] == 0)
+	result[0]--;
+
+    /* Free temporary arrays */
+    for (i = 0; i < scratchlen; i++)
+	scratch[i] = 0;
+    sfree(scratch);
+    for (i = 0; i < 2 * len; i++)
+	a[i] = 0;
+    sfree(a);
+    for (i = 0; i < 2 * len; i++)
+	b[i] = 0;
+    sfree(b);
+    for (i = 0; i < len; i++)
+	mninv[i] = 0;
+    sfree(mninv);
+    for (i = 0; i < len; i++)
+	n[i] = 0;
+    sfree(n);
+    for (i = 0; i < len; i++)
+	x[i] = 0;
+    sfree(x);
+
+    return result;
+}
+
+/*
  * Compute (p * q) % mod.
  * The most significant word of mod MUST be non-zero.
  * We assume that the result array is the same size as the mod array.
  */
 Bignum modmul(Bignum p, Bignum q, Bignum mod)
 {
-    BignumInt *a, *n, *m, *o;
-    int mshift;
+    BignumInt *a, *n, *m, *o, *scratch;
+    int mshift, scratchlen;
     int pqlen, mlen, rlen, i, j;
     Bignum result;
 
@@ -449,8 +1037,12 @@
     /* Allocate a of size 2*pqlen for result */
     a = snewn(2 * pqlen, BignumInt);
 
+    /* Scratch space for multiplies */
+    scratchlen = mul_compute_scratch(pqlen);
+    scratch = snewn(scratchlen, BignumInt);
+
     /* Main computation */
-    internal_mul(n, o, a, pqlen);
+    internal_mul(n, o, a, pqlen, scratch);
     internal_mod(a, pqlen * 2, m, mlen, NULL, 0);
 
     /* Fixup result in case the modulus was shifted */
@@ -472,6 +1064,9 @@
 	result[0]--;
 
     /* Free temporary arrays */
+    for (i = 0; i < scratchlen; i++)
+	scratch[i] = 0;
+    sfree(scratch);
     for (i = 0; i < 2 * pqlen; i++)
 	a[i] = 0;
     sfree(a);
@@ -760,18 +1355,21 @@
     int alen = a[0], blen = b[0];
     int mlen = (alen > blen ? alen : blen);
     int rlen, i, maxspot;
+    int wslen;
     BignumInt *workspace;
     Bignum ret;
 
-    /* mlen space for a, mlen space for b, 2*mlen for result */
-    workspace = snewn(mlen * 4, BignumInt);
+    /* mlen space for a, mlen space for b, 2*mlen for result,
+     * plus scratch space for multiplication */
+    wslen = mlen * 4 + mul_compute_scratch(mlen);
+    workspace = snewn(wslen, BignumInt);
     for (i = 0; i < mlen; i++) {
 	workspace[0 * mlen + i] = (mlen - i <= (int)a[0] ? a[mlen - i] : 0);
 	workspace[1 * mlen + i] = (mlen - i <= (int)b[0] ? b[mlen - i] : 0);
     }
 
     internal_mul(workspace + 0 * mlen, workspace + 1 * mlen,
-		 workspace + 2 * mlen, mlen);
+		 workspace + 2 * mlen, mlen, workspace + 4 * mlen);
 
     /* now just copy the result back */
     rlen = alen + blen + 1;
@@ -800,6 +1398,8 @@
     }
     ret[0] = maxspot;
 
+    for (i = 0; i < wslen; i++)
+        workspace[i] = 0;
     sfree(workspace);
     return ret;
 }
@@ -813,6 +1413,69 @@
 }
 
 /*
+ * Simple addition.
+ */
+Bignum bigadd(Bignum a, Bignum b)
+{
+    int alen = a[0], blen = b[0];
+    int rlen = (alen > blen ? alen : blen) + 1;
+    int i, maxspot;
+    Bignum ret;
+    BignumDblInt carry;
+
+    ret = newbn(rlen);
+
+    carry = 0;
+    maxspot = 0;
+    for (i = 1; i <= rlen; i++) {
+        carry += (i <= (int)a[0] ? a[i] : 0);
+        carry += (i <= (int)b[0] ? b[i] : 0);
+        ret[i] = (BignumInt) carry & BIGNUM_INT_MASK;
+        carry >>= BIGNUM_INT_BITS;
+        if (ret[i] != 0 && i > maxspot)
+            maxspot = i;
+    }
+    ret[0] = maxspot;
+
+    return ret;
+}
+
+/*
+ * Subtraction. Returns a-b, or NULL if the result would come out
+ * negative (recall that this entire bignum module only handles
+ * positive numbers).
+ */
+Bignum bigsub(Bignum a, Bignum b)
+{
+    int alen = a[0], blen = b[0];
+    int rlen = (alen > blen ? alen : blen);
+    int i, maxspot;
+    Bignum ret;
+    BignumDblInt carry;
+
+    ret = newbn(rlen);
+
+    carry = 1;
+    maxspot = 0;
+    for (i = 1; i <= rlen; i++) {
+        carry += (i <= (int)a[0] ? a[i] : 0);
+        carry += (i <= (int)b[0] ? b[i] ^ BIGNUM_INT_MASK : BIGNUM_INT_MASK);
+        ret[i] = (BignumInt) carry & BIGNUM_INT_MASK;
+        carry >>= BIGNUM_INT_BITS;
+        if (ret[i] != 0 && i > maxspot)
+            maxspot = i;
+    }
+    ret[0] = maxspot;
+
+    if (!carry) {
+        freebn(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+/*
  * Create a bignum which is the bitmask covering another one. That
  * is, the smallest integer which is >= N and is also one less than
  * a power of two.
@@ -1090,3 +1753,166 @@
     sfree(workspace);
     return ret;
 }
+
+#ifdef TESTBN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+/*
+ * gcc -g -O0 -DTESTBN -o testbn sshbn.c misc.c -I unix -I charset
+ *
+ * Then feed to this program's standard input the output of
+ * testdata/bignum.py .
+ */
+
+void modalfatalbox(char *p, ...)
+{
+    va_list ap;
+    fprintf(stderr, "FATAL ERROR: ");
+    va_start(ap, p);
+    vfprintf(stderr, p, ap);
+    va_end(ap);
+    fputc('\n', stderr);
+    exit(1);
+}
+
+#define fromxdigit(c) ( (c)>'9' ? ((c)&0xDF) - 'A' + 10 : (c) - '0' )
+
+int main(int argc, char **argv)
+{
+    char *buf;
+    int line = 0;
+    int passes = 0, fails = 0;
+
+    while ((buf = fgetline(stdin)) != NULL) {
+        int maxlen = strlen(buf);
+        unsigned char *data = snewn(maxlen, unsigned char);
+        unsigned char *ptrs[5], *q;
+        int ptrnum;
+        char *bufp = buf;
+
+        line++;
+
+        q = data;
+        ptrnum = 0;
+
+        while (*bufp && !isspace((unsigned char)*bufp))
+            bufp++;
+        if (bufp)
+            *bufp++ = '\0';
+
+        while (*bufp) {
+            char *start, *end;
+            int i;
+
+            while (*bufp && !isxdigit((unsigned char)*bufp))
+                bufp++;
+            start = bufp;
+
+            if (!*bufp)
+                break;
+
+            while (*bufp && isxdigit((unsigned char)*bufp))
+                bufp++;
+            end = bufp;
+
+            if (ptrnum >= lenof(ptrs))
+                break;
+            ptrs[ptrnum++] = q;
+            
+            for (i = -((end - start) & 1); i < end-start; i += 2) {
+                unsigned char val = (i < 0 ? 0 : fromxdigit(start[i]));
+                val = val * 16 + fromxdigit(start[i+1]);
+                *q++ = val;
+            }
+
+            ptrs[ptrnum] = q;
+        }
+
+        if (!strcmp(buf, "mul")) {
+            Bignum a, b, c, p;
+
+            if (ptrnum != 3) {
+                printf("%d: mul with %d parameters, expected 3\n", line);
+                exit(1);
+            }
+            a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]);
+            b = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]);
+            c = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]);
+            p = bigmul(a, b);
+
+            if (bignum_cmp(c, p) == 0) {
+                passes++;
+            } else {
+                char *as = bignum_decimal(a);
+                char *bs = bignum_decimal(b);
+                char *cs = bignum_decimal(c);
+                char *ps = bignum_decimal(p);
+                
+                printf("%d: fail: %s * %s gave %s expected %s\n",
+                       line, as, bs, ps, cs);
+                fails++;
+
+                sfree(as);
+                sfree(bs);
+                sfree(cs);
+                sfree(ps);
+            }
+            freebn(a);
+            freebn(b);
+            freebn(c);
+            freebn(p);
+        } else if (!strcmp(buf, "pow")) {
+            Bignum base, expt, modulus, expected, answer;
+
+            if (ptrnum != 4) {
+                printf("%d: mul with %d parameters, expected 3\n", line);
+                exit(1);
+            }
+
+            base = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]);
+            expt = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]);
+            modulus = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]);
+            expected = bignum_from_bytes(ptrs[3], ptrs[4]-ptrs[3]);
+            answer = modpow(base, expt, modulus);
+
+            if (bignum_cmp(expected, answer) == 0) {
+                passes++;
+            } else {
+                char *as = bignum_decimal(base);
+                char *bs = bignum_decimal(expt);
+                char *cs = bignum_decimal(modulus);
+                char *ds = bignum_decimal(answer);
+                char *ps = bignum_decimal(expected);
+                
+                printf("%d: fail: %s ^ %s mod %s gave %s expected %s\n",
+                       line, as, bs, cs, ds, ps);
+                fails++;
+
+                sfree(as);
+                sfree(bs);
+                sfree(cs);
+                sfree(ds);
+                sfree(ps);
+            }
+            freebn(base);
+            freebn(expt);
+            freebn(modulus);
+            freebn(expected);
+            freebn(answer);
+        } else {
+            printf("%d: unrecognised test keyword: '%s'\n", line, buf);
+            exit(1);
+        }
+
+        sfree(buf);
+        sfree(data);
+    }
+
+    printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
+    return fails != 0;
+}
+
+#endif

Modified: trunk/Tools/GMEplink/SSHDES.C
==============================================================================
--- trunk/Tools/GMEplink/SSHDES.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHDES.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -959,7 +959,7 @@
 
 /*
  * Single DES in SSH-2. "des-cbc" is marked as HISTORIC in
- * draft-ietf-secsh-assignednumbers-04.txt, referring to
+ * RFC 4250, referring to
  * FIPS-46-3.  ("Single DES (i.e., DES) will be permitted 
  * for legacy systems only.") , but ssh.com support it and 
  * apparently aren't the only people to do so, so we sigh 

Modified: trunk/Tools/GMEplink/SSHDH.C
==============================================================================
--- trunk/Tools/GMEplink/SSHDH.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHDH.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -52,7 +52,7 @@
 
 static const struct ssh_kex ssh_diffiehellman_group1_sha1 = {
     "diffie-hellman-group1-sha1", "group1",
-    P1, G, lenof(P1), lenof(G), &ssh_sha1
+    KEXTYPE_DH, P1, G, lenof(P1), lenof(G), &ssh_sha1
 };
 
 static const struct ssh_kex *const group1_list[] = {
@@ -66,7 +66,7 @@
 
 static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {
     "diffie-hellman-group14-sha1", "group14",
-    P14, G, lenof(P14), lenof(G), &ssh_sha1
+    KEXTYPE_DH, P14, G, lenof(P14), lenof(G), &ssh_sha1
 };
 
 static const struct ssh_kex *const group14_list[] = {
@@ -80,12 +80,12 @@
 
 static const struct ssh_kex ssh_diffiehellman_gex_sha256 = {
     "diffie-hellman-group-exchange-sha256", NULL,
-    NULL, NULL, 0, 0, &ssh_sha256
+    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha256
 };
 
 static const struct ssh_kex ssh_diffiehellman_gex_sha1 = {
     "diffie-hellman-group-exchange-sha1", NULL,
-    NULL, NULL, 0, 0, &ssh_sha1
+    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha1
 };
 
 static const struct ssh_kex *const gex_list[] = {

Modified: trunk/Tools/GMEplink/SSHDSS.C
==============================================================================
--- trunk/Tools/GMEplink/SSHDSS.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHDSS.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -231,14 +231,14 @@
 #endif
     /*
      * Commercial SSH (2.0.13) and OpenSSH disagree over the format
-     * of a DSA signature. OpenSSH is in line with the IETF drafts:
+     * of a DSA signature. OpenSSH is in line with RFC 4253:
      * it uses a string "ssh-dss", followed by a 40-byte string
      * containing two 160-bit integers end-to-end. Commercial SSH
      * can't be bothered with the header bit, and considers a DSA
      * signature blob to be _just_ the 40-byte string containing
      * the two 160-bit integers. We tell them apart by measuring
      * the length: length 40 means the commercial-SSH bug, anything
-     * else is assumed to be IETF-compliant.
+     * else is assumed to be RFC-compliant.
      */
     if (siglen != 40) {		       /* bug not present; read admin fields */
 	getstring(&sig, &siglen, &p, &slen);

Modified: trunk/Tools/GMEplink/SSHMD5.C
==============================================================================
--- trunk/Tools/GMEplink/SSHMD5.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHMD5.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -222,7 +222,7 @@
 
 void *hmacmd5_make_context(void)
 {
-    return snewn(2, struct MD5Context);
+    return snewn(3, struct MD5Context);
 }
 
 void hmacmd5_free_context(void *handle)
@@ -257,24 +257,50 @@
     hmacmd5_key(handle, key, 16);
 }
 
-static void hmacmd5_do_hmac_internal(void *handle,
-				     unsigned char const *blk, int len,
-				     unsigned char const *blk2, int len2,
-				     unsigned char *hmac)
+static void hmacmd5_start(void *handle)
+{
+    struct MD5Context *keys = (struct MD5Context *)handle;
+
+    keys[2] = keys[0];		      /* structure copy */
+}
+
+static void hmacmd5_bytes(void *handle, unsigned char const *blk, int len)
+{
+    struct MD5Context *keys = (struct MD5Context *)handle;
+    MD5Update(&keys[2], blk, len);
+}
+
+static void hmacmd5_genresult(void *handle, unsigned char *hmac)
 {
     struct MD5Context *keys = (struct MD5Context *)handle;
     struct MD5Context s;
     unsigned char intermediate[16];
 
-    s = keys[0];		       /* structure copy */
-    MD5Update(&s, blk, len);
-    if (blk2) MD5Update(&s, blk2, len2);
+    s = keys[2];		       /* structure copy */
     MD5Final(intermediate, &s);
     s = keys[1];		       /* structure copy */
     MD5Update(&s, intermediate, 16);
     MD5Final(hmac, &s);
 }
 
+static int hmacmd5_verresult(void *handle, unsigned char const *hmac)
+{
+    unsigned char correct[16];
+    hmacmd5_genresult(handle, correct);
+    return !memcmp(correct, hmac, 16);
+}
+
+static void hmacmd5_do_hmac_internal(void *handle,
+				     unsigned char const *blk, int len,
+				     unsigned char const *blk2, int len2,
+				     unsigned char *hmac)
+{
+    hmacmd5_start(handle);
+    hmacmd5_bytes(handle, blk, len);
+    if (blk2) hmacmd5_bytes(handle, blk2, len2);
+    hmacmd5_genresult(handle, hmac);
+}
+
 void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
 		     unsigned char *hmac)
 {
@@ -311,6 +337,7 @@
 const struct ssh_mac ssh_hmac_md5 = {
     hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,
     hmacmd5_generate, hmacmd5_verify,
+    hmacmd5_start, hmacmd5_bytes, hmacmd5_genresult, hmacmd5_verresult,
     "hmac-md5",
     16,
     "HMAC-MD5"

Modified: trunk/Tools/GMEplink/SSHPUBK.C
==============================================================================
--- trunk/Tools/GMEplink/SSHPUBK.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHPUBK.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -495,16 +495,14 @@
 
     while (1) {
 	c = fgetc(fp);
-	if (c == '\r' || c == '\n') {
-	    c = fgetc(fp);
-	    if (c != '\r' && c != '\n' && c != EOF)
-		ungetc(c, fp);
+	if (c == '\r' || c == '\n' || c == EOF) {
+	    if (c != EOF) {
+		c = fgetc(fp);
+		if (c != '\r' && c != '\n')
+		    ungetc(c, fp);
+	    }
 	    return text;
 	}
-	if (c == EOF) {
-	    sfree(text);
-	    return NULL;
-	}
 	if (len + 1 >= size) {
 	    size += 128;
 	    text = sresize(text, size, char);

Modified: trunk/Tools/GMEplink/SSHRAND.C
==============================================================================
--- trunk/Tools/GMEplink/SSHRAND.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHRAND.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -4,6 +4,7 @@
 
 #include "putty.h"
 #include "ssh.h"
+#include <assert.h>
 
 /* Collect environmental noise every 5 minutes */
 #define NOISE_REGULAR_INTERVAL (5*60*TICKSPERSEC)
@@ -225,6 +226,10 @@
 void random_unref(void)
 {
     random_active--;
+    assert(random_active >= 0);
+    if (random_active) return;
+
+    expire_timer_context(&pool);
 }
 
 int random_byte(void)

Modified: trunk/Tools/GMEplink/SSHRSA.C
==============================================================================
--- trunk/Tools/GMEplink/SSHRSA.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHRSA.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -114,9 +114,83 @@
 }
 
 /*
- * This function is a wrapper on modpow(). It has the same effect
- * as modpow(), but employs RSA blinding to protect against timing
- * attacks.
+ * Compute (base ^ exp) % mod, provided mod == p * q, with p,q
+ * distinct primes, and iqmp is the multiplicative inverse of q mod p.
+ * Uses Chinese Remainder Theorem to speed computation up over the
+ * obvious implementation of a single big modpow.
+ */
+Bignum crt_modpow(Bignum base, Bignum exp, Bignum mod,
+                  Bignum p, Bignum q, Bignum iqmp)
+{
+    Bignum pm1, qm1, pexp, qexp, presult, qresult, diff, multiplier, ret0, ret;
+
+    /*
+     * Reduce the exponent mod phi(p) and phi(q), to save time when
+     * exponentiating mod p and mod q respectively. Of course, since p
+     * and q are prime, phi(p) == p-1 and similarly for q.
+     */
+    pm1 = copybn(p);
+    decbn(pm1);
+    qm1 = copybn(q);
+    decbn(qm1);
+    pexp = bigmod(exp, pm1);
+    qexp = bigmod(exp, qm1);
+
+    /*
+     * Do the two modpows.
+     */
+    presult = modpow(base, pexp, p);
+    qresult = modpow(base, qexp, q);
+
+    /*
+     * Recombine the results. We want a value which is congruent to
+     * qresult mod q, and to presult mod p.
+     *
+     * We know that iqmp * q is congruent to 1 * mod p (by definition
+     * of iqmp) and to 0 mod q (obviously). So we start with qresult
+     * (which is congruent to qresult mod both primes), and add on
+     * (presult-qresult) * (iqmp * q) which adjusts it to be congruent
+     * to presult mod p without affecting its value mod q.
+     */
+    if (bignum_cmp(presult, qresult) < 0) {
+        /*
+         * Can't subtract presult from qresult without first adding on
+         * p.
+         */
+        Bignum tmp = presult;
+        presult = bigadd(presult, p);
+        freebn(tmp);
+    }
+    diff = bigsub(presult, qresult);
+    multiplier = bigmul(iqmp, q);
+    ret0 = bigmuladd(multiplier, diff, qresult);
+
+    /*
+     * Finally, reduce the result mod n.
+     */
+    ret = bigmod(ret0, mod);
+
+    /*
+     * Free all the intermediate results before returning.
+     */
+    freebn(pm1);
+    freebn(qm1);
+    freebn(pexp);
+    freebn(qexp);
+    freebn(presult);
+    freebn(qresult);
+    freebn(diff);
+    freebn(multiplier);
+    freebn(ret0);
+
+    return ret;
+}
+
+/*
+ * This function is a wrapper on modpow(). It has the same effect as
+ * modpow(), but employs RSA blinding to protect against timing
+ * attacks and also uses the Chinese Remainder Theorem (implemented
+ * above, in crt_modpow()) to speed up the main operation.
  */
 static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key)
 {
@@ -218,10 +292,12 @@
      * _y^d_, and use the _public_ exponent to compute (y^d)^e = y
      * from it, which is much faster to do.
      */
-    random_encrypted = modpow(random, key->exponent, key->modulus);
+    random_encrypted = crt_modpow(random, key->exponent,
+                                  key->modulus, key->p, key->q, key->iqmp);
     random_inverse = modinv(random, key->modulus);
     input_blinded = modmul(input, random_encrypted, key->modulus);
-    ret_blinded = modpow(input_blinded, key->private_exponent, key->modulus);
+    ret_blinded = crt_modpow(input_blinded, key->private_exponent,
+                             key->modulus, key->p, key->q, key->iqmp);
     ret = modmul(ret_blinded, random_inverse, key->modulus);
 
     freebn(ret_blinded);
@@ -352,9 +428,20 @@
 
     /*
      * Ensure p > q.
+     *
+     * I have seen key blobs in the wild which were generated with
+     * p < q, so instead of rejecting the key in this case we
+     * should instead flip them round into the canonical order of
+     * p > q. This also involves regenerating iqmp.
      */
-    if (bignum_cmp(key->p, key->q) <= 0)
-	return 0;
+    if (bignum_cmp(key->p, key->q) <= 0) {
+	Bignum tmp = key->p;
+	key->p = key->q;
+	key->q = tmp;
+
+	freebn(key->iqmp);
+	key->iqmp = modinv(key->q, key->p);
+    }
 
     /*
      * Ensure iqmp * q is congruent to 1, modulo p.
@@ -419,6 +506,12 @@
 	freebn(key->exponent);
     if (key->private_exponent)
 	freebn(key->private_exponent);
+    if (key->p)
+	freebn(key->p);
+    if (key->q)
+	freebn(key->q);
+    if (key->iqmp)
+	freebn(key->iqmp);
     if (key->comment)
 	sfree(key->comment);
 }
@@ -472,6 +565,7 @@
     rsa->exponent = getmp(&data, &len);
     rsa->modulus = getmp(&data, &len);
     rsa->private_exponent = NULL;
+    rsa->p = rsa->q = rsa->iqmp = NULL;
     rsa->comment = NULL;
 
     return rsa;
@@ -836,3 +930,157 @@
     "ssh-rsa",
     "rsa2"
 };
+
+void *ssh_rsakex_newkey(char *data, int len)
+{
+    return rsa2_newkey(data, len);
+}
+
+void ssh_rsakex_freekey(void *key)
+{
+    rsa2_freekey(key);
+}
+
+int ssh_rsakex_klen(void *key)
+{
+    struct RSAKey *rsa = (struct RSAKey *) key;
+
+    return bignum_bitcount(rsa->modulus);
+}
+
+static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
+		      void *vdata, int datalen)
+{
+    unsigned char *data = (unsigned char *)vdata;
+    unsigned count = 0;
+
+    while (datalen > 0) {
+        int i, max = (datalen > h->hlen ? h->hlen : datalen);
+        void *s;
+        unsigned char counter[4], hash[SSH2_KEX_MAX_HASH_LEN];
+
+	assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN);
+        PUT_32BIT(counter, count);
+        s = h->init();
+        h->bytes(s, seed, seedlen);
+        h->bytes(s, counter, 4);
+        h->final(s, hash);
+        count++;
+
+        for (i = 0; i < max; i++)
+            data[i] ^= hash[i];
+
+        data += max;
+        datalen -= max;
+    }
+}
+
+void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+                        unsigned char *out, int outlen,
+                        void *key)
+{
+    Bignum b1, b2;
+    struct RSAKey *rsa = (struct RSAKey *) key;
+    int k, i;
+    char *p;
+    const int HLEN = h->hlen;
+
+    /*
+     * Here we encrypt using RSAES-OAEP. Essentially this means:
+     * 
+     *  - we have a SHA-based `mask generation function' which
+     *    creates a pseudo-random stream of mask data
+     *    deterministically from an input chunk of data.
+     * 
+     *  - we have a random chunk of data called a seed.
+     * 
+     *  - we use the seed to generate a mask which we XOR with our
+     *    plaintext.
+     * 
+     *  - then we use _the masked plaintext_ to generate a mask
+     *    which we XOR with the seed.
+     * 
+     *  - then we concatenate the masked seed and the masked
+     *    plaintext, and RSA-encrypt that lot.
+     * 
+     * The result is that the data input to the encryption function
+     * is random-looking and (hopefully) contains no exploitable
+     * structure such as PKCS1-v1_5 does.
+     * 
+     * For a precise specification, see RFC 3447, section 7.1.1.
+     * Some of the variable names below are derived from that, so
+     * it'd probably help to read it anyway.
+     */
+
+    /* k denotes the length in octets of the RSA modulus. */
+    k = (7 + bignum_bitcount(rsa->modulus)) / 8;
+
+    /* The length of the input data must be at most k - 2hLen - 2. */
+    assert(inlen > 0 && inlen <= k - 2*HLEN - 2);
+
+    /* The length of the output data wants to be precisely k. */
+    assert(outlen == k);
+
+    /*
+     * Now perform EME-OAEP encoding. First set up all the unmasked
+     * output data.
+     */
+    /* Leading byte zero. */
+    out[0] = 0;
+    /* At position 1, the seed: HLEN bytes of random data. */
+    for (i = 0; i < HLEN; i++)
+        out[i + 1] = random_byte();
+    /* At position 1+HLEN, the data block DB, consisting of: */
+    /* The hash of the label (we only support an empty label here) */
+    h->final(h->init(), out + HLEN + 1);
+    /* A bunch of zero octets */
+    memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
+    /* A single 1 octet, followed by the input message data. */
+    out[outlen - inlen - 1] = 1;
+    memcpy(out + outlen - inlen, in, inlen);
+
+    /*
+     * Now use the seed data to mask the block DB.
+     */
+    oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
+
+    /*
+     * And now use the masked DB to mask the seed itself.
+     */
+    oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
+
+    /*
+     * Now `out' contains precisely the data we want to
+     * RSA-encrypt.
+     */
+    b1 = bignum_from_bytes(out, outlen);
+    b2 = modpow(b1, rsa->exponent, rsa->modulus);
+    p = (char *)out;
+    for (i = outlen; i--;) {
+	*p++ = bignum_byte(b2, i);
+    }
+    freebn(b1);
+    freebn(b2);
+
+    /*
+     * And we're done.
+     */
+}
+
+static const struct ssh_kex ssh_rsa_kex_sha1 = {
+    "rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1
+};
+
+static const struct ssh_kex ssh_rsa_kex_sha256 = {
+    "rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256
+};
+
+static const struct ssh_kex *const rsa_kex_list[] = {
+    &ssh_rsa_kex_sha256,
+    &ssh_rsa_kex_sha1
+};
+
+const struct ssh_kexes ssh_rsa_kex = {
+    sizeof(rsa_kex_list) / sizeof(*rsa_kex_list),
+    rsa_kex_list
+};

Modified: trunk/Tools/GMEplink/SSHSH512.C
==============================================================================
--- trunk/Tools/GMEplink/SSHSH512.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHSH512.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -13,14 +13,14 @@
  * overlap destination with one source, but the others can't.
  */
 #define add(r,x,y) ( r.lo = y.lo + x.lo, \
-		     r.hi = y.hi + x.hi + (r.lo < y.lo) )
-#define rorB(r,x,y) ( r.lo = (x.hi >> ((y)-32)) | (x.lo << (64-(y))), \
-		      r.hi = (x.lo >> ((y)-32)) | (x.hi << (64-(y))) )
-#define rorL(r,x,y) ( r.lo = (x.lo >> (y)) | (x.hi << (32-(y))), \
-		      r.hi = (x.hi >> (y)) | (x.lo << (32-(y))) )
-#define shrB(r,x,y) ( r.lo = x.hi >> ((y)-32), r.hi = 0 )
-#define shrL(r,x,y) ( r.lo = (x.lo >> (y)) | (x.hi << (32-(y))), \
-		      r.hi = x.hi >> (y) )
+		     r.hi = y.hi + x.hi + ((uint32)r.lo < (uint32)y.lo) )
+#define rorB(r,x,y) ( r.lo = ((uint32)x.hi >> ((y)-32)) | ((uint32)x.lo << (64-(y))), \
+		      r.hi = ((uint32)x.lo >> ((y)-32)) | ((uint32)x.hi << (64-(y))) )
+#define rorL(r,x,y) ( r.lo = ((uint32)x.lo >> (y)) | ((uint32)x.hi << (32-(y))), \
+		      r.hi = ((uint32)x.hi >> (y)) | ((uint32)x.lo << (32-(y))) )
+#define shrB(r,x,y) ( r.lo = (uint32)x.hi >> ((y)-32), r.hi = 0 )
+#define shrL(r,x,y) ( r.lo = ((uint32)x.lo >> (y)) | ((uint32)x.hi << (32-(y))), \
+		      r.hi = (uint32)x.hi >> (y) )
 #define and(r,x,y) ( r.lo = x.lo & y.lo, r.hi = x.hi & y.hi )
 #define xor(r,x,y) ( r.lo = x.lo ^ y.lo, r.hi = x.hi ^ y.hi )
 #define not(r,x) ( r.lo = ~x.lo, r.hi = ~x.hi )

Modified: trunk/Tools/GMEplink/SSHSHA.C
==============================================================================
--- trunk/Tools/GMEplink/SSHSHA.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHSHA.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -227,7 +227,7 @@
 
 static void *sha1_make_context(void)
 {
-    return snewn(2, SHA_State);
+    return snewn(3, SHA_State);
 }
 
 static void sha1_free_context(void *handle)
@@ -266,33 +266,61 @@
     sha1_key_internal(handle, key, 16);
 }
 
-static void sha1_do_hmac(void *handle, unsigned char *blk, int len,
-			 unsigned long seq, unsigned char *hmac)
+static void hmacsha1_start(void *handle)
+{
+    SHA_State *keys = (SHA_State *)handle;
+
+    keys[2] = keys[0];		      /* structure copy */
+}
+
+static void hmacsha1_bytes(void *handle, unsigned char const *blk, int len)
+{
+    SHA_State *keys = (SHA_State *)handle;
+    SHA_Bytes(&keys[2], (void *)blk, len);
+}
+
+static void hmacsha1_genresult(void *handle, unsigned char *hmac)
 {
     SHA_State *keys = (SHA_State *)handle;
     SHA_State s;
     unsigned char intermediate[20];
 
-    intermediate[0] = (unsigned char) ((seq >> 24) & 0xFF);
-    intermediate[1] = (unsigned char) ((seq >> 16) & 0xFF);
-    intermediate[2] = (unsigned char) ((seq >> 8) & 0xFF);
-    intermediate[3] = (unsigned char) ((seq) & 0xFF);
-
-    s = keys[0];		       /* structure copy */
-    SHA_Bytes(&s, intermediate, 4);
-    SHA_Bytes(&s, blk, len);
+    s = keys[2];		       /* structure copy */
     SHA_Final(&s, intermediate);
     s = keys[1];		       /* structure copy */
     SHA_Bytes(&s, intermediate, 20);
     SHA_Final(&s, hmac);
 }
 
+static void sha1_do_hmac(void *handle, unsigned char *blk, int len,
+			 unsigned long seq, unsigned char *hmac)
+{
+    unsigned char seqbuf[4];
+
+    seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF);
+    seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF);
+    seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF);
+    seqbuf[3] = (unsigned char) ((seq) & 0xFF);
+
+    hmacsha1_start(handle);
+    hmacsha1_bytes(handle, seqbuf, 4);
+    hmacsha1_bytes(handle, blk, len);
+    hmacsha1_genresult(handle, hmac);
+}
+
 static void sha1_generate(void *handle, unsigned char *blk, int len,
 			  unsigned long seq)
 {
     sha1_do_hmac(handle, blk, len, seq, blk + len);
 }
 
+static int hmacsha1_verresult(void *handle, unsigned char const *hmac)
+{
+    unsigned char correct[20];
+    hmacsha1_genresult(handle, correct);
+    return !memcmp(correct, hmac, 20);
+}
+
 static int sha1_verify(void *handle, unsigned char *blk, int len,
 		       unsigned long seq)
 {
@@ -301,6 +329,13 @@
     return !memcmp(correct, blk + len, 20);
 }
 
+static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)
+{
+    unsigned char full[20];
+    hmacsha1_genresult(handle, full);
+    memcpy(hmac, full, 12);
+}
+
 static void sha1_96_generate(void *handle, unsigned char *blk, int len,
 			     unsigned long seq)
 {
@@ -309,6 +344,13 @@
     memcpy(blk + len, full, 12);
 }
 
+static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)
+{
+    unsigned char correct[20];
+    hmacsha1_genresult(handle, correct);
+    return !memcmp(correct, hmac, 12);
+}
+
 static int sha1_96_verify(void *handle, unsigned char *blk, int len,
 		       unsigned long seq)
 {
@@ -333,6 +375,7 @@
 const struct ssh_mac ssh_hmac_sha1 = {
     sha1_make_context, sha1_free_context, sha1_key,
     sha1_generate, sha1_verify,
+    hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
     "hmac-sha1",
     20,
     "HMAC-SHA1"
@@ -341,6 +384,8 @@
 const struct ssh_mac ssh_hmac_sha1_96 = {
     sha1_make_context, sha1_free_context, sha1_key,
     sha1_96_generate, sha1_96_verify,
+    hmacsha1_start, hmacsha1_bytes,
+    hmacsha1_96_genresult, hmacsha1_96_verresult,
     "hmac-sha1-96",
     12,
     "HMAC-SHA1-96"
@@ -349,6 +394,7 @@
 const struct ssh_mac ssh_hmac_sha1_buggy = {
     sha1_make_context, sha1_free_context, sha1_key_buggy,
     sha1_generate, sha1_verify,
+    hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
     "hmac-sha1",
     20,
     "bug-compatible HMAC-SHA1"
@@ -357,6 +403,8 @@
 const struct ssh_mac ssh_hmac_sha1_96_buggy = {
     sha1_make_context, sha1_free_context, sha1_key_buggy,
     sha1_96_generate, sha1_96_verify,
+    hmacsha1_start, hmacsha1_bytes,
+    hmacsha1_96_genresult, hmacsha1_96_verresult,
     "hmac-sha1-96",
     12,
     "bug-compatible HMAC-SHA1-96"

Modified: trunk/Tools/GMEplink/SSHZLIB.C
==============================================================================
--- trunk/Tools/GMEplink/SSHZLIB.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/SSHZLIB.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -1259,6 +1259,8 @@
 		goto finished;
 	    nlen = dctx->bits & 0xFFFF;
 	    EATBITS(16);
+	    if (dctx->uncomplen != (nlen ^ 0xFFFF))
+		goto decode_error;
 	    if (dctx->uncomplen == 0)
 		dctx->state = OUTSIDEBLK;	/* block is empty */
 	    else
@@ -1369,6 +1371,7 @@
 
 const struct ssh_compress ssh_zlib = {
     "zlib",
+    "zlib at openssh.com", /* delayed version */
     zlib_compress_init,
     zlib_compress_cleanup,
     zlib_compress_block,

Modified: trunk/Tools/GMEplink/TELNET.C
==============================================================================
--- trunk/Tools/GMEplink/TELNET.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/TELNET.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -36,60 +36,65 @@
 #define SUSP    237		       /* Suspend process */
 #define xEOF    236		       /* End of file: EOF is already used... */
 
-#define TELOPT_BINARY	0	       /* 8-bit data path */
-#define TELOPT_ECHO	1	       /* echo */
-#define	TELOPT_RCP	2	       /* prepare to reconnect */
-#define	TELOPT_SGA	3	       /* suppress go ahead */
-#define	TELOPT_NAMS	4	       /* approximate message size */
-#define	TELOPT_STATUS	5	       /* give status */
-#define	TELOPT_TM	6	       /* timing mark */
-#define	TELOPT_RCTE	7	       /* remote controlled transmission and echo */
-#define TELOPT_NAOL 	8	       /* negotiate about output line width */
-#define TELOPT_NAOP 	9	       /* negotiate about output page size */
-#define TELOPT_NAOCRD	10	       /* negotiate about CR disposition */
-#define TELOPT_NAOHTS	11	       /* negotiate about horizontal tabstops */
-#define TELOPT_NAOHTD	12	       /* negotiate about horizontal tab disposition */
-#define TELOPT_NAOFFD	13	       /* negotiate about formfeed disposition */
-#define TELOPT_NAOVTS	14	       /* negotiate about vertical tab stops */
-#define TELOPT_NAOVTD	15	       /* negotiate about vertical tab disposition */
-#define TELOPT_NAOLFD	16	       /* negotiate about output LF disposition */
-#define TELOPT_XASCII	17	       /* extended ascic character set */
-#define	TELOPT_LOGOUT	18	       /* force logout */
-#define	TELOPT_BM	19	       /* byte macro */
-#define	TELOPT_DET	20	       /* data entry terminal */
-#define	TELOPT_SUPDUP	21	       /* supdup protocol */
-#define	TELOPT_SUPDUPOUTPUT 22	       /* supdup output */
-#define	TELOPT_SNDLOC	23	       /* send location */
-#define	TELOPT_TTYPE	24	       /* terminal type */
-#define	TELOPT_EOR	25	       /* end or record */
-#define	TELOPT_TUID	26	       /* TACACS user identification */
-#define	TELOPT_OUTMRK	27	       /* output marking */
-#define	TELOPT_TTYLOC	28	       /* terminal location number */
-#define	TELOPT_3270REGIME 29	       /* 3270 regime */
-#define	TELOPT_X3PAD	30	       /* X.3 PAD */
-#define	TELOPT_NAWS	31	       /* window size */
-#define	TELOPT_TSPEED	32	       /* terminal speed */
-#define	TELOPT_LFLOW	33	       /* remote flow control */
-#define TELOPT_LINEMODE	34	       /* Linemode option */
-#define TELOPT_XDISPLOC	35	       /* X Display Location */
-#define TELOPT_OLD_ENVIRON 36	       /* Old - Environment variables */
-#define	TELOPT_AUTHENTICATION 37       /* Authenticate */
-#define	TELOPT_ENCRYPT	38	       /* Encryption option */
-#define TELOPT_NEW_ENVIRON 39	       /* New - Environment variables */
-#define TELOPT_TN3270E	40	       /* TN3270 enhancements */
-#define TELOPT_XAUTH	41
-#define TELOPT_CHARSET	42	       /* Character set */
-#define TELOPT_RSP	43	       /* Remote serial port */
-#define TELOPT_COM_PORT_OPTION 44      /* Com port control */
-#define TELOPT_SLE	45	       /* Suppress local echo */
-#define TELOPT_STARTTLS	46	       /* Start TLS */
-#define TELOPT_KERMIT	47	       /* Automatic Kermit file transfer */
-#define TELOPT_SEND_URL	48
-#define TELOPT_FORWARD_X 49
-#define TELOPT_PRAGMA_LOGON	138
-#define TELOPT_SSPI_LOGON	139
-#define TELOPT_PRAGMA_HEARTBEAT	140
-#define	TELOPT_EXOPL	255	       /* extended-options-list */
+#define TELOPTS(X) \
+    X(BINARY, 0)                       /* 8-bit data path */ \
+    X(ECHO, 1)                         /* echo */ \
+    X(RCP, 2)                          /* prepare to reconnect */ \
+    X(SGA, 3)                          /* suppress go ahead */ \
+    X(NAMS, 4)                         /* approximate message size */ \
+    X(STATUS, 5)                       /* give status */ \
+    X(TM, 6)                           /* timing mark */ \
+    X(RCTE, 7)                         /* remote controlled transmission and echo */ \
+    X(NAOL, 8)                         /* negotiate about output line width */ \
+    X(NAOP, 9)                         /* negotiate about output page size */ \
+    X(NAOCRD, 10)                      /* negotiate about CR disposition */ \
+    X(NAOHTS, 11)                      /* negotiate about horizontal tabstops */ \
+    X(NAOHTD, 12)                      /* negotiate about horizontal tab disposition */ \
+    X(NAOFFD, 13)                      /* negotiate about formfeed disposition */ \
+    X(NAOVTS, 14)                      /* negotiate about vertical tab stops */ \
+    X(NAOVTD, 15)                      /* negotiate about vertical tab disposition */ \
+    X(NAOLFD, 16)                      /* negotiate about output LF disposition */ \
+    X(XASCII, 17)                      /* extended ascic character set */ \
+    X(LOGOUT, 18)                      /* force logout */ \
+    X(BM, 19)                          /* byte macro */ \
+    X(DET, 20)                         /* data entry terminal */ \
+    X(SUPDUP, 21)                      /* supdup protocol */ \
+    X(SUPDUPOUTPUT, 22)                /* supdup output */ \
+    X(SNDLOC, 23)                      /* send location */ \
+    X(TTYPE, 24)                       /* terminal type */ \
+    X(EOR, 25)                         /* end or record */ \
+    X(TUID, 26)                        /* TACACS user identification */ \
+    X(OUTMRK, 27)                      /* output marking */ \
+    X(TTYLOC, 28)                      /* terminal location number */ \
+    X(3270REGIME, 29)                  /* 3270 regime */ \
+    X(X3PAD, 30)                       /* X.3 PAD */ \
+    X(NAWS, 31)                        /* window size */ \
+    X(TSPEED, 32)                      /* terminal speed */ \
+    X(LFLOW, 33)                       /* remote flow control */ \
+    X(LINEMODE, 34)                    /* Linemode option */ \
+    X(XDISPLOC, 35)                    /* X Display Location */ \
+    X(OLD_ENVIRON, 36)                 /* Old - Environment variables */ \
+    X(AUTHENTICATION, 37)              /* Authenticate */ \
+    X(ENCRYPT, 38)                     /* Encryption option */ \
+    X(NEW_ENVIRON, 39)                 /* New - Environment variables */ \
+    X(TN3270E, 40)                     /* TN3270 enhancements */ \
+    X(XAUTH, 41)                       \
+    X(CHARSET, 42)                     /* Character set */ \
+    X(RSP, 43)                         /* Remote serial port */ \
+    X(COM_PORT_OPTION, 44)             /* Com port control */ \
+    X(SLE, 45)                         /* Suppress local echo */ \
+    X(STARTTLS, 46)                    /* Start TLS */ \
+    X(KERMIT, 47)                      /* Automatic Kermit file transfer */ \
+    X(SEND_URL, 48)                    \
+    X(FORWARD_X, 49)                   \
+    X(PRAGMA_LOGON, 138)               \
+    X(SSPI_LOGON, 139)                 \
+    X(PRAGMA_HEARTBEAT, 140)           \
+    X(EXOPL, 255)                      /* extended-options-list */
+
+#define telnet_enum(x,y) TELOPT_##x = y,
+enum { TELOPTS(telnet_enum) dummy=0 };
+#undef telnet_enum
 
 #define	TELQUAL_IS	0	       /* option is... */
 #define	TELQUAL_SEND	1	       /* send option */
@@ -109,62 +114,13 @@
 
 static char *telopt(int opt)
 {
-#define i(x) if (opt == TELOPT_ ## x) return #x;
-    i(BINARY);
-    i(ECHO);
-    i(RCP);
-    i(SGA);
-    i(NAMS);
-    i(STATUS);
-    i(TM);
-    i(RCTE);
-    i(NAOL);
-    i(NAOP);
-    i(NAOCRD);
-    i(NAOHTS);
-    i(NAOHTD);
-    i(NAOFFD);
-    i(NAOVTS);
-    i(NAOVTD);
-    i(NAOLFD);
-    i(XASCII);
-    i(LOGOUT);
-    i(BM);
-    i(DET);
-    i(SUPDUP);
-    i(SUPDUPOUTPUT);
-    i(SNDLOC);
-    i(TTYPE);
-    i(EOR);
-    i(TUID);
-    i(OUTMRK);
-    i(TTYLOC);
-    i(X3PAD);
-    i(NAWS);
-    i(TSPEED);
-    i(LFLOW);
-    i(LINEMODE);
-    i(XDISPLOC);
-    i(OLD_ENVIRON);
-    i(AUTHENTICATION);
-    i(ENCRYPT);
-    i(NEW_ENVIRON);
-    i(TN3270E);
-    i(XAUTH);
-    i(CHARSET);
-    i(RSP);
-    i(COM_PORT_OPTION);
-    i(SLE);
-    i(STARTTLS);
-    i(KERMIT);
-    i(SEND_URL);
-    i(FORWARD_X);
-    i(PRAGMA_LOGON);
-    i(SSPI_LOGON);
-    i(PRAGMA_HEARTBEAT);
-    i(EXOPL);
-#undef i
-    return "<unknown>";
+#define telnet_str(x,y) case TELOPT_##x: return #x;
+    switch (opt) {
+	TELOPTS(telnet_str)
+      default:
+	return "<unknown>";
+    }
+#undef telnet_str
 }
 
 static void telnet_size(void *handle, int width, int height);
@@ -510,29 +466,33 @@
 		    b[n++] = *e++;
 		e++;
 	    }
-	    if (*telnet->cfg.username) {
-		b[n++] = var;
-		b[n++] = 'U';
-		b[n++] = 'S';
-		b[n++] = 'E';
-		b[n++] = 'R';
-		b[n++] = value;
-		e = telnet->cfg.username;
-		while (*e)
-		    b[n++] = *e++;
+	    {
+		char user[sizeof(telnet->cfg.username)];
+		(void) get_remote_username(&telnet->cfg, user, sizeof(user));
+		if (*user) {
+		    b[n++] = var;
+		    b[n++] = 'U';
+		    b[n++] = 'S';
+		    b[n++] = 'E';
+		    b[n++] = 'R';
+		    b[n++] = value;
+		    e = user;
+		    while (*e)
+			b[n++] = *e++;
+		}
+		b[n++] = IAC;
+		b[n++] = SE;
+		telnet->bufsize = sk_write(telnet->s, (char *)b, n);
+		logbuf = dupprintf("client:\tSB %s IS %s%s%s%s",
+				   telopt(telnet->sb_opt),
+				   *user ? "USER=" : "",
+				   user,
+				   *user ? " " : "",
+				   n == 6 ? "<nothing>" :
+				   (*telnet->cfg.environmt ? "<stuff>" : ""));
+		logevent(telnet->frontend, logbuf);
+		sfree(logbuf);
 	    }
-	    b[n++] = IAC;
-	    b[n++] = SE;
-	    telnet->bufsize = sk_write(telnet->s, (char *)b, n);
-	    logbuf = dupprintf("client:\tSB %s IS %s%s%s%s",
-			       telopt(telnet->sb_opt),
-			       *telnet->cfg.username ? "USER=" : "",
-			       telnet->cfg.username,
-			       *telnet->cfg.username ? " " : "",
-			       n == 6 ? "<nothing>" :
-			       (*telnet->cfg.environmt ? "<stuff>" : ""));
-	    logevent(telnet->frontend, logbuf);
-	    sfree(logbuf);
 	}
 	break;
     }
@@ -805,6 +765,25 @@
      */
     update_specials_menu(telnet->frontend);
 
+    /*
+     * loghost overrides realhost, if specified.
+     */
+    if (*telnet->cfg.loghost) {
+	char *colon;
+
+	sfree(*realhost);
+	*realhost = dupstr(telnet->cfg.loghost);
+	colon = strrchr(*realhost, ':');
+	if (colon) {
+	    /*
+	     * FIXME: if we ever update this aspect of ssh.c for
+	     * IPv6 literal management, this should change in line
+	     * with it.
+	     */
+	    *colon++ = '\0';
+	}
+    }
+
     return NULL;
 }
 
@@ -1106,5 +1085,7 @@
     telnet_provide_logctx,
     telnet_unthrottle,
     telnet_cfg_info,
+    "telnet",
+    PROT_TELNET,
     23
 };

Modified: trunk/Tools/GMEplink/TERMINAL.H
==============================================================================
--- trunk/Tools/GMEplink/TERMINAL.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/TERMINAL.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -151,7 +151,7 @@
     int seen_disp_event;
     int big_cursor;
 
-    int xterm_mouse;		       /* send mouse messages to app */
+    int xterm_mouse;		       /* send mouse messages to host */
     int mouse_is_down;		       /* used while tracking mouse buttons */
 
     int cset_attr[2];

Modified: trunk/Tools/GMEplink/VERSION.C
==============================================================================
--- trunk/Tools/GMEplink/VERSION.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/VERSION.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -23,9 +23,14 @@
 char ver[] = "Release " STR(RELEASE);
 char sshver[] = "PuTTY-Release-" STR(RELEASE);
 
+#elif defined PRERELEASE
+
+char ver[] = "Pre-release " STR(PRERELEASE) ":r" STR(SVN_REV);
+char sshver[] = "PuTTY-Prerelease-" STR(PRERELEASE) ":r" STR(SVN_REV);
+
 #elif defined SVN_REV
 
-char ver[] = "Custom build r" STR(SVN_REV);
+char ver[] = "Custom build r" STR(SVN_REV) ", " __DATE__ " " __TIME__;
 char sshver[] = "PuTTY-Custom-r" STR(SVN_REV);
 
 #else

Modified: trunk/Tools/GMEplink/Windows/WINCONS.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINCONS.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINCONS.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -318,14 +318,31 @@
 	    memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
     }
 
-    if (console_batch_mode)
-	return 0;
+    /*
+     * The prompts_t might contain a message to be displayed but no
+     * actual prompt. More usually, though, it will contain
+     * questions that the user needs to answer, in which case we
+     * need to ensure that we're able to get the answers.
+     */
+    if (p->n_prompts) {
+	if (console_batch_mode)
+	    return 0;
+	hin = GetStdHandle(STD_INPUT_HANDLE);
+	if (hin == INVALID_HANDLE_VALUE) {
+	    fprintf(stderr, "Cannot get standard input handle\n");
+	    cleanup_exit(1);
+	}
+    }
 
-    hin = GetStdHandle(STD_INPUT_HANDLE);
-    hout = GetStdHandle(STD_OUTPUT_HANDLE);
-    if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
-	fprintf(stderr, "Cannot get standard input/output handles\n");
-	cleanup_exit(1);
+    /*
+     * And if we have anything to print, we need standard output.
+     */
+    if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) {
+	hout = GetStdHandle(STD_OUTPUT_HANDLE);
+	if (hout == INVALID_HANDLE_VALUE) {
+	    fprintf(stderr, "Cannot get standard output handle\n");
+	    cleanup_exit(1);
+	}
     }
 
     /*

Modified: trunk/Tools/GMEplink/Windows/WINHANDL.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINHANDL.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINHANDL.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -262,12 +262,15 @@
 {
     struct handle_output *ctx = (struct handle_output *) param;
     OVERLAPPED ovl, *povl;
+    HANDLE oev;
     int writeret;
 
-    if (ctx->flags & HANDLE_FLAG_OVERLAPPED)
+    if (ctx->flags & HANDLE_FLAG_OVERLAPPED) {
 	povl = &ovl;
-    else
+	oev = CreateEvent(NULL, TRUE, FALSE, NULL);
+    } else {
 	povl = NULL;
+    }
 
     while (1) {
 	WaitForSingleObject(ctx->ev_from_main, INFINITE);
@@ -275,8 +278,11 @@
 	    SetEvent(ctx->ev_to_main);
 	    break;
 	}
-	if (povl)
+	if (povl) {
 	    memset(povl, 0, sizeof(OVERLAPPED));
+	    povl->hEvent = oev;
+	}
+
 	writeret = WriteFile(ctx->h, ctx->buffer, ctx->len,
 			     &ctx->lenwritten, povl);
 	if (!writeret)
@@ -297,6 +303,9 @@
 	    break;
     }
 
+    if (povl)
+	CloseHandle(oev);
+
     return 0;
 }
 
@@ -407,7 +416,7 @@
     add234(handles_by_evtomain, h);
 
     CreateThread(NULL, 0, handle_output_threadfunc,
-		 &h->u.i, 0, &out_threadid);
+		 &h->u.o, 0, &out_threadid);
 
     return h;
 }

Modified: trunk/Tools/GMEplink/Windows/WINHELP.H
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINHELP.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINHELP.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -1,6 +1,10 @@
 /*
- * winhelp.h - define Windows Help context names. These match up with
- * the \cfg{winhelp-topic} directives in the Halibut source.
+ * winhelp.h - define Windows Help context names.
+ * Each definition has the form "winhelp-topic:halibut-topic", where:
+ *  - "winhelp-topic" matches up with the \cfg{winhelp-topic} directives
+ *    in the Halibut source, and is used for WinHelp;
+ *  - "halibut-topic" matches up with the Halibut keywords in the source,
+ *    and is used for HTML Help.
  */
 
 /* Maximum length for WINHELP_CTX_foo strings */
@@ -42,6 +46,7 @@
 #define WINHELP_CTX_terminal_autowrap "terminal.autowrap:config-autowrap"
 #define WINHELP_CTX_terminal_decom "terminal.decom:config-decom"
 #define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr:config-crlf"
+#define WINHELP_CTX_terminal_crhaslf "terminal.crhaslf:config-lfcr"
 #define WINHELP_CTX_terminal_bce "terminal.bce:config-erase"
 #define WINHELP_CTX_terminal_blink "terminal.blink:config-blink"
 #define WINHELP_CTX_terminal_answerback "terminal.answerback:config-answerback"
@@ -69,10 +74,12 @@
 #define WINHELP_CTX_connection_termtype "connection.termtype:config-termtype"
 #define WINHELP_CTX_connection_termspeed "connection.termspeed:config-termspeed"
 #define WINHELP_CTX_connection_username "connection.username:config-username"
+#define WINHELP_CTX_connection_username_from_env "connection.usernamefromenv:config-username-from-env"
 #define WINHELP_CTX_connection_keepalive "connection.keepalive:config-keepalive"
 #define WINHELP_CTX_connection_nodelay "connection.nodelay:config-nodelay"
 #define WINHELP_CTX_connection_ipversion "connection.ipversion:config-address-family"
 #define WINHELP_CTX_connection_tcpkeepalive "connection.tcpkeepalive:config-tcp-keepalives"
+#define WINHELP_CTX_connection_loghost "connection.loghost:config-loghost"
 #define WINHELP_CTX_proxy_type "proxy.type:config-proxy-type"
 #define WINHELP_CTX_proxy_main "proxy.main:config-proxy"
 #define WINHELP_CTX_proxy_exclude "proxy.exclude:config-proxy-exclude"
@@ -95,12 +102,16 @@
 #define WINHELP_CTX_ssh_kexlist "ssh.kex.order:config-ssh-kex-order"
 #define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat:config-ssh-kex-rekey"
 #define WINHELP_CTX_ssh_auth_bypass "ssh.auth.bypass:config-ssh-noauth"
+#define WINHELP_CTX_ssh_auth_banner "ssh.auth.banner:config-ssh-banner"
 #define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey:config-ssh-privkey"
 #define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd:config-ssh-agentfwd"
 #define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser:config-ssh-changeuser"
 #define WINHELP_CTX_ssh_auth_pageant "ssh.auth.pageant:config-ssh-tryagent"
 #define WINHELP_CTX_ssh_auth_tis "ssh.auth.tis:config-ssh-tis"
 #define WINHELP_CTX_ssh_auth_ki "ssh.auth.ki:config-ssh-ki"
+#define WINHELP_CTX_ssh_gssapi "ssh.auth.gssapi:config-ssh-auth-gssapi"
+#define WINHELP_CTX_ssh_gssapi_delegation "ssh.auth.gssapi.delegation:config-ssh-auth-gssapi-delegation"
+#define WINHELP_CTX_ssh_gssapi_libraries "ssh.auth.gssapi.libraries:config-ssh-auth-gssapi-libraries"
 #define WINHELP_CTX_selection_buttons "selection.buttons:config-mouse"
 #define WINHELP_CTX_selection_shiftdrag "selection.shiftdrag:config-mouseshift"
 #define WINHELP_CTX_selection_rect "selection.rect:config-rectselect"
@@ -119,17 +130,20 @@
 #define WINHELP_CTX_translation_linedraw "translation.linedraw:config-linedraw"
 #define WINHELP_CTX_ssh_tunnels_x11 "ssh.tunnels.x11:config-ssh-x11"
 #define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth:config-ssh-x11auth"
+#define WINHELP_CTX_ssh_tunnels_xauthority "ssh.tunnels.xauthority:config-ssh-xauthority"
 #define WINHELP_CTX_ssh_tunnels_portfwd "ssh.tunnels.portfwd:config-ssh-portfwd"
 #define WINHELP_CTX_ssh_tunnels_portfwd_localhost "ssh.tunnels.portfwd.localhost:config-ssh-portfwd-localhost"
 #define WINHELP_CTX_ssh_tunnels_portfwd_ipversion "ssh.tunnels.portfwd.ipversion:config-ssh-portfwd-address-family"
 #define WINHELP_CTX_ssh_bugs_ignore1 "ssh.bugs.ignore1:config-ssh-bug-ignore1"
 #define WINHELP_CTX_ssh_bugs_plainpw1 "ssh.bugs.plainpw1:config-ssh-bug-plainpw1"
 #define WINHELP_CTX_ssh_bugs_rsa1 "ssh.bugs.rsa1:config-ssh-bug-rsa1"
+#define WINHELP_CTX_ssh_bugs_ignore2 "ssh.bugs.ignore2:config-ssh-bug-ignore2"
 #define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2:config-ssh-bug-hmac2"
 #define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2:config-ssh-bug-derivekey2"
 #define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2:config-ssh-bug-sig"
 #define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2:config-ssh-bug-pksessid2"
 #define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2:config-ssh-bug-rekey"
+#define WINHELP_CTX_ssh_bugs_maxpkt2 "ssh.bugs.maxpkt2:config-ssh-bug-maxpkt2"
 #define WINHELP_CTX_serial_line "serial.line:config-serial-line"
 #define WINHELP_CTX_serial_speed "serial.speed:config-serial-speed"
 #define WINHELP_CTX_serial_databits "serial.databits:config-serial-databits"

Modified: trunk/Tools/GMEplink/Windows/WINMISC.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINMISC.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINMISC.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -5,17 +5,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "putty.h"
+#include <security.h>
 
 OSVERSIONINFO osVersion;
 
-void platform_get_x11_auth(char *display, int *proto,
-                           unsigned char *data, int *datalen)
-{
-    /* We don't support this at all under Windows. */
-}
-
-const char platform_x11_best_transport[] = "localhost";
-
 char *platform_get_x_display(void) {
     /* We may as well check for DISPLAY in case it's useful. */
     return dupstr(getenv("DISPLAY"));
@@ -48,21 +41,61 @@
 {
     DWORD namelen;
     char *user;
+    int got_username = FALSE;
+    DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
+			  (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
+
+    {
+	static int tried_usernameex = FALSE;
+	if (!tried_usernameex) {
+	    /* Not available on Win9x, so load dynamically */
+	    HMODULE secur32 = load_system32_dll("secur32.dll");
+	    GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
+	    tried_usernameex = TRUE;
+	}
+    }
 
-    namelen = 0;
-    if (GetUserName(NULL, &namelen) == FALSE) {
+    if (p_GetUserNameExA) {
 	/*
-	 * Apparently this doesn't work at least on Windows XP SP2.
-	 * Thus assume a maximum of 256. It will fail again if it
-	 * doesn't fit.
+	 * If available, use the principal -- this avoids the problem
+	 * that the local username is case-insensitive but Kerberos
+	 * usernames are case-sensitive.
 	 */
-	namelen = 256;
+
+	/* Get the length */
+	namelen = 0;
+	(void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
+
+	user = snewn(namelen, char);
+	got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
+	if (got_username) {
+	    char *p = strchr(user, '@');
+	    if (p) *p = 0;
+	} else {
+	    sfree(user);
+	}
     }
 
-    user = snewn(namelen, char);
-    GetUserName(user, &namelen);
+    if (!got_username) {
+	/* Fall back to local user name */
+	namelen = 0;
+	if (GetUserName(NULL, &namelen) == FALSE) {
+	    /*
+	     * Apparently this doesn't work at least on Windows XP SP2.
+	     * Thus assume a maximum of 256. It will fail again if it
+	     * doesn't fit.
+	     */
+	    namelen = 256;
+	}
+
+	user = snewn(namelen, char);
+	got_username = GetUserName(user, &namelen);
+	if (!got_username) {
+	    sfree(user);
+	}
+    }
 
-    return user;
+    return got_username ? user : NULL;
 }
 
 BOOL init_winver(void)
@@ -72,6 +105,33 @@
     return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
 }
 
+HMODULE load_system32_dll(const char *libname)
+{
+    /*
+     * Wrapper function to load a DLL out of c:\windows\system32
+     * without going through the full DLL search path. (Hence no
+     * attack is possible by placing a substitute DLL earlier on that
+     * path.)
+     */
+    static char *sysdir = NULL;
+    char *fullpath;
+    HMODULE ret;
+
+    if (!sysdir) {
+	int size = 0, len;
+	do {
+	    size = 3*size/2 + 512;
+	    sysdir = sresize(sysdir, size, char);
+	    len = GetSystemDirectory(sysdir, size);
+	} while (len >= size);
+    }
+
+    fullpath = dupcat(sysdir, "\\", libname, NULL);
+    ret = LoadLibrary(fullpath);
+    sfree(fullpath);
+    return ret;
+}
+
 #ifdef DEBUG
 static FILE *debug_fp = NULL;
 static HANDLE debug_hdl = INVALID_HANDLE_VALUE;

Modified: trunk/Tools/GMEplink/Windows/WINNET.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINNET.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINNET.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -34,6 +34,19 @@
  */
 typedef struct Socket_tag *Actual_Socket;
 
+/*
+ * Mutable state that goes with a SockAddr: stores information
+ * about where in the list of candidate IP(v*) addresses we've
+ * currently got to.
+ */
+typedef struct SockAddrStep_tag SockAddrStep;
+struct SockAddrStep_tag {
+#ifndef NO_IPV6
+    struct addrinfo *ai;	       /* steps along addr->ais */
+#endif
+    int curraddr;
+};
+
 struct Socket_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -52,6 +65,7 @@
     int sending_oob;
     int oobinline, nodelay, keepalive, privport;
     SockAddr addr;
+    SockAddrStep step;
     int port;
     int pending_error;		       /* in case send() returns error */
     /*
@@ -64,28 +78,44 @@
 };
 
 struct SockAddr_tag {
+    int refcount;
     char *error;
-    /* 
-     * Which address family this address belongs to. AF_INET for
-     * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name
-     * resolution has not been done and a simple host name is held
-     * in this SockAddr structure.
-     * The hostname field is also used when the hostname has both
-     * an IPv6 and IPv4 address and the IPv6 connection attempt
-     * fails. We then try the IPv4 address.
-     * This 'family' should become an option in the GUI and
-     * on the commandline for selecting a default protocol.
-     */
-    int family;
+    int resolved;
 #ifndef NO_IPV6
     struct addrinfo *ais;	       /* Addresses IPv6 style. */
-    struct addrinfo *ai;	       /* steps along the linked list */
 #endif
     unsigned long *addresses;	       /* Addresses IPv4 style. */
-    int naddresses, curraddr;
+    int naddresses;
     char hostname[512];		       /* Store an unresolved host name. */
 };
 
+/*
+ * Which address family this address belongs to. AF_INET for IPv4;
+ * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has
+ * not been done and a simple host name is held in this SockAddr
+ * structure.
+ */
+#ifndef NO_IPV6
+#define SOCKADDR_FAMILY(addr, step) \
+    (!(addr)->resolved ? AF_UNSPEC : \
+     (step).ai ? (step).ai->ai_family : AF_INET)
+#else
+#define SOCKADDR_FAMILY(addr, step) \
+    (!(addr)->resolved ? AF_UNSPEC : AF_INET)
+#endif
+
+/*
+ * Start a SockAddrStep structure to step through multiple
+ * addresses.
+ */
+#ifndef NO_IPV6
+#define START_STEP(addr, step) \
+    ((step).ai = (addr)->ais, (step).curraddr = 0)
+#else
+#define START_STEP(addr, step) \
+    ((step).curraddr = 0)
+#endif
+
 static tree234 *sktree;
 
 static int cmpfortree(void *av, void *bv)
@@ -96,6 +126,10 @@
 	return -1;
     if (as > bs)
 	return +1;
+    if (a < b)
+	return -1;
+    if (a > b)
+	return +1;
     return 0;
 }
 
@@ -110,66 +144,51 @@
     return 0;
 }
 
-#define NOTHING
-#define DECL_WINSOCK_FUNCTION(linkage, rettype, name, params) \
-    typedef rettype (WINAPI *t_##name) params; \
-    linkage t_##name p_##name
-#define GET_WINSOCK_FUNCTION(module, name) \
-    p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
-
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAAsyncSelect,
-		      (SOCKET, HWND, u_int, long));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEventSelect, (SOCKET, WSAEVENT, long));
-DECL_WINSOCK_FUNCTION(NOTHING, int, select,
-		      (int, fd_set FAR *, fd_set FAR *,
-		       fd_set FAR *, const struct timeval FAR *));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAGetLastError, (void));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEnumNetworkEvents,
-		      (SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
-DECL_WINSOCK_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));
-DECL_WINSOCK_FUNCTION(static, int, WSACleanup, (void));
-DECL_WINSOCK_FUNCTION(static, int, closesocket, (SOCKET));
-DECL_WINSOCK_FUNCTION(static, u_long, ntohl, (u_long));
-DECL_WINSOCK_FUNCTION(static, u_long, htonl, (u_long));
-DECL_WINSOCK_FUNCTION(static, u_short, htons, (u_short));
-DECL_WINSOCK_FUNCTION(static, u_short, ntohs, (u_short));
-DECL_WINSOCK_FUNCTION(static, struct hostent FAR *, gethostbyname,
+DECL_WINDOWS_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));
+DECL_WINDOWS_FUNCTION(static, int, WSACleanup, (void));
+DECL_WINDOWS_FUNCTION(static, int, closesocket, (SOCKET));
+DECL_WINDOWS_FUNCTION(static, u_long, ntohl, (u_long));
+DECL_WINDOWS_FUNCTION(static, u_long, htonl, (u_long));
+DECL_WINDOWS_FUNCTION(static, u_short, htons, (u_short));
+DECL_WINDOWS_FUNCTION(static, u_short, ntohs, (u_short));
+DECL_WINDOWS_FUNCTION(static, int, gethostname, (char *, int));
+DECL_WINDOWS_FUNCTION(static, struct hostent FAR *, gethostbyname,
 		      (const char FAR *));
-DECL_WINSOCK_FUNCTION(static, struct servent FAR *, getservbyname,
+DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname,
 		      (const char FAR *, const char FAR *));
-DECL_WINSOCK_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
-DECL_WINSOCK_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
-DECL_WINSOCK_FUNCTION(static, int, connect,
+DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
+DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
+DECL_WINDOWS_FUNCTION(static, int, connect,
 		      (SOCKET, const struct sockaddr FAR *, int));
-DECL_WINSOCK_FUNCTION(static, int, bind,
+DECL_WINDOWS_FUNCTION(static, int, bind,
 		      (SOCKET, const struct sockaddr FAR *, int));
-DECL_WINSOCK_FUNCTION(static, int, setsockopt,
+DECL_WINDOWS_FUNCTION(static, int, setsockopt,
 		      (SOCKET, int, int, const char FAR *, int));
-DECL_WINSOCK_FUNCTION(static, SOCKET, socket, (int, int, int));
-DECL_WINSOCK_FUNCTION(static, int, listen, (SOCKET, int));
-DECL_WINSOCK_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
-DECL_WINSOCK_FUNCTION(static, int, ioctlsocket,
+DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int));
+DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int));
+DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
+DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,
 		      (SOCKET, long, u_long FAR *));
-DECL_WINSOCK_FUNCTION(static, SOCKET, accept,
+DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
 		      (SOCKET, struct sockaddr FAR *, int FAR *));
-DECL_WINSOCK_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
-DECL_WINSOCK_FUNCTION(static, int, WSAIoctl,
+DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
+DECL_WINDOWS_FUNCTION(static, int, WSAIoctl,
 		      (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD,
 		       LPDWORD, LPWSAOVERLAPPED,
 		       LPWSAOVERLAPPED_COMPLETION_ROUTINE));
 #ifndef NO_IPV6
-DECL_WINSOCK_FUNCTION(static, int, getaddrinfo,
+DECL_WINDOWS_FUNCTION(static, int, getaddrinfo,
 		      (const char *nodename, const char *servname,
 		       const struct addrinfo *hints, struct addrinfo **res));
-DECL_WINSOCK_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
-DECL_WINSOCK_FUNCTION(static, int, getnameinfo,
+DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
+DECL_WINDOWS_FUNCTION(static, int, getnameinfo,
 		      (const struct sockaddr FAR * sa, socklen_t salen,
 		       char FAR * host, size_t hostlen, char FAR * serv,
 		       size_t servlen, int flags));
-DECL_WINSOCK_FUNCTION(static, char *, gai_strerror, (int ecode));
-DECL_WINSOCK_FUNCTION(static, int, WSAAddressToStringA,
+DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode));
+DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA,
 		      (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO,
-		       LPTSTR, LPDWORD));
+		       LPSTR, LPDWORD));
 #endif
 
 static HMODULE winsock_module = NULL;
@@ -208,9 +227,9 @@
 #ifndef NO_IPV6
     winsock2_module =
 #endif
-        winsock_module = LoadLibrary("WS2_32.DLL");
+        winsock_module = load_system32_dll("ws2_32.dll");
     if (!winsock_module) {
-	winsock_module = LoadLibrary("WSOCK32.DLL");
+	winsock_module = load_system32_dll("wsock32.dll");
     }
     if (!winsock_module)
 	fatalbox("Unable to load any WinSock library");
@@ -221,60 +240,61 @@
 #ifdef NET_SETUP_DIAGNOSTICS
 	logevent(NULL, "Native WinSock IPv6 support detected");
 #endif
-	GET_WINSOCK_FUNCTION(winsock_module, getaddrinfo);
-	GET_WINSOCK_FUNCTION(winsock_module, freeaddrinfo);
-	GET_WINSOCK_FUNCTION(winsock_module, getnameinfo);
-	GET_WINSOCK_FUNCTION(winsock_module, gai_strerror);
+	GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo);
+	GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo);
+	GET_WINDOWS_FUNCTION(winsock_module, getnameinfo);
+	GET_WINDOWS_FUNCTION(winsock_module, gai_strerror);
     } else {
 	/* Fall back to wship6.dll for Windows 2000 */
-	wship6_module = LoadLibrary("wship6.dll");
+	wship6_module = load_system32_dll("wship6.dll");
 	if (wship6_module) {
 #ifdef NET_SETUP_DIAGNOSTICS
 	    logevent(NULL, "WSH IPv6 support detected");
 #endif
-	    GET_WINSOCK_FUNCTION(wship6_module, getaddrinfo);
-	    GET_WINSOCK_FUNCTION(wship6_module, freeaddrinfo);
-	    GET_WINSOCK_FUNCTION(wship6_module, getnameinfo);
-	    GET_WINSOCK_FUNCTION(wship6_module, gai_strerror);
+	    GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo);
+	    GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo);
+	    GET_WINDOWS_FUNCTION(wship6_module, getnameinfo);
+	    GET_WINDOWS_FUNCTION(wship6_module, gai_strerror);
 	} else {
 #ifdef NET_SETUP_DIAGNOSTICS
 	    logevent(NULL, "No IPv6 support detected");
 #endif
 	}
     }
-    GET_WINSOCK_FUNCTION(winsock2_module, WSAAddressToStringA);
+    GET_WINDOWS_FUNCTION(winsock2_module, WSAAddressToStringA);
 #else
 #ifdef NET_SETUP_DIAGNOSTICS
     logevent(NULL, "PuTTY was built without IPv6 support");
 #endif
 #endif
 
-    GET_WINSOCK_FUNCTION(winsock_module, WSAAsyncSelect);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAEventSelect);
-    GET_WINSOCK_FUNCTION(winsock_module, select);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAGetLastError);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAEnumNetworkEvents);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAStartup);
-    GET_WINSOCK_FUNCTION(winsock_module, WSACleanup);
-    GET_WINSOCK_FUNCTION(winsock_module, closesocket);
-    GET_WINSOCK_FUNCTION(winsock_module, ntohl);
-    GET_WINSOCK_FUNCTION(winsock_module, htonl);
-    GET_WINSOCK_FUNCTION(winsock_module, htons);
-    GET_WINSOCK_FUNCTION(winsock_module, ntohs);
-    GET_WINSOCK_FUNCTION(winsock_module, gethostbyname);
-    GET_WINSOCK_FUNCTION(winsock_module, getservbyname);
-    GET_WINSOCK_FUNCTION(winsock_module, inet_addr);
-    GET_WINSOCK_FUNCTION(winsock_module, inet_ntoa);
-    GET_WINSOCK_FUNCTION(winsock_module, connect);
-    GET_WINSOCK_FUNCTION(winsock_module, bind);
-    GET_WINSOCK_FUNCTION(winsock_module, setsockopt);
-    GET_WINSOCK_FUNCTION(winsock_module, socket);
-    GET_WINSOCK_FUNCTION(winsock_module, listen);
-    GET_WINSOCK_FUNCTION(winsock_module, send);
-    GET_WINSOCK_FUNCTION(winsock_module, ioctlsocket);
-    GET_WINSOCK_FUNCTION(winsock_module, accept);
-    GET_WINSOCK_FUNCTION(winsock_module, recv);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAIoctl);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect);
+    GET_WINDOWS_FUNCTION(winsock_module, select);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);
+    GET_WINDOWS_FUNCTION(winsock_module, WSACleanup);
+    GET_WINDOWS_FUNCTION(winsock_module, closesocket);
+    GET_WINDOWS_FUNCTION(winsock_module, ntohl);
+    GET_WINDOWS_FUNCTION(winsock_module, htonl);
+    GET_WINDOWS_FUNCTION(winsock_module, htons);
+    GET_WINDOWS_FUNCTION(winsock_module, ntohs);
+    GET_WINDOWS_FUNCTION(winsock_module, gethostname);
+    GET_WINDOWS_FUNCTION(winsock_module, gethostbyname);
+    GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
+    GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
+    GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);
+    GET_WINDOWS_FUNCTION(winsock_module, connect);
+    GET_WINDOWS_FUNCTION(winsock_module, bind);
+    GET_WINDOWS_FUNCTION(winsock_module, setsockopt);
+    GET_WINDOWS_FUNCTION(winsock_module, socket);
+    GET_WINDOWS_FUNCTION(winsock_module, listen);
+    GET_WINDOWS_FUNCTION(winsock_module, send);
+    GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);
+    GET_WINDOWS_FUNCTION(winsock_module, accept);
+    GET_WINDOWS_FUNCTION(winsock_module, recv);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl);
 
     /* Try to get the best WinSock version we can get */
     if (!sk_startup(2,2) &&
@@ -299,7 +319,8 @@
 	sktree = NULL;
     }
 
-    p_WSACleanup();
+    if (p_WSACleanup)
+	p_WSACleanup();
     if (winsock_module)
 	FreeLibrary(winsock_module);
 #ifndef NO_IPV6
@@ -392,26 +413,29 @@
 {
     SockAddr ret = snew(struct SockAddr_tag);
     unsigned long a;
-    struct hostent *h = NULL;
     char realhost[8192];
-    int ret_family;
-    int err;
+    int hint_family;
 
-    /* Clear the structure and default to IPv4. */
-    memset(ret, 0, sizeof(struct SockAddr_tag));
-    ret->family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+    /* Default to IPv4. */
+    hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
 #ifndef NO_IPV6
 		   address_family == ADDRTYPE_IPV6 ? AF_INET6 :
 #endif
 		   AF_UNSPEC);
+
+    /* Clear the structure and default to IPv4. */
+    memset(ret, 0, sizeof(struct SockAddr_tag));
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #endif
     ret->addresses = NULL;
-    ret_family = AF_UNSPEC;
+    ret->resolved = FALSE;
+    ret->refcount = 1;
     *realhost = '\0';
 
     if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
+	struct hostent *h = NULL;
+	int err;
 #ifndef NO_IPV6
 	/*
 	 * Use getaddrinfo when it's available
@@ -422,11 +446,10 @@
 	    logevent(NULL, "Using getaddrinfo() for resolving");
 #endif
 	    memset(&hints, 0, sizeof(hints));
-	    hints.ai_family = ret->family;
+	    hints.ai_family = hint_family;
 	    hints.ai_flags = AI_CANONNAME;
 	    if ((err = p_getaddrinfo(host, NULL, &hints, &ret->ais)) == 0)
-		ret_family = ret->ais->ai_family;
-	    ret->ai = ret->ais;
+		ret->resolved = TRUE;
 	} else
 #endif
 	{
@@ -438,12 +461,12 @@
 	     * (NOTE: we don't use gethostbyname as a fallback!)
 	     */
 	    if ( (h = p_gethostbyname(host)) )
-		ret_family = AF_INET;
+		ret->resolved = TRUE;
 	    else
 		err = p_WSAGetLastError();
 	}
 
-	if (ret_family == AF_UNSPEC) {
+	if (!ret->resolved) {
 	    ret->error = (err == WSAENETDOWN ? "Network is down" :
 			  err == WSAHOST_NOT_FOUND ? "Host does not exist" :
 			  err == WSATRY_AGAIN ? "Host not found" :
@@ -453,20 +476,19 @@
 			  "gethostbyname: unknown error");
 	} else {
 	    ret->error = NULL;
-	    ret->family = ret_family;
 
 #ifndef NO_IPV6
 	    /* If we got an address info use that... */
-	    if (ret->ai) {
+	    if (ret->ais) {
 		/* Are we in IPv4 fallback mode? */
 		/* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */
-		if (ret->family == AF_INET)
+		if (ret->ais->ai_family == AF_INET)
 		    memcpy(&a,
-			   (char *) &((SOCKADDR_IN *) ret->ai->
+			   (char *) &((SOCKADDR_IN *) ret->ais->
 				      ai_addr)->sin_addr, sizeof(a));
 
-		if (ret->ai->ai_canonname)
-		    strncpy(realhost, ret->ai->ai_canonname, lenof(realhost));
+		if (ret->ais->ai_canonname)
+		    strncpy(realhost, ret->ais->ai_canonname, lenof(realhost));
 		else
 		    strncpy(realhost, host, lenof(realhost));
 	    }
@@ -482,7 +504,6 @@
 		    memcpy(&a, h->h_addr_list[n], sizeof(a));
 		    ret->addresses[n] = p_ntohl(a);
 		}
-		ret->curraddr = 0;
 		memcpy(&a, h->h_addr, sizeof(a));
 		/* This way we are always sure the h->h_name is valid :) */
 		strncpy(realhost, h->h_name, sizeof(realhost));
@@ -495,9 +516,8 @@
 	 */
 	ret->addresses = snewn(1, unsigned long);
 	ret->naddresses = 1;
-	ret->curraddr = 0;
 	ret->addresses[0] = p_ntohl(a);
-	ret->family = AF_INET;
+	ret->resolved = TRUE;
 	strncpy(realhost, host, sizeof(realhost));
     }
     realhost[lenof(realhost)-1] = '\0';
@@ -510,31 +530,31 @@
 {
     SockAddr ret = snew(struct SockAddr_tag);
     ret->error = NULL;
-    ret->family = AF_UNSPEC;
+    ret->resolved = FALSE;
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #endif
     ret->addresses = NULL;
     ret->naddresses = 0;
+    ret->refcount = 1;
     strncpy(ret->hostname, host, lenof(ret->hostname));
     ret->hostname[lenof(ret->hostname)-1] = '\0';
     return ret;
 }
 
-int sk_nextaddr(SockAddr addr)
+int sk_nextaddr(SockAddr addr, SockAddrStep *step)
 {
 #ifndef NO_IPV6
-    if (addr->ai) {
-	if (addr->ai->ai_next) {
-	    addr->ai = addr->ai->ai_next;
-	    addr->family = addr->ai->ai_family;
+    if (step->ai) {
+	if (step->ai->ai_next) {
+	    step->ai = step->ai->ai_next;
 	    return TRUE;
 	} else
 	    return FALSE;
     }
 #endif
-    if (addr->curraddr+1 < addr->naddresses) {
-	addr->curraddr++;
+    if (step->curraddr+1 < addr->naddresses) {
+	step->curraddr++;
 	return TRUE;
     } else {
 	return FALSE;
@@ -543,19 +563,30 @@
 
 void sk_getaddr(SockAddr addr, char *buf, int buflen)
 {
+    SockAddrStep step;
+    START_STEP(addr, step);
+
 #ifndef NO_IPV6
-    if (addr->ai) {
+    if (step.ai) {
+	int err = 0;
 	if (p_WSAAddressToStringA) {
-	    p_WSAAddressToStringA(addr->ai->ai_addr, addr->ai->ai_addrlen,
-				  NULL, buf, &buflen);
+	    DWORD dwbuflen = buflen;
+	    err = p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,
+					NULL, buf, &dwbuflen);
 	} else
-	    strncpy(buf, "IPv6", buflen);
+	    err = -1;
+	if (err) {
+	    strncpy(buf, addr->hostname, buflen);
+	    if (!buf[0])
+		strncpy(buf, "<unknown>", buflen);
+	    buf[buflen-1] = '\0';
+	}
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (SOCKADDR_FAMILY(addr, step) == AF_INET) {
 	struct in_addr a;
-	assert(addr->addresses && addr->curraddr < addr->naddresses);
-	a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+	assert(addr->addresses && step.curraddr < addr->naddresses);
+	a.s_addr = p_htonl(addr->addresses[step.curraddr]);
 	strncpy(buf, p_inet_ntoa(a), buflen);
 	buf[buflen-1] = '\0';
     } else {
@@ -566,7 +597,9 @@
 
 int sk_hostname_is_local(char *name)
 {
-    return !strcmp(name, "localhost");
+    return !strcmp(name, "localhost") ||
+	   !strcmp(name, "::1") ||
+	   !strncmp(name, "127.", 4);
 }
 
 static INTERFACE_INFO local_interfaces[16];
@@ -602,64 +635,81 @@
 
 int sk_address_is_local(SockAddr addr)
 {
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
 #ifndef NO_IPV6
-    if (addr->family == AF_INET6) {
-    	return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)addr->ai->ai_addr);
+    if (family == AF_INET6) {
+    	return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)step.ai->ai_addr);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (family == AF_INET) {
 #ifndef NO_IPV6
-	if (addr->ai) {
-	    return ipv4_is_local_addr(((struct sockaddr_in *)addr->ai->ai_addr)
+	if (step.ai) {
+	    return ipv4_is_local_addr(((struct sockaddr_in *)step.ai->ai_addr)
 				      ->sin_addr);
 	} else
 #endif
 	{
 	    struct in_addr a;
-	    assert(addr->addresses && addr->curraddr < addr->naddresses);
-	    a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+	    assert(addr->addresses && step.curraddr < addr->naddresses);
+	    a.s_addr = p_htonl(addr->addresses[step.curraddr]);
 	    return ipv4_is_local_addr(a);
 	}
     } else {
-	assert(addr->family == AF_UNSPEC);
+	assert(family == AF_UNSPEC);
 	return 0;		       /* we don't know; assume not */
     }
 }
 
 int sk_addrtype(SockAddr addr)
 {
-    return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    return (family == AF_INET ? ADDRTYPE_IPV4 :
 #ifndef NO_IPV6
-	    addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
+	    family == AF_INET6 ? ADDRTYPE_IPV6 :
 #endif
 	    ADDRTYPE_NAME);
 }
 
 void sk_addrcopy(SockAddr addr, char *buf)
 {
-    assert(addr->family != AF_UNSPEC);
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    assert(family != AF_UNSPEC);
 #ifndef NO_IPV6
-    if (addr->ai) {
-	if (addr->family == AF_INET)
-	    memcpy(buf, &((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr,
+    if (step.ai) {
+	if (family == AF_INET)
+	    memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,
 		   sizeof(struct in_addr));
-	else if (addr->family == AF_INET6)
-	    memcpy(buf, &((struct sockaddr_in6 *)addr->ai->ai_addr)->sin6_addr,
+	else if (family == AF_INET6)
+	    memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,
 		   sizeof(struct in6_addr));
 	else
 	    assert(FALSE);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (family == AF_INET) {
 	struct in_addr a;
-	assert(addr->addresses && addr->curraddr < addr->naddresses);
-	a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+	assert(addr->addresses && step.curraddr < addr->naddresses);
+	a.s_addr = p_htonl(addr->addresses[step.curraddr]);
 	memcpy(buf, (char*) &a.s_addr, 4);
     }
 }
 
 void sk_addr_free(SockAddr addr)
 {
+    if (--addr->refcount > 0)
+	return;
 #ifndef NO_IPV6
     if (addr->ais && p_freeaddrinfo)
 	p_freeaddrinfo(addr->ais);
@@ -669,6 +719,12 @@
     sfree(addr);
 }
 
+SockAddr sk_addr_dup(SockAddr addr)
+{
+    addr->refcount++;
+    return addr;
+}
+
 static Plug sk_tcp_plug(Socket sock, Plug p)
 {
     Actual_Socket s = (Actual_Socket) sock;
@@ -776,17 +832,15 @@
     /*
      * Open socket.
      */
-#ifndef NO_IPV6
-    /* Let's default to IPv6, this shouldn't hurt anybody
-     * If the stack supports IPv6 it will also allow IPv4 connections. */
-    if (sock->addr->ai) {
-	family = sock->addr->ai->ai_family;
-    } else
-#endif
-    {
-	/* Default to IPv4 */
-	family = AF_INET;
-    }
+    family = SOCKADDR_FAMILY(sock->addr, sock->step);
+
+    /*
+     * Remove the socket from the tree before we overwrite its
+     * internal socket id, because that forms part of the tree's
+     * sorting criterion. We'll add it back before exiting this
+     * function, whether we changed anything or not.
+     */
+    del234(sktree, sock);
 
     s = p_socket(family, SOCK_STREAM, 0);
     sock->s = s;
@@ -838,11 +892,10 @@
 	    a.sin_port = p_htons(localport);
 	}
 #ifndef NO_IPV6
-	sockcode = p_bind(s, (sock->addr->family == AF_INET6 ?
-			   (struct sockaddr *) &a6 :
-			   (struct sockaddr *) &a),
-		       (sock->addr->family ==
-			AF_INET6 ? sizeof(a6) : sizeof(a)));
+	sockcode = p_bind(s, (family == AF_INET6 ?
+			      (struct sockaddr *) &a6 :
+			      (struct sockaddr *) &a),
+			  (family == AF_INET6 ? sizeof(a6) : sizeof(a)));
 #else
 	sockcode = p_bind(s, (struct sockaddr *) &a, sizeof(a));
 #endif
@@ -871,26 +924,26 @@
      * Connect to remote address.
      */
 #ifndef NO_IPV6
-    if (sock->addr->ai) {
+    if (sock->step.ai) {
 	if (family == AF_INET6) {
 	    a6.sin6_family = AF_INET6;
 	    a6.sin6_port = p_htons((short) sock->port);
 	    a6.sin6_addr =
-		((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_addr;
-	    a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_flowinfo;
-	    a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_scope_id;
+		((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_addr;
+	    a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_flowinfo;
+	    a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_scope_id;
 	} else {
 	    a.sin_family = AF_INET;
 	    a.sin_addr =
-		((struct sockaddr_in *) sock->addr->ai->ai_addr)->sin_addr;
+		((struct sockaddr_in *) sock->step.ai->ai_addr)->sin_addr;
 	    a.sin_port = p_htons((short) sock->port);
 	}
     } else
 #endif
     {
-	assert(sock->addr->addresses && sock->addr->curraddr < sock->addr->naddresses);
+	assert(sock->addr->addresses && sock->step.curraddr < sock->addr->naddresses);
 	a.sin_family = AF_INET;
-	a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->addr->curraddr]);
+	a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->step.curraddr]);
 	a.sin_port = p_htons((short) sock->port);
     }
 
@@ -932,11 +985,15 @@
 	sock->writable = 1;
     }
 
-    add234(sktree, sock);
-
     err = 0;
 
     ret:
+
+    /*
+     * No matter what happened, put the socket back in the tree.
+     */
+    add234(sktree, sock);
+
     if (err)
 	plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err);
     return err;
@@ -982,12 +1039,13 @@
     ret->privport = privport;
     ret->port = port;
     ret->addr = addr;
+    START_STEP(ret->addr, ret->step);
     ret->s = INVALID_SOCKET;
 
     err = 0;
     do {
         err = try_connect(ret);
-    } while (err && sk_nextaddr(ret->addr));
+    } while (err && sk_nextaddr(ret->addr, &ret->step));
 
     return (Socket) ret;
 }
@@ -1333,7 +1391,7 @@
 	if (s->addr) {
 	    plug_log(s->plug, 1, s->addr, s->port,
 		     winsock_error_string(err), err);
-	    while (s->addr && sk_nextaddr(s->addr)) {
+	    while (s->addr && sk_nextaddr(s->addr, &s->step)) {
 		err = try_connect(s);
 	    }
 	}
@@ -1621,10 +1679,28 @@
 	return 0;
 }
 
-SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname)
+char *get_hostname(void)
+{
+    int len = 128;
+    char *hostname = NULL;
+    do {
+	len *= 2;
+	hostname = sresize(hostname, len, char);
+	if (p_gethostname(hostname, len) < 0) {
+	    sfree(hostname);
+	    hostname = NULL;
+	    break;
+	}
+    } while (strlen(hostname) >= (size_t)(len-1));
+    return hostname;
+}
+
+SockAddr platform_get_x11_unix_address(const char *display, int displaynum,
+				       char **canonicalname)
 {
     SockAddr ret = snew(struct SockAddr_tag);
     memset(ret, 0, sizeof(struct SockAddr_tag));
     ret->error = "unix sockets not supported on this platform";
+    ret->refcount = 1;
     return ret;
 }

Modified: trunk/Tools/GMEplink/Windows/WINPGNTC.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINPGNTC.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINPGNTC.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -7,6 +7,10 @@
 
 #include "putty.h"
 
+#ifndef NO_SECURITY
+#include <aclapi.h>
+#endif
+
 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
 #define AGENT_MAX_MSGLEN  8192
 
@@ -66,6 +70,88 @@
 
 #endif
 
+/*
+ * Dynamically load advapi32.dll for SID manipulation. In its absence,
+ * we degrade gracefully.
+ */
+#ifndef NO_SECURITY
+int advapi_initialised = FALSE;
+static HMODULE advapi;
+DECL_WINDOWS_FUNCTION(static, BOOL, OpenProcessToken,
+		      (HANDLE, DWORD, PHANDLE));
+DECL_WINDOWS_FUNCTION(static, BOOL, GetTokenInformation,
+		      (HANDLE, TOKEN_INFORMATION_CLASS,
+                       LPVOID, DWORD, PDWORD));
+DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor,
+		      (PSECURITY_DESCRIPTOR, DWORD));
+DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner,
+		      (PSECURITY_DESCRIPTOR, PSID, BOOL));
+DECL_WINDOWS_FUNCTION(, DWORD, GetSecurityInfo,
+		      (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
+		       PSID *, PSID *, PACL *, PACL *,
+		       PSECURITY_DESCRIPTOR *));
+int init_advapi(void)
+{
+    advapi = load_system32_dll("advapi32.dll");
+    return advapi &&
+	GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
+        GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
+	GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
+	GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
+	GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner);
+}
+
+PSID get_user_sid(void)
+{
+    HANDLE proc = NULL, tok = NULL;
+    TOKEN_USER *user = NULL;
+    DWORD toklen, sidlen;
+    PSID sid = NULL, ret = NULL;
+
+    if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
+                            GetCurrentProcessId())) == NULL)
+        goto cleanup;
+
+    if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))
+        goto cleanup;
+
+    if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&
+        GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        goto cleanup;
+
+    if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)
+        goto cleanup;
+
+    if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))
+        goto cleanup;
+
+    sidlen = GetLengthSid(user->User.Sid);
+
+    sid = (PSID)smalloc(sidlen);
+
+    if (!CopySid(sidlen, sid, user->User.Sid))
+        goto cleanup;
+
+    /* Success. Move sid into the return value slot, and null it out
+     * to stop the cleanup code freeing it. */
+    ret = sid;
+    sid = NULL;
+
+  cleanup:
+    if (proc != NULL)
+        CloseHandle(proc);
+    if (tok != NULL)
+        CloseHandle(tok);
+    if (user != NULL)
+        LocalFree(user);
+    if (sid != NULL)
+        sfree(sid);
+
+    return ret;
+}
+
+#endif
+
 int agent_query(void *in, int inlen, void **out, int *outlen,
 		void (*callback)(void *, void *, int), void *callback_ctx)
 {
@@ -75,6 +161,9 @@
     unsigned char *p, *ret;
     int id, retlen;
     COPYDATASTRUCT cds;
+    SECURITY_ATTRIBUTES sa, *psa;
+    PSECURITY_DESCRIPTOR psd = NULL;
+    PSID usersid = NULL;
 
     *out = NULL;
     *outlen = 0;
@@ -83,7 +172,42 @@
     if (!hwnd)
 	return 1;		       /* *out == NULL, so failure */
     mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
-    filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+
+#ifndef NO_SECURITY
+    if (advapi_initialised || init_advapi()) {
+        /*
+         * Make the file mapping we create for communication with
+         * Pageant owned by the user SID rather than the default. This
+         * should make communication between processes with slightly
+         * different contexts more reliable: in particular, command
+         * prompts launched as administrator should still be able to
+         * run PSFTPs which refer back to the owning user's
+         * unprivileged Pageant.
+         */
+        usersid = get_user_sid();
+
+        psa = NULL;
+        if (usersid) {
+            psd = (PSECURITY_DESCRIPTOR)
+                LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+            if (psd) {
+                if (p_InitializeSecurityDescriptor
+                    (psd, SECURITY_DESCRIPTOR_REVISION) &&
+                    p_SetSecurityDescriptorOwner(psd, usersid, FALSE)) {
+                    sa.nLength = sizeof(sa);
+                    sa.bInheritHandle = TRUE;
+                    sa.lpSecurityDescriptor = psd;
+                    psa = &sa;
+                } else {
+                    LocalFree(psd);
+                    psd = NULL;
+                }
+            }
+        }
+    }
+#endif /* NO_SECURITY */
+
+    filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
 				0, AGENT_MAX_MSGLEN, mapname);
     if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)
 	return 1;		       /* *out == NULL, so failure */
@@ -134,5 +258,8 @@
     }
     UnmapViewOfFile(p);
     CloseHandle(filemap);
+    if (psd)
+        LocalFree(psd);
+    sfree(usersid);
     return 1;
 }

Modified: trunk/Tools/GMEplink/Windows/WINPROXY.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINPROXY.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINPROXY.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -39,7 +39,7 @@
     } else if (len == 0) {
 	return plug_closing(ps->plug, NULL, 0, 0);
     } else {
-	return plug_receive(ps->plug, 1, data, len);
+	return plug_receive(ps->plug, 0, data, len);
     }
 }
 
@@ -199,6 +199,8 @@
 		  CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
 		  NULL, NULL, &si, &pi);
 
+    sfree(cmd);
+
     CloseHandle(cmd_from_us);
     CloseHandle(cmd_to_us);
 

Modified: trunk/Tools/GMEplink/Windows/WINSER.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINSER.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINSER.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -454,5 +454,7 @@
     serial_provide_logctx,
     serial_unthrottle,
     serial_cfg_info,
-    1
+    "serial",
+    PROT_SERIAL,
+    0
 };

Modified: trunk/Tools/GMEplink/Windows/WINSTORE.C
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINSTORE.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINSTORE.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -17,15 +17,16 @@
 #define CSIDL_LOCAL_APPDATA 0x001c
 #endif
 
+static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist";
+static const char *const reg_jumplist_value = "Recent sessions";
 static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
 
 static const char hex[16] = "0123456789ABCDEF";
 
 static int tried_shgetfolderpath = FALSE;
 static HMODULE shell32_module = NULL;
-typedef HRESULT (WINAPI *p_SHGetFolderPath_t)
-    (HWND, int, HANDLE, DWORD, LPTSTR);
-static p_SHGetFolderPath_t p_SHGetFolderPath = NULL;
+DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA, 
+		      (HWND, int, HANDLE, DWORD, LPSTR));
 
 static void mungestr(const char *in, char *out)
 {
@@ -244,6 +245,8 @@
     sfree(p);
 
     RegCloseKey(subkey1);
+
+    remove_session_from_jumplist(sessionname);
 }
 
 struct enumsettings {
@@ -498,22 +501,20 @@
 	 * on older versions of Windows if we cared enough.
 	 * However, the invocation below requires IE5+ anyway,
 	 * so stuff that. */
-	shell32_module = LoadLibrary("SHELL32.DLL");
-	if (shell32_module) {
-	    p_SHGetFolderPath = (p_SHGetFolderPath_t)
-		GetProcAddress(shell32_module, "SHGetFolderPathA");
-	}
-    }
-    if (p_SHGetFolderPath) {
-	if (SUCCEEDED(p_SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA,
-					NULL, SHGFP_TYPE_CURRENT, seedpath))) {
+	shell32_module = load_system32_dll("shell32.dll");
+	GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA);
+	tried_shgetfolderpath = TRUE;
+    }
+    if (p_SHGetFolderPathA) {
+	if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
+					 NULL, SHGFP_TYPE_CURRENT, seedpath))) {
 	    strcat(seedpath, "\\PUTTY.RND");
 	    if (try_random_seed(seedpath, action, &rethandle))
 		return rethandle;
 	}
 
-	if (SUCCEEDED(p_SHGetFolderPath(NULL, CSIDL_APPDATA,
-					NULL, SHGFP_TYPE_CURRENT, seedpath))) {
+	if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
+					 NULL, SHGFP_TYPE_CURRENT, seedpath))) {
 	    strcat(seedpath, "\\PUTTY.RND");
 	    if (try_random_seed(seedpath, action, &rethandle))
 		return rethandle;
@@ -585,6 +586,169 @@
 }
 
 /*
+ * Internal function supporting the jump list registry code. All the
+ * functions to add, remove and read the list have substantially
+ * similar content, so this is a generalisation of all of them which
+ * transforms the list in the registry by prepending 'add' (if
+ * non-null), removing 'rem' from what's left (if non-null), and
+ * returning the resulting concatenated list of strings in 'out' (if
+ * non-null).
+ */
+static int transform_jumplist_registry
+    (const char *add, const char *rem, char **out)
+{
+    int ret;
+    HKEY pjumplist_key, psettings_tmp;
+    DWORD type;
+    int value_length;
+    char *old_value, *new_value;
+    char *piterator_old, *piterator_new, *piterator_tmp;
+
+    ret = RegCreateKeyEx(HKEY_CURRENT_USER, reg_jumplist_key, 0, NULL,
+                         REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL,
+                         &pjumplist_key, NULL);
+    if (ret != ERROR_SUCCESS) {
+	return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE;
+    }
+
+    /* Get current list of saved sessions in the registry. */
+    value_length = 200;
+    old_value = snewn(value_length, char);
+    ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,
+                          old_value, &value_length);
+    /* When the passed buffer is too small, ERROR_MORE_DATA is
+     * returned and the required size is returned in the length
+     * argument. */
+    if (ret == ERROR_MORE_DATA) {
+        sfree(old_value);
+        old_value = snewn(value_length, char);
+        ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,
+                              old_value, &value_length);
+    }
+
+    if (ret == ERROR_FILE_NOT_FOUND) {
+        /* Value doesn't exist yet. Start from an empty value. */
+        *old_value = '\0';
+        *(old_value + 1) = '\0';
+    } else if (ret != ERROR_SUCCESS) {
+        /* Some non-recoverable error occurred. */
+        sfree(old_value);
+        RegCloseKey(pjumplist_key);
+        return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;
+    } else if (type != REG_MULTI_SZ) {
+        /* The value present in the registry has the wrong type: we
+         * try to delete it and start from an empty value. */
+        ret = RegDeleteValue(pjumplist_key, reg_jumplist_value);
+        if (ret != ERROR_SUCCESS) {
+            sfree(old_value);
+            RegCloseKey(pjumplist_key);
+            return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;
+        }
+
+        *old_value = '\0';
+        *(old_value + 1) = '\0';
+    }
+
+    /* Check validity of registry data: REG_MULTI_SZ value must end
+     * with \0\0. */
+    piterator_tmp = old_value;
+    while (((piterator_tmp - old_value) < (value_length - 1)) &&
+           !(*piterator_tmp == '\0' && *(piterator_tmp+1) == '\0')) {
+        ++piterator_tmp;
+    }
+
+    if ((piterator_tmp - old_value) >= (value_length-1)) {
+        /* Invalid value. Start from an empty value. */
+        *old_value = '\0';
+        *(old_value + 1) = '\0';
+    }
+
+    /*
+     * Modify the list, if we're modifying.
+     */
+    if (add || rem) {
+        /* Walk through the existing list and construct the new list of
+         * saved sessions. */
+        new_value = snewn(value_length + (add ? strlen(add) + 1 : 0), char);
+        piterator_new = new_value;
+        piterator_old = old_value;
+
+        /* First add the new item to the beginning of the list. */
+        if (add) {
+            strcpy(piterator_new, add);
+            piterator_new += strlen(piterator_new) + 1;
+        }
+        /* Now add the existing list, taking care to leave out the removed
+         * item, if it was already in the existing list. */
+        while (*piterator_old != '\0') {
+            if (!rem || strcmp(piterator_old, rem) != 0) {
+                /* Check if this is a valid session, otherwise don't add. */
+                psettings_tmp = open_settings_r(piterator_old);
+                if (psettings_tmp != NULL) {
+                    close_settings_r(psettings_tmp);
+                    strcpy(piterator_new, piterator_old);
+                    piterator_new += strlen(piterator_new) + 1;
+                }
+            }
+            piterator_old += strlen(piterator_old) + 1;
+        }
+        *piterator_new = '\0';
+        ++piterator_new;
+
+        /* Save the new list to the registry. */
+        ret = RegSetValueEx(pjumplist_key, reg_jumplist_value, 0, REG_MULTI_SZ,
+                            new_value, piterator_new - new_value);
+
+        sfree(old_value);
+        old_value = new_value;
+    } else
+        ret = ERROR_SUCCESS;
+
+    /*
+     * Either return or free the result.
+     */
+    if (out)
+        *out = old_value;
+    else
+        sfree(old_value);
+
+    /* Clean up and return. */
+    RegCloseKey(pjumplist_key);
+
+    if (ret != ERROR_SUCCESS) {
+        return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;
+    } else {
+        return JUMPLISTREG_OK;
+    }
+}
+
+/* Adds a new entry to the jumplist entries in the registry. */
+int add_to_jumplist_registry(const char *item)
+{
+    return transform_jumplist_registry(item, item, NULL);
+}
+
+/* Removes an item from the jumplist entries in the registry. */
+int remove_from_jumplist_registry(const char *item)
+{
+    return transform_jumplist_registry(NULL, item, NULL);
+}
+
+/* Returns the jumplist entries from the registry. Caller must free
+ * the returned pointer. */
+char *get_jumplist_registry_entries (void)
+{
+    char *list_value;
+
+    if (transform_jumplist_registry(NULL,NULL,&list_value) != ERROR_SUCCESS) {
+	list_value = snewn(2, char);
+        *list_value = '\0';
+        *(list_value + 1) = '\0';
+    }
+    return list_value;
+}
+
+/*
  * Recursively delete a registry key and everything under it.
  */
 static void registry_recursive_remove(HKEY key)
@@ -616,6 +780,12 @@
     access_random_seed(DEL);
 
     /* ------------------------------------------------------------
+     * Ask Windows to delete any jump list information associated
+     * with this installation of PuTTY.
+     */
+    clear_jumplist();
+
+    /* ------------------------------------------------------------
      * Destroy all registry information associated with PuTTY.
      */
 

Modified: trunk/Tools/GMEplink/Windows/WINSTUFF.H
==============================================================================
--- trunk/Tools/GMEplink/Windows/WINSTUFF.H	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/WINSTUFF.H	Wed Nov  7 10:26:59 2012	(r2100)
@@ -36,6 +36,9 @@
     (fq) == FQ_NONANTIALIASED ? NONANTIALIASED_QUALITY : \
     CLEARTYPE_QUALITY)
 
+#define PLATFORM_IS_UTF16 /* enable UTF-16 processing when exchanging
+			   * wchar_t strings with environment */
+
 /*
  * Where we can, we use GetWindowLongPtr and friends because they're
  * more useful on 64-bit platforms, but they're a relatively recent
@@ -71,6 +74,33 @@
 #define DF_END 0x0001
 
 /*
+ * Dynamically linked functions. These come in two flavours:
+ *
+ *  - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor,
+ *    so will always dynamically link against exactly what is specified
+ *    in "name". If you're not sure, use this one.
+ *
+ *  - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via
+ *    preprocessor definitions like "#define foo bar"; this is principally
+ *    intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW.
+ *    If your function has an argument of type "LPTSTR" or similar, this
+ *    is the variant to use.
+ *    (However, it can't always be used, as it trips over more complicated
+ *    macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.)
+ *
+ * (DECL_WINDOWS_FUNCTION works with both these variants.)
+ */
+#define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \
+    typedef rettype (WINAPI *t_##name) params; \
+    linkage t_##name p_##name
+#define STR1(x) #x
+#define STR(x) STR1(x)
+#define GET_WINDOWS_FUNCTION_PP(module, name) \
+    (p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL)
+#define GET_WINDOWS_FUNCTION(module, name) \
+    (p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL)
+
+/*
  * Global variables. Most modules declare these `extern', but
  * window.c will do `#define PUTTY_DO_GLOBALS' before including this
  * module, and so will get them properly defined.
@@ -96,6 +126,14 @@
 #define PUTTY_REG_GPARENT "Software"
 #define PUTTY_REG_GPARENT_CHILD "SimonTatham"
 
+/* Result values for the jumplist registry functions. */
+#define JUMPLISTREG_OK 0
+#define JUMPLISTREG_ERROR_INVALID_PARAMETER 1
+#define JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE 2
+#define JUMPLISTREG_ERROR_VALUEREAD_FAILURE 3
+#define JUMPLISTREG_ERROR_VALUEWRITE_FAILURE 4
+#define JUMPLISTREG_ERROR_INVALID_VALUE 5
+
 #define PUTTY_HELP_FILE "putty.hlp"
 #define PUTTY_CHM_FILE "putty.chm"
 #define PUTTY_HELP_CONTENTS "putty.cnt"
@@ -108,6 +146,25 @@
 
 typedef HDC Context;
 
+typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */
+#define PUTTY_UINT32_DEFINED
+
+#ifndef NO_GSSAPI
+/*
+ * GSS-API stuff
+ */
+#define GSS_CC CALLBACK
+/*
+typedef struct Ssh_gss_buf {
+    size_t length;
+    char *value;
+} Ssh_gss_buf;
+
+#define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL}
+typedef void *Ssh_gss_name;
+*/
+#endif
+
 /*
  * Window handles for the windows that can be running during a
  * PuTTY session.
@@ -173,6 +230,8 @@
 			      "All Files (*.*)\0*\0\0\0")
 #define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \
 			       "All Files (*.*)\0*\0\0\0")
+#define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \
+				 "All Files (*.*)\0*\0\0\0")
 
 /*
  * On some versions of Windows, it has been known for WM_TIMER to
@@ -189,16 +248,16 @@
  * that module must be exported from it as function pointers. So
  * here they are.
  */
-extern int (WINAPI *p_WSAAsyncSelect)
-    (SOCKET s, HWND hWnd, u_int wMsg, long lEvent);
-extern int (WINAPI *p_WSAEventSelect)
-    (SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
-extern int (WINAPI *p_select)
-    (int nfds, fd_set FAR * readfds, fd_set FAR * writefds,
-     fd_set FAR *exceptfds, const struct timeval FAR * timeout);
-extern int (WINAPI *p_WSAGetLastError)(void);
-extern int (WINAPI *p_WSAEnumNetworkEvents)
-    (SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAAsyncSelect,
+		      (SOCKET, HWND, u_int, long));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEventSelect,
+		      (SOCKET, WSAEVENT, long));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, select,
+		      (int, fd_set FAR *, fd_set FAR *,
+		       fd_set FAR *, const struct timeval FAR *));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAGetLastError, (void));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEnumNetworkEvents,
+		      (SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
 
 extern int socket_writable(SOCKET skt);
 
@@ -257,6 +316,7 @@
     struct { unsigned char r, g, b, ok; } coloursel_result;   /* 0-255 */
     tree234 *privdata;		       /* stores per-control private data */
     int ended, endresult;	       /* has the dialog been ended? */
+    int fixed_pitch_fonts;             /* are we constrained to fixed fonts? */
 };
 
 /*
@@ -315,6 +375,10 @@
 	       char *btext, int bid,
 	       char *r1text, int r1id, char *r2text, int r2id);
 
+void dlg_auto_set_fixed_pitch_flag(void *dlg);
+int dlg_get_fixed_pitch_flag(void *dlg);
+void dlg_set_fixed_pitch_flag(void *dlg, int flag);
+
 #define MAX_SHORTCUTS_PER_CTRL 16
 
 /*
@@ -397,6 +461,7 @@
  */
 extern OSVERSIONINFO osVersion;
 BOOL init_winver(void);
+HMODULE load_system32_dll(const char *libname);
 
 /*
  * Exports from sizetip.c.
@@ -432,7 +497,7 @@
 void *handle_get_privdata(struct handle *h);
 
 /*
- * pageantc.c needs to schedule callbacks for asynchronous agent
+ * winpgntc.c needs to schedule callbacks for asynchronous agent
  * requests. This has to be done differently in GUI and console, so
  * there's an exported function used for the purpose.
  * 
@@ -444,8 +509,47 @@
 #define FLAG_SYNCAGENT 0x1000
 
 /*
+ * winpgntc.c also exports these two functions which are used by the
+ * server side of Pageant as well, to get the user SID for comparing
+ * with clients'.
+ */
+int init_advapi(void);  /* initialises everything needed by get_user_sid */
+PSID get_user_sid(void);
+
+/*
  * Exports from winser.c.
  */
 extern Backend serial_backend;
 
+/*
+ * Exports from winjump.c.
+ */
+#define JUMPLIST_SUPPORTED             /* suppress #defines in putty.h */
+void add_session_to_jumplist(const char * const sessionname);
+void remove_session_from_jumplist(const char * const sessionname);
+void clear_jumplist(void);
+
+/*
+ * Extra functions in winstore.c over and above the interface in
+ * storage.h.
+ *
+ * These functions manipulate the Registry section which mirrors the
+ * current Windows 7 jump list. (Because the real jump list storage is
+ * write-only, we need to keep another copy of whatever we put in it,
+ * so that we can put in a slightly modified version the next time.)
+ */
+
+/* Adds a saved session to the registry jump list mirror. 'item' is a
+ * string naming a saved session. */
+int add_to_jumplist_registry(const char *item);
+
+/* Removes an item from the registry jump list mirror. */
+int remove_from_jumplist_registry(const char *item);
+
+/* Returns the current jump list entries from the registry. Caller
+ * must free the returned pointer, which points to a contiguous
+ * sequence of NUL-terminated strings in memory, terminated with an
+ * empty one. */
+char *get_jumplist_registry_entries(void);
+
 #endif

Modified: trunk/Tools/GMEplink/Windows/winplink.c
==============================================================================
--- trunk/Tools/GMEplink/Windows/winplink.c	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/Windows/winplink.c	Wed Nov  7 10:26:59 2012	(r2100)
@@ -166,7 +166,7 @@
     printf("  -pgpfp    print PGP key fingerprints and exit\n");
     printf("  -v        show verbose messages\n");
     printf("  -load sessname  Load settings from saved session\n");
-    printf("  -ssh -telnet -rlogin -raw\n");
+    printf("  -ssh -telnet -rlogin -raw -serial\n");
     printf("            force use of a particular protocol\n");
     printf("  -P port   connect to specified port\n");
     printf("  -l user   connect with specified username\n");
@@ -193,6 +193,8 @@
     printf("  -N        don't start a shell/command (SSH-2 only)\n");
     printf("  -nc host:port\n");
     printf("            open tunnel in place of session (SSH-2 only)\n");
+    printf("  -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");
+    printf("            Specify the serial configuration (serial only)\n");
     exit(1);
 }
 
@@ -279,6 +281,7 @@
     int skcount, sksize;
     int exitcode;
     int errors;
+    int got_host = FALSE;
     int use_subsystem = 0;
     long now, next;
 
@@ -305,15 +308,11 @@
 	 * Override the default protocol if PLINK_PROTOCOL is set.
 	 */
 	char *p = getenv("PLINK_PROTOCOL");
-	int i;
 	if (p) {
-	    for (i = 0; backends[i].backend != NULL; i++) {
-		if (!strcmp(backends[i].name, p)) {
-		    default_protocol = cfg.protocol = backends[i].protocol;
-		    default_port = cfg.port =
-			backends[i].backend->default_port;
-		    break;
-		}
+	    const Backend *b = backend_from_name(p);
+	    if (b) {
+		default_protocol = cfg.protocol = b->protocol;
+		default_port = cfg.port = b->default_port;
 	    }
 	}
     }
@@ -345,7 +344,7 @@
 		errors = 1;
 	    }
 	} else if (*p) {
-	    if (!cfg_launchable(&cfg)) {
+	    if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
 		char *q = p;
 		/*
 		 * If the hostname starts with "telnet:", set the
@@ -371,6 +370,7 @@
 			cfg.port = -1;
 		    strncpy(cfg.host, q, sizeof(cfg.host) - 1);
 		    cfg.host[sizeof(cfg.host) - 1] = '\0';
+		    got_host = TRUE;
 		} else {
 		    char *r, *user, *host;
 		    /*
@@ -380,19 +380,14 @@
 		     */
 		    r = strchr(p, ',');
 		    if (r) {
-			int i, j;
-			for (i = 0; backends[i].backend != NULL; i++) {
-			    j = strlen(backends[i].name);
-			    if (j == r - p &&
-				!memcmp(backends[i].name, p, j)) {
-				default_protocol = cfg.protocol =
-				    backends[i].protocol;
-				portnumber =
-				    backends[i].backend->default_port;
-				p = r + 1;
-				break;
-			    }
+			const Backend *b;
+			*r = '\0';
+			b = backend_from_name(p);
+			if (b) {
+			    default_protocol = cfg.protocol = b->protocol;
+			    portnumber = b->default_port;
 			}
+			p = r + 1;
 		    }
 
 		    /*
@@ -424,8 +419,10 @@
 			    strncpy(cfg.host, host, sizeof(cfg.host) - 1);
 			    cfg.host[sizeof(cfg.host) - 1] = '\0';
 			    cfg.port = default_port;
+			    got_host = TRUE;
 			} else {
 			    cfg = cfg2;
+			    loaded_session = TRUE;
 			}
 		    }
 
@@ -472,7 +469,7 @@
     if (errors)
 	return 1;
 
-    if (!cfg_launchable(&cfg)) {
+    if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
 	usage();
     }
 
@@ -535,19 +532,11 @@
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    {
-	int i;
-	back = NULL;
-	for (i = 0; backends[i].backend != NULL; i++)
-	    if (backends[i].protocol == cfg.protocol) {
-		back = backends[i].backend;
-		break;
-	    }
-	if (back == NULL) {
-	    fprintf(stderr,
-		    "Internal fault: Unsupported protocol found\n");
-	    return 1;
-	}
+    back = backend_from_proto(cfg.protocol);
+    if (back == NULL) {
+	fprintf(stderr,
+		"Internal fault: Unsupported protocol found\n");
+	return 1;
     }
 
     /*

Modified: trunk/Tools/GMEplink/X11FWD.C
==============================================================================
--- trunk/Tools/GMEplink/X11FWD.C	Wed Nov  7 10:24:56 2012	(r2099)
+++ trunk/Tools/GMEplink/X11FWD.C	Wed Nov  7 10:26:59 2012	(r2100)
@@ -26,18 +26,11 @@
     unsigned char clientid[6];
 };
 
-struct X11Auth {
-    unsigned char fakedata[64], realdata[64];
-    int fakeproto, realproto;
-    int fakelen, reallen;
-    tree234 *xdmseen;
-};
-
 struct X11Private {
     const struct plug_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
     unsigned char firstpkt[12];	       /* first X data packet */
-    struct X11Auth *auth;
+    struct X11Display *disp;
     char *auth_protocol;
     unsigned char *auth_data;
     int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;
@@ -57,86 +50,247 @@
            memcmp(sa->clientid, sb->clientid, sizeof(sa->clientid));
 }
 
-void *x11_invent_auth(char *proto, int protomaxlen,
-		      char *data, int datamaxlen, int proto_id)
+/* Do-nothing "plug" implementation, used by x11_setup_display() when it
+ * creates a trial connection (and then immediately closes it).
+ * XXX: bit out of place here, could in principle live in a platform-
+ *      independent network.c or something */
+static void dummy_plug_log(Plug p, int type, SockAddr addr, int port,
+			   const char *error_msg, int error_code) { }
+static int dummy_plug_closing
+     (Plug p, const char *error_msg, int error_code, int calling_back)
+{ return 1; }
+static int dummy_plug_receive(Plug p, int urgent, char *data, int len)
+{ return 1; }
+static void dummy_plug_sent(Plug p, int bufsize) { }
+static int dummy_plug_accepting(Plug p, OSSocket sock) { return 1; }
+static const struct plug_function_table dummy_plug = {
+    dummy_plug_log, dummy_plug_closing, dummy_plug_receive,
+    dummy_plug_sent, dummy_plug_accepting
+};
+
+struct X11Display *x11_setup_display(char *display, int authtype,
+				     const Config *cfg)
 {
-    struct X11Auth *auth = snew(struct X11Auth);
-    char ourdata[64];
+    struct X11Display *disp = snew(struct X11Display);
+    char *localcopy;
     int i;
 
-    if (proto_id == X11_MIT) {
-	auth->fakeproto = X11_MIT;
+    if (!display || !*display) {
+	localcopy = platform_get_x_display();
+	if (!localcopy || !*localcopy) {
+	    sfree(localcopy);
+	    localcopy = dupstr(":0");  /* plausible default for any platform */
+	}
+    } else
+	localcopy = dupstr(display);
+
+    /*
+     * Parse the display name.
+     *
+     * We expect this to have one of the following forms:
+     * 
+     *  - the standard X format which looks like
+     *    [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ]
+     *    (X11 also permits a double colon to indicate DECnet, but
+     *    that's not our problem, thankfully!)
+     *
+     * 	- only seen in the wild on MacOS (so far): a pathname to a
+     * 	  Unix-domain socket, which will typically and confusingly
+     * 	  end in ":0", and which I'm currently distinguishing from
+     * 	  the standard scheme by noting that it starts with '/'.
+     */
+    if (localcopy[0] == '/') {
+	disp->unixsocketpath = localcopy;
+	disp->unixdomain = TRUE;
+	disp->hostname = NULL;
+	disp->displaynum = -1;
+	disp->screennum = 0;
+	disp->addr = NULL;
+    } else {
+	char *colon, *dot, *slash;
+	char *protocol, *hostname;
+
+	colon = strrchr(localcopy, ':');
+	if (!colon) {
+	    sfree(disp);
+	    sfree(localcopy);
+	    return NULL;	       /* FIXME: report a specific error? */
+	}
+
+	*colon++ = '\0';
+	dot = strchr(colon, '.');
+	if (dot)
+	    *dot++ = '\0';
+
+	disp->displaynum = atoi(colon);
+	if (dot)
+	    disp->screennum = atoi(dot);
+	else
+	    disp->screennum = 0;
+
+	protocol = NULL;
+	hostname = localcopy;
+	if (colon > localcopy) {
+	    slash = strchr(localcopy, '/');
+	    if (slash) {
+		*slash++ = '\0';
+		protocol = localcopy;
+		hostname = slash;
+	    }
+	}
+
+	disp->hostname = *hostname ? dupstr(hostname) : NULL;
+
+	if (protocol)
+	    disp->unixdomain = (!strcmp(protocol, "local") ||
+				!strcmp(protocol, "unix"));
+	else if (!*hostname || !strcmp(hostname, "unix"))
+	    disp->unixdomain = platform_uses_x11_unix_by_default;
+	else
+	    disp->unixdomain = FALSE;
+
+	if (!disp->hostname && !disp->unixdomain)
+	    disp->hostname = dupstr("localhost");
+
+	disp->unixsocketpath = NULL;
+	disp->addr = NULL;
+
+	sfree(localcopy);
+    }
+
+    /*
+     * Look up the display hostname, if we need to.
+     */
+    if (!disp->unixdomain) {
+	const char *err;
+
+	disp->port = 6000 + disp->displaynum;
+	disp->addr = name_lookup(disp->hostname, disp->port,
+				 &disp->realhost, cfg, ADDRTYPE_UNSPEC);
+    
+	if ((err = sk_addr_error(disp->addr)) != NULL) {
+	    sk_addr_free(disp->addr);
+	    sfree(disp->hostname);
+	    sfree(disp->unixsocketpath);
+	    return NULL;	       /* FIXME: report an error */
+	}
+    }
+
+    /*
+     * Try upgrading an IP-style localhost display to a Unix-socket
+     * display (as the standard X connection libraries do).
+     */
+    if (!disp->unixdomain && sk_address_is_local(disp->addr)) {
+	SockAddr ux = platform_get_x11_unix_address(NULL, disp->displaynum);
+	const char *err = sk_addr_error(ux);
+	if (!err) {
+	    /* Create trial connection to see if there is a useful Unix-domain
+	     * socket */
+	    const struct plug_function_table *dummy = &dummy_plug;
+	    Socket s = sk_new(sk_addr_dup(ux), 0, 0, 0, 0, 0, (Plug)&dummy);
+	    err = sk_socket_error(s);
+	    sk_close(s);
+	}
+	if (err) {
+	    sk_addr_free(ux);
+	} else {
+	    sk_addr_free(disp->addr);
+	    disp->unixdomain = TRUE;
+	    disp->addr = ux;
+	    /* Fill in the rest in a moment */
+	}
+    }
+
+    if (disp->unixdomain) {
+	if (!disp->addr)
+	    disp->addr = platform_get_x11_unix_address(disp->unixsocketpath,
+						       disp->displaynum);
+	if (disp->unixsocketpath)
+	    disp->realhost = dupstr(disp->unixsocketpath);
+	else
+	    disp->realhost = dupprintf("unix:%d", disp->displaynum);
+	disp->port = 0;
+    }
+
+    /*
+     * Invent the remote authorisation details.
+     */
+    if (authtype == X11_MIT) {
+	disp->remoteauthproto = X11_MIT;
 
 	/* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */
-	auth->fakelen = 16;
+	disp->remoteauthdata = snewn(16, unsigned char);
 	for (i = 0; i < 16; i++)
-	    auth->fakedata[i] = random_byte();
-	auth->xdmseen = NULL;
+	    disp->remoteauthdata[i] = random_byte();
+	disp->remoteauthdatalen = 16;
+
+	disp->xdmseen = NULL;
     } else {
-	assert(proto_id == X11_XDM);
-	auth->fakeproto = X11_XDM;
+	assert(authtype == X11_XDM);
+	disp->remoteauthproto = X11_XDM;
 
 	/* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */
-	auth->fakelen = 16;
+	disp->remoteauthdata = snewn(16, unsigned char);
 	for (i = 0; i < 16; i++)
-	    auth->fakedata[i] = (i == 8 ? 0 : random_byte());
-	auth->xdmseen = newtree234(xdmseen_cmp);
+	    disp->remoteauthdata[i] = (i == 8 ? 0 : random_byte());
+	disp->remoteauthdatalen = 16;
+
+	disp->xdmseen = newtree234(xdmseen_cmp);
     }
+    disp->remoteauthprotoname = dupstr(x11_authnames[disp->remoteauthproto]);
+    disp->remoteauthdatastring = snewn(disp->remoteauthdatalen * 2 + 1, char);
+    for (i = 0; i < disp->remoteauthdatalen; i++)
+	sprintf(disp->remoteauthdatastring + i*2, "%02x",
+		disp->remoteauthdata[i]);
 
-    /* Now format for the recipient. */
-    strncpy(proto, x11_authnames[auth->fakeproto], protomaxlen);
-    ourdata[0] = '\0';
-    for (i = 0; i < auth->fakelen; i++)
-	sprintf(ourdata + strlen(ourdata), "%02x", auth->fakedata[i]);
-    strncpy(data, ourdata, datamaxlen);
+    /*
+     * Fetch the local authorisation details.
+     */
+    disp->localauthproto = X11_NO_AUTH;
+    disp->localauthdata = NULL;
+    disp->localauthdatalen = 0;
+    platform_get_x11_auth(disp, cfg);
 
-    return auth;
+    return disp;
 }
 
-void x11_free_auth(void *authv)
+void x11_free_display(struct X11Display *disp)
 {
-    struct X11Auth *auth = (struct X11Auth *)authv;
-    struct XDMSeen *seen;
-
-    if (auth->xdmseen != NULL) {
-	while ((seen = delpos234(auth->xdmseen, 0)) != NULL)
+    if (disp->xdmseen != NULL) {
+	struct XDMSeen *seen;
+	while ((seen = delpos234(disp->xdmseen, 0)) != NULL)
 	    sfree(seen);
-	freetree234(auth->xdmseen);
+	freetree234(disp->xdmseen);
     }
-    sfree(auth);
-}
-
-/*
- * Fetch the real auth data for a given display string, and store
- * it in an X11Auth structure. Returns NULL on success, or an error
- * string.
- */
-void x11_get_real_auth(void *authv, char *display)
-{
-    struct X11Auth *auth = (struct X11Auth *)authv;
-
-    auth->realproto = X11_NO_AUTH;     /* in case next call does nothing */
-
-    auth->reallen = sizeof(auth->realdata);
-    platform_get_x11_auth(display, &auth->realproto,
-                          auth->realdata, &auth->reallen);
+    sfree(disp->hostname);
+    sfree(disp->unixsocketpath);
+    if (disp->localauthdata)
+	memset(disp->localauthdata, 0, disp->localauthdatalen);
+    sfree(disp->localauthdata);
+    if (disp->remoteauthdata)
+	memset(disp->remoteauthdata, 0, disp->remoteauthdatalen);
+    sfree(disp->remoteauthdata);
+    sfree(disp->remoteauthprotoname);
+    sfree(disp->remoteauthdatastring);
+    sk_addr_free(disp->addr);
+    sfree(disp);
 }
 
 #define XDM_MAXSKEW 20*60      /* 20 minute clock skew should be OK */
 
 static char *x11_verify(unsigned long peer_ip, int peer_port,
-			struct X11Auth *auth, char *proto,
+			struct X11Display *disp, char *proto,
 			unsigned char *data, int dlen)
 {
-    if (strcmp(proto, x11_authnames[auth->fakeproto]) != 0)
-	return "wrong authentication protocol attempted";
-    if (auth->fakeproto == X11_MIT) {
-        if (dlen != auth->fakelen)
+    if (strcmp(proto, x11_authnames[disp->remoteauthproto]) != 0)
+	return "wrong authorisation protocol attempted";
+    if (disp->remoteauthproto == X11_MIT) {
+        if (dlen != disp->remoteauthdatalen)
             return "MIT-MAGIC-COOKIE-1 data was wrong length";
-        if (memcmp(auth->fakedata, data, dlen) != 0)
+        if (memcmp(disp->remoteauthdata, data, dlen) != 0)
             return "MIT-MAGIC-COOKIE-1 data did not match";
     }
-    if (auth->fakeproto == X11_XDM) {
+    if (disp->remoteauthproto == X11_XDM) {
 	unsigned long t;
 	time_t tim;
 	int i;
@@ -146,8 +300,8 @@
             return "XDM-AUTHORIZATION-1 data was wrong length";
 	if (peer_port == -1)
             return "cannot do XDM-AUTHORIZATION-1 without remote address data";
-	des_decrypt_xdmauth(auth->fakedata+9, data, 24);
-        if (memcmp(auth->fakedata, data, 8) != 0)
+	des_decrypt_xdmauth(disp->remoteauthdata+9, data, 24);
+        if (memcmp(disp->remoteauthdata, data, 8) != 0)
             return "XDM-AUTHORIZATION-1 data failed check"; /* cookie wrong */
 	if (GET_32BIT_MSB_FIRST(data+8) != peer_ip)
             return "XDM-AUTHORIZATION-1 data failed check";   /* IP wrong */
@@ -163,25 +317,182 @@
 	seen = snew(struct XDMSeen);
 	seen->time = t;
 	memcpy(seen->clientid, data+8, 6);
-	assert(auth->xdmseen != NULL);
-	ret = add234(auth->xdmseen, seen);
+	assert(disp->xdmseen != NULL);
+	ret = add234(disp->xdmseen, seen);
 	if (ret != seen) {
 	    sfree(seen);
 	    return "XDM-AUTHORIZATION-1 data replayed";
 	}
 	/* While we're here, purge entries too old to be replayed. */
 	for (;;) {
-	    seen = index234(auth->xdmseen, 0);
+	    seen = index234(disp->xdmseen, 0);
 	    assert(seen != NULL);
 	    if (t - seen->time <= XDM_MAXSKEW)
 		break;
-	    sfree(delpos234(auth->xdmseen, 0));
+	    sfree(delpos234(disp->xdmseen, 0));
 	}
     }
     /* implement other protocols here if ever required */
     return NULL;
 }
 
+void x11_get_auth_from_authfile(struct X11Display *disp,
+				const char *authfilename)
+{
+    FILE *authfp;
+    char *buf, *ptr, *str[4];
+    int len[4];
+    int family, protocol;
+    int ideal_match = FALSE;
+    char *ourhostname = get_hostname();
+
+    /*
+     * Normally we should look for precisely the details specified in
+     * `disp'. However, there's an oddity when the display is local:
+     * displays like "localhost:0" usually have their details stored
+     * in a Unix-domain-socket record (even if there isn't actually a
+     * real Unix-domain socket available, as with OpenSSH's proxy X11
+     * server).
+     *
+     * This is apparently a fudge to get round the meaninglessness of
+     * "localhost" in a shared-home-directory context -- xauth entries
+     * for Unix-domain sockets already disambiguate this by storing
+     * the *local* hostname in the conveniently-blank hostname field,
+     * but IP "localhost" records couldn't do this. So, typically, an
+     * IP "localhost" entry in the auth database isn't present and if
+     * it were it would be ignored.
+     *
+     * However, we don't entirely trust that (say) Windows X servers
+     * won't rely on a straight "localhost" entry, bad idea though
+     * that is; so if we can't find a Unix-domain-socket entry we'll
+     * fall back to an IP-based entry if we can find one.
+     */
+    int localhost = !disp->unixdomain && sk_address_is_local(disp->addr);
+
+    authfp = fopen(authfilename, "rb");
+    if (!authfp)
+	return;
+
+    /* Records in .Xauthority contain four strings of up to 64K each */
+    buf = snewn(65537 * 4, char);
+
+    while (!ideal_match) {
+	int c, i, j, match = FALSE;
+	
+#define GET do { c = fgetc(authfp); if (c == EOF) goto done; c = (unsigned char)c; } while (0)
+	/* Expect a big-endian 2-byte number giving address family */
+	GET; family = c;
+	GET; family = (family << 8) | c;
+	/* Then expect four strings, each composed of a big-endian 2-byte
+	 * length field followed by that many bytes of data */
+	ptr = buf;
+	for (i = 0; i < 4; i++) {
+	    GET; len[i] = c;
+	    GET; len[i] = (len[i] << 8) | c;
+	    str[i] = ptr;
+	    for (j = 0; j < len[i]; j++) {
+		GET; *ptr++ = c;
+	    }
+	    *ptr++ = '\0';
+	}
+#undef GET
+
+	/*
+	 * Now we have a full X authority record in memory. See
+	 * whether it matches the display we're trying to
+	 * authenticate to.
+	 *
+	 * The details we've just read should be interpreted as
+	 * follows:
+	 * 
+	 *  - 'family' is the network address family used to
+	 *    connect to the display. 0 means IPv4; 6 means IPv6;
+	 *    256 means Unix-domain sockets.
+	 * 
+	 *  - str[0] is the network address itself. For IPv4 and
+	 *    IPv6, this is a string of binary data of the
+	 *    appropriate length (respectively 4 and 16 bytes)
+	 *    representing the address in big-endian format, e.g.
+	 *    7F 00 00 01 means IPv4 localhost. For Unix-domain
+	 *    sockets, this is the host name of the machine on
+	 *    which the Unix-domain display resides (so that an
+	 *    .Xauthority file on a shared file system can contain
+	 *    authority entries for Unix-domain displays on
+	 *    several machines without them clashing).
+	 * 
+	 *  - str[1] is the display number. I've no idea why
+	 *    .Xauthority stores this as a string when it has a
+	 *    perfectly good integer format, but there we go.
+	 * 
+	 *  - str[2] is the authorisation method, encoded as its
+	 *    canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
+	 *    "XDM-AUTHORIZATION-1" or something we don't
+	 *    recognise).
+	 * 
+	 *  - str[3] is the actual authorisation data, stored in
+	 *    binary form.
+	 */
+
+	if (disp->displaynum < 0 || disp->displaynum != atoi(str[1]))
+	    continue;		       /* not the one */
+
+	for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
+	    if (!strcmp(str[2], x11_authnames[protocol]))
+		break;
+	if (protocol == lenof(x11_authnames))
+	    continue;  /* don't recognise this protocol, look for another */
+
+	switch (family) {
+	  case 0:   /* IPv4 */
+	    if (!disp->unixdomain &&
+		sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {
+		char buf[4];
+		sk_addrcopy(disp->addr, buf);
+		if (len[0] == 4 && !memcmp(str[0], buf, 4)) {
+		    match = TRUE;
+		    /* If this is a "localhost" entry, note it down
+		     * but carry on looking for a Unix-domain entry. */
+		    ideal_match = !localhost;
+		}
+	    }
+	    break;
+	  case 6:   /* IPv6 */
+	    if (!disp->unixdomain &&
+		sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {
+		char buf[16];
+		sk_addrcopy(disp->addr, buf);
+		if (len[0] == 16 && !memcmp(str[0], buf, 16)) {
+		    match = TRUE;
+		    ideal_match = !localhost;
+		}
+	    }
+	    break;
+	  case 256: /* Unix-domain / localhost */
+	    if ((disp->unixdomain || localhost)
+	       	&& ourhostname && !strcmp(ourhostname, str[0]))
+		/* A matching Unix-domain socket is always the best
+		 * match. */
+		match = ideal_match = TRUE;
+	    break;
+	}
+
+	if (match) {
+	    /* Current best guess -- may be overridden if !ideal_match */
+	    disp->localauthproto = protocol;
+	    sfree(disp->localauthdata); /* free previous guess, if any */
+	    disp->localauthdata = snewn(len[3], unsigned char);
+	    memcpy(disp->localauthdata, str[3], len[3]);
+	    disp->localauthdatalen = len[3];
+	}
+    }
+
+    done:
+    fclose(authfp);
+    memset(buf, 0, 65537 * 4);
+    sfree(buf);
+    sfree(ourhostname);
+}
+
 static void x11_log(Plug p, int type, SockAddr addr, int port,
 		    const char *error_msg, int error_code)
 {
@@ -240,33 +551,15 @@
     return atoi(display + n + 1);
 }
 
-/* Find the right display, returns an allocated string */
-char *x11_display(const char *display) {
-    char *ret;
-    if(!display || !*display) {
-	/* try to find platform-specific local display */
-	if((ret = platform_get_x_display())==0 || !*ret)
-	    /* plausible default for all platforms */
-	    ret = dupstr(":0");
-    } else
-	ret = dupstr(display);
-    if(ret[0] == ':') {
-	/* no transport specified, use whatever we think is best */
-	char *s = dupcat(platform_x11_best_transport, ret, (char *)0);
-	sfree(ret);
-	return s;
-    } else
-	return ret;
-}
-
 /*
  * Called to set up the raw connection.
  * 
  * Returns an error message, or NULL on success.
  * also, fills the SocketsStructure
  */
-const char *x11_init(Socket * s, char *display, void *c, void *auth,
-		     const char *peeraddr, int peerport, const Config *cfg)
+extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
+			    const char *peeraddr, int peerport,
+			    const Config *cfg)
 {
     static const struct plug_function_table fn_table = {
 	x11_log,
@@ -276,62 +569,23 @@
 	NULL
     };
 
-    SockAddr addr;
-    int port;
     const char *err;
-    char *dummy_realhost;
-    char host[128];
-    int n, displaynum;
     struct X11Private *pr;
 
-    /* default display */
-    display = x11_display(display);
-    /*
-     * Split up display name into host and display-number parts.
-     */
-    n = strcspn(display, ":");
-    assert(n != 0);		/* x11_display() promises this */
-    if (display[n])
-	displaynum = atoi(display + n + 1);
-    else
-	displaynum = 0;		       /* sensible default */
-    if (n > sizeof(host) - 1)
-	n = sizeof(host) - 1;
-    strncpy(host, display, n);
-    host[n] = '\0';
-    sfree(display);
-    
-    if(!strcmp(host, "unix")) {
-	/* use AF_UNIX sockets (doesn't make sense on all platforms) */
-	addr = platform_get_x11_unix_address(displaynum,
-					     &dummy_realhost);
-	port = 0;		/* to show we are not confused */
-    } else {
-	port = 6000 + displaynum;
-	
-	/*
-	 * Try to find host.
-	 */
-	addr = name_lookup(host, port, &dummy_realhost, cfg, ADDRTYPE_UNSPEC);
-	if ((err = sk_addr_error(addr)) != NULL) {
-	    sk_addr_free(addr);
-	    return err;
-	}
-    }
-
     /*
      * Open socket.
      */
     pr = snew(struct X11Private);
     pr->fn = &fn_table;
     pr->auth_protocol = NULL;
-    pr->auth = (struct X11Auth *)auth;
+    pr->disp = disp;
     pr->verified = 0;
     pr->data_read = 0;
     pr->throttled = pr->throttle_override = 0;
     pr->c = c;
 
-    pr->s = *s = new_connection(addr, dummy_realhost, port,
+    pr->s = *s = new_connection(sk_addr_dup(disp->addr),
+				disp->realhost, disp->port,
 				0, 1, 0, 0, (Plug) pr, cfg);
     if ((err = sk_socket_error(*s)) != NULL) {
 	sfree(pr);
@@ -439,18 +693,18 @@
 	return 0;
 
     /*
-     * If we haven't verified the authentication, do so now.
+     * If we haven't verified the authorisation, do so now.
      */
     if (!pr->verified) {
 	char *err;
 
 	pr->auth_protocol[pr->auth_plen] = '\0';	/* ASCIZ */
 	err = x11_verify(pr->peer_ip, pr->peer_port,
-			 pr->auth, pr->auth_protocol,
+			 pr->disp, pr->auth_protocol,
 			 pr->auth_data, pr->auth_dlen);
 
 	/*
-	 * If authentication failed, construct and send an error
+	 * If authorisation failed, construct and send an error
 	 * packet, then terminate the connection.
 	 */
 	if (err) {
@@ -458,7 +712,7 @@
 	    int msglen, msgsize;
 	    unsigned char *reply;
 
-	    message = dupprintf("PuTTY X11 proxy: %s", err);
+	    message = dupprintf("%s X11 proxy: %s", appname, err);
 	    msglen = strlen(message);
 	    reply = snewn(8 + msglen+1 + 4, unsigned char); /* include zero */
 	    msgsize = (msglen + 3) & ~3;
@@ -484,27 +738,27 @@
         {
             char realauthdata[64];
             int realauthlen = 0;
-            int authstrlen = strlen(x11_authnames[pr->auth->realproto]);
+            int authstrlen = strlen(x11_authnames[pr->disp->localauthproto]);
 	    int buflen = 0;	       /* initialise to placate optimiser */
             static const char zeroes[4] = { 0,0,0,0 };
 	    void *buf;
 
-            if (pr->auth->realproto == X11_MIT) {
-                assert(pr->auth->reallen <= lenof(realauthdata));
-                realauthlen = pr->auth->reallen;
-                memcpy(realauthdata, pr->auth->realdata, realauthlen);
-            } else if (pr->auth->realproto == X11_XDM &&
-		       pr->auth->reallen == 16 &&
+            if (pr->disp->localauthproto == X11_MIT) {
+                assert(pr->disp->localauthdatalen <= lenof(realauthdata));
+                realauthlen = pr->disp->localauthdatalen;
+                memcpy(realauthdata, pr->disp->localauthdata, realauthlen);
+            } else if (pr->disp->localauthproto == X11_XDM &&
+		       pr->disp->localauthdatalen == 16 &&
 		       ((buf = sk_getxdmdata(s, &buflen))!=0)) {
 		time_t t;
                 realauthlen = (buflen+12+7) & ~7;
 		assert(realauthlen <= lenof(realauthdata));
 		memset(realauthdata, 0, realauthlen);
-		memcpy(realauthdata, pr->auth->realdata, 8);
+		memcpy(realauthdata, pr->disp->localauthdata, 8);
 		memcpy(realauthdata+8, buf, buflen);
 		t = time(NULL);
 		PUT_32BIT_MSB_FIRST(realauthdata+8+buflen, t);
-		des_encrypt_xdmauth(pr->auth->realdata+9,
+		des_encrypt_xdmauth(pr->disp->localauthdata+9,
 				    (unsigned char *)realauthdata,
 				    realauthlen);
 		sfree(buf);
@@ -517,7 +771,8 @@
             sk_write(s, (char *)pr->firstpkt, 12);
 
             if (authstrlen) {
-                sk_write(s, x11_authnames[pr->auth->realproto], authstrlen);
+                sk_write(s, x11_authnames[pr->disp->localauthproto],
+			 authstrlen);
                 sk_write(s, zeroes, 3 & (-authstrlen));
             }
             if (realauthlen) {


More information about the gme-commit mailing list