s->tried_agent = FALSE;
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,			     struct Packet *pktin){    struct do_ssh2_authconn_state {	enum {	    AUTH_TYPE_NONE,		AUTH_TYPE_PUBLICKEY,		AUTH_TYPE_PUBLICKEY_OFFER_LOUD,		AUTH_TYPE_PUBLICKEY_OFFER_QUIET,		AUTH_TYPE_PASSWORD,		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, tried_agent;	int kbd_inter_refused;	int we_are_in;	prompts_t *cur_prompt;	int num_prompts;	char username[100];	char *password;	int got_username;	void *publickey_blob;	int publickey_bloblen;	int publickey_encrypted;	char *publickey_algorithm;	char *publickey_comment;	unsigned char request[5], *response, *p;	int responselen;	int keyi, nkeys;	int authed;	char *pkblob, *alg, *commentp;	int pklen, alglen, commentlen;	int siglen, retlen, len;	char *q, *agentreq, *ret;	int try_send;	int num_env, env_left, env_ok;	struct Packet *pktout;    };    crState(do_ssh2_authconn_state);    crBegin(ssh->do_ssh2_authconn_crstate);    s->done_service_req = FALSE;    s->we_are_in = FALSE;    if (!ssh->cfg.ssh_no_userauth) {	/*	 * Request userauth protocol, and await a response to it.	 */	s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);	ssh2_pkt_addstring(s->pktout, "ssh-userauth");	ssh2_pkt_send(ssh, s->pktout);	crWaitUntilV(pktin);	if (pktin->type == SSH2_MSG_SERVICE_ACCEPT)	    s->done_service_req = TRUE;    }    if (!s->done_service_req) {	/*	 * Request connection protocol directly, without authentication.	 */	s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);	ssh2_pkt_addstring(s->pktout, "ssh-connection");	ssh2_pkt_send(ssh, s->pktout);	crWaitUntilV(pktin);	if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) {	    s->we_are_in = TRUE; /* no auth required */	} else {	    bombout(("Server refused service request"));	    crStopV;	}    }    /*     * Misc one-time setup for authentication.     */    s->publickey_blob = NULL;    if (!s->we_are_in) {	/*	 * Load the public half of any configured public key file	 * for later use.	 */	if (!filename_is_null(ssh->cfg.keyfile)) {	    int keytype;	    logeventf(ssh, "Reading private key file \"%.150s\"",		      filename_to_str(&ssh->cfg.keyfile));	    keytype = key_type(&ssh->cfg.keyfile);	    if (keytype == SSH_KEYTYPE_SSH2) {		const char *error;		s->publickey_blob =		    ssh2_userkey_loadpub(&ssh->cfg.keyfile,					 &s->publickey_algorithm,					 &s->publickey_bloblen, 					 &s->publickey_comment, &error);		if (s->publickey_blob) {		    s->publickey_encrypted =			ssh2_userkey_encrypted(&ssh->cfg.keyfile, NULL);		} else {		    char *msgbuf;		    logeventf(ssh, "Unable to load private key (%s)", 			      error);		    msgbuf = dupprintf("Unable to load private key file "				       "\"%.150s\" (%s)\r\n",				       filename_to_str(&ssh->cfg.keyfile),				       error);		    c_write_str(ssh, msgbuf);		    sfree(msgbuf);		}	    } else {		char *msgbuf;		logeventf(ssh, "Unable to use this key file (%s)",			  key_type_to_str(keytype));		msgbuf = dupprintf("Unable to use key file \"%.150s\""				   " (%s)\r\n",				   filename_to_str(&ssh->cfg.keyfile),				   key_type_to_str(keytype));		c_write_str(ssh, msgbuf);		sfree(msgbuf);		s->publickey_blob = NULL;	    }	}    }    /*     * We repeat this whole loop, including the username prompt,     * until we manage a successful authentication. If the user     * types the wrong _password_, they can be sent back to the     * beginning to try another username, if this is configured on.     * (If they specify a username in the config, they are never     * asked, even if they do give a wrong password.)     *      * I think this best serves the needs of     *      *  - the people who have no configuration, no keys, and just     *    want to try repeated (username,password) pairs until they     *    type both correctly     *      *  - people who have keys and configuration but occasionally     *    need to fall back to passwords     *      *  - people with a key held in Pageant, who might not have     *    logged in to a particular machine before; so they want to     *    type a username, and then _either_ their key will be     *    accepted, _or_ they will type a password. If they mistype     *    the username they will want to be able to get back and     *    retype it!     */    s->username[0] = '\0';    s->got_username = FALSE;    bufchain_init(&ssh->banner);    ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =	ssh2_msg_userauth_banner;    while (!s->we_are_in) {	/*	 * Get a username.	 */	if (s->got_username && !ssh->cfg.change_username) {	    /*	     * We got a username last time round this loop, and	     * with change_username turned off we don't try to get	     * it again.	     */	} else if (!*ssh->cfg.username) {	    int ret; /* need not be kept over crReturn */	    s->cur_prompt = new_prompts(ssh->frontend);	    s->cur_prompt->to_server = TRUE;	    s->cur_prompt->name = dupstr("SSH login name");	    add_prompt(s->cur_prompt, dupstr("login as: "), TRUE,		       lenof(s->username)); 	    ret = get_userpass_input(s->cur_prompt, NULL, 0);	    while (ret < 0) {		ssh->send_ok = 1;		crWaitUntilV(!pktin);		ret = get_userpass_input(s->cur_prompt, in, inlen);		ssh->send_ok = 0;	    }	    if (!ret) {		/*		 * get_userpass_input() failed to get a username.		 * Terminate.		 */		free_prompts(s->cur_prompt);		ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);		crStopV;	    }	    memcpy(s->username, s->cur_prompt->prompts[0]->result,		   lenof(s->username));	    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);		sfree(stuff);	    }	}	s->got_username = TRUE;	/*	 * Send an authentication request using method "none": (a)	 * 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;	s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);	ssh2_pkt_addstring(s->pktout, s->username);	ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */	ssh2_pkt_addstring(s->pktout, "none");    /* method */	ssh2_pkt_send(ssh, s->pktout);	s->type = AUTH_TYPE_NONE;	s->gotit = FALSE;	s->we_are_in = FALSE;	s->tried_pubkey_config = FALSE;	s->tried_agent = FALSE;	s->kbd_inter_refused = FALSE;	while (1) {	    /*	     * Wait for the result of the last authentication request.	     */	    if (!s->gotit)		crWaitUntilV(pktin);	    /*	     * Now is a convenient point to spew any banner material	     * that we've accumulated. (This should ensure that when	     * we exit the auth loop, we haven't any left to deal	     * with.)	     */	    {		int size = bufchain_size(&ssh->banner);		/*		 * Don't show the banner if we're operating in		 * non-verbose non-interactive mode. (It's probably		 * a script, which means nobody will read the		 * banner _anyway_, and moreover the printing of		 * the banner will screw up processing on the		 * output of (say) plink.)		 */		if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {		    char *banner = snewn(size, char);		    bufchain_fetch(&ssh->banner, banner, size);		    c_write_untrusted(ssh, banner, size);		    sfree(banner);		}		bufchain_clear(&ssh->banner);	    }	    if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {		logevent("Access granted");		s->we_are_in = TRUE;		break;	    }	    if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {		bombout(("Strange packet received during authentication: "			 "type %d", pktin->type));		crStopV;	    }	    s->gotit = FALSE;	    /*	     * OK, we're now sitting on a USERAUTH_FAILURE message, so	     * we can look at the string in it and know what we can	     * 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".		     * 		     * Additionally, if we'd just tried password		     * authentication, we should break out of this		     * whole loop so as to go back to the username		     * prompt (iff we're configured to allow		     * username change attempts).		     */		    if (s->type == AUTH_TYPE_NONE) {			/* do nothing */		    } else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||			       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");		    } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {			/* server declined keyboard-interactive; ignore */		    } else {			c_write_str(ssh, "Access denied\r\n");			logevent("Access denied");			if (s->type == AUTH_TYPE_PASSWORD &&			    ssh->cfg.change_username) {			    /* XXX perhaps we should allow			     * keyboard-interactive to do this too? */			    s->we_are_in = FALSE;			    break;			}		    }		} else {		    c_write_str(ssh, "Further authentication required\r\n");		    logevent("Further authentication required");		}		s->can_pubkey =		    in_commasep_string("publickey", methods, methlen);		s->can_passwd =		    in_commasep_string("password", methods, methlen);		s->can_keyb_inter = ssh->cfg.try_ki_auth &&		    in_commasep_string("keyboard-interactive", methods, methlen);	    }	    ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;	    if (s->can_pubkey && agent_exists() && !s->tried_agent) {		/*		 * Attempt public-key authentication using Pageant.		 */		void *r;		s->authed = FALSE;		ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;		ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;		s->tried_agent = TRUE;		logevent("Pageant is running. Requesting keys.");		/* Request the keys held by the agent. */		PUT_32BIT(s->request, 1);		s->request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;		if (!agent_query(s->request, 5, &r, &s->responselen,				 ssh_agent_callback, ssh)) {		    do {			crReturnV;			if (pktin) {			    bombout(("Unexpected data from server while"				     " waiting for agent response"));			    crStopV;			}		    } while (pktin || inlen > 0);		    r = ssh->agent_response;		    s->responselen = ssh->agent_response_len;		}		s->response = (unsigned char *) r;		if (s->response && s->responselen >= 5 &&		    s->response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {		    s->p = s->response + 5;		    s->nkeys = GET_32BIT(s->p);		    s->p += 4;		    logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);		    for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {			void *vret;			logeventf(ssh, "Trying Pageant key #%d", s->keyi);			s->pklen = GET_32BIT(s->p);			s->p += 4;			if (s->publickey_blob &&			    s->pklen == s->publickey_bloblen &&			    !memcmp(s->p, s->publickey_blob,				    s->publickey_bloblen)) {			    logevent("This key matches configured key file");			    s->tried_pubkey_config = 1;			}			s->pkblob = (char *)s->p;			s->p += s->pklen;			s->alglen = GET_32BIT(s->pkblob);			s->alg = s->pkblob + 4;			s->commentlen = GET_32BIT(s->p);			s->p += 4;			s->commentp = (char *)s->p;			s->p += s->commentlen;			s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);			ssh2_pkt_addstring(s->pktout, s->username);			ssh2_pkt_addstring(s->pktout, "ssh-connection");	/* service requested */			ssh2_pkt_addstring(s->pktout, "publickey");	/* method */			ssh2_pkt_addbool(s->pktout, FALSE);	/* no signature included */			ssh2_pkt_addstring_start(s->pktout);			ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);			ssh2_pkt_addstring_start(s->pktout);			ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);			ssh2_pkt_send(ssh, s->pktout);			crWaitUntilV(pktin);			if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {			    logevent("Key refused");			    continue;			}			if (flags & FLAG_VERBOSE) {			    c_write_str(ssh, "Authenticating with "					"public key \"");			    c_write(ssh, s->commentp, s->commentlen);			    c_write_str(ssh, "\" from agent\r\n");			}			/*			 * Server is willing to accept the key.			 * Construct a SIGN_REQUEST.			 */			s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);			ssh2_pkt_addstring(s->pktout, s->username);			ssh2_pkt_addstring(s->pktout, "ssh-connection");	/* service requested */			ssh2_pkt_addstring(s->pktout, "publickey");	/* method */			ssh2_pkt_addbool(s->pktout, TRUE);			ssh2_pkt_addstring_start(s->pktout);			ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);			ssh2_pkt_addstring_start(s->pktout);			ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);			s->siglen = s->pktout->length - 5 + 4 +			    ssh->v2_session_id_len;                        if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)                            s->siglen -= 4;			s->len = 1;       /* message type */			s->len += 4 + s->pklen;	/* key blob */			s->len += 4 + s->siglen;	/* data to sign */			s->len += 4;      /* flags */			s->agentreq = snewn(4 + s->len, char);			PUT_32BIT(s->agentreq, s->len);			s->q = s->agentreq + 4;			*s->q++ = SSH2_AGENTC_SIGN_REQUEST;			PUT_32BIT(s->q, s->pklen);			s->q += 4;			memcpy(s->q, s->pkblob, s->pklen);			s->q += s->pklen;			PUT_32BIT(s->q, s->siglen);			s->q += 4;			/* Now the data to be signed... */                        if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {                            PUT_32BIT(s->q, ssh->v2_session_id_len);                            s->q += 4;                        }			memcpy(s->q, ssh->v2_session_id,			       ssh->v2_session_id_len);			s->q += ssh->v2_session_id_len;			memcpy(s->q, s->pktout->data + 5,			       s->pktout->length - 5);			s->q += s->pktout->length - 5;			/* And finally the (zero) flags word. */			PUT_32BIT(s->q, 0);			if (!agent_query(s->agentreq, s->len + 4,					 &vret, &s->retlen,					 ssh_agent_callback, ssh)) {			    do {				crReturnV;				if (pktin) {				    bombout(("Unexpected data from server"					     " while waiting for agent"					     " response"));				    crStopV;				}			    } while (pktin || inlen > 0);			    vret = ssh->agent_response;			    s->retlen = ssh->agent_response_len;			}			s->ret = vret;			sfree(s->agentreq);			if (s->ret) {			    if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {				logevent("Sending Pageant's response");				ssh2_add_sigblob(ssh, s->pktout,						 s->pkblob, s->pklen,						 s->ret + 9,						 GET_32BIT(s->ret + 5));				ssh2_pkt_send(ssh, s->pktout);				s->authed = TRUE;				break;			    } else {				logevent				    ("Pageant failed to answer challenge");				sfree(s->ret);			    }			}		    }		    if (s->authed)			continue;		}		sfree(s->response);	    } else if (s->can_pubkey && s->publickey_blob &&		       !s->tried_pubkey_config) {		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;		s->tried_pubkey_config = TRUE;		/*		 * Try the public key supplied in the configuration.		 *		 * First, offer the public blob to see if the server is		 * willing to accept it.		 */		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);		ssh2_pkt_addstring(s->pktout, s->username);		ssh2_pkt_addstring(s->pktout, "ssh-connection");						/* service requested */		ssh2_pkt_addstring(s->pktout, "publickey");	/* method */		ssh2_pkt_addbool(s->pktout, FALSE);						/* no signature included */		ssh2_pkt_addstring(s->pktout, s->publickey_algorithm);		ssh2_pkt_addstring_start(s->pktout);		ssh2_pkt_addstring_data(s->pktout,					(char *)s->publickey_blob,					s->publickey_bloblen);		ssh2_pkt_send(ssh, s->pktout);		logevent("Offered public key");		crWaitUntilV(pktin);		if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {		    /* Key refused. Give up. */		    s->gotit = TRUE; /* reconsider message next loop */		    s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;		    continue; /* process this new message */		}		logevent("Offer of public key accepted");		/*		 * Actually attempt a serious authentication using		 * the key.		 */		if (flags & FLAG_VERBOSE) {		    c_write_str(ssh, "Authenticating with public key \"");		    c_write_str(ssh, s->publickey_comment);		    c_write_str(ssh, "\"\r\n");		}		key = NULL;		while (!key) {		    const char *error;  /* not live over crReturn */		    if (s->publickey_encrypted) {			/*			 * Get a passphrase from the user.			 */			int ret; /* need not be kept over crReturn */			s->cur_prompt = new_prompts(ssh->frontend);			s->cur_prompt->to_server = FALSE;			s->cur_prompt->name = dupstr("SSH key passphrase");			add_prompt(s->cur_prompt,				   dupprintf("Passphrase for key \"%.100s\": ",					     s->publickey_comment),				   FALSE, SSH_MAX_PASSWORD_LEN);			ret = get_userpass_input(s->cur_prompt, NULL, 0);			while (ret < 0) {			    ssh->send_ok = 1;			    crWaitUntilV(!pktin);			    ret = get_userpass_input(s->cur_prompt,						     in, inlen);			    ssh->send_ok = 0;			}			if (!ret) {			    /* Failed to get a passphrase. Terminate. */			    free_prompts(s->cur_prompt);			    ssh_disconnect(ssh, NULL,					   "Unable to authenticate",					   SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,					   TRUE);			    crStopV;			}			passphrase =			    dupstr(s->cur_prompt->prompts[0]->result);			free_prompts(s->cur_prompt);		    } else {			passphrase = NULL; /* no passphrase needed */		    }		    /*		     * Try decrypting the key.		     */		    key = ssh2_load_userkey(&ssh->cfg.keyfile, passphrase,					    &error);		    if (passphrase) {			/* burn the evidence */			memset(passphrase, 0, strlen(passphrase));			sfree(passphrase);		    }		    if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {			if (passphrase &&			    (key == SSH2_WRONG_PASSPHRASE)) {			    c_write_str(ssh, "Wrong passphrase\r\n");			    key = NULL;			    /* and loop again */			} else {			    c_write_str(ssh, "Unable to load private key (");			    c_write_str(ssh, error);			    c_write_str(ssh, ")\r\n");			    key = NULL;			    break; /* try something else */			}		    }		}		if (key) {		    unsigned char *pkblob, *sigblob, *sigdata;		    int pkblob_len, sigblob_len, sigdata_len;		    int p;		    /*		     * We have loaded the private key and the server		     * has announced that it's willing to accept it.		     * Hallelujah. Generate a signature and send it.		     */		    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);		    ssh2_pkt_addstring(s->pktout, s->username);		    ssh2_pkt_addstring(s->pktout, "ssh-connection");						    /* service requested */		    ssh2_pkt_addstring(s->pktout, "publickey");						    /* method */		    ssh2_pkt_addbool(s->pktout, TRUE);						    /* signature follows */		    ssh2_pkt_addstring(s->pktout, key->alg->name);		    pkblob = key->alg->public_blob(key->data,						   &pkblob_len);		    ssh2_pkt_addstring_start(s->pktout);		    ssh2_pkt_addstring_data(s->pktout, (char *)pkblob,					    pkblob_len);		    /*		     * The data to be signed is:		     *		     *   string  session-id		     *		     * followed by everything so far placed in the		     * outgoing packet.		     */		    sigdata_len = s->pktout->length - 5 + 4 +			ssh->v2_session_id_len;		    if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)			sigdata_len -= 4;		    sigdata = snewn(sigdata_len, unsigned char);		    p = 0;		    if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {			PUT_32BIT(sigdata+p, ssh->v2_session_id_len);			p += 4;		    }		    memcpy(sigdata+p, ssh->v2_session_id,			   ssh->v2_session_id_len);		    p += ssh->v2_session_id_len;		    memcpy(sigdata+p, s->pktout->data + 5,			   s->pktout->length - 5);		    p += s->pktout->length - 5;		    assert(p == sigdata_len);		    sigblob = key->alg->sign(key->data, (char *)sigdata,					     sigdata_len, &sigblob_len);		    ssh2_add_sigblob(ssh, s->pktout, pkblob, pkblob_len,				     sigblob, sigblob_len);		    sfree(pkblob);		    sfree(sigblob);		    sfree(sigdata);		    ssh2_pkt_send(ssh, s->pktout);		    s->type = AUTH_TYPE_PUBLICKEY;		    key->alg->freekey(key->data);		}	    } else if (s->can_keyb_inter && !s->kbd_inter_refused) {		/*		 * Keyboard-interactive authentication.		 */		s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;		ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;		ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);		ssh2_pkt_addstring(s->pktout, s->username);		ssh2_pkt_addstring(s->pktout, "ssh-connection");							/* service requested */		ssh2_pkt_addstring(s->pktout, "keyboard-interactive");							/* method */		ssh2_pkt_addstring(s->pktout, "");	/* lang */		ssh2_pkt_addstring(s->pktout, "");	/* submethods */		ssh2_pkt_send(ssh, s->pktout);		crWaitUntilV(pktin);		if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {		    /* Server is not willing to do keyboard-interactive		     * at all (or, bizarrely but legally, accepts the		     * 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;		}		/*		 * Loop while the server continues to send INFO_REQUESTs.		 */		while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {		    char *name, *inst, *lang;		    int name_len, inst_len, lang_len;		    int i;		    /*		     * We've got a fresh USERAUTH_INFO_REQUEST.		     * Get the preamble and start building a prompt.		     */		    ssh_pkt_getstring(pktin, &name, &name_len);		    ssh_pkt_getstring(pktin, &inst, &inst_len);		    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.		     */		    s->num_prompts = ssh_pkt_getuint32(pktin);		    for (i = 0; i < s->num_prompts; i++) {			char *prompt;			int prompt_len;			int echo;			static char noprompt[] =			    "<server failed to send prompt>: ";			ssh_pkt_getstring(pktin, &prompt, &prompt_len);			echo = ssh2_pkt_getbool(pktin);			if (!prompt_len) {			    prompt = noprompt;			    prompt_len = lenof(noprompt)-1;			}			add_prompt(s->cur_prompt,				   dupprintf("%.*s", prompt_len, prompt),				   echo, SSH_MAX_PASSWORD_LEN);		    }		    /*		     * Get the user's responses.		     */		    if (s->num_prompts) {			int ret; /* not live over crReturn */			ret = get_userpass_input(s->cur_prompt, NULL, 0);			while (ret < 0) {			    ssh->send_ok = 1;			    crWaitUntilV(!pktin);			    ret = get_userpass_input(s->cur_prompt, in, inlen);			    ssh->send_ok = 0;			}			if (!ret) {			    /*			     * Failed to get responses. Terminate.			     */			    free_prompts(s->cur_prompt);			    ssh_disconnect(ssh, NULL, "Unable to authenticate",					   SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,					   TRUE);			    crStopV;			}		    }		    /*		     * Send the responses to the server.		     */		    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);		    s->pktout->forcepad = 256;		    ssh2_pkt_adduint32(s->pktout, s->num_prompts);		    for (i=0; i < s->num_prompts; i++) {			dont_log_password(ssh, s->pktout, PKTLOG_BLANK);			ssh2_pkt_addstring(s->pktout,					   s->cur_prompt->prompts[i]->result);			end_log_omission(ssh, s->pktout);		    }		    ssh2_pkt_send(ssh, s->pktout);		    /*		     * Get the next packet in case it's another		     * INFO_REQUEST.		     */		    crWaitUntilV(pktin);		}		/*		 * We should have SUCCESS or FAILURE now.		 */		s->gotit = TRUE;	    } else if (s->can_passwd) {		/*		 * Plain old password authentication.		 */		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;		s->cur_prompt = new_prompts(ssh->frontend);		s->cur_prompt->to_server = TRUE;		s->cur_prompt->name = dupstr("SSH password");		add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",						    s->username,						    ssh->savedhost),			   FALSE, SSH_MAX_PASSWORD_LEN);		ret = get_userpass_input(s->cur_prompt, NULL, 0);		while (ret < 0) {		    ssh->send_ok = 1;		    crWaitUntilV(!pktin);		    ret = get_userpass_input(s->cur_prompt, in, inlen);		    ssh->send_ok = 0;		}		if (!ret) {		    /*		     * Failed to get responses. Terminate.		     */		    free_prompts(s->cur_prompt);		    ssh_disconnect(ssh, NULL, "Unable to authenticate",				   SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,				   TRUE);		    crStopV;		}		/*		 * Squirrel away the password. (We may need it later if		 * asked to change it.)		 */		s->password = dupstr(s->cur_prompt->prompts[0]->result);		free_prompts(s->cur_prompt);		/*		 * Send the password packet.		 *		 * We pad out the password packet to 256 bytes to make		 * it harder for an attacker to find the length of the		 * user's password.		 *		 * Anyone using a password longer than 256 bytes		 * probably doesn't have much to worry about from		 * people who find out how long their password is!		 */		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);		s->pktout->forcepad = 256;		ssh2_pkt_addstring(s->pktout, s->username);		ssh2_pkt_addstring(s->pktout, "ssh-connection");							/* service requested */		ssh2_pkt_addstring(s->pktout, "password");		ssh2_pkt_addbool(s->pktout, FALSE);		dont_log_password(ssh, s->pktout, PKTLOG_BLANK);		ssh2_pkt_addstring(s->pktout, s->password);		end_log_omission(ssh, s->pktout);		ssh2_pkt_send(ssh, s->pktout);		logevent("Sent password");		s->type = AUTH_TYPE_PASSWORD;		/*		 * Wait for next packet, in case it's a password change		 * request.		 */		crWaitUntilV(pktin);		changereq_first_time = TRUE;		while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {		    /* 		     * We're being asked for a new password		     * (perhaps not for the first time).		     * Loop until the server accepts it.		     */		    int got_new = FALSE; /* not live over crReturn */		    char *prompt;   /* not live over crReturn */		    int prompt_len; /* not live over crReturn */		    		    {			char *msg;			if (changereq_first_time)			    msg = "Server requested password change";			else			    msg = "Server rejected new password";			logevent(msg);			c_write_str(ssh, msg);			c_write_str(ssh, "\r\n");		    }		    ssh_pkt_getstring(pktin, &prompt, &prompt_len);		    s->cur_prompt = new_prompts(ssh->frontend);		    s->cur_prompt->to_server = TRUE;		    s->cur_prompt->name = dupstr("New SSH password");		    s->cur_prompt->instruction =			dupprintf("%.*s", prompt_len, prompt);		    s->cur_prompt->instr_reqd = TRUE;		    add_prompt(s->cur_prompt, dupstr("Enter new password: "),			       FALSE, SSH_MAX_PASSWORD_LEN);		    add_prompt(s->cur_prompt, dupstr("Confirm new password: "),			       FALSE, SSH_MAX_PASSWORD_LEN);		    /*		     * Loop until the user manages to enter the same		     * password twice.		     */		    while (!got_new) {			ret = get_userpass_input(s->cur_prompt, NULL, 0);			while (ret < 0) {			    ssh->send_ok = 1;			    crWaitUntilV(!pktin);			    ret = get_userpass_input(s->cur_prompt, in, inlen);			    ssh->send_ok = 0;			}			if (!ret) {			    /*			     * Failed to get responses. Terminate.			     */			    /* burn the evidence */			    free_prompts(s->cur_prompt);			    memset(s->password, 0, strlen(s->password));			    sfree(s->password);			    ssh_disconnect(ssh, NULL, "Unable to authenticate",					   SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,					   TRUE);			    crStopV;			}			/*			 * Check the two passwords match.			 */			got_new = (strcmp(s->cur_prompt->prompts[0]->result,					  s->cur_prompt->prompts[1]->result)				   == 0);			if (!got_new)			    /* They don't. Silly user. */			    c_write_str(ssh, "Passwords do not match\r\n");		    }		    /*		     * Send the new password (along with the old one).		     * (see above for padding rationale)		     */		    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);		    s->pktout->forcepad = 256;		    ssh2_pkt_addstring(s->pktout, s->username);		    ssh2_pkt_addstring(s->pktout, "ssh-connection");							/* service requested */		    ssh2_pkt_addstring(s->pktout, "password");		    ssh2_pkt_addbool(s->pktout, TRUE);		    dont_log_password(ssh, s->pktout, PKTLOG_BLANK);		    ssh2_pkt_addstring(s->pktout, s->password);		    ssh2_pkt_addstring(s->pktout,				       s->cur_prompt->prompts[0]->result);		    free_prompts(s->cur_prompt);		    end_log_omission(ssh, s->pktout);		    ssh2_pkt_send(ssh, s->pktout);		    logevent("Sent new password");		    		    /*		     * Now see what the server has to say about it.		     * (If it's CHANGEREQ again, it's not happy with the		     * new password.)		     */		    crWaitUntilV(pktin);		    changereq_first_time = FALSE;		}		/*		 * We need to reexamine the current pktin at the top		 * of the loop. Either:		 *  - we weren't asked to change password at all, in		 *    which case it's a SUCCESS or FAILURE with the		 *    usual meaning		 *  - we sent a new password, and the server was		 *    either OK with it (SUCCESS or FAILURE w/partial		 *    success) or unhappy with the _old_ password		 *    (FAILURE w/o partial success)		 * In any of these cases, we go back to the top of		 * the loop and start again.		 */		s->gotit = TRUE;		/*		 * We don't need the old password any more, in any		 * case. Burn the evidence.		 */		memset(s->password, 0, strlen(s->password));		sfree(s->password);	    } else {		ssh_disconnect(ssh, NULL,			       "No supported authentication methods available",			       SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,			       FALSE);		crStopV;	    }	}    }    ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;    /* Clear up various bits and pieces from authentication. */    if (s->publickey_blob) {	sfree(s->publickey_blob);	sfree(s->publickey_comment);    }    /*     * Now the connection protocol has started, one way or another.     */    ssh->channels = newtree234(ssh_channelcmp);    /*     * Set up handlers for some connection protocol messages, so we     * don't have to handle them repeatedly in this coroutine.     */    ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] =	ssh2_msg_channel_window_adjust;    ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] =	ssh2_msg_global_request;    /*     * Create the main session channel.     */    if (!ssh->cfg.ssh_no_shell) {	ssh->mainchan = snew(struct ssh_channel);	ssh->mainchan->ssh = ssh;	ssh->mainchan->localid = alloc_channel_id(ssh);	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);	crWaitUntilV(pktin);	if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {	    bombout(("Server refused to open a session"));	    crStopV;	    /* FIXME: error data comes back in FAILURE packet */	}	if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {	    bombout(("Server's channel confirmation cited wrong channel"));	    crStopV;	}	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");    } else	ssh->mainchan = NULL;    /*     * Now we have a channel, make dispatch table entries for     * general channel-based messages.     */    ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] =    ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] =	ssh2_msg_channel_data;    ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_channel_eof;    ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_channel_close;    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] =	ssh2_msg_channel_open_confirmation;    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] =	ssh2_msg_channel_open_failure;    ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] =	ssh2_msg_channel_request;    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =	ssh2_msg_channel_open;    /*     * Potentially enable X11 forwarding.     */    if (ssh->mainchan && ssh->cfg.x11_forward) {	char proto[20], data[64];	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);	/*	 * 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,	 * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection	 * without having session blanking enabled is likely to leak your	 * cookie into the log.	 */	dont_log_password(ssh, s->pktout, PKTLOG_BLANK);	ssh2_pkt_addstring(s->pktout, data);	end_log_omission(ssh, s->pktout);	ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display));	ssh2_pkt_send(ssh, s->pktout);	crWaitUntilV(pktin);	if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {	    if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {		bombout(("Unexpected response to X11 forwarding request:"			 " packet type %d", pktin->type));		crStopV;	    }	    logevent("X11 forwarding refused");	} else {	    logevent("X11 forwarding enabled");	    ssh->X11_fwd_enabled = TRUE;	}    }    /*     * Enable port forwardings.     */    ssh_setup_portfwd(ssh, &ssh->cfg);    /*     * Potentially enable agent forwarding.     */    if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) {	logevent("Requesting OpenSSH-style agent forwarding");	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);	ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com");	ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */	ssh2_pkt_send(ssh, s->pktout);	crWaitUntilV(pktin);	if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {	    if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {		bombout(("Unexpected response to agent forwarding request:"			 " packet type %d", pktin->type));		crStopV;	    }	    logevent("Agent forwarding refused");	} else {	    logevent("Agent forwarding enabled");	    ssh->agentfwd_enabled = TRUE;	}    }    /*     * Now allocate a pty for the session.     */    if (ssh->mainchan && !ssh->cfg.nopty) {	/* Unpick the terminal-speed string. */	/* XXX perhaps we should allow no speeds to be sent. */        ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */	sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);	/* Build the pty request. */	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);	/* recipient channel */	ssh2_pkt_addstring(s->pktout, "pty-req");	ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */	ssh2_pkt_addstring(s->pktout, ssh->cfg.termtype);	ssh2_pkt_adduint32(s->pktout, ssh->term_width);	ssh2_pkt_adduint32(s->pktout, ssh->term_height);	ssh2_pkt_adduint32(s->pktout, 0);	       /* pixel width */	ssh2_pkt_adduint32(s->pktout, 0);	       /* pixel height */	ssh2_pkt_addstring_start(s->pktout);	parse_ttymodes(ssh, ssh->cfg.ttymodes,		       ssh2_send_ttymode, (void *)s->pktout);	ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED);	ssh2_pkt_adduint32(s->pktout, ssh->ispeed);	ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED);	ssh2_pkt_adduint32(s->pktout, ssh->ospeed);	ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */	ssh2_pkt_send(ssh, s->pktout);	ssh->state = SSH_STATE_INTERMED;	crWaitUntilV(pktin);	if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {	    if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {		bombout(("Unexpected response to pty request:"			 " packet type %d", pktin->type));		crStopV;	    }	    c_write_str(ssh, "Server refused to allocate pty\r\n");	    ssh->editing = ssh->echoing = 1;	} else {	    logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",		      ssh->ospeed, ssh->ispeed);	}    } else {	ssh->editing = ssh->echoing = 1;    }    /*     * Send environment variables.     *      * Simplest thing here is to send all the requests at once, and     * then wait for a whole bunch of successes or failures.     */    if (ssh->mainchan && *ssh->cfg.environmt) {	char *e = ssh->cfg.environmt;	char *var, *varend, *val;	s->num_env = 0;	while (*e) {	    var = e;	    while (*e && *e != '\t') e++;	    varend = e;	    if (*e == '\t') e++;	    val = e;	    while (*e) e++;	    e++;	    s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);	    ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);	    ssh2_pkt_addstring(s->pktout, "env");	    ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */	    ssh2_pkt_addstring_start(s->pktout);	    ssh2_pkt_addstring_data(s->pktout, var, varend-var);	    ssh2_pkt_addstring(s->pktout, val);	    ssh2_pkt_send(ssh, s->pktout);	    s->num_env++;	}	logeventf(ssh, "Sent %d environment variables", s->num_env);	s->env_ok = 0;	s->env_left = s->num_env;	while (s->env_left > 0) {	    crWaitUntilV(pktin);	    if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {		if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {		    bombout(("Unexpected response to environment request:"			     " packet type %d", pktin->type));		    crStopV;		}	    } else {		s->env_ok++;	    }	    s->env_left--;	}	if (s->env_ok == s->num_env) {	    logevent("All environment variables successfully set");	} else if (s->env_ok == 0) {	    logevent("All environment variables refused");	    c_write_str(ssh, "Server refused to set environment variables\r\n");	} else {	    logeventf(ssh, "%d environment variables refused",		      s->num_env - s->env_ok);	    c_write_str(ssh, "Server refused to set all environment variables\r\n");	}    }    /*     * Start a shell or a remote command. We may have to attempt     * this twice if the config data has provided a second choice     * of command.     */    if (ssh->mainchan) while (1) {	int subsys;	char *cmd;	if (ssh->fallback_cmd) {	    subsys = ssh->cfg.ssh_subsys2;	    cmd = ssh->cfg.remote_cmd_ptr2;	} else {	    subsys = ssh->cfg.ssh_subsys;	    cmd = ssh->cfg.remote_cmd_ptr;	    if (!cmd) cmd = ssh->cfg.remote_cmd;	}	s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);	ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);	/* recipient channel */	if (subsys) {	    ssh2_pkt_addstring(s->pktout, "subsystem");	    ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */	    ssh2_pkt_addstring(s->pktout, cmd);	} else if (*cmd) {	    ssh2_pkt_addstring(s->pktout, "exec");	    ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */	    ssh2_pkt_addstring(s->pktout, cmd);	} else {	    ssh2_pkt_addstring(s->pktout, "shell");	    ssh2_pkt_addbool(s->pktout, 1);	       /* want reply */	}	ssh2_pkt_send(ssh, s->pktout);	crWaitUntilV(pktin);	if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {	    if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {		bombout(("Unexpected response to shell/command request:"			 " packet type %d", pktin->type));		crStopV;	    }	    /*	     * We failed to start the command. If this is the	     * fallback command, we really are finished; if it's	     * not, and if the fallback command exists, try falling	     * back to it before complaining.	     */	    if (!ssh->fallback_cmd && ssh->cfg.remote_cmd_ptr2 != NULL) {		logevent("Primary command failed; attempting fallback");		ssh->fallback_cmd = TRUE;		continue;	    }	    bombout(("Server refused to start a shell/command"));	    crStopV;	} else {	    logevent("Started a shell/command");	}	break;    }    ssh->state = SSH_STATE_SESSION;    if (ssh->size_needed)	ssh_size(ssh, ssh->term_width, ssh->term_height);    if (ssh->eof_needed)	ssh_special(ssh, TS_EOF);    /*     * Transfer data!     */    if (ssh->ldisc)	ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */    if (ssh->mainchan)	ssh->send_ok = 1;    while (1) {	crReturnV;	s->try_send = FALSE;	if (pktin) {	    /*	     * _All_ the connection-layer packets we expect to	     * receive are now handled by the dispatch table.	     * Anything that reaches here must be bogus.	     */	    bombout(("Strange packet received: type %d", pktin->type));	    crStopV;	} else if (ssh->mainchan) {	    /*	     * We have spare data. Add it to the channel buffer.	     */	    ssh2_add_channel_data(ssh->mainchan, (char *)in, inlen);	    s->try_send = TRUE;	}	if (s->try_send) {	    int i;	    struct ssh_channel *c;	    /*	     * 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);	}    }    crFinishV;}