aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/plink/cmdline.c214
-rw-r--r--tools/plink/cproxy.c11
-rw-r--r--tools/plink/ldisc.c42
-rw-r--r--tools/plink/ldisc.h6
-rw-r--r--tools/plink/logging.c100
-rw-r--r--tools/plink/misc.c62
-rw-r--r--tools/plink/misc.h1
-rw-r--r--tools/plink/network.h12
-rw-r--r--tools/plink/pinger.c11
-rw-r--r--tools/plink/portfwd.c37
-rw-r--r--tools/plink/proxy.c189
-rw-r--r--tools/plink/proxy.h5
-rw-r--r--tools/plink/putty.h634
-rw-r--r--tools/plink/raw.c80
-rw-r--r--tools/plink/rlogin.c125
-rw-r--r--tools/plink/settings.c1180
-rw-r--r--tools/plink/ssh.c1472
-rw-r--r--tools/plink/ssh.h25
-rw-r--r--tools/plink/sshbn.c884
-rw-r--r--tools/plink/sshgss.h2
-rw-r--r--tools/plink/sshpubk.c18
-rw-r--r--tools/plink/sshrsa.c86
-rw-r--r--tools/plink/sshzlib.c3
-rw-r--r--tools/plink/storage.h25
-rw-r--r--tools/plink/telnet.c167
-rw-r--r--tools/plink/terminal.h58
-rw-r--r--tools/plink/version.c21
-rw-r--r--tools/plink/wincons.c47
-rw-r--r--tools/plink/windefs.c24
-rw-r--r--tools/plink/wingss.c18
-rw-r--r--tools/plink/winhandl.c24
-rw-r--r--tools/plink/winmisc.c95
-rw-r--r--tools/plink/winnet.c43
-rw-r--r--tools/plink/winpgntc.c84
-rw-r--r--tools/plink/winplink.c161
-rw-r--r--tools/plink/winproxy.c14
-rw-r--r--tools/plink/winstore.c76
-rw-r--r--tools/plink/winstuff.h24
-rw-r--r--tools/plink/winx11.c7
-rw-r--r--tools/plink/x11fwd.c27
40 files changed, 4066 insertions, 2048 deletions
diff --git a/tools/plink/cmdline.c b/tools/plink/cmdline.c
index aa376a053..1a5e2cbb6 100644
--- a/tools/plink/cmdline.c
+++ b/tools/plink/cmdline.c
@@ -105,15 +105,12 @@ int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) {
if (tried_once)
return 0;
- strncpy(p->prompts[0]->result, cmdline_password,
- p->prompts[0]->result_len);
- p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0';
+ prompt_set_result(p->prompts[0], cmdline_password);
memset(cmdline_password, 0, strlen(cmdline_password));
sfree(cmdline_password);
cmdline_password = NULL;
tried_once = 1;
return 1;
-
}
/*
@@ -162,7 +159,7 @@ static int cmdline_check_unavailable(int flag, char *p)
if (need_save < 0) return x; \
} while (0)
-int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
+int cmdline_process_param(char *p, char *value, int need_save, Conf *conf)
{
int ret = 0;
@@ -170,7 +167,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
RETURN(2);
/* This parameter must be processed immediately rather than being
* saved. */
- do_defaults(value, cfg);
+ do_defaults(value, conf);
loaded_session = TRUE;
cmdline_session_name = dupstr(value);
return 2;
@@ -179,41 +176,49 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- default_protocol = cfg->protocol = PROT_SSH;
- default_port = cfg->port = 22;
+ default_protocol = PROT_SSH;
+ default_port = 22;
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ conf_set_int(conf, CONF_port, default_port);
return 1;
}
if (!strcmp(p, "-telnet")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- default_protocol = cfg->protocol = PROT_TELNET;
- default_port = cfg->port = 23;
+ default_protocol = PROT_TELNET;
+ default_port = 23;
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ conf_set_int(conf, CONF_port, default_port);
return 1;
}
if (!strcmp(p, "-rlogin")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- default_protocol = cfg->protocol = PROT_RLOGIN;
- default_port = cfg->port = 513;
+ default_protocol = PROT_RLOGIN;
+ default_port = 513;
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ conf_set_int(conf, CONF_port, default_port);
return 1;
}
if (!strcmp(p, "-raw")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- default_protocol = cfg->protocol = PROT_RAW;
+ default_protocol = PROT_RAW;
+ conf_set_int(conf, CONF_protocol, default_protocol);
}
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';
+ default_protocol = PROT_SERIAL;
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ /* The host parameter will already be loaded into CONF_host,
+ * so copy it across */
+ conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host));
}
if (!strcmp(p, "-v")) {
RETURN(1);
@@ -223,41 +228,23 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
RETURN(2);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- strncpy(cfg->username, value, sizeof(cfg->username));
- cfg->username[sizeof(cfg->username) - 1] = '\0';
+ conf_set_str(conf, CONF_username, value);
}
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';
+ conf_set_str(conf, CONF_loghost, value);
}
if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {
- char *fwd, *ptr, *q, *qq;
- int dynamic, i=0;
+ char type, *q, *qq, *key, *val;
RETURN(2);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- dynamic = !strcmp(p, "-D");
- fwd = value;
- ptr = cfg->portfwd;
- /* if existing forwards, find end of list */
- while (*ptr) {
- while (*ptr)
- ptr++;
- ptr++;
- }
- i = ptr - cfg->portfwd;
- ptr[0] = p[1]; /* insert a 'L', 'R' or 'D' at the start */
- ptr++;
- if (1 + strlen(fwd) + 2 > sizeof(cfg->portfwd) - i) {
- cmdline_error("out of space for port forwardings");
- return ret;
- }
- strncpy(ptr, fwd, sizeof(cfg->portfwd) - i - 2);
- if (!dynamic) {
+ if (strcmp(p, "-D")) {
/*
+ * For -L or -R forwarding types:
+ *
* We expect _at least_ two colons in this string. The
* possible formats are `sourceport:desthost:destport',
* or `sourceip:sourceport:desthost:destport' if you're
@@ -265,19 +252,47 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
* replace the one between source and dest with a \t;
* this means we must find the second-to-last colon in
* the string.
+ *
+ * (This looks like a foolish way of doing it given the
+ * existence of strrchr, but it's more efficient than
+ * two strrchrs - not to mention that the second strrchr
+ * would require us to modify the input string!)
*/
- q = qq = strchr(ptr, ':');
+
+ type = p[1]; /* 'L' or 'R' */
+
+ q = qq = strchr(value, ':');
while (qq) {
char *qqq = strchr(qq+1, ':');
if (qqq)
q = qq;
qq = qqq;
}
- if (q) *q = '\t'; /* replace second-last colon with \t */
+
+ if (!q) {
+ cmdline_error("-%c expects at least two colons in its"
+ " argument", type);
+ return ret;
+ }
+
+ key = dupprintf("%c%.*s", type, q - value, value);
+ val = dupstr(q+1);
+ } else {
+ /*
+ * Dynamic port forwardings are entered under the same key
+ * as if they were local (because they occupy the same
+ * port space - a local and a dynamic forwarding on the
+ * same local port are mutually exclusive), with the
+ * special value "D" (which can be distinguished from
+ * anything in the ordinary -L case by containing no
+ * colon).
+ */
+ key = dupprintf("L%s", value);
+ val = dupstr("D");
}
- cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0';
- cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0';
- ptr[strlen(ptr)+1] = '\000'; /* append 2nd '\000' */
+ conf_set_str_str(conf, CONF_portfwd, key, val);
+ sfree(key);
+ sfree(val);
}
if ((!strcmp(p, "-nc"))) {
char *host, *portp;
@@ -286,20 +301,16 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- host = portp = value;
- while (*portp && *portp != ':')
- portp++;
- if (*portp) {
- unsigned len = portp - host;
- if (len >= sizeof(cfg->ssh_nc_host))
- len = sizeof(cfg->ssh_nc_host) - 1;
- memcpy(cfg->ssh_nc_host, value, len);
- cfg->ssh_nc_host[len] = '\0';
- cfg->ssh_nc_port = atoi(portp+1);
- } else {
+ portp = strchr(value, ':');
+ if (!portp) {
cmdline_error("-nc expects argument of form 'host:port'");
return ret;
}
+
+ host = dupprintf("%.*s", portp - value, value);
+ conf_set_str(conf, CONF_ssh_nc_host, host);
+ conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1));
+ sfree(host);
}
if (!strcmp(p, "-m")) {
char *filename, *command;
@@ -317,8 +328,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
command = NULL;
fp = fopen(filename, "r");
if (!fp) {
- cmdline_error("unable to open command "
- "file \"%s\"", filename);
+ cmdline_error("unable to open command file \"%s\"", filename);
return ret;
}
do {
@@ -332,16 +342,17 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
}
command[cmdlen++] = d;
} while (c != EOF);
- cfg->remote_cmd_ptr = command;
- cfg->remote_cmd_ptr2 = NULL;
- cfg->nopty = TRUE; /* command => no terminal */
fclose(fp);
+ conf_set_str(conf, CONF_remote_cmd, command);
+ conf_set_str(conf, CONF_remote_cmd2, "");
+ conf_set_int(conf, CONF_nopty, TRUE); /* command => no terminal */
+ sfree(command);
}
if (!strcmp(p, "-P")) {
RETURN(2);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(1); /* lower priority than -ssh,-telnet */
- cfg->port = atoi(value);
+ conf_set_int(conf, CONF_port, atoi(value));
}
if (!strcmp(p, "-pw")) {
RETURN(2);
@@ -349,7 +360,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
SAVEABLE(1);
/* We delay evaluating this until after the protocol is decided,
* so that we can warn if it's of no use with the selected protocol */
- if (cfg->protocol != PROT_SSH)
+ if (conf_get_int(conf, CONF_protocol) != PROT_SSH)
cmdline_error("the -pw option can only be used with the "
"SSH protocol");
else {
@@ -366,105 +377,108 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->tryagent = TRUE;
+ conf_set_int(conf, CONF_tryagent, TRUE);
}
if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||
!strcmp(p, "-nopageant")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->tryagent = FALSE;
+ conf_set_int(conf, CONF_tryagent, FALSE);
}
if (!strcmp(p, "-A")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->agentfwd = 1;
+ conf_set_int(conf, CONF_agentfwd, 1);
}
if (!strcmp(p, "-a")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->agentfwd = 0;
+ conf_set_int(conf, CONF_agentfwd, 0);
}
if (!strcmp(p, "-X")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->x11_forward = 1;
+ conf_set_int(conf, CONF_x11_forward, 1);
}
if (!strcmp(p, "-x")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->x11_forward = 0;
+ conf_set_int(conf, CONF_x11_forward, 0);
}
if (!strcmp(p, "-t")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(1); /* lower priority than -m */
- cfg->nopty = 0;
+ conf_set_int(conf, CONF_nopty, 0);
}
if (!strcmp(p, "-T")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(1);
- cfg->nopty = 1;
+ conf_set_int(conf, CONF_nopty, 1);
}
if (!strcmp(p, "-N")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->ssh_no_shell = 1;
+ conf_set_int(conf, CONF_ssh_no_shell, 1);
}
if (!strcmp(p, "-C")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->compression = 1;
+ conf_set_int(conf, CONF_compression, 1);
}
if (!strcmp(p, "-1")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->sshprot = 0; /* ssh protocol 1 only */
+ conf_set_int(conf, CONF_sshprot, 0); /* ssh protocol 1 only */
}
if (!strcmp(p, "-2")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->sshprot = 3; /* ssh protocol 2 only */
+ conf_set_int(conf, CONF_sshprot, 3); /* ssh protocol 2 only */
}
if (!strcmp(p, "-i")) {
+ Filename *fn;
RETURN(2);
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
SAVEABLE(0);
- cfg->keyfile = filename_from_str(value);
+ fn = filename_from_str(value);
+ conf_set_filename(conf, CONF_keyfile, fn);
+ filename_free(fn);
}
if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {
RETURN(1);
SAVEABLE(1);
- cfg->addressfamily = ADDRTYPE_IPV4;
+ conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4);
}
if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {
RETURN(1);
SAVEABLE(1);
- cfg->addressfamily = ADDRTYPE_IPV6;
+ conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6);
}
if (!strcmp(p, "-sercfg")) {
char* nextitem;
RETURN(2);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
SAVEABLE(1);
- if (cfg->protocol != PROT_SERIAL)
+ if (conf_get_int(conf, CONF_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 */
@@ -483,55 +497,45 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
if (length == 1) {
switch (*nextitem) {
case '1':
- cfg->serstopbits = 2;
- break;
case '2':
- cfg->serstopbits = 4;
+ conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0'));
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;
+ conf_set_int(conf, CONF_serdatabits, *nextitem-'0');
break;
case 'n':
- cfg->serparity = SER_PAR_NONE;
+ conf_set_int(conf, CONF_serparity, SER_PAR_NONE);
break;
case 'o':
- cfg->serparity = SER_PAR_ODD;
+ conf_set_int(conf, CONF_serparity, SER_PAR_ODD);
break;
case 'e':
- cfg->serparity = SER_PAR_EVEN;
+ conf_set_int(conf, CONF_serparity, SER_PAR_EVEN);
break;
case 'm':
- cfg->serparity = SER_PAR_MARK;
+ conf_set_int(conf, CONF_serparity, SER_PAR_MARK);
break;
case 's':
- cfg->serparity = SER_PAR_SPACE;
+ conf_set_int(conf, CONF_serparity, SER_PAR_SPACE);
break;
case 'N':
- cfg->serflow = SER_FLOW_NONE;
+ conf_set_int(conf, CONF_serflow, SER_FLOW_NONE);
break;
case 'X':
- cfg->serflow = SER_FLOW_XONXOFF;
+ conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF);
break;
case 'R':
- cfg->serflow = SER_FLOW_RTSCTS;
+ conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS);
break;
case 'D':
- cfg->serflow = SER_FLOW_DSRDTR;
+ conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR);
break;
default:
@@ -540,11 +544,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
}
} else if (length == 3 && !strncmp(nextitem,"1.5",3)) {
/* Messy special case */
- cfg->serstopbits = 3;
+ conf_set_int(conf, CONF_serstopbits, 3);
} else {
int serspeed = atoi(nextitem);
if (serspeed != 0) {
- cfg->serspeed = serspeed;
+ conf_set_int(conf, CONF_serspeed, serspeed);
} else {
cmdline_error("Unrecognised suboption \"-sercfg %s\"",
nextitem);
@@ -556,11 +560,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
return ret; /* unrecognised */
}
-void cmdline_run_saved(Config *cfg)
+void cmdline_run_saved(Conf *conf)
{
int pri, i;
for (pri = 0; pri < NPRIORITIES; pri++)
for (i = 0; i < saves[pri].nsaved; i++)
cmdline_process_param(saves[pri].params[i].p,
- saves[pri].params[i].value, 0, cfg);
+ saves[pri].params[i].value, 0, conf);
}
diff --git a/tools/plink/cproxy.c b/tools/plink/cproxy.c
index 5537fca80..d5049af3a 100644
--- a/tools/plink/cproxy.c
+++ b/tools/plink/cproxy.c
@@ -130,7 +130,8 @@ int proxy_socks5_handlechap (Proxy_Socket p)
outbuf[2] = 0x04; /* Response */
outbuf[3] = 0x10; /* Length */
hmacmd5_chap(data, p->chap_current_datalen,
- p->cfg.proxy_password, &outbuf[4]);
+ conf_get_str(p->conf, CONF_proxy_password),
+ &outbuf[4]);
sk_write(p->sub_socket, (char *)outbuf, 20);
break;
case 0x11:
@@ -159,7 +160,9 @@ int proxy_socks5_handlechap (Proxy_Socket p)
int proxy_socks5_selectchap(Proxy_Socket p)
{
- if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
+ char *username = conf_get_str(p->conf, CONF_proxy_username);
+ char *password = conf_get_str(p->conf, CONF_proxy_password);
+ if (username[0] || password[0]) {
char chapbuf[514];
int ulen;
chapbuf[0] = '\x01'; /* Version */
@@ -169,11 +172,11 @@ int proxy_socks5_selectchap(Proxy_Socket p)
chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
chapbuf[5] = '\x02'; /* Second attribute - username */
- ulen = strlen(p->cfg.proxy_username);
+ ulen = strlen(username);
if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;
chapbuf[6] = ulen;
- memcpy(chapbuf+7, p->cfg.proxy_username, ulen);
+ memcpy(chapbuf+7, username, ulen);
sk_write(p->sub_socket, chapbuf, ulen + 7);
p->chap_num_attributes = 0;
diff --git a/tools/plink/ldisc.c b/tools/plink/ldisc.c
index 119a02acb..7ed42a37c 100644
--- a/tools/plink/ldisc.c
+++ b/tools/plink/ldisc.c
@@ -12,12 +12,12 @@
#include "terminal.h"
#include "ldisc.h"
-#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \
- (ldisc->cfg->localecho == AUTO && \
+#define ECHOING (ldisc->localecho == FORCE_ON || \
+ (ldisc->localecho == AUTO && \
(ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \
term_ldisc(ldisc->term, LD_ECHO))))
-#define EDITING (ldisc->cfg->localedit == FORCE_ON || \
- (ldisc->cfg->localedit == AUTO && \
+#define EDITING (ldisc->localedit == FORCE_ON || \
+ (ldisc->localedit == AUTO && \
(ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \
term_ldisc(ldisc->term, LD_EDIT))))
@@ -76,7 +76,7 @@ static void bsb(Ldisc ldisc, int n)
#define CTRL(x) (x^'@')
#define KCTRL(x) ((x^'@') | 0x100)
-void *ldisc_create(Config *mycfg, Terminal *term,
+void *ldisc_create(Conf *conf, Terminal *term,
Backend *back, void *backhandle,
void *frontend)
{
@@ -87,12 +87,13 @@ void *ldisc_create(Config *mycfg, Terminal *term,
ldisc->bufsiz = 0;
ldisc->quotenext = 0;
- ldisc->cfg = mycfg;
ldisc->back = back;
ldisc->backhandle = backhandle;
ldisc->term = term;
ldisc->frontend = frontend;
+ ldisc_configure(ldisc, conf);
+
/* Link ourselves into the backend and the terminal */
if (term)
term->ldisc = ldisc;
@@ -102,6 +103,17 @@ void *ldisc_create(Config *mycfg, Terminal *term,
return ldisc;
}
+void ldisc_configure(void *handle, Conf *conf)
+{
+ Ldisc ldisc = (Ldisc) handle;
+
+ ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard);
+ ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline);
+ ldisc->protocol = conf_get_int(conf, CONF_protocol);
+ ldisc->localecho = conf_get_int(conf, CONF_localecho);
+ ldisc->localedit = conf_get_int(conf, CONF_localedit);
+}
+
void ldisc_free(void *handle)
{
Ldisc ldisc = (Ldisc) handle;
@@ -203,7 +215,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
* configured telnet specials off! This breaks
* talkers otherwise.
*/
- if (!ldisc->cfg->telnet_keyboard)
+ if (!ldisc->telnet_keyboard)
goto default_case;
if (c == CTRL('C'))
ldisc->back->special(ldisc->backhandle, TS_IP);
@@ -255,7 +267,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
* default clause because of the break.
*/
case CTRL('J'):
- if (ldisc->cfg->protocol == PROT_RAW &&
+ if (ldisc->protocol == PROT_RAW &&
ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {
if (ECHOING)
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
@@ -264,9 +276,9 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
case KCTRL('M'): /* send with newline */
if (ldisc->buflen > 0)
ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
- if (ldisc->cfg->protocol == PROT_RAW)
+ if (ldisc->protocol == PROT_RAW)
ldisc->back->send(ldisc->backhandle, "\r\n", 2);
- else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)
+ else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
ldisc->back->special(ldisc->backhandle, TS_EOL);
else
ldisc->back->send(ldisc->backhandle, "\r", 1);
@@ -300,27 +312,27 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
if (len > 0) {
if (ECHOING)
c_write(ldisc, buf, len);
- if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) {
+ if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) {
switch (buf[0]) {
case CTRL('M'):
- if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)
+ if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
ldisc->back->special(ldisc->backhandle, TS_EOL);
else
ldisc->back->send(ldisc->backhandle, "\r", 1);
break;
case CTRL('?'):
case CTRL('H'):
- if (ldisc->cfg->telnet_keyboard) {
+ if (ldisc->telnet_keyboard) {
ldisc->back->special(ldisc->backhandle, TS_EC);
break;
}
case CTRL('C'):
- if (ldisc->cfg->telnet_keyboard) {
+ if (ldisc->telnet_keyboard) {
ldisc->back->special(ldisc->backhandle, TS_IP);
break;
}
case CTRL('Z'):
- if (ldisc->cfg->telnet_keyboard) {
+ if (ldisc->telnet_keyboard) {
ldisc->back->special(ldisc->backhandle, TS_SUSP);
break;
}
diff --git a/tools/plink/ldisc.h b/tools/plink/ldisc.h
index ef84f6d6d..030c0ce8d 100644
--- a/tools/plink/ldisc.h
+++ b/tools/plink/ldisc.h
@@ -11,10 +11,14 @@
typedef struct ldisc_tag {
Terminal *term;
Backend *back;
- Config *cfg;
void *backhandle;
void *frontend;
+ /*
+ * Values cached out of conf.
+ */
+ int telnet_keyboard, telnet_newline, protocol, localecho, localedit;
+
char *buf;
int buflen, bufsiz, quotenext;
} *Ldisc;
diff --git a/tools/plink/logging.c b/tools/plink/logging.c
index 4c7aa918c..a7f76c35a 100644
--- a/tools/plink/logging.c
+++ b/tools/plink/logging.c
@@ -16,12 +16,13 @@ struct LogContext {
FILE *lgfp;
enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state;
bufchain queue;
- Filename currlogfilename;
+ Filename *currlogfilename;
void *frontend;
- Config cfg;
+ Conf *conf;
+ int logtype; /* cached out of conf */
};
-static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm);
+static Filename *xlatlognam(Filename *s, char *hostname, struct tm *tm);
/*
* Internal wrapper function which must be called for _all_ output
@@ -43,7 +44,7 @@ static void logwrite(struct LogContext *ctx, void *data, int len)
bufchain_add(&ctx->queue, data, len);
} else if (ctx->state == L_OPEN) {
assert(ctx->lgfp);
- if (fwrite(data, 1, len, ctx->lgfp) < len) {
+ 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 */
@@ -75,7 +76,7 @@ static void logprintf(struct LogContext *ctx, const char *fmt, ...)
*/
void logflush(void *handle) {
struct LogContext *ctx = (struct LogContext *)handle;
- if (ctx->cfg.logtype > 0)
+ if (ctx->logtype > 0)
if (ctx->state == L_OPEN)
fflush(ctx->lgfp);
}
@@ -110,12 +111,12 @@ static void logfopen_callback(void *handle, int mode)
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" :
- ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" :
+ (ctx->logtype == LGTYP_ASCII ? "ASCII" :
+ ctx->logtype == LGTYP_DEBUG ? "raw" :
+ ctx->logtype == LGTYP_PACKETS ? "SSH packets" :
+ ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" :
"unknown"),
- filename_to_str(&ctx->currlogfilename));
+ filename_to_str(ctx->currlogfilename));
logevent(ctx->frontend, event);
sfree(event);
@@ -148,19 +149,24 @@ void logfopen(void *handle)
if (ctx->state != L_CLOSED)
return;
- if (!ctx->cfg.logtype)
+ if (!ctx->logtype)
return;
tm = ltime();
/* substitute special codes in file name */
- xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm);
+ if (ctx->currlogfilename)
+ filename_free(ctx->currlogfilename);
+ ctx->currlogfilename =
+ xlatlognam(conf_get_filename(ctx->conf, CONF_logfilename),
+ conf_get_str(ctx->conf, CONF_host), &tm);
ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE); /* file already present? */
if (ctx->lgfp) {
+ int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr);
fclose(ctx->lgfp);
- if (ctx->cfg.logxfovr != LGXF_ASK) {
- mode = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1);
+ if (logxfovr != LGXF_ASK) {
+ mode = ((logxfovr == LGXF_OVR) ? 2 : 1);
} else
mode = askappend(ctx->frontend, ctx->currlogfilename,
logfopen_callback, ctx);
@@ -189,8 +195,8 @@ void logfclose(void *handle)
void logtraffic(void *handle, unsigned char c, int logmode)
{
struct LogContext *ctx = (struct LogContext *)handle;
- if (ctx->cfg.logtype > 0) {
- if (ctx->cfg.logtype == logmode)
+ if (ctx->logtype > 0) {
+ if (ctx->logtype == logmode)
logwrite(ctx, &c, 1);
}
}
@@ -214,8 +220,8 @@ void log_eventlog(void *handle, const char *event)
/* If we don't have a context yet (eg winnet.c init) then skip entirely */
if (!ctx)
return;
- if (ctx->cfg.logtype != LGTYP_PACKETS &&
- ctx->cfg.logtype != LGTYP_SSHRAW)
+ if (ctx->logtype != LGTYP_PACKETS &&
+ ctx->logtype != LGTYP_SSHRAW)
return;
logprintf(ctx, "Event Log: %s\r\n", event);
logflush(ctx);
@@ -236,8 +242,8 @@ void log_packet(void *handle, int direction, int type,
int p = 0, b = 0, omitted = 0;
int output_pos = 0; /* NZ if pending output in dumpdata */
- if (!(ctx->cfg.logtype == LGTYP_SSHRAW ||
- (ctx->cfg.logtype == LGTYP_PACKETS && texttype)))
+ if (!(ctx->logtype == LGTYP_SSHRAW ||
+ (ctx->logtype == LGTYP_PACKETS && texttype)))
return;
/* Packet header. */
@@ -326,13 +332,15 @@ void log_packet(void *handle, int direction, int type,
logflush(ctx);
}
-void *log_init(void *frontend, Config *cfg)
+void *log_init(void *frontend, Conf *conf)
{
struct LogContext *ctx = snew(struct LogContext);
ctx->lgfp = NULL;
ctx->state = L_CLOSED;
ctx->frontend = frontend;
- ctx->cfg = *cfg; /* STRUCTURE COPY */
+ ctx->conf = conf_copy(conf);
+ ctx->logtype = conf_get_int(ctx->conf, CONF_logtype);
+ ctx->currlogfilename = NULL;
bufchain_init(&ctx->queue);
return ctx;
}
@@ -343,16 +351,20 @@ void log_free(void *handle)
logfclose(ctx);
bufchain_clear(&ctx->queue);
+ if (ctx->currlogfilename)
+ filename_free(ctx->currlogfilename);
sfree(ctx);
}
-void log_reconfig(void *handle, Config *cfg)
+void log_reconfig(void *handle, Conf *conf)
{
struct LogContext *ctx = (struct LogContext *)handle;
int reset_logging;
- if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) ||
- ctx->cfg.logtype != cfg->logtype)
+ if (!filename_equal(conf_get_filename(ctx->conf, CONF_logfilename),
+ conf_get_filename(conf, CONF_logfilename)) ||
+ conf_get_int(ctx->conf, CONF_logtype) !=
+ conf_get_int(conf, CONF_logtype))
reset_logging = TRUE;
else
reset_logging = FALSE;
@@ -360,7 +372,10 @@ void log_reconfig(void *handle, Config *cfg)
if (reset_logging)
logfclose(ctx);
- ctx->cfg = *cfg; /* STRUCTURE COPY */
+ conf_free(ctx->conf);
+ ctx->conf = conf_copy(conf);
+
+ ctx->logtype = conf_get_int(ctx->conf, CONF_logtype);
if (reset_logging)
logfopen(ctx);
@@ -372,17 +387,19 @@ void log_reconfig(void *handle, Config *cfg)
*
* "&Y":YYYY "&m":MM "&d":DD "&T":hhmmss "&h":<hostname> "&&":&
*/
-static void xlatlognam(Filename *dest, Filename src,
- char *hostname, struct tm *tm) {
+static Filename *xlatlognam(Filename *src, char *hostname, struct tm *tm)
+{
char buf[10], *bufp;
int size;
- char buffer[FILENAME_MAX];
- int len = sizeof(buffer)-1;
- char *d;
+ char *buffer;
+ int buflen, bufsize;
const char *s;
+ Filename *ret;
- d = buffer;
- s = filename_to_str(&src);
+ bufsize = FILENAME_MAX;
+ buffer = snewn(bufsize, char);
+ buflen = 0;
+ s = filename_to_str(src);
while (*s) {
/* Let (bufp, len) be the string to append. */
@@ -418,13 +435,16 @@ static void xlatlognam(Filename *dest, Filename src,
buf[0] = *s++;
size = 1;
}
- if (size > len)
- size = len;
- memcpy(d, bufp, size);
- d += size;
- len -= size;
+ if (bufsize <= buflen + size) {
+ bufsize = (buflen + size) * 5 / 4 + 512;
+ buffer = sresize(buffer, bufsize, char);
+ }
+ memcpy(buffer + buflen, bufp, size);
+ buflen += size;
}
- *d = '\0';
+ buffer[buflen] = '\0';
- *dest = filename_from_str(buffer);
+ ret = filename_from_str(buffer);
+ sfree(buffer);
+ return ret;
}
diff --git a/tools/plink/misc.c b/tools/plink/misc.c
index 4aeab5028..89a21f74e 100644
--- a/tools/plink/misc.c
+++ b/tools/plink/misc.c
@@ -99,24 +99,48 @@ prompts_t *new_prompts(void *frontend)
p->name_reqd = p->instr_reqd = FALSE;
return p;
}
-void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len)
+void add_prompt(prompts_t *p, char *promptstr, int echo)
{
prompt_t *pr = snew(prompt_t);
- char *result = snewn(len, char);
pr->prompt = promptstr;
pr->echo = echo;
- pr->result = result;
- pr->result_len = len;
+ pr->result = NULL;
+ pr->resultsize = 0;
p->n_prompts++;
p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *);
p->prompts[p->n_prompts-1] = pr;
}
+void prompt_ensure_result_size(prompt_t *pr, int newlen)
+{
+ if ((int)pr->resultsize < newlen) {
+ char *newbuf;
+ newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */
+
+ /*
+ * We don't use sresize / realloc here, because we will be
+ * storing sensitive stuff like passwords in here, and we want
+ * to make sure that the data doesn't get copied around in
+ * memory without the old copy being destroyed.
+ */
+ newbuf = snewn(newlen, char);
+ memcpy(newbuf, pr->result, pr->resultsize);
+ memset(pr->result, '\0', pr->resultsize);
+ sfree(pr->result);
+ pr->result = newbuf;
+ pr->resultsize = newlen;
+ }
+}
+void prompt_set_result(prompt_t *pr, const char *newstr)
+{
+ prompt_ensure_result_size(pr, strlen(newstr) + 1);
+ strcpy(pr->result, newstr);
+}
void free_prompts(prompts_t *p)
{
size_t i;
for (i=0; i < p->n_prompts; i++) {
prompt_t *pr = p->prompts[i];
- memset(pr->result, 0, pr->result_len); /* burn the evidence */
+ memset(pr->result, 0, pr->resultsize); /* burn the evidence */
sfree(pr->result);
sfree(pr->prompt);
sfree(pr);
@@ -176,6 +200,14 @@ char *dupcat(const char *s1, ...)
return p;
}
+void burnstr(char *string) /* sfree(str), only clear it first */
+{
+ if (string) {
+ memset(string, 0, strlen(string));
+ sfree(string);
+ }
+}
+
/*
* Do an sprintf(), but into a custom-allocated buffer.
*
@@ -635,21 +667,21 @@ void debug_memdump(void *buf, int len, int L)
#endif /* def DEBUG */
/*
- * Determine whether or not a Config structure represents a session
- * which can sensibly be launched right now.
+ * Determine whether or not a Conf represents a session which can
+ * sensibly be launched right now.
*/
-int cfg_launchable(const Config *cfg)
+int conf_launchable(Conf *conf)
{
- if (cfg->protocol == PROT_SERIAL)
- return cfg->serline[0] != 0;
+ if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
+ return conf_get_str(conf, CONF_serline)[0] != 0;
else
- return cfg->host[0] != 0;
+ return conf_get_str(conf, CONF_host)[0] != 0;
}
-char const *cfg_dest(const Config *cfg)
+char const *conf_dest(Conf *conf)
{
- if (cfg->protocol == PROT_SERIAL)
- return cfg->serline;
+ if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
+ return conf_get_str(conf, CONF_serline);
else
- return cfg->host;
+ return conf_get_str(conf, CONF_host);
}
diff --git a/tools/plink/misc.h b/tools/plink/misc.h
index 11233147a..0ecb34445 100644
--- a/tools/plink/misc.h
+++ b/tools/plink/misc.h
@@ -28,6 +28,7 @@ char *dupstr(const char *s);
char *dupcat(const char *s1, ...);
char *dupprintf(const char *fmt, ...);
char *dupvprintf(const char *fmt, va_list ap);
+void burnstr(char *string);
char *fgetline(FILE *fp);
diff --git a/tools/plink/network.h b/tools/plink/network.h
index b1b559047..eee5452ed 100644
--- a/tools/plink/network.h
+++ b/tools/plink/network.h
@@ -15,7 +15,7 @@
#ifndef DONE_TYPEDEFS
#define DONE_TYPEDEFS
-typedef struct config_tag Config;
+typedef struct conf_tag Conf;
typedef struct backend_tag Backend;
typedef struct terminal_tag Terminal;
#endif
@@ -37,6 +37,7 @@ struct socket_function_table {
void (*close) (Socket s);
int (*write) (Socket s, const char *data, int len);
int (*write_oob) (Socket s, const char *data, int len);
+ void (*write_eof) (Socket s);
void (*flush) (Socket s);
void (*set_private_ptr) (Socket s, void *ptr);
void *(*get_private_ptr) (Socket s);
@@ -94,18 +95,18 @@ struct plug_function_table {
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, int keepalive,
- Plug plug, const Config *cfg);
+ Plug plug, Conf *conf);
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
- const Config *cfg, int addressfamily);
+ Conf *conf, int addressfamily);
SockAddr name_lookup(char *host, int port, char **canonicalname,
- const Config *cfg, int addressfamily);
+ Conf *conf, int addressfamily);
/* platform-dependent callback from new_connection() */
/* (same caveat about addr as new_connection()) */
Socket platform_new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, int keepalive,
- Plug plug, const Config *cfg);
+ Plug plug, Conf *conf);
/* socket functions */
@@ -140,6 +141,7 @@ Socket sk_register(OSSocket sock, Plug plug);
#define sk_close(s) (((*s)->close) (s))
#define sk_write(s,buf,len) (((*s)->write) (s, buf, len))
#define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len))
+#define sk_write_eof(s) (((*s)->write_eof) (s))
#define sk_flush(s) (((*s)->flush) (s))
#ifdef DEFINE_PLUG_METHOD_MACROS
diff --git a/tools/plink/pinger.c b/tools/plink/pinger.c
index b6fde2456..00cd6675e 100644
--- a/tools/plink/pinger.c
+++ b/tools/plink/pinger.c
@@ -43,11 +43,11 @@ static void pinger_schedule(Pinger pinger)
}
}
-Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)
+Pinger pinger_new(Conf *conf, Backend *back, void *backhandle)
{
Pinger pinger = snew(struct pinger_tag);
- pinger->interval = cfg->ping_interval;
+ pinger->interval = conf_get_int(conf, CONF_ping_interval);
pinger->pending = FALSE;
pinger->back = back;
pinger->backhandle = backhandle;
@@ -56,10 +56,11 @@ Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)
return pinger;
}
-void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg)
+void pinger_reconfig(Pinger pinger, Conf *oldconf, Conf *newconf)
{
- if (oldcfg->ping_interval != newcfg->ping_interval) {
- pinger->interval = newcfg->ping_interval;
+ int newinterval = conf_get_int(newconf, CONF_ping_interval);
+ if (conf_get_int(oldconf, CONF_ping_interval) != newinterval) {
+ pinger->interval = newinterval;
pinger_schedule(pinger);
}
}
diff --git a/tools/plink/portfwd.c b/tools/plink/portfwd.c
index e5874a697..545dfecd4 100644
--- a/tools/plink/portfwd.c
+++ b/tools/plink/portfwd.c
@@ -61,14 +61,20 @@ static int pfd_closing(Plug plug, const char *error_msg, int error_code,
{
struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
- /*
- * We have no way to communicate down the forwarded connection,
- * so if an error occurred on the socket, we just ignore it
- * and treat it like a proper close.
- */
- if (pr->c)
- sshfwd_close(pr->c);
- pfd_close(pr->s);
+ if (error_msg) {
+ /*
+ * Socket error. Slam the connection instantly shut.
+ */
+ sshfwd_unclean_close(pr->c);
+ } else {
+ /*
+ * Ordinary EOF received on socket. Send an EOF on the SSH
+ * channel.
+ */
+ if (pr->c)
+ sshfwd_write_eof(pr->c);
+ }
+
return 1;
}
@@ -325,7 +331,7 @@ static void pfd_sent(Plug plug, int bufsize)
* Called when receiving a PORT OPEN from the server
*/
const char *pfd_newconnect(Socket *s, char *hostname, int port,
- void *c, const Config *cfg, int addressfamily)
+ void *c, Conf *conf, int addressfamily)
{
static const struct plug_function_table fn_table = {
pfd_log,
@@ -343,7 +349,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
/*
* Try to find host.
*/
- addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily);
+ addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
@@ -362,7 +368,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
pr->dynamic = 0;
pr->s = *s = new_connection(addr, dummy_realhost, port,
- 0, 1, 0, 0, (Plug) pr, cfg);
+ 0, 1, 0, 0, (Plug) pr, conf);
if ((err = sk_socket_error(*s)) != NULL) {
sfree(pr);
return err;
@@ -435,7 +441,7 @@ static int pfd_accepting(Plug p, OSSocket sock)
sets up a listener on the local machine on (srcaddr:)port
*/
const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
- int port, void *backhandle, const Config *cfg,
+ int port, void *backhandle, Conf *conf,
void **sockdata, int address_family)
{
static const struct plug_function_table fn_table = {
@@ -468,7 +474,8 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
pr->backhandle = backhandle;
pr->s = s = new_listener(srcaddr, port, (Plug) pr,
- !cfg->lport_acceptall, cfg, address_family);
+ !conf_get_int(conf, CONF_lport_acceptall),
+ conf, address_family);
if ((err = sk_socket_error(s)) != NULL) {
sfree(pr);
return err;
@@ -536,6 +543,10 @@ int pfd_send(Socket s, char *data, int len)
return sk_write(s, data, len);
}
+void pfd_send_eof(Socket s)
+{
+ sk_write_eof(s);
+}
void pfd_confirm(Socket s)
{
diff --git a/tools/plink/proxy.c b/tools/plink/proxy.c
index 1f4299951..fcc81475b 100644
--- a/tools/plink/proxy.c
+++ b/tools/plink/proxy.c
@@ -14,9 +14,10 @@
#include "network.h"
#include "proxy.h"
-#define do_proxy_dns(cfg) \
- (cfg->proxy_dns == FORCE_ON || \
- (cfg->proxy_dns == AUTO && cfg->proxy_type != PROXY_SOCKS4))
+#define do_proxy_dns(conf) \
+ (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \
+ (conf_get_int(conf, CONF_proxy_dns) == AUTO && \
+ conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4))
/*
* Call this when proxy negotiation is complete, so that this
@@ -64,6 +65,9 @@ void proxy_activate (Proxy_Socket p)
*/
if (p->pending_flush) sk_flush(p->sub_socket);
+ /* if we have a pending EOF to send, send it */
+ if (p->pending_eof) sk_write_eof(p->sub_socket);
+
/* if the backend wanted the socket unfrozen, try to unfreeze.
* our set_frozen handler will flush buffered receive data before
* unfreezing the actual underlying socket.
@@ -116,6 +120,17 @@ static int sk_proxy_write_oob (Socket s, const char *data, int len)
return sk_write_oob(ps->sub_socket, data, len);
}
+static void sk_proxy_write_eof (Socket s)
+{
+ Proxy_Socket ps = (Proxy_Socket) s;
+
+ if (ps->state != PROXY_STATE_ACTIVE) {
+ ps->pending_eof = 1;
+ return;
+ }
+ sk_write_eof(ps->sub_socket);
+}
+
static void sk_proxy_flush (Socket s)
{
Proxy_Socket ps = (Proxy_Socket) s;
@@ -263,7 +278,7 @@ static int plug_proxy_accepting (Plug p, OSSocket sock)
* it will only check the host name.
*/
static int proxy_for_destination (SockAddr addr, char *hostname, int port,
- const Config *cfg)
+ Conf *conf)
{
int s = 0, e = 0;
char hostip[64];
@@ -274,7 +289,7 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port,
* Check the host name and IP against the hard-coded
* representations of `localhost'.
*/
- if (!cfg->even_proxy_localhost &&
+ if (!conf_get_int(conf, CONF_even_proxy_localhost) &&
(sk_hostname_is_local(hostname) ||
(addr && sk_address_is_local(addr))))
return 0; /* do not proxy */
@@ -288,7 +303,7 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port,
hostname_len = strlen(hostname);
- exclude_list = cfg->proxy_exclude_list;
+ exclude_list = conf_get_str(conf, CONF_proxy_exclude_list);
/* now parse the exclude list, and see if either our IP
* or hostname matches anything in it.
@@ -349,11 +364,11 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port,
}
SockAddr name_lookup(char *host, int port, char **canonicalname,
- const Config *cfg, int addressfamily)
+ Conf *conf, int addressfamily)
{
- if (cfg->proxy_type != PROXY_NONE &&
- do_proxy_dns(cfg) &&
- proxy_for_destination(NULL, host, port, cfg)) {
+ if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
+ do_proxy_dns(conf) &&
+ proxy_for_destination(NULL, host, port, conf)) {
*canonicalname = dupstr(host);
return sk_nonamelookup(host);
}
@@ -364,13 +379,14 @@ SockAddr name_lookup(char *host, int port, char **canonicalname,
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, int keepalive,
- Plug plug, const Config *cfg)
+ Plug plug, Conf *conf)
{
static const struct socket_function_table socket_fn_table = {
sk_proxy_plug,
sk_proxy_close,
sk_proxy_write,
sk_proxy_write_oob,
+ sk_proxy_write_eof,
sk_proxy_flush,
sk_proxy_set_private_ptr,
sk_proxy_get_private_ptr,
@@ -386,30 +402,32 @@ Socket new_connection(SockAddr addr, char *hostname,
plug_proxy_accepting
};
- if (cfg->proxy_type != PROXY_NONE &&
- proxy_for_destination(addr, hostname, port, cfg))
+ if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
+ proxy_for_destination(addr, hostname, port, conf))
{
Proxy_Socket ret;
Proxy_Plug pplug;
SockAddr proxy_addr;
char *proxy_canonical_name;
Socket sret;
+ int type;
if ((sret = platform_new_connection(addr, hostname, port, privport,
oobinline, nodelay, keepalive,
- plug, cfg)) !=
+ plug, conf)) !=
NULL)
return sret;
ret = snew(struct Socket_proxy_tag);
ret->fn = &socket_fn_table;
- ret->cfg = *cfg; /* STRUCTURE COPY */
+ ret->conf = conf_copy(conf);
ret->plug = plug;
ret->remote_addr = addr; /* will need to be freed on close */
ret->remote_port = port;
ret->error = NULL;
ret->pending_flush = 0;
+ ret->pending_eof = 0;
ret->freeze = 0;
bufchain_init(&ret->pending_input_data);
@@ -419,14 +437,15 @@ Socket new_connection(SockAddr addr, char *hostname,
ret->sub_socket = NULL;
ret->state = PROXY_STATE_NEW;
ret->negotiate = NULL;
-
- if (cfg->proxy_type == PROXY_HTTP) {
+
+ type = conf_get_int(conf, CONF_proxy_type);
+ if (type == PROXY_HTTP) {
ret->negotiate = proxy_http_negotiate;
- } else if (cfg->proxy_type == PROXY_SOCKS4) {
+ } else if (type == PROXY_SOCKS4) {
ret->negotiate = proxy_socks4_negotiate;
- } else if (cfg->proxy_type == PROXY_SOCKS5) {
+ } else if (type == PROXY_SOCKS5) {
ret->negotiate = proxy_socks5_negotiate;
- } else if (cfg->proxy_type == PROXY_TELNET) {
+ } else if (type == PROXY_TELNET) {
ret->negotiate = proxy_telnet_negotiate;
} else {
ret->error = "Proxy error: Unknown proxy method";
@@ -440,8 +459,9 @@ Socket new_connection(SockAddr addr, char *hostname,
pplug->proxy_socket = ret;
/* look-up proxy */
- proxy_addr = sk_namelookup(cfg->proxy_host,
- &proxy_canonical_name, cfg->addressfamily);
+ proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),
+ &proxy_canonical_name,
+ conf_get_int(conf, CONF_addressfamily));
if (sk_addr_error(proxy_addr) != NULL) {
ret->error = "Proxy error: Unable to resolve proxy host name";
return (Socket)ret;
@@ -451,7 +471,8 @@ Socket new_connection(SockAddr addr, char *hostname,
/* create the actual socket we will be using,
* connected to our proxy server and port.
*/
- ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port,
+ ret->sub_socket = sk_new(proxy_addr,
+ conf_get_int(conf, CONF_proxy_port),
privport, oobinline,
nodelay, keepalive, (Plug) pplug);
if (sk_socket_error(ret->sub_socket) != NULL)
@@ -469,7 +490,7 @@ Socket new_connection(SockAddr addr, char *hostname,
}
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
- const Config *cfg, int addressfamily)
+ Conf *conf, int addressfamily)
{
/* TODO: SOCKS (and potentially others) support inbound
* TODO: connections via the proxy. support them.
@@ -525,6 +546,7 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
* request
*/
char *buf, dest[512];
+ char *username, *password;
sk_getaddr(p->remote_addr, dest, lenof(dest));
@@ -533,18 +555,22 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
sk_write(p->sub_socket, buf, strlen(buf));
sfree(buf);
- if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
- char buf[sizeof(p->cfg.proxy_username)+sizeof(p->cfg.proxy_password)];
- char buf2[sizeof(buf)*4/3 + 100];
+ username = conf_get_str(p->conf, CONF_proxy_username);
+ password = conf_get_str(p->conf, CONF_proxy_password);
+ if (username[0] || password[0]) {
+ char *buf, *buf2;
int i, j, len;
- sprintf(buf, "%s:%s", p->cfg.proxy_username, p->cfg.proxy_password);
+ buf = dupprintf("%s:%s", username, password);
len = strlen(buf);
+ buf2 = snewn(len * 4 / 3 + 100, char);
sprintf(buf2, "Proxy-Authorization: Basic ");
for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)
base64_encode_atom((unsigned char *)(buf+i),
(len-i > 3 ? 3 : len-i), buf2+j);
strcpy(buf2+j, "\r\n");
sk_write(p->sub_socket, buf2, strlen(buf2));
+ sfree(buf);
+ sfree(buf2);
}
sk_write(p->sub_socket, "\r\n", 2);
@@ -711,6 +737,7 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
int length, type, namelen;
char *command, addr[4], hostname[512];
+ char *username;
type = sk_addrtype(p->remote_addr);
if (type == ADDRTYPE_IPV6) {
@@ -728,9 +755,10 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
addr[3] = 1;
}
- length = strlen(p->cfg.proxy_username) + namelen + 9;
+ username = conf_get_str(p->conf, CONF_proxy_username);
+ length = strlen(username) + namelen + 9;
command = snewn(length, char);
- strcpy(command + 8, p->cfg.proxy_username);
+ strcpy(command + 8, username);
command[0] = 4; /* version 4 */
command[1] = 1; /* CONNECT command */
@@ -743,10 +771,11 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
memcpy(command + 4, addr, 4);
/* hostname */
- memcpy(command + 8 + strlen(p->cfg.proxy_username) + 1,
+ memcpy(command + 8 + strlen(username) + 1,
hostname, namelen);
sk_write(p->sub_socket, command, length);
+ sfree(username);
sfree(command);
p->state = 1;
@@ -868,10 +897,13 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
*/
char command[5];
+ char *username, *password;
int len;
command[0] = 5; /* version 5 */
- if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
+ username = conf_get_str(p->conf, CONF_proxy_username);
+ password = conf_get_str(p->conf, CONF_proxy_password);
+ if (username[0] || password[0]) {
command[2] = 0x00; /* no authentication */
len = 3;
proxy_socks5_offerencryptedauth (command, &len);
@@ -1148,18 +1180,20 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
}
if (p->state == 5) {
- if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
- char userpwbuf[514];
+ char *username = conf_get_str(p->conf, CONF_proxy_username);
+ char *password = conf_get_str(p->conf, CONF_proxy_password);
+ if (username[0] || password[0]) {
+ char userpwbuf[255 + 255 + 3];
int ulen, plen;
- ulen = strlen(p->cfg.proxy_username);
+ ulen = strlen(username);
if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;
- plen = strlen(p->cfg.proxy_password);
+ plen = strlen(password);
if (plen > 255) plen = 255; if (plen < 1) plen = 1;
userpwbuf[0] = 1; /* version number of subnegotiation */
userpwbuf[1] = ulen;
- memcpy(userpwbuf+2, p->cfg.proxy_username, ulen);
+ memcpy(userpwbuf+2, username, ulen);
userpwbuf[ulen+2] = plen;
- memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen);
+ memcpy(userpwbuf+ulen+3, password, plen);
sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);
p->state = 7;
} else
@@ -1192,8 +1226,9 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* standardised or at all well-defined.)
*/
-char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
+char *format_telnet_command(SockAddr addr, int port, Conf *conf)
{
+ char *fmt = conf_get_str(conf, CONF_proxy_telnet_command);
char *ret = NULL;
int retlen = 0, retsize = 0;
int so = 0, eo = 0;
@@ -1208,22 +1243,21 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
* %%, %host, %port, %user, and %pass
*/
- while (cfg->proxy_telnet_command[eo] != 0) {
+ while (fmt[eo] != 0) {
/* scan forward until we hit end-of-line,
* or an escape character (\ or %) */
- while (cfg->proxy_telnet_command[eo] != 0 &&
- cfg->proxy_telnet_command[eo] != '%' &&
- cfg->proxy_telnet_command[eo] != '\\') eo++;
+ while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\')
+ eo++;
/* if we hit eol, break out of our escaping loop */
- if (cfg->proxy_telnet_command[eo] == 0) break;
+ if (fmt[eo] == 0) break;
/* if there was any unescaped text before the escape
* character, send that now */
if (eo != so) {
ENSURE(eo - so);
- memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);
+ memcpy(ret + retlen, fmt + so, eo - so);
retlen += eo - so;
}
@@ -1231,15 +1265,15 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
/* if the escape character was the last character of
* the line, we'll just stop and send it. */
- if (cfg->proxy_telnet_command[eo] == 0) break;
+ if (fmt[eo] == 0) break;
- if (cfg->proxy_telnet_command[so] == '\\') {
+ if (fmt[so] == '\\') {
/* we recognize \\, \%, \r, \n, \t, \x??.
* anything else, we just send unescaped (including the \).
*/
- switch (cfg->proxy_telnet_command[eo]) {
+ switch (fmt[eo]) {
case '\\':
ENSURE(1);
@@ -1280,15 +1314,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
for (;;) {
eo++;
- if (cfg->proxy_telnet_command[eo] >= '0' &&
- cfg->proxy_telnet_command[eo] <= '9')
- v += cfg->proxy_telnet_command[eo] - '0';
- else if (cfg->proxy_telnet_command[eo] >= 'a' &&
- cfg->proxy_telnet_command[eo] <= 'f')
- v += cfg->proxy_telnet_command[eo] - 'a' + 10;
- else if (cfg->proxy_telnet_command[eo] >= 'A' &&
- cfg->proxy_telnet_command[eo] <= 'F')
- v += cfg->proxy_telnet_command[eo] - 'A' + 10;
+ if (fmt[eo] >= '0' && fmt[eo] <= '9')
+ v += fmt[eo] - '0';
+ else if (fmt[eo] >= 'a' && fmt[eo] <= 'f')
+ v += fmt[eo] - 'a' + 10;
+ else if (fmt[eo] >= 'A' && fmt[eo] <= 'F')
+ v += fmt[eo] - 'A' + 10;
else {
/* non hex character, so we abort and just
* send the whole thing unescaped (including \x)
@@ -1315,7 +1346,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
default:
ENSURE(2);
- memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2);
+ memcpy(ret+retlen, fmt + so, 2);
retlen += 2;
eo++;
break;
@@ -1327,13 +1358,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
* unescaped (including the %).
*/
- if (cfg->proxy_telnet_command[eo] == '%') {
+ if (fmt[eo] == '%') {
ENSURE(1);
ret[retlen++] = '%';
eo++;
}
- else if (strnicmp(cfg->proxy_telnet_command + eo,
- "host", 4) == 0) {
+ else if (strnicmp(fmt + eo, "host", 4) == 0) {
char dest[512];
int destlen;
sk_getaddr(addr, dest, lenof(dest));
@@ -1343,8 +1373,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
retlen += destlen;
eo += 4;
}
- else if (strnicmp(cfg->proxy_telnet_command + eo,
- "port", 4) == 0) {
+ else if (strnicmp(fmt + eo, "port", 4) == 0) {
char portstr[8], portlen;
portlen = sprintf(portstr, "%i", port);
ENSURE(portlen);
@@ -1352,35 +1381,35 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
retlen += portlen;
eo += 4;
}
- else if (strnicmp(cfg->proxy_telnet_command + eo,
- "user", 4) == 0) {
- int userlen = strlen(cfg->proxy_username);
+ else if (strnicmp(fmt + eo, "user", 4) == 0) {
+ char *username = conf_get_str(conf, CONF_proxy_username);
+ int userlen = strlen(username);
ENSURE(userlen);
- memcpy(ret+retlen, cfg->proxy_username, userlen);
+ memcpy(ret+retlen, username, userlen);
retlen += userlen;
eo += 4;
}
- else if (strnicmp(cfg->proxy_telnet_command + eo,
- "pass", 4) == 0) {
- int passlen = strlen(cfg->proxy_password);
+ else if (strnicmp(fmt + eo, "pass", 4) == 0) {
+ char *password = conf_get_str(conf, CONF_proxy_password);
+ int passlen = strlen(password);
ENSURE(passlen);
- memcpy(ret+retlen, cfg->proxy_password, passlen);
+ memcpy(ret+retlen, password, passlen);
retlen += passlen;
eo += 4;
}
- else if (strnicmp(cfg->proxy_telnet_command + eo,
- "proxyhost", 9) == 0) {
- int phlen = strlen(cfg->proxy_host);
+ else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) {
+ char *host = conf_get_str(conf, CONF_proxy_host);
+ int phlen = strlen(host);
ENSURE(phlen);
- memcpy(ret+retlen, cfg->proxy_host, phlen);
+ memcpy(ret+retlen, host, phlen);
retlen += phlen;
eo += 9;
}
- else if (strnicmp(cfg->proxy_telnet_command + eo,
- "proxyport", 9) == 0) {
+ else if (strnicmp(fmt + eo, "proxyport", 9) == 0) {
+ int port = conf_get_int(conf, CONF_proxy_port);
char pport[50];
int pplen;
- sprintf(pport, "%d", cfg->proxy_port);
+ sprintf(pport, "%d", port);
pplen = strlen(pport);
ENSURE(pplen);
memcpy(ret+retlen, pport, pplen);
@@ -1404,7 +1433,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
/* if there is any unescaped text at the end of the line, send it */
if (eo != so) {
ENSURE(eo - so);
- memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);
+ memcpy(ret + retlen, fmt + so, eo - so);
retlen += eo - so;
}
@@ -1421,7 +1450,7 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change)
char *formatted_cmd;
formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,
- &p->cfg);
+ p->conf);
sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));
sfree(formatted_cmd);
diff --git a/tools/plink/proxy.h b/tools/plink/proxy.h
index 683b2603d..9e64aadd0 100644
--- a/tools/plink/proxy.h
+++ b/tools/plink/proxy.h
@@ -30,6 +30,7 @@ struct Socket_proxy_tag {
bufchain pending_oob_output_data;
int pending_flush;
bufchain pending_input_data;
+ int pending_eof;
#define PROXY_STATE_NEW -1
#define PROXY_STATE_ACTIVE 0
@@ -80,7 +81,7 @@ struct Socket_proxy_tag {
OSSocket accepting_sock;
/* configuration, used to look up proxy settings */
- Config cfg;
+ Conf *conf;
/* CHAP transient data */
int chap_num_attributes;
@@ -110,7 +111,7 @@ extern int proxy_socks5_negotiate (Proxy_Socket, int);
* This may be reused by local-command proxies on individual
* platforms.
*/
-char *format_telnet_command(SockAddr addr, int port, const Config *cfg);
+char *format_telnet_command(SockAddr addr, int port, Conf *conf);
/*
* These are implemented in cproxy.c or nocproxy.c, depending on
diff --git a/tools/plink/putty.h b/tools/plink/putty.h
index c72d8eb76..33a9b16d1 100644
--- a/tools/plink/putty.h
+++ b/tools/plink/putty.h
@@ -18,7 +18,7 @@
#ifndef DONE_TYPEDEFS
#define DONE_TYPEDEFS
-typedef struct config_tag Config;
+typedef struct conf_tag Conf;
typedef struct backend_tag Backend;
typedef struct terminal_tag Terminal;
#endif
@@ -304,7 +304,7 @@ enum {
};
enum {
- /* Protocol back ends. (cfg.protocol) */
+ /* Protocol back ends. (CONF_protocol) */
PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH,
/* PROT_SERIAL is supported on a subset of platforms, but it doesn't
* hurt to define it globally. */
@@ -312,22 +312,22 @@ enum {
};
enum {
- /* Bell settings (cfg.beep) */
+ /* Bell settings (CONF_beep) */
BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER
};
enum {
- /* Taskbar flashing indication on bell (cfg.beep_ind) */
+ /* Taskbar flashing indication on bell (CONF_beep_ind) */
B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY
};
enum {
- /* Resize actions (cfg.resize_action) */
+ /* Resize actions (CONF_resize_action) */
RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER
};
enum {
- /* Function key types (cfg.funky_type) */
+ /* Function key types (CONF_funky_type) */
FUNKY_TILDE,
FUNKY_LINUX,
FUNKY_XTERM,
@@ -353,12 +353,52 @@ enum {
* Defined here so that backends can export their GSS library tables
* to the cross-platform settings code.
*/
-struct keyval { char *s; int v; };
+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 keyval gsslibkeywords[]; /* for storing by settings.c */
+extern const char *const gsslibnames[]; /* for displaying in configuration */
+extern const struct keyvalwhere gsslibkeywords[]; /* for settings.c */
#endif
extern const char *const ttymodes[];
@@ -375,12 +415,11 @@ enum {
struct backend_tag {
const char *(*init) (void *frontend_handle, void **backend_handle,
- Config *cfg,
- char *host, int port, char **realhost, int nodelay,
- int keepalive);
+ Conf *conf, char *host, int port, char **realhost,
+ int nodelay, int keepalive);
void (*free) (void *handle);
/* back->reconfig() passes in a replacement configuration. */
- void (*reconfig) (void *handle, Config *cfg);
+ void (*reconfig) (void *handle, Conf *conf);
/* back->send() returns the current amount of buffered data. */
int (*send) (void *handle, char *buf, int len);
/* back->sendbuffer() does the same thing but without attempting a send */
@@ -422,214 +461,6 @@ extern const int be_default_protocol;
extern const char *const appname;
/*
- * IMPORTANT POLICY POINT: everything in this structure which wants
- * to be treated like an integer must be an actual, honest-to-
- * goodness `int'. No enum-typed variables. This is because parts
- * of the code will want to pass around `int *' pointers to them
- * and we can't run the risk of porting to some system on which the
- * enum comes out as a different size from int.
- */
-struct config_tag {
- /* Basic options */
- char host[512];
- int port;
- int protocol;
- int addressfamily;
- int close_on_exit;
- int warn_on_close;
- 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;
- int even_proxy_localhost;
- int proxy_type;
- char proxy_host[512];
- int proxy_port;
- char proxy_username[128];
- char proxy_password[128];
- char proxy_telnet_command[512];
- /* SSH options */
- char remote_cmd[512];
- char *remote_cmd_ptr; /* might point to a larger command
- * but never for loading/saving */
- char *remote_cmd_ptr2; /* might point to a larger command
- * but never for loading/saving */
- int nopty;
- int compression;
- int ssh_kexlist[KEX_MAX];
- int ssh_rekey_time; /* in minutes */
- char ssh_rekey_data[16];
- int tryagent;
- int agentfwd;
- int change_username; /* allow username switching in SSH-2 */
- int ssh_cipherlist[CIPHER_MAX];
- Filename keyfile;
- 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 */
- char ssh_nc_host[512]; /* host to connect to in `nc' mode */
- int ssh_nc_port; /* port to connect to in `nc' mode */
- /* Telnet options */
- char termtype[32];
- char termspeed[32];
- 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;
- /* Serial port options */
- char serline[256];
- int serspeed;
- int serdatabits, serstopbits;
- int serparity;
- int serflow;
- /* Keyboard options */
- int bksp_is_delete;
- int rxvt_homeend;
- int funky_type;
- int no_applic_c; /* totally disable app cursor keys */
- int no_applic_k; /* totally disable app keypad */
- int no_mouse_rep; /* totally disable mouse reporting */
- int no_remote_resize; /* disable remote resizing */
- int no_alt_screen; /* disable alternate screen */
- int no_remote_wintitle; /* disable remote retitling */
- int no_dbackspace; /* disable destructive backspace */
- int no_remote_charset; /* disable remote charset config */
- int remote_qtitle_action; /* remote win title query action */
- int app_cursor;
- int app_keypad;
- int nethack_keypad;
- int telnet_keyboard;
- int telnet_newline;
- int alt_f4; /* is it special? */
- int alt_space; /* is it special? */
- int alt_only; /* is it special? */
- int localecho;
- int localedit;
- int alwaysontop;
- int fullscreenonaltenter;
- int scroll_on_key;
- int scroll_on_disp;
- int erase_to_scrollback;
- int compose_key;
- int ctrlaltkeys;
- char wintitle[256]; /* initial window title */
- /* Terminal options */
- int savelines;
- int dec_om;
- int wrap_mode;
- int lfhascr;
- int cursor_type; /* 0=block 1=underline 2=vertical */
- int blink_cur;
- int beep;
- int beep_ind;
- int bellovl; /* bell overload protection active? */
- int bellovl_n; /* number of bells to cause overload */
- int bellovl_t; /* time interval for overload (seconds) */
- int bellovl_s; /* period of silence to re-enable bell (s) */
- Filename bell_wavefile;
- int scrollbar;
- int scrollbar_in_fullscreen;
- int resize_action;
- int bce;
- int blinktext;
- int win_name_always;
- int width, height;
- FontSpec font;
- int font_quality;
- Filename logfilename;
- int logtype;
- int logxfovr;
- int logflush;
- int logomitpass;
- int logomitdata;
- int hide_mouseptr;
- int sunken_edge;
- int window_border;
- char answerback[256];
- char printer[128];
- int arabicshaping;
- int bidi;
- /* Colour options */
- int ansi_colour;
- int xterm_256_colour;
- int system_colour;
- int try_palette;
- int bold_colour;
- unsigned char colours[22][3];
- /* Selection options */
- int mouse_is_xterm;
- int rect_select;
- int rawcnp;
- int rtf_paste;
- int mouse_override;
- short wordness[256];
- /* translations */
- int vtmode;
- char line_codepage[128];
- int cjk_ambig_wide;
- int utf8_override;
- int xlat_capslockcyr;
- /* X11 forwarding */
- 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) */
- /*
- * The port forwarding string contains a number of
- * NUL-terminated substrings, terminated in turn by an empty
- * string (i.e. a second NUL immediately after the previous
- * one). Each string can be of one of the following forms:
- *
- * [LR]localport\thost:port
- * [LR]localaddr:localport\thost:port
- * Dlocalport
- * Dlocaladdr:localport
- */
- char portfwd[1024];
- /* SSH bug compatibility modes */
- int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
- sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
- 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;
- int scrollbar_on_left;
- int shadowbold;
- FontSpec boldfont;
- FontSpec widefont;
- FontSpec wideboldfont;
- int shadowboldoffset;
- int crhaslf;
- char winclass[256];
-};
-
-/*
* Some global flags denoting the type of application.
*
* FLAG_VERBOSE is set when the user requests verbose details.
@@ -694,8 +525,19 @@ struct RSAKey; /* be a little careful of scope */
typedef struct {
char *prompt;
int echo;
- char *result; /* allocated/freed by caller */
- size_t result_len;
+ /*
+ * 'result' must be a dynamically allocated array of exactly
+ * 'resultsize' chars. The code for actually reading input may
+ * realloc it bigger (and adjust resultsize accordingly) if it has
+ * to. The caller should free it again when finished with it.
+ *
+ * If resultsize==0, then result may be NULL. When setting up a
+ * prompt_t, it's therefore easiest to initialise them this way,
+ * which means all actual allocation is done by the callee. This
+ * is what add_prompt does.
+ */
+ char *result;
+ size_t resultsize;
} prompt_t;
typedef struct {
/*
@@ -718,7 +560,9 @@ typedef struct {
* get_userpass_input(); initially NULL */
} prompts_t;
prompts_t *new_prompts(void *frontend);
-void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len);
+void add_prompt(prompts_t *p, char *promptstr, int echo);
+void prompt_set_result(prompt_t *pr, const char *newstr);
+void prompt_ensure_result_size(prompt_t *pr, int len);
/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
void free_prompts(prompts_t *p);
@@ -764,6 +608,11 @@ void ldisc_update(void *frontend, int echo, int edit);
void update_specials_menu(void *frontend);
int from_backend(void *frontend, int is_stderr, const char *data, int len);
int from_backend_untrusted(void *frontend, const char *data, int len);
+/* Called when the back end wants to indicate that EOF has arrived on
+ * the server-to-client stream. Returns FALSE to indicate that we
+ * intend to keep the session open in the other direction, or TRUE to
+ * indicate that if they're closing so are we. */
+int from_backend_eof(void *frontend);
void notify_remote_exit(void *frontend);
/* Get a sensible value for a tty mode. NULL return = don't set.
* Otherwise, returned value should be freed by caller. */
@@ -799,6 +648,269 @@ void set_busy_status(void *frontend, int status);
void cleanup_exit(int);
/*
+ * Exports from conf.c, and a big enum (via parametric macro) of
+ * configuration option keys.
+ */
+#define CONFIG_OPTIONS(X) \
+ /* X(value-type, subkey-type, keyword) */ \
+ X(STR, NONE, host) \
+ X(INT, NONE, port) \
+ X(INT, NONE, protocol) \
+ X(INT, NONE, addressfamily) \
+ X(INT, NONE, close_on_exit) \
+ X(INT, NONE, warn_on_close) \
+ X(INT, NONE, ping_interval) /* in seconds */ \
+ X(INT, NONE, tcp_nodelay) \
+ X(INT, NONE, tcp_keepalives) \
+ X(STR, NONE, loghost) /* logical host being contacted, for host key check */ \
+ /* Proxy options */ \
+ X(STR, NONE, proxy_exclude_list) \
+ X(INT, NONE, proxy_dns) \
+ X(INT, NONE, even_proxy_localhost) \
+ X(INT, NONE, proxy_type) \
+ X(STR, NONE, proxy_host) \
+ X(INT, NONE, proxy_port) \
+ X(STR, NONE, proxy_username) \
+ X(STR, NONE, proxy_password) \
+ X(STR, NONE, proxy_telnet_command) \
+ /* SSH options */ \
+ X(STR, NONE, remote_cmd) \
+ X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \
+ X(INT, NONE, nopty) \
+ X(INT, NONE, compression) \
+ X(INT, INT, ssh_kexlist) \
+ X(INT, NONE, ssh_rekey_time) /* in minutes */ \
+ X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \
+ X(INT, NONE, tryagent) \
+ X(INT, NONE, agentfwd) \
+ X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \
+ X(INT, INT, ssh_cipherlist) \
+ X(FILENAME, NONE, keyfile) \
+ X(INT, NONE, sshprot) /* use v1 or v2 when both available */ \
+ X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \
+ X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \
+ X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \
+ X(INT, NONE, try_tis_auth) \
+ X(INT, NONE, try_ki_auth) \
+ X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \
+ X(INT, NONE, gssapifwd) /* forward tgt via gss */ \
+ X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \
+ X(FILENAME, NONE, ssh_gss_custom) \
+ X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \
+ X(INT, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \
+ X(INT, NONE, ssh_no_shell) /* avoid running a shell */ \
+ X(STR, NONE, ssh_nc_host) /* host to connect to in `nc' mode */ \
+ X(INT, NONE, ssh_nc_port) /* port to connect to in `nc' mode */ \
+ /* Telnet options */ \
+ X(STR, NONE, termtype) \
+ X(STR, NONE, termspeed) \
+ X(STR, STR, ttymodes) /* values are "Vvalue" or "A" */ \
+ X(STR, STR, environmt) \
+ X(STR, NONE, username) \
+ X(INT, NONE, username_from_env) \
+ X(STR, NONE, localusername) \
+ X(INT, NONE, rfc_environ) \
+ X(INT, NONE, passive_telnet) \
+ /* Serial port options */ \
+ X(STR, NONE, serline) \
+ X(INT, NONE, serspeed) \
+ X(INT, NONE, serdatabits) \
+ X(INT, NONE, serstopbits) \
+ X(INT, NONE, serparity) \
+ X(INT, NONE, serflow) \
+ /* Keyboard options */ \
+ X(INT, NONE, bksp_is_delete) \
+ X(INT, NONE, rxvt_homeend) \
+ X(INT, NONE, funky_type) \
+ X(INT, NONE, no_applic_c) /* totally disable app cursor keys */ \
+ X(INT, NONE, no_applic_k) /* totally disable app keypad */ \
+ X(INT, NONE, no_mouse_rep) /* totally disable mouse reporting */ \
+ X(INT, NONE, no_remote_resize) /* disable remote resizing */ \
+ X(INT, NONE, no_alt_screen) /* disable alternate screen */ \
+ X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \
+ X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \
+ X(INT, NONE, no_remote_charset) /* disable remote charset config */ \
+ X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \
+ X(INT, NONE, app_cursor) \
+ X(INT, NONE, app_keypad) \
+ X(INT, NONE, nethack_keypad) \
+ X(INT, NONE, telnet_keyboard) \
+ X(INT, NONE, telnet_newline) \
+ X(INT, NONE, alt_f4) /* is it special? */ \
+ X(INT, NONE, alt_space) /* is it special? */ \
+ X(INT, NONE, alt_only) /* is it special? */ \
+ X(INT, NONE, localecho) \
+ X(INT, NONE, localedit) \
+ X(INT, NONE, alwaysontop) \
+ X(INT, NONE, fullscreenonaltenter) \
+ X(INT, NONE, scroll_on_key) \
+ X(INT, NONE, scroll_on_disp) \
+ X(INT, NONE, erase_to_scrollback) \
+ X(INT, NONE, compose_key) \
+ X(INT, NONE, ctrlaltkeys) \
+ X(STR, NONE, wintitle) /* initial window title */ \
+ /* Terminal options */ \
+ X(INT, NONE, savelines) \
+ X(INT, NONE, dec_om) \
+ X(INT, NONE, wrap_mode) \
+ X(INT, NONE, lfhascr) \
+ X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \
+ X(INT, NONE, blink_cur) \
+ X(INT, NONE, beep) \
+ X(INT, NONE, beep_ind) \
+ X(INT, NONE, bellovl) /* bell overload protection active? */ \
+ X(INT, NONE, bellovl_n) /* number of bells to cause overload */ \
+ X(INT, NONE, bellovl_t) /* time interval for overload (seconds) */ \
+ X(INT, NONE, bellovl_s) /* period of silence to re-enable bell (s) */ \
+ X(FILENAME, NONE, bell_wavefile) \
+ X(INT, NONE, scrollbar) \
+ X(INT, NONE, scrollbar_in_fullscreen) \
+ X(INT, NONE, resize_action) \
+ X(INT, NONE, bce) \
+ X(INT, NONE, blinktext) \
+ X(INT, NONE, win_name_always) \
+ X(INT, NONE, width) \
+ X(INT, NONE, height) \
+ X(FONT, NONE, font) \
+ X(INT, NONE, font_quality) \
+ X(FILENAME, NONE, logfilename) \
+ X(INT, NONE, logtype) \
+ X(INT, NONE, logxfovr) \
+ X(INT, NONE, logflush) \
+ X(INT, NONE, logomitpass) \
+ X(INT, NONE, logomitdata) \
+ X(INT, NONE, hide_mouseptr) \
+ X(INT, NONE, sunken_edge) \
+ X(INT, NONE, window_border) \
+ X(STR, NONE, answerback) \
+ X(STR, NONE, printer) \
+ X(INT, NONE, arabicshaping) \
+ X(INT, NONE, bidi) \
+ /* Colour options */ \
+ X(INT, NONE, ansi_colour) \
+ X(INT, NONE, xterm_256_colour) \
+ X(INT, NONE, system_colour) \
+ X(INT, NONE, try_palette) \
+ X(INT, NONE, bold_colour) \
+ X(INT, INT, colours) \
+ /* Selection options */ \
+ X(INT, NONE, mouse_is_xterm) \
+ X(INT, NONE, rect_select) \
+ X(INT, NONE, rawcnp) \
+ X(INT, NONE, rtf_paste) \
+ X(INT, NONE, mouse_override) \
+ X(INT, INT, wordness) \
+ /* translations */ \
+ X(INT, NONE, vtmode) \
+ X(STR, NONE, line_codepage) \
+ X(INT, NONE, cjk_ambig_wide) \
+ X(INT, NONE, utf8_override) \
+ X(INT, NONE, xlat_capslockcyr) \
+ /* X11 forwarding */ \
+ X(INT, NONE, x11_forward) \
+ X(STR, NONE, x11_display) \
+ X(INT, NONE, x11_auth) \
+ X(FILENAME, NONE, xauthfile) \
+ /* port forwarding */ \
+ X(INT, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \
+ X(INT, NONE, rport_acceptall) /* same for remote forwarded ports (SSH-2 only) */ \
+ /* \
+ * Subkeys for 'portfwd' can have the following forms: \
+ * \
+ * [LR]localport \
+ * [LR]localaddr:localport \
+ * \
+ * Dynamic forwardings are indicated by an 'L' key, and the \
+ * special value "D". For all other forwardings, the value \
+ * should be of the form 'host:port'. \
+ */ \
+ X(STR, STR, portfwd) \
+ /* SSH bug compatibility modes */ \
+ X(INT, NONE, sshbug_ignore1) \
+ X(INT, NONE, sshbug_plainpw1) \
+ X(INT, NONE, sshbug_rsa1) \
+ X(INT, NONE, sshbug_hmac2) \
+ X(INT, NONE, sshbug_derivekey2) \
+ X(INT, NONE, sshbug_rsapad2) \
+ X(INT, NONE, sshbug_pksessid2) \
+ X(INT, NONE, sshbug_rekey2) \
+ X(INT, NONE, sshbug_maxpkt2) \
+ X(INT, NONE, 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. \
+ */ \
+ X(INT, NONE, ssh_simple) \
+ /* Options for pterm. Should split out into platform-dependent part. */ \
+ X(INT, NONE, stamp_utmp) \
+ X(INT, NONE, login_shell) \
+ X(INT, NONE, scrollbar_on_left) \
+ X(INT, NONE, shadowbold) \
+ X(FONT, NONE, boldfont) \
+ X(FONT, NONE, widefont) \
+ X(FONT, NONE, wideboldfont) \
+ X(INT, NONE, shadowboldoffset) \
+ X(INT, NONE, crhaslf) \
+ X(STR, NONE, winclass) \
+
+/* Now define the actual enum of option keywords using that macro. */
+#define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword,
+enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS };
+#undef CONF_ENUM_DEF
+
+#define NCFGCOLOURS 22 /* number of colours in CONF_colours above */
+
+/* Functions handling configuration structures. */
+Conf *conf_new(void); /* create an empty configuration */
+void conf_free(Conf *conf);
+Conf *conf_copy(Conf *oldconf);
+void conf_copy_into(Conf *dest, Conf *src);
+/* Mandatory accessor functions: enforce by assertion that keys exist. */
+int conf_get_int(Conf *conf, int key);
+int conf_get_int_int(Conf *conf, int key, int subkey);
+char *conf_get_str(Conf *conf, int key); /* result still owned by conf */
+char *conf_get_str_str(Conf *conf, int key, const char *subkey);
+Filename *conf_get_filename(Conf *conf, int key);
+FontSpec *conf_get_fontspec(Conf *conf, int key); /* still owned by conf */
+/* Optional accessor function: return NULL if key does not exist. */
+char *conf_get_str_str_opt(Conf *conf, int key, const char *subkey);
+/* Accessor function to step through a string-subkeyed list.
+ * Returns the next subkey after the provided one, or the first if NULL.
+ * Returns NULL if there are none left.
+ * Both the return value and *subkeyout are still owned by conf. */
+char *conf_get_str_strs(Conf *conf, int key, char *subkeyin, char **subkeyout);
+/* Return the nth string subkey in a list. Owned by conf. NULL if beyond end */
+char *conf_get_str_nthstrkey(Conf *conf, int key, int n);
+/* Functions to set entries in configuration. Always copy their inputs. */
+void conf_set_int(Conf *conf, int key, int value);
+void conf_set_int_int(Conf *conf, int key, int subkey, int value);
+void conf_set_str(Conf *conf, int key, const char *value);
+void conf_set_str_str(Conf *conf, int key,
+ const char *subkey, const char *val);
+void conf_del_str_str(Conf *conf, int key, const char *subkey);
+void conf_set_filename(Conf *conf, int key, const Filename *val);
+void conf_set_fontspec(Conf *conf, int key, const FontSpec *val);
+/* Serialisation functions for Duplicate Session */
+int conf_serialised_size(Conf *conf);
+void conf_serialise(Conf *conf, void *data);
+int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/
+
+/*
+ * Functions to copy, free, serialise and deserialise FontSpecs.
+ * Provided per-platform, to go with the platform's idea of a
+ * FontSpec's contents.
+ *
+ * fontspec_serialise returns the number of bytes written, and can
+ * handle data==NULL without crashing. So you can call it once to find
+ * out a size, then again once you've allocated a buffer.
+ */
+FontSpec *fontspec_copy(const FontSpec *f);
+void fontspec_free(FontSpec *f);
+int fontspec_serialise(FontSpec *f, void *data);
+FontSpec *fontspec_deserialise(void *data, int maxsize, int *used);
+
+/*
* Exports from noise.c.
*/
void noise_get_heavy(void (*func) (void *, int));
@@ -813,13 +925,13 @@ void random_destroy_seed(void);
*/
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);
-void load_open_settings(void *sesskey, Config *cfg);
+char *get_remote_username(Conf *conf); /* dynamically allocated */
+char *save_settings(char *section, Conf *conf);
+void save_open_settings(void *sesskey, Conf *conf);
+void load_settings(char *section, Conf *conf);
+void load_open_settings(void *sesskey, Conf *conf);
void get_sesslist(struct sesslist *, int allocate);
-void do_defaults(char *, Config *);
+void do_defaults(char *, Conf *);
void registry_cleanup(void);
/*
@@ -832,17 +944,21 @@ void registry_cleanup(void);
* function is perfectly all right returning NULL, of course. The
* Filename and FontSpec functions are _not allowed_ to fail to
* return, since these defaults _must_ be per-platform.)
+ *
+ * The 'Filename *' returned by platform_default_filename, and the
+ * 'FontSpec *' returned by platform_default_fontspec, have ownership
+ * transferred to the caller, and must be freed.
*/
char *platform_default_s(const char *name);
int platform_default_i(const char *name, int def);
-Filename platform_default_filename(const char *name);
-FontSpec platform_default_fontspec(const char *name);
+Filename *platform_default_filename(const char *name);
+FontSpec *platform_default_fontspec(const char *name);
/*
* Exports from terminal.c.
*/
-Terminal *term_init(Config *, struct unicode_data *, void *);
+Terminal *term_init(Conf *, struct unicode_data *, void *);
void term_free(Terminal *);
void term_size(Terminal *, int, int, int);
void term_paint(Terminal *, Context, int, int, int, int, int);
@@ -864,7 +980,7 @@ void term_paste(Terminal *);
void term_nopaste(Terminal *);
int term_ldisc(Terminal *, int option);
void term_copyall(Terminal *);
-void term_reconfig(Terminal *, Config *);
+void term_reconfig(Terminal *, Conf *);
void term_seen_key_event(Terminal *);
int term_data(Terminal *, int is_stderr, const char *data, int len);
int term_data_untrusted(Terminal *, const char *data, int len);
@@ -882,9 +998,9 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
/*
* Exports from logging.c.
*/
-void *log_init(void *frontend, Config *cfg);
+void *log_init(void *frontend, Conf *conf);
void log_free(void *logctx);
-void log_reconfig(void *logctx, Config *cfg);
+void log_reconfig(void *logctx, Conf *conf);
void logfopen(void *logctx);
void logfclose(void *logctx);
void logtraffic(void *logctx, unsigned char c, int logmode);
@@ -935,7 +1051,8 @@ extern Backend ssh_backend;
/*
* Exports from ldisc.c.
*/
-void *ldisc_create(Config *, Terminal *, Backend *, void *, void *);
+void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
+void ldisc_configure(void *, Conf *);
void ldisc_free(void *);
void ldisc_send(void *handle, char *buf, int len, int interactive);
@@ -963,8 +1080,8 @@ void random_unref(void);
* Exports from pinger.c.
*/
typedef struct pinger_tag *Pinger;
-Pinger pinger_new(Config *cfg, Backend *back, void *backhandle);
-void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg);
+Pinger pinger_new(Conf *conf, Backend *back, void *backhandle);
+void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf);
void pinger_free(Pinger);
/*
@@ -972,8 +1089,8 @@ void pinger_free(Pinger);
*/
#include "misc.h"
-int cfg_launchable(const Config *cfg);
-char const *cfg_dest(const Config *cfg);
+int conf_launchable(Conf *conf);
+char const *conf_dest(Conf *conf);
/*
* Exports from sercfg.c.
@@ -994,9 +1111,9 @@ extern char ver[];
#endif
/* void init_ucs(void); -- this is now in platform-specific headers */
int is_dbcs_leadbyte(int codepage, char byte);
-int mb_to_wc(int codepage, int flags, char *mbstr, int mblen,
+int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen,
wchar_t *wcstr, int wclen);
-int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen,
+int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen,
char *mbstr, int mblen, char *defchr, int *defused,
struct unicode_data *ucsdata);
wchar_t xlat_uskbd2cyrllic(int ch);
@@ -1078,7 +1195,7 @@ int askalg(void *frontend, const char *algtype, const char *algname,
* - 0 means cancel logging for this session
* - -1 means please wait.
*/
-int askappend(void *frontend, Filename filename,
+int askappend(void *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx);
/*
@@ -1107,8 +1224,8 @@ void printer_finish_job(printer_job *);
* defined differently in various places and required _by_
* cmdline.c).
*/
-int cmdline_process_param(char *, char *, int, Config *);
-void cmdline_run_saved(Config *);
+int cmdline_process_param(char *, char *, int, Conf *);
+void cmdline_run_saved(Conf *);
void cmdline_cleanup(void);
int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen);
#define TOOLTYPE_FILETRANSFER 1
@@ -1121,6 +1238,18 @@ void cmdline_error(char *, ...);
* Exports from config.c.
*/
struct controlbox;
+union control;
+void conf_radiobutton_handler(union control *ctrl, void *dlg,
+ void *data, int event);
+#define CHECKBOX_INVERT (1<<30)
+void conf_checkbox_handler(union control *ctrl, void *dlg,
+ void *data, int event);
+void conf_editbox_handler(union control *ctrl, void *dlg,
+ void *data, int event);
+void conf_filesel_handler(union control *ctrl, void *dlg,
+ void *data, int event);
+void conf_fontsel_handler(union control *ctrl, void *dlg,
+ void *data, int event);
void setup_config_box(struct controlbox *b, int midsession,
int protocol, int protcfginfo);
@@ -1148,11 +1277,18 @@ extern const char *const x11_authnames[]; /* declared in x11fwd.c */
/*
* Miscellaneous exports from the platform-specific code.
+ *
+ * filename_serialise and filename_deserialise have the same semantics
+ * as fontspec_serialise and fontspec_deserialise above.
*/
-Filename filename_from_str(const char *string);
+Filename *filename_from_str(const char *string);
const char *filename_to_str(const Filename *fn);
-int filename_equal(Filename f1, Filename f2);
-int filename_is_null(Filename fn);
+int filename_equal(const Filename *f1, const Filename *f2);
+int filename_is_null(const Filename *fn);
+Filename *filename_copy(const Filename *fn);
+void filename_free(Filename *fn);
+int filename_serialise(const Filename *f, void *data);
+Filename *filename_deserialise(void *data, int maxsize, int *used);
char *get_username(void); /* return value needs freeing */
char *get_random_data(int bytes); /* used in cmdgen.c */
diff --git a/tools/plink/raw.c b/tools/plink/raw.c
index ea51d74a7..ee06a2d73 100644
--- a/tools/plink/raw.c
+++ b/tools/plink/raw.c
@@ -23,6 +23,7 @@ typedef struct raw_backend_data {
Socket s;
int bufsize;
void *frontend;
+ int sent_console_eof, sent_socket_eof;
} *Raw;
static void raw_size(void *handle, int width, int height);
@@ -49,21 +50,51 @@ static void raw_log(Plug plug, int type, SockAddr addr, int port,
logevent(raw->frontend, msg);
}
+static void raw_check_close(Raw raw)
+{
+ /*
+ * Called after we send EOF on either the socket or the console.
+ * Its job is to wind up the session once we have sent EOF on both.
+ */
+ if (raw->sent_console_eof && raw->sent_socket_eof) {
+ if (raw->s) {
+ sk_close(raw->s);
+ raw->s = NULL;
+ notify_remote_exit(raw->frontend);
+ }
+ }
+}
+
static int raw_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
Raw raw = (Raw) plug;
- if (raw->s) {
- sk_close(raw->s);
- raw->s = NULL;
- notify_remote_exit(raw->frontend);
- }
if (error_msg) {
- /* A socket error has occurred. */
- logevent(raw->frontend, error_msg);
- connection_fatal(raw->frontend, "%s", error_msg);
- } /* Otherwise, the remote side closed the connection normally. */
+ /* A socket error has occurred. */
+ if (raw->s) {
+ sk_close(raw->s);
+ raw->s = NULL;
+ notify_remote_exit(raw->frontend);
+ }
+ logevent(raw->frontend, error_msg);
+ connection_fatal(raw->frontend, "%s", error_msg);
+ } else {
+ /* Otherwise, the remote side closed the connection normally. */
+ if (!raw->sent_console_eof && from_backend_eof(raw->frontend)) {
+ /*
+ * The front end wants us to close the outgoing side of the
+ * connection as soon as we see EOF from the far end.
+ */
+ if (!raw->sent_socket_eof) {
+ if (raw->s)
+ sk_write_eof(raw->s);
+ raw->sent_socket_eof= TRUE;
+ }
+ }
+ raw->sent_console_eof = TRUE;
+ raw_check_close(raw);
+ }
return 0;
}
@@ -89,7 +120,7 @@ static void raw_sent(Plug plug, int bufsize)
* freed by the caller.
*/
static const char *raw_init(void *frontend_handle, void **backend_handle,
- Config *cfg,
+ Conf *conf,
char *host, int port, char **realhost, int nodelay,
int keepalive)
{
@@ -102,27 +133,31 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
SockAddr addr;
const char *err;
Raw raw;
+ int addressfamily;
+ char *loghost;
raw = snew(struct raw_backend_data);
raw->fn = &fn_table;
raw->s = NULL;
*backend_handle = raw;
+ raw->sent_console_eof = raw->sent_socket_eof = FALSE;
raw->frontend = frontend_handle;
+ addressfamily = conf_get_int(conf, CONF_addressfamily);
/*
* Try to find host.
*/
{
char *buf;
buf = dupprintf("Looking up host \"%s\"%s", host,
- (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
"")));
logevent(raw->frontend, buf);
sfree(buf);
}
- addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);
+ addr = name_lookup(host, port, realhost, conf, addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
@@ -135,15 +170,16 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
* Open socket.
*/
raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive,
- (Plug) raw, cfg);
+ (Plug) raw, conf);
if ((err = sk_socket_error(raw->s)) != NULL)
return err;
- if (*cfg->loghost) {
+ loghost = conf_get_str(conf, CONF_loghost);
+ if (*loghost) {
char *colon;
sfree(*realhost);
- *realhost = dupstr(cfg->loghost);
+ *realhost = dupstr(loghost);
colon = strrchr(*realhost, ':');
if (colon) {
/*
@@ -170,7 +206,7 @@ static void raw_free(void *handle)
/*
* Stub routine (we don't have any need to reconfigure this backend).
*/
-static void raw_reconfig(void *handle, Config *cfg)
+static void raw_reconfig(void *handle, Conf *conf)
{
}
@@ -208,11 +244,17 @@ static void raw_size(void *handle, int width, int height)
}
/*
- * Send raw special codes.
+ * Send raw special codes. We only handle outgoing EOF here.
*/
static void raw_special(void *handle, Telnet_Special code)
{
- /* Do nothing! */
+ Raw raw = (Raw) handle;
+ if (code == TS_EOF && raw->s) {
+ sk_write_eof(raw->s);
+ raw->sent_socket_eof= TRUE;
+ raw_check_close(raw);
+ }
+
return;
}
diff --git a/tools/plink/rlogin.c b/tools/plink/rlogin.c
index b514d7a5b..29ae5fd14 100644
--- a/tools/plink/rlogin.c
+++ b/tools/plink/rlogin.c
@@ -27,6 +27,11 @@ typedef struct rlogin_tag {
int cansize;
int term_width, term_height;
void *frontend;
+
+ Conf *conf;
+
+ /* 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);
@@ -57,6 +62,13 @@ static int rlogin_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
Rlogin rlogin = (Rlogin) plug;
+
+ /*
+ * We don't implement independent EOF in each direction for Telnet
+ * connections; as soon as we get word that the remote side has
+ * sent us EOF, we wind up the whole connection.
+ */
+
if (rlogin->s) {
sk_close(rlogin->s);
rlogin->s = NULL;
@@ -113,6 +125,27 @@ static void rlogin_sent(Plug plug, int bufsize)
rlogin->bufsize = bufsize;
}
+static void rlogin_startup(Rlogin rlogin, const char *ruser)
+{
+ char z = 0;
+ char *p;
+
+ sk_write(rlogin->s, &z, 1);
+ p = conf_get_str(rlogin->conf, CONF_localusername);
+ sk_write(rlogin->s, p, strlen(p));
+ sk_write(rlogin->s, &z, 1);
+ sk_write(rlogin->s, ruser, strlen(ruser));
+ sk_write(rlogin->s, &z, 1);
+ p = conf_get_str(rlogin->conf, CONF_termtype);
+ sk_write(rlogin->s, p, strlen(p));
+ sk_write(rlogin->s, "/", 1);
+ p = conf_get_str(rlogin->conf, CONF_termspeed);
+ sk_write(rlogin->s, p, strspn(p, "0123456789"));
+ rlogin->bufsize = sk_write(rlogin->s, &z, 1);
+
+ rlogin->prompt = NULL;
+}
+
/*
* Called to set up the rlogin connection.
*
@@ -122,7 +155,7 @@ static void rlogin_sent(Plug plug, int bufsize)
* freed by the caller.
*/
static const char *rlogin_init(void *frontend_handle, void **backend_handle,
- Config *cfg,
+ Conf *conf,
char *host, int port, char **realhost,
int nodelay, int keepalive)
{
@@ -135,30 +168,36 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
SockAddr addr;
const char *err;
Rlogin rlogin;
+ char *ruser;
+ int addressfamily;
+ char *loghost;
rlogin = snew(struct rlogin_tag);
rlogin->fn = &fn_table;
rlogin->s = NULL;
rlogin->frontend = frontend_handle;
- rlogin->term_width = cfg->width;
- rlogin->term_height = cfg->height;
+ rlogin->term_width = conf_get_int(conf, CONF_width);
+ rlogin->term_height = conf_get_int(conf, CONF_height);
rlogin->firstbyte = 1;
rlogin->cansize = 0;
+ rlogin->prompt = NULL;
+ rlogin->conf = conf_copy(conf);
*backend_handle = rlogin;
+ addressfamily = conf_get_int(conf, CONF_addressfamily);
/*
* Try to find host.
*/
{
char *buf;
buf = dupprintf("Looking up host \"%s\"%s", host,
- (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
"")));
logevent(rlogin->frontend, buf);
sfree(buf);
}
- addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);
+ addr = name_lookup(host, port, realhost, conf, addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
@@ -171,39 +210,16 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
* Open socket.
*/
rlogin->s = new_connection(addr, *realhost, port, 1, 0,
- nodelay, keepalive, (Plug) rlogin, cfg);
+ nodelay, keepalive, (Plug) rlogin, conf);
if ((err = sk_socket_error(rlogin->s)) != NULL)
return err;
- /*
- * Send local username, remote username, terminal/speed
- */
-
- {
- char z = 0;
- char *p;
- char ruser[sizeof(cfg->username)];
- (void) get_remote_username(cfg, ruser, sizeof(ruser));
- 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, ruser,
- strlen(ruser));
- 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);
- }
-
- if (*cfg->loghost) {
+ loghost = conf_get_str(conf, CONF_loghost);
+ if (*loghost) {
char *colon;
sfree(*realhost);
- *realhost = dupstr(cfg->loghost);
+ *realhost = dupstr(loghost);
colon = strrchr(*realhost, ':');
if (colon) {
/*
@@ -215,6 +231,28 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
}
}
+ /*
+ * 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 ((ruser = get_remote_username(conf)) == NULL) {
+ rlogin_startup(rlogin, ruser);
+ sfree(ruser);
+ } else {
+ int ret;
+
+ 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);
+ ret = get_userpass_input(rlogin->prompt, NULL, 0);
+ if (ret >= 0) {
+ rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
+ }
+ }
+
return NULL;
}
@@ -222,15 +260,18 @@ static void rlogin_free(void *handle)
{
Rlogin rlogin = (Rlogin) handle;
+ if (rlogin->prompt)
+ free_prompts(rlogin->prompt);
if (rlogin->s)
sk_close(rlogin->s);
+ conf_free(rlogin->conf);
sfree(rlogin);
}
/*
* Stub routine (we don't have any need to reconfigure this backend).
*/
-static void rlogin_reconfig(void *handle, Config *cfg)
+static void rlogin_reconfig(void *handle, Conf *conf)
{
}
@@ -244,7 +285,21 @@ static int rlogin_send(void *handle, char *buf, int len)
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;
}
diff --git a/tools/plink/settings.c b/tools/plink/settings.c
index 46e19f49c..725e468f1 100644
--- a/tools/plink/settings.c
+++ b/tools/plink/settings.c
@@ -9,21 +9,21 @@
#include "storage.h"
/* 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 },
- { "rsa", KEX_RSA },
- { "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 }
};
/*
@@ -70,67 +70,68 @@ Backend *backend_from_proto(int proto)
return NULL;
}
-int get_remote_username(Config *cfg, char *user, size_t len)
+char *get_remote_username(Conf *conf)
{
- if (*cfg->username) {
- strncpy(user, cfg->username, len);
- user[len-1] = '\0';
+ char *username = conf_get_str(conf, CONF_username);
+ if (*username) {
+ return dupstr(username);
+ } else if (conf_get_int(conf, CONF_username_from_env)) {
+ /* Use local username. */
+ return get_username(); /* might still be NULL */
} 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 NULL;
}
- return (*user != '\0');
}
-static void gpps(void *handle, const char *name, const char *def,
- char *val, int len)
+static char *gpps_raw(void *handle, const char *name, const char *def)
{
- if (!read_setting_s(handle, name, val, len)) {
- char *pdef;
-
- pdef = platform_default_s(name);
- if (pdef) {
- strncpy(val, pdef, len);
- sfree(pdef);
- } else {
- strncpy(val, def, len);
- }
+ char *ret = read_setting_s(handle, name);
+ if (!ret)
+ ret = platform_default_s(name);
+ if (!ret)
+ ret = def ? dupstr(def) : NULL; /* permit NULL as final fallback */
+ return ret;
+}
- val[len - 1] = '\0';
- }
+static void gpps(void *handle, const char *name, const char *def,
+ Conf *conf, int primary)
+{
+ char *val = gpps_raw(handle, name, def);
+ conf_set_str(conf, primary, val);
+ sfree(val);
}
/*
* gppfont and gppfile cannot have local defaults, since the very
- * format of a Filename or Font is platform-dependent. So the
+ * format of a Filename or FontSpec is platform-dependent. So the
* platform-dependent functions MUST return some sort of value.
*/
-static void gppfont(void *handle, const char *name, FontSpec *result)
+static void gppfont(void *handle, const char *name, Conf *conf, int primary)
{
- if (!read_setting_fontspec(handle, name, result))
- *result = platform_default_fontspec(name);
+ FontSpec *result = read_setting_fontspec(handle, name);
+ if (!result)
+ result = platform_default_fontspec(name);
+ conf_set_fontspec(conf, primary, result);
+ fontspec_free(result);
}
-static void gppfile(void *handle, const char *name, Filename *result)
+static void gppfile(void *handle, const char *name, Conf *conf, int primary)
{
- if (!read_setting_filename(handle, name, result))
- *result = platform_default_filename(name);
+ Filename *result = read_setting_filename(handle, name);
+ if (!result)
+ result = platform_default_filename(name);
+ conf_set_filename(conf, primary, result);
+ filename_free(result);
}
-static void gppi(void *handle, char *name, int def, int *i)
+static int gppi_raw(void *handle, char *name, int def)
{
def = platform_default_i(name, def);
- *i = read_setting_i(handle, name, def);
+ return read_setting_i(handle, name, def);
+}
+
+static void gppi(void *handle, char *name, int def, Conf *conf, int primary)
+{
+ conf_set_int(conf, primary, gppi_raw(handle, name, def));
}
/*
@@ -139,56 +140,131 @@ static void gppi(void *handle, char *name, int def, int *i)
* NAME=VALUE,NAME=VALUE, in storage
* `def' is in the storage format.
*/
-static void gppmap(void *handle, char *name, char *def, char *val, int len)
+static int gppmap(void *handle, char *name, Conf *conf, int primary)
{
- char *buf = snewn(2*len, char), *p, *q;
- gpps(handle, name, def, buf, 2*len);
+ char *buf, *p, *q, *key, *val;
+
+ /*
+ * Start by clearing any existing subkeys of this key from conf.
+ */
+ while ((key = conf_get_str_nthstrkey(conf, primary, 0)) != NULL)
+ conf_del_str_str(conf, primary, key);
+
+ /*
+ * Now read a serialised list from the settings and unmarshal it
+ * into its components.
+ */
+ buf = gpps_raw(handle, name, NULL);
+ if (!buf)
+ return FALSE;
+
p = buf;
- q = val;
while (*p) {
+ q = buf;
+ val = NULL;
while (*p && *p != ',') {
int c = *p++;
if (c == '=')
- c = '\t';
+ c = '\0';
if (c == '\\')
c = *p++;
*q++ = c;
+ if (!c)
+ val = q;
}
if (*p == ',')
p++;
- *q++ = '\0';
+ if (!val)
+ val = q;
+ *q = '\0';
+
+ if (primary == CONF_portfwd && buf[0] == 'D') {
+ /*
+ * Backwards-compatibility hack: dynamic forwardings are
+ * indexed in the data store as a third type letter in the
+ * key, 'D' alongside 'L' and 'R' - but really, they
+ * should be filed under 'L' with a special _value_,
+ * because local and dynamic forwardings both involve
+ * _listening_ on a local port, and are hence mutually
+ * exclusive on the same port number. So here we translate
+ * the legacy storage format into the sensible internal
+ * form.
+ */
+ char *newkey = dupcat("L", buf+1, NULL);
+ conf_set_str_str(conf, primary, newkey, "D");
+ sfree(newkey);
+ } else {
+ conf_set_str_str(conf, primary, buf, val);
+ }
}
- *q = '\0';
sfree(buf);
+
+ return TRUE;
}
/*
* Write a set of name/value pairs in the above format.
*/
-static void wmap(void *handle, char const *key, char const *value, int len)
+static void wmap(void *handle, char const *outkey, Conf *conf, int primary)
{
- char *buf = snewn(2*len, char), *p;
- const char *q;
+ char *buf, *p, *q, *key, *realkey, *val;
+ int len;
+
+ len = 1; /* allow for NUL */
+
+ for (val = conf_get_str_strs(conf, primary, NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(conf, primary, key, &key))
+ len += 2 + 2 * (strlen(key) + strlen(val)); /* allow for escaping */
+
+ buf = snewn(len, char);
p = buf;
- q = value;
- while (*q) {
- while (*q) {
- int c = *q++;
- if (c == '=' || c == ',' || c == '\\')
+
+ for (val = conf_get_str_strs(conf, primary, NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(conf, primary, key, &key)) {
+
+ if (primary == CONF_portfwd && !strcmp(val, "D")) {
+ /*
+ * Backwards-compatibility hack, as above: translate from
+ * the sensible internal representation of dynamic
+ * forwardings (key "L<port>", value "D") to the
+ * conceptually incoherent legacy storage format (key
+ * "D<port>", value empty).
+ */
+ realkey = key; /* restore it at end of loop */
+ val = "";
+ key = dupcat("D", key+1, NULL);
+ } else {
+ realkey = NULL;
+ }
+
+ if (p != buf)
+ *p++ = ',';
+ for (q = key; *q; q++) {
+ if (*q == '=' || *q == ',' || *q == '\\')
*p++ = '\\';
- if (c == '\t')
- c = '=';
- *p++ = c;
+ *p++ = *q;
}
- *p++ = ',';
- q++;
+ *p++ = '=';
+ for (q = val; *q; q++) {
+ if (*q == '=' || *q == ',' || *q == '\\')
+ *p++ = '\\';
+ *p++ = *q;
+ }
+
+ if (realkey) {
+ free(key);
+ key = realkey;
+ }
}
*p = '\0';
- write_setting_s(handle, key, buf);
+ write_setting_s(handle, outkey, buf);
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++)
@@ -196,7 +272,8 @@ static int key2val(const struct keyval *mapping, int nmaps, char *key)
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++)
@@ -211,40 +288,85 @@ static const char *val2key(const struct keyval *mapping, int nmaps, int val)
* XXX: assumes vals in 'mapping' are small +ve integers
*/
static void gprefs(void *sesskey, char *name, char *def,
- const struct keyval *mapping, int nvals,
- int *array)
+ const struct keyvalwhere *mapping, int nvals,
+ Conf *conf, int primary)
{
- char commalist[80];
- char *tokarg = commalist;
- int n;
+ char *commalist;
+ char *p, *q;
+ int i, j, n, v, pos;
unsigned long seen = 0; /* bitmap for weeding dups etc */
- gpps(sesskey, name, def, commalist, sizeof(commalist));
- /* Grotty parsing of commalist. */
+ /*
+ * Fetch the string which we'll parse as a comma-separated list.
+ */
+ commalist = gpps_raw(sesskey, name, def);
+
+ /*
+ * 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);
+ conf_set_int_int(conf, primary, n, v);
+ n++;
}
- } while (n < nvals);
- /* Add any missing values (backward compatibility ect). */
- {
- int i;
- for (i = 0; i < nvals; i++) {
+ }
+
+ sfree(commalist);
+
+ /*
+ * 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 (conf_get_int_int(conf, primary, 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--)
+ conf_set_int_int(conf, primary, j+1,
+ conf_get_int_int(conf, primary, j));
+ conf_set_int_int(conf, primary, pos, mapping[i].v);
+ n++;
+ }
+ }
}
}
@@ -252,28 +374,40 @@ static void gprefs(void *sesskey, char *name, char *def,
* Write out a preference list.
*/
static void wprefs(void *sesskey, char *name,
- const struct keyval *mapping, int nvals,
- int *array)
+ const struct keyvalwhere *mapping, int nvals,
+ Conf *conf, int primary)
{
- char buf[80] = ""; /* XXX assumed big enough */
- int l = sizeof(buf)-1, i;
- buf[l] = '\0';
- for (i = 0; l > 0 && i < nvals; i++) {
- const char *s = val2key(mapping, nvals, array[i]);
+ char *buf, *p;
+ int i, maxlen;
+
+ for (maxlen = i = 0; i < nvals; i++) {
+ const char *s = val2key(mapping, nvals,
+ conf_get_int_int(conf, primary, 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,
+ conf_get_int_int(conf, primary, 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)
+char *save_settings(char *section, Conf *conf)
{
void *sesskey;
char *errmsg;
@@ -281,169 +415,169 @@ char *save_settings(char *section, Config * cfg)
sesskey = open_settings_w(section, &errmsg);
if (!sesskey)
return errmsg;
- save_open_settings(sesskey, cfg);
+ save_open_settings(sesskey, conf);
close_settings_w(sesskey);
return NULL;
}
-void save_open_settings(void *sesskey, Config *cfg)
+void save_open_settings(void *sesskey, Conf *conf)
{
int i;
char *p;
write_setting_i(sesskey, "Present", 1);
- write_setting_s(sesskey, "HostName", cfg->host);
- write_setting_filename(sesskey, "LogFileName", cfg->logfilename);
- write_setting_i(sesskey, "LogType", cfg->logtype);
- write_setting_i(sesskey, "LogFileClash", cfg->logxfovr);
- write_setting_i(sesskey, "LogFlush", cfg->logflush);
- write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass);
- write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata);
+ write_setting_s(sesskey, "HostName", conf_get_str(conf, CONF_host));
+ write_setting_filename(sesskey, "LogFileName", conf_get_filename(conf, CONF_logfilename));
+ write_setting_i(sesskey, "LogType", conf_get_int(conf, CONF_logtype));
+ write_setting_i(sesskey, "LogFileClash", conf_get_int(conf, CONF_logxfovr));
+ write_setting_i(sesskey, "LogFlush", conf_get_int(conf, CONF_logflush));
+ write_setting_i(sesskey, "SSHLogOmitPasswords", conf_get_int(conf, CONF_logomitpass));
+ write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata));
p = "raw";
{
- const Backend *b = backend_from_proto(cfg->protocol);
+ const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol));
if (b)
p = b->name;
}
write_setting_s(sesskey, "Protocol", p);
- write_setting_i(sesskey, "PortNumber", cfg->port);
+ write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port));
/* The CloseOnExit numbers are arranged in a different order from
* the standard FORCE_ON / FORCE_OFF / AUTO. */
- write_setting_i(sesskey, "CloseOnExit", (cfg->close_on_exit+2)%3);
- write_setting_i(sesskey, "WarnOnClose", !!cfg->warn_on_close);
- write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */
- write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60); /* seconds */
- write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay);
- write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives);
- write_setting_s(sesskey, "TerminalType", cfg->termtype);
- write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);
- wmap(sesskey, "TerminalModes", cfg->ttymodes, lenof(cfg->ttymodes));
+ write_setting_i(sesskey, "CloseOnExit", (conf_get_int(conf, CONF_close_on_exit)+2)%3);
+ write_setting_i(sesskey, "WarnOnClose", !!conf_get_int(conf, CONF_warn_on_close));
+ write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */
+ write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */
+ write_setting_i(sesskey, "TCPNoDelay", conf_get_int(conf, CONF_tcp_nodelay));
+ write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives));
+ write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype));
+ write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed));
+ wmap(sesskey, "TerminalModes", conf, CONF_ttymodes);
/* Address family selection */
- write_setting_i(sesskey, "AddressFamily", cfg->addressfamily);
+ write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily));
/* proxy settings */
- write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list);
- write_setting_i(sesskey, "ProxyDNS", (cfg->proxy_dns+2)%3);
- write_setting_i(sesskey, "ProxyLocalhost", cfg->even_proxy_localhost);
- write_setting_i(sesskey, "ProxyMethod", cfg->proxy_type);
- write_setting_s(sesskey, "ProxyHost", cfg->proxy_host);
- write_setting_i(sesskey, "ProxyPort", cfg->proxy_port);
- write_setting_s(sesskey, "ProxyUsername", cfg->proxy_username);
- write_setting_s(sesskey, "ProxyPassword", cfg->proxy_password);
- 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);
- wprefs(sesskey, "KEX", kexnames, KEX_MAX, cfg->ssh_kexlist);
- 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);
+ write_setting_s(sesskey, "ProxyExcludeList", conf_get_str(conf, CONF_proxy_exclude_list));
+ write_setting_i(sesskey, "ProxyDNS", (conf_get_int(conf, CONF_proxy_dns)+2)%3);
+ write_setting_i(sesskey, "ProxyLocalhost", conf_get_int(conf, CONF_even_proxy_localhost));
+ write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type));
+ write_setting_s(sesskey, "ProxyHost", conf_get_str(conf, CONF_proxy_host));
+ write_setting_i(sesskey, "ProxyPort", conf_get_int(conf, CONF_proxy_port));
+ write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username));
+ write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password));
+ write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command));
+ wmap(sesskey, "Environment", conf, CONF_environmt);
+ write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username));
+ write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env));
+ write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername));
+ write_setting_i(sesskey, "NoPTY", conf_get_int(conf, CONF_nopty));
+ write_setting_i(sesskey, "Compression", conf_get_int(conf, CONF_compression));
+ write_setting_i(sesskey, "TryAgent", conf_get_int(conf, CONF_tryagent));
+ write_setting_i(sesskey, "AgentFwd", conf_get_int(conf, CONF_agentfwd));
+ write_setting_i(sesskey, "GssapiFwd", conf_get_int(conf, CONF_gssapifwd));
+ write_setting_i(sesskey, "ChangeUsername", conf_get_int(conf, CONF_change_username));
+ wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist);
+ wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
+ write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time));
+ write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data));
+ write_setting_i(sesskey, "SshNoAuth", conf_get_int(conf, CONF_ssh_no_userauth));
+ write_setting_i(sesskey, "SshBanner", conf_get_int(conf, CONF_ssh_show_banner));
+ write_setting_i(sesskey, "AuthTIS", conf_get_int(conf, CONF_try_tis_auth));
+ write_setting_i(sesskey, "AuthKI", conf_get_int(conf, CONF_try_ki_auth));
+ write_setting_i(sesskey, "AuthGSSAPI", conf_get_int(conf, CONF_try_gssapi_auth));
#ifndef NO_GSSAPI
- wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs,
- cfg->ssh_gsslist);
- write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom);
+ wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist);
+ write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_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);
- write_setting_i(sesskey, "RFCEnviron", cfg->rfc_environ);
- write_setting_i(sesskey, "PassiveTelnet", cfg->passive_telnet);
- write_setting_i(sesskey, "BackspaceIsDelete", cfg->bksp_is_delete);
- write_setting_i(sesskey, "RXVTHomeEnd", cfg->rxvt_homeend);
- write_setting_i(sesskey, "LinuxFunctionKeys", cfg->funky_type);
- write_setting_i(sesskey, "NoApplicationKeys", cfg->no_applic_k);
- write_setting_i(sesskey, "NoApplicationCursors", cfg->no_applic_c);
- write_setting_i(sesskey, "NoMouseReporting", cfg->no_mouse_rep);
- write_setting_i(sesskey, "NoRemoteResize", cfg->no_remote_resize);
- write_setting_i(sesskey, "NoAltScreen", cfg->no_alt_screen);
- write_setting_i(sesskey, "NoRemoteWinTitle", cfg->no_remote_wintitle);
- write_setting_i(sesskey, "RemoteQTitleAction", cfg->remote_qtitle_action);
- write_setting_i(sesskey, "NoDBackspace", cfg->no_dbackspace);
- write_setting_i(sesskey, "NoRemoteCharset", cfg->no_remote_charset);
- write_setting_i(sesskey, "ApplicationCursorKeys", cfg->app_cursor);
- write_setting_i(sesskey, "ApplicationKeypad", cfg->app_keypad);
- write_setting_i(sesskey, "NetHackKeypad", cfg->nethack_keypad);
- write_setting_i(sesskey, "AltF4", cfg->alt_f4);
- write_setting_i(sesskey, "AltSpace", cfg->alt_space);
- write_setting_i(sesskey, "AltOnly", cfg->alt_only);
- write_setting_i(sesskey, "ComposeKey", cfg->compose_key);
- write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys);
- write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard);
- write_setting_i(sesskey, "TelnetRet", cfg->telnet_newline);
- write_setting_i(sesskey, "LocalEcho", cfg->localecho);
- write_setting_i(sesskey, "LocalEdit", cfg->localedit);
- write_setting_s(sesskey, "Answerback", cfg->answerback);
- write_setting_i(sesskey, "AlwaysOnTop", cfg->alwaysontop);
- write_setting_i(sesskey, "FullScreenOnAltEnter", cfg->fullscreenonaltenter);
- write_setting_i(sesskey, "HideMousePtr", cfg->hide_mouseptr);
- write_setting_i(sesskey, "SunkenEdge", cfg->sunken_edge);
- write_setting_i(sesskey, "WindowBorder", cfg->window_border);
- write_setting_i(sesskey, "CurType", cfg->cursor_type);
- write_setting_i(sesskey, "BlinkCur", cfg->blink_cur);
- write_setting_i(sesskey, "Beep", cfg->beep);
- write_setting_i(sesskey, "BeepInd", cfg->beep_ind);
- write_setting_filename(sesskey, "BellWaveFile", cfg->bell_wavefile);
- write_setting_i(sesskey, "BellOverload", cfg->bellovl);
- write_setting_i(sesskey, "BellOverloadN", cfg->bellovl_n);
- write_setting_i(sesskey, "BellOverloadT", cfg->bellovl_t
+ write_setting_i(sesskey, "SshNoShell", conf_get_int(conf, CONF_ssh_no_shell));
+ write_setting_i(sesskey, "SshProt", conf_get_int(conf, CONF_sshprot));
+ write_setting_s(sesskey, "LogHost", conf_get_str(conf, CONF_loghost));
+ write_setting_i(sesskey, "SSH2DES", conf_get_int(conf, CONF_ssh2_des_cbc));
+ write_setting_filename(sesskey, "PublicKeyFile", conf_get_filename(conf, CONF_keyfile));
+ write_setting_s(sesskey, "RemoteCommand", conf_get_str(conf, CONF_remote_cmd));
+ write_setting_i(sesskey, "RFCEnviron", conf_get_int(conf, CONF_rfc_environ));
+ write_setting_i(sesskey, "PassiveTelnet", conf_get_int(conf, CONF_passive_telnet));
+ write_setting_i(sesskey, "BackspaceIsDelete", conf_get_int(conf, CONF_bksp_is_delete));
+ write_setting_i(sesskey, "RXVTHomeEnd", conf_get_int(conf, CONF_rxvt_homeend));
+ write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type));
+ write_setting_i(sesskey, "NoApplicationKeys", conf_get_int(conf, CONF_no_applic_k));
+ write_setting_i(sesskey, "NoApplicationCursors", conf_get_int(conf, CONF_no_applic_c));
+ write_setting_i(sesskey, "NoMouseReporting", conf_get_int(conf, CONF_no_mouse_rep));
+ write_setting_i(sesskey, "NoRemoteResize", conf_get_int(conf, CONF_no_remote_resize));
+ write_setting_i(sesskey, "NoAltScreen", conf_get_int(conf, CONF_no_alt_screen));
+ write_setting_i(sesskey, "NoRemoteWinTitle", conf_get_int(conf, CONF_no_remote_wintitle));
+ write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action));
+ write_setting_i(sesskey, "NoDBackspace", conf_get_int(conf, CONF_no_dbackspace));
+ write_setting_i(sesskey, "NoRemoteCharset", conf_get_int(conf, CONF_no_remote_charset));
+ write_setting_i(sesskey, "ApplicationCursorKeys", conf_get_int(conf, CONF_app_cursor));
+ write_setting_i(sesskey, "ApplicationKeypad", conf_get_int(conf, CONF_app_keypad));
+ write_setting_i(sesskey, "NetHackKeypad", conf_get_int(conf, CONF_nethack_keypad));
+ write_setting_i(sesskey, "AltF4", conf_get_int(conf, CONF_alt_f4));
+ write_setting_i(sesskey, "AltSpace", conf_get_int(conf, CONF_alt_space));
+ write_setting_i(sesskey, "AltOnly", conf_get_int(conf, CONF_alt_only));
+ write_setting_i(sesskey, "ComposeKey", conf_get_int(conf, CONF_compose_key));
+ write_setting_i(sesskey, "CtrlAltKeys", conf_get_int(conf, CONF_ctrlaltkeys));
+ write_setting_i(sesskey, "TelnetKey", conf_get_int(conf, CONF_telnet_keyboard));
+ write_setting_i(sesskey, "TelnetRet", conf_get_int(conf, CONF_telnet_newline));
+ write_setting_i(sesskey, "LocalEcho", conf_get_int(conf, CONF_localecho));
+ write_setting_i(sesskey, "LocalEdit", conf_get_int(conf, CONF_localedit));
+ write_setting_s(sesskey, "Answerback", conf_get_str(conf, CONF_answerback));
+ write_setting_i(sesskey, "AlwaysOnTop", conf_get_int(conf, CONF_alwaysontop));
+ write_setting_i(sesskey, "FullScreenOnAltEnter", conf_get_int(conf, CONF_fullscreenonaltenter));
+ write_setting_i(sesskey, "HideMousePtr", conf_get_int(conf, CONF_hide_mouseptr));
+ write_setting_i(sesskey, "SunkenEdge", conf_get_int(conf, CONF_sunken_edge));
+ write_setting_i(sesskey, "WindowBorder", conf_get_int(conf, CONF_window_border));
+ write_setting_i(sesskey, "CurType", conf_get_int(conf, CONF_cursor_type));
+ write_setting_i(sesskey, "BlinkCur", conf_get_int(conf, CONF_blink_cur));
+ write_setting_i(sesskey, "Beep", conf_get_int(conf, CONF_beep));
+ write_setting_i(sesskey, "BeepInd", conf_get_int(conf, CONF_beep_ind));
+ write_setting_filename(sesskey, "BellWaveFile", conf_get_filename(conf, CONF_bell_wavefile));
+ write_setting_i(sesskey, "BellOverload", conf_get_int(conf, CONF_bellovl));
+ write_setting_i(sesskey, "BellOverloadN", conf_get_int(conf, CONF_bellovl_n));
+ write_setting_i(sesskey, "BellOverloadT", conf_get_int(conf, CONF_bellovl_t)
#ifdef PUTTY_UNIX_H
* 1000
#endif
);
- write_setting_i(sesskey, "BellOverloadS", cfg->bellovl_s
+ write_setting_i(sesskey, "BellOverloadS", conf_get_int(conf, CONF_bellovl_s)
#ifdef PUTTY_UNIX_H
* 1000
#endif
);
- write_setting_i(sesskey, "ScrollbackLines", cfg->savelines);
- 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);
- write_setting_s(sesskey, "WinTitle", cfg->wintitle);
- write_setting_i(sesskey, "TermWidth", cfg->width);
- write_setting_i(sesskey, "TermHeight", cfg->height);
- write_setting_fontspec(sesskey, "Font", cfg->font);
- write_setting_i(sesskey, "FontQuality", cfg->font_quality);
- write_setting_i(sesskey, "FontVTMode", cfg->vtmode);
- write_setting_i(sesskey, "UseSystemColours", cfg->system_colour);
- write_setting_i(sesskey, "TryPalette", cfg->try_palette);
- write_setting_i(sesskey, "ANSIColour", cfg->ansi_colour);
- write_setting_i(sesskey, "Xterm256Colour", cfg->xterm_256_colour);
- write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour);
+ write_setting_i(sesskey, "ScrollbackLines", conf_get_int(conf, CONF_savelines));
+ write_setting_i(sesskey, "DECOriginMode", conf_get_int(conf, CONF_dec_om));
+ write_setting_i(sesskey, "AutoWrapMode", conf_get_int(conf, CONF_wrap_mode));
+ write_setting_i(sesskey, "LFImpliesCR", conf_get_int(conf, CONF_lfhascr));
+ write_setting_i(sesskey, "CRImpliesLF", conf_get_int(conf, CONF_crhaslf));
+ write_setting_i(sesskey, "DisableArabicShaping", conf_get_int(conf, CONF_arabicshaping));
+ write_setting_i(sesskey, "DisableBidi", conf_get_int(conf, CONF_bidi));
+ write_setting_i(sesskey, "WinNameAlways", conf_get_int(conf, CONF_win_name_always));
+ write_setting_s(sesskey, "WinTitle", conf_get_str(conf, CONF_wintitle));
+ write_setting_i(sesskey, "TermWidth", conf_get_int(conf, CONF_width));
+ write_setting_i(sesskey, "TermHeight", conf_get_int(conf, CONF_height));
+ write_setting_fontspec(sesskey, "Font", conf_get_fontspec(conf, CONF_font));
+ write_setting_i(sesskey, "FontQuality", conf_get_int(conf, CONF_font_quality));
+ write_setting_i(sesskey, "FontVTMode", conf_get_int(conf, CONF_vtmode));
+ write_setting_i(sesskey, "UseSystemColours", conf_get_int(conf, CONF_system_colour));
+ write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette));
+ write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour));
+ write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour));
+ write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_colour));
for (i = 0; i < 22; i++) {
char buf[20], buf2[30];
sprintf(buf, "Colour%d", i);
- sprintf(buf2, "%d,%d,%d", cfg->colours[i][0],
- cfg->colours[i][1], cfg->colours[i][2]);
+ sprintf(buf2, "%d,%d,%d",
+ conf_get_int_int(conf, CONF_colours, i*3+0),
+ conf_get_int_int(conf, CONF_colours, i*3+1),
+ conf_get_int_int(conf, CONF_colours, i*3+2));
write_setting_s(sesskey, buf, buf2);
}
- write_setting_i(sesskey, "RawCNP", cfg->rawcnp);
- write_setting_i(sesskey, "PasteRTF", cfg->rtf_paste);
- write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm);
- write_setting_i(sesskey, "RectSelect", cfg->rect_select);
- write_setting_i(sesskey, "MouseOverride", cfg->mouse_override);
+ write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp));
+ write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste));
+ write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm));
+ write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select));
+ write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override));
for (i = 0; i < 256; i += 32) {
char buf[20], buf2[256];
int j;
@@ -451,305 +585,287 @@ void save_open_settings(void *sesskey, Config *cfg)
*buf2 = '\0';
for (j = i; j < i + 32; j++) {
sprintf(buf2 + strlen(buf2), "%s%d",
- (*buf2 ? "," : ""), cfg->wordness[j]);
+ (*buf2 ? "," : ""),
+ conf_get_int_int(conf, CONF_wordness, j));
}
write_setting_s(sesskey, buf, buf2);
}
- write_setting_s(sesskey, "LineCodePage", cfg->line_codepage);
- write_setting_i(sesskey, "CJKAmbigWide", cfg->cjk_ambig_wide);
- write_setting_i(sesskey, "UTF8Override", cfg->utf8_override);
- write_setting_s(sesskey, "Printer", cfg->printer);
- write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr);
- write_setting_i(sesskey, "ScrollBar", cfg->scrollbar);
- write_setting_i(sesskey, "ScrollBarFullScreen", cfg->scrollbar_in_fullscreen);
- write_setting_i(sesskey, "ScrollOnKey", cfg->scroll_on_key);
- write_setting_i(sesskey, "ScrollOnDisp", cfg->scroll_on_disp);
- write_setting_i(sesskey, "EraseToScrollback", cfg->erase_to_scrollback);
- write_setting_i(sesskey, "LockSize", cfg->resize_action);
- write_setting_i(sesskey, "BCE", cfg->bce);
- write_setting_i(sesskey, "BlinkText", cfg->blinktext);
- 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);
- write_setting_fontspec(sesskey, "BoldFont", cfg->boldfont);
- write_setting_fontspec(sesskey, "WideFont", cfg->widefont);
- write_setting_fontspec(sesskey, "WideBoldFont", cfg->wideboldfont);
- write_setting_i(sesskey, "ShadowBold", cfg->shadowbold);
- write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset);
- write_setting_s(sesskey, "SerialLine", cfg->serline);
- write_setting_i(sesskey, "SerialSpeed", cfg->serspeed);
- write_setting_i(sesskey, "SerialDataBits", cfg->serdatabits);
- 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);
+ write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage));
+ write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide));
+ write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override));
+ write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer));
+ write_setting_i(sesskey, "CapsLockCyr", conf_get_int(conf, CONF_xlat_capslockcyr));
+ write_setting_i(sesskey, "ScrollBar", conf_get_int(conf, CONF_scrollbar));
+ write_setting_i(sesskey, "ScrollBarFullScreen", conf_get_int(conf, CONF_scrollbar_in_fullscreen));
+ write_setting_i(sesskey, "ScrollOnKey", conf_get_int(conf, CONF_scroll_on_key));
+ write_setting_i(sesskey, "ScrollOnDisp", conf_get_int(conf, CONF_scroll_on_disp));
+ write_setting_i(sesskey, "EraseToScrollback", conf_get_int(conf, CONF_erase_to_scrollback));
+ write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action));
+ write_setting_i(sesskey, "BCE", conf_get_int(conf, CONF_bce));
+ write_setting_i(sesskey, "BlinkText", conf_get_int(conf, CONF_blinktext));
+ write_setting_i(sesskey, "X11Forward", conf_get_int(conf, CONF_x11_forward));
+ write_setting_s(sesskey, "X11Display", conf_get_str(conf, CONF_x11_display));
+ write_setting_i(sesskey, "X11AuthType", conf_get_int(conf, CONF_x11_auth));
+ write_setting_filename(sesskey, "X11AuthFile", conf_get_filename(conf, CONF_xauthfile));
+ write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall));
+ write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall));
+ wmap(sesskey, "PortForwardings", conf, CONF_portfwd);
+ write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1));
+ write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1));
+ write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1));
+ write_setting_i(sesskey, "BugIgnore2", 2-conf_get_int(conf, CONF_sshbug_ignore2));
+ write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2));
+ write_setting_i(sesskey, "BugDeriveKey2", 2-conf_get_int(conf, CONF_sshbug_derivekey2));
+ write_setting_i(sesskey, "BugRSAPad2", 2-conf_get_int(conf, CONF_sshbug_rsapad2));
+ write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2));
+ write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2));
+ write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2));
+ write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp));
+ write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell));
+ write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left));
+ write_setting_fontspec(sesskey, "BoldFont", conf_get_fontspec(conf, CONF_boldfont));
+ write_setting_fontspec(sesskey, "WideFont", conf_get_fontspec(conf, CONF_widefont));
+ write_setting_fontspec(sesskey, "WideBoldFont", conf_get_fontspec(conf, CONF_wideboldfont));
+ write_setting_i(sesskey, "ShadowBold", conf_get_int(conf, CONF_shadowbold));
+ write_setting_i(sesskey, "ShadowBoldOffset", conf_get_int(conf, CONF_shadowboldoffset));
+ write_setting_s(sesskey, "SerialLine", conf_get_str(conf, CONF_serline));
+ write_setting_i(sesskey, "SerialSpeed", conf_get_int(conf, CONF_serspeed));
+ write_setting_i(sesskey, "SerialDataBits", conf_get_int(conf, CONF_serdatabits));
+ write_setting_i(sesskey, "SerialStopHalfbits", conf_get_int(conf, CONF_serstopbits));
+ write_setting_i(sesskey, "SerialParity", conf_get_int(conf, CONF_serparity));
+ write_setting_i(sesskey, "SerialFlowControl", conf_get_int(conf, CONF_serflow));
+ write_setting_s(sesskey, "WindowClass", conf_get_str(conf, CONF_winclass));
}
-void load_settings(char *section, Config * cfg)
+void load_settings(char *section, Conf *conf)
{
void *sesskey;
sesskey = open_settings_r(section);
- load_open_settings(sesskey, cfg);
+ load_open_settings(sesskey, conf);
close_settings_r(sesskey);
- if (cfg_launchable(cfg))
+ if (conf_launchable(conf))
add_session_to_jumplist(section);
}
-void load_open_settings(void *sesskey, Config *cfg)
+void load_open_settings(void *sesskey, Conf *conf)
{
int i;
- char prot[10];
-
- cfg->ssh_subsys = 0; /* FIXME: load this properly */
- cfg->remote_cmd_ptr = NULL;
- cfg->remote_cmd_ptr2 = NULL;
- cfg->ssh_nc_host[0] = '\0';
-
- gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host));
- gppfile(sesskey, "LogFileName", &cfg->logfilename);
- gppi(sesskey, "LogType", 0, &cfg->logtype);
- gppi(sesskey, "LogFileClash", LGXF_ASK, &cfg->logxfovr);
- gppi(sesskey, "LogFlush", 1, &cfg->logflush);
- gppi(sesskey, "SSHLogOmitPasswords", 1, &cfg->logomitpass);
- gppi(sesskey, "SSHLogOmitData", 0, &cfg->logomitdata);
-
- gpps(sesskey, "Protocol", "default", prot, 10);
- cfg->protocol = default_protocol;
- cfg->port = default_port;
+ char *prot;
+
+ conf_set_int(conf, CONF_ssh_subsys, 0); /* FIXME: load this properly */
+ conf_set_str(conf, CONF_remote_cmd, "");
+ conf_set_str(conf, CONF_remote_cmd2, "");
+ conf_set_str(conf, CONF_ssh_nc_host, "");
+
+ gpps(sesskey, "HostName", "", conf, CONF_host);
+ gppfile(sesskey, "LogFileName", conf, CONF_logfilename);
+ gppi(sesskey, "LogType", 0, conf, CONF_logtype);
+ gppi(sesskey, "LogFileClash", LGXF_ASK, conf, CONF_logxfovr);
+ gppi(sesskey, "LogFlush", 1, conf, CONF_logflush);
+ gppi(sesskey, "SSHLogOmitPasswords", 1, conf, CONF_logomitpass);
+ gppi(sesskey, "SSHLogOmitData", 0, conf, CONF_logomitdata);
+
+ prot = gpps_raw(sesskey, "Protocol", "default");
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ conf_set_int(conf, CONF_port, default_port);
{
const Backend *b = backend_from_name(prot);
if (b) {
- cfg->protocol = b->protocol;
- gppi(sesskey, "PortNumber", default_port, &cfg->port);
+ conf_set_int(conf, CONF_protocol, b->protocol);
+ gppi(sesskey, "PortNumber", default_port, conf, CONF_port);
}
}
+ sfree(prot);
/* Address family selection */
- gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily);
+ gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, conf, CONF_addressfamily);
/* The CloseOnExit numbers are arranged in a different order from
* the standard FORCE_ON / FORCE_OFF / AUTO. */
- gppi(sesskey, "CloseOnExit", 1, &i); cfg->close_on_exit = (i+1)%3;
- gppi(sesskey, "WarnOnClose", 1, &cfg->warn_on_close);
+ i = gppi_raw(sesskey, "CloseOnExit", 1); conf_set_int(conf, CONF_close_on_exit, (i+1)%3);
+ gppi(sesskey, "WarnOnClose", 1, conf, CONF_warn_on_close);
{
/* This is two values for backward compatibility with 0.50/0.51 */
int pingmin, pingsec;
- gppi(sesskey, "PingInterval", 0, &pingmin);
- gppi(sesskey, "PingIntervalSecs", 0, &pingsec);
- cfg->ping_interval = pingmin * 60 + pingsec;
+ pingmin = gppi_raw(sesskey, "PingInterval", 0);
+ pingsec = gppi_raw(sesskey, "PingIntervalSecs", 0);
+ conf_set_int(conf, CONF_ping_interval, pingmin * 60 + pingsec);
}
- gppi(sesskey, "TCPNoDelay", 1, &cfg->tcp_nodelay);
- gppi(sesskey, "TCPKeepalives", 0, &cfg->tcp_keepalives);
- gpps(sesskey, "TerminalType", "xterm", cfg->termtype,
- sizeof(cfg->termtype));
- gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed,
- sizeof(cfg->termspeed));
- {
+ gppi(sesskey, "TCPNoDelay", 1, conf, CONF_tcp_nodelay);
+ gppi(sesskey, "TCPKeepalives", 0, conf, CONF_tcp_keepalives);
+ gpps(sesskey, "TerminalType", "xterm", conf, CONF_termtype);
+ gpps(sesskey, "TerminalSpeed", "38400,38400", conf, CONF_termspeed);
+ if (!gppmap(sesskey, "TerminalModes", conf, CONF_ttymodes)) {
/* This hardcodes a big set of defaults in any new saved
* sessions. Let's hope we don't change our mind. */
- int i;
- char *def = dupstr("");
- /* Default: all set to "auto" */
- for (i = 0; ttymodes[i]; i++) {
- char *def2 = dupprintf("%s%s=A,", def, ttymodes[i]);
- sfree(def);
- def = def2;
- }
- gppmap(sesskey, "TerminalModes", def,
- cfg->ttymodes, lenof(cfg->ttymodes));
- sfree(def);
+ for (i = 0; ttymodes[i]; i++)
+ conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A");
}
/* proxy settings */
- gpps(sesskey, "ProxyExcludeList", "", cfg->proxy_exclude_list,
- sizeof(cfg->proxy_exclude_list));
- gppi(sesskey, "ProxyDNS", 1, &i); cfg->proxy_dns = (i+1)%3;
- gppi(sesskey, "ProxyLocalhost", 0, &cfg->even_proxy_localhost);
- gppi(sesskey, "ProxyMethod", -1, &cfg->proxy_type);
- if (cfg->proxy_type == -1) {
+ gpps(sesskey, "ProxyExcludeList", "", conf, CONF_proxy_exclude_list);
+ i = gppi_raw(sesskey, "ProxyDNS", 1); conf_set_int(conf, CONF_proxy_dns, (i+1)%3);
+ gppi(sesskey, "ProxyLocalhost", 0, conf, CONF_even_proxy_localhost);
+ gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type);
+ if (conf_get_int(conf, CONF_proxy_type) == -1) {
int i;
- gppi(sesskey, "ProxyType", 0, &i);
+ i = gppi_raw(sesskey, "ProxyType", 0);
if (i == 0)
- cfg->proxy_type = PROXY_NONE;
+ conf_set_int(conf, CONF_proxy_type, PROXY_NONE);
else if (i == 1)
- cfg->proxy_type = PROXY_HTTP;
+ conf_set_int(conf, CONF_proxy_type, PROXY_HTTP);
else if (i == 3)
- cfg->proxy_type = PROXY_TELNET;
+ conf_set_int(conf, CONF_proxy_type, PROXY_TELNET);
else if (i == 4)
- cfg->proxy_type = PROXY_CMD;
+ conf_set_int(conf, CONF_proxy_type, PROXY_CMD);
else {
- gppi(sesskey, "ProxySOCKSVersion", 5, &i);
+ i = gppi_raw(sesskey, "ProxySOCKSVersion", 5);
if (i == 5)
- cfg->proxy_type = PROXY_SOCKS5;
+ conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5);
else
- cfg->proxy_type = PROXY_SOCKS4;
+ conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4);
}
}
- gpps(sesskey, "ProxyHost", "proxy", cfg->proxy_host,
- sizeof(cfg->proxy_host));
- gppi(sesskey, "ProxyPort", 80, &cfg->proxy_port);
- gpps(sesskey, "ProxyUsername", "", cfg->proxy_username,
- sizeof(cfg->proxy_username));
- gpps(sesskey, "ProxyPassword", "", cfg->proxy_password,
- sizeof(cfg->proxy_password));
+ gpps(sesskey, "ProxyHost", "proxy", conf, CONF_proxy_host);
+ gppi(sesskey, "ProxyPort", 80, conf, CONF_proxy_port);
+ gpps(sesskey, "ProxyUsername", "", conf, CONF_proxy_username);
+ gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password);
gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n",
- 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);
- gppi(sesskey, "Compression", 0, &cfg->compression);
- 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);
+ conf, CONF_proxy_telnet_command);
+ gppmap(sesskey, "Environment", conf, CONF_environmt);
+ gpps(sesskey, "UserName", "", conf, CONF_username);
+ gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env);
+ gpps(sesskey, "LocalUserName", "", conf, CONF_localusername);
+ gppi(sesskey, "NoPTY", 0, conf, CONF_nopty);
+ gppi(sesskey, "Compression", 0, conf, CONF_compression);
+ gppi(sesskey, "TryAgent", 1, conf, CONF_tryagent);
+ gppi(sesskey, "AgentFwd", 0, conf, CONF_agentfwd);
+ gppi(sesskey, "ChangeUsername", 0, conf, CONF_change_username);
+ gppi(sesskey, "GssapiFwd", 0, conf, CONF_gssapifwd);
gprefs(sesskey, "Cipher", "\0",
- ciphernames, CIPHER_MAX, cfg->ssh_cipherlist);
+ ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist);
{
/* Backward-compatibility: we used to have an option to
* disable gex under the "bugs" panel after one report of
* a server which offered it then choked, but we never got
* a server version string or any other reports. */
char *default_kexes;
- gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;
+ i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0);
if (i == FORCE_ON)
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,rsa,WARN";
gprefs(sesskey, "KEX", default_kexes,
- kexnames, KEX_MAX, cfg->ssh_kexlist);
+ kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
}
- gppi(sesskey, "RekeyTime", 60, &cfg->ssh_rekey_time);
- 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);
+ gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time);
+ gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data);
+ gppi(sesskey, "SshProt", 2, conf, CONF_sshprot);
+ gpps(sesskey, "LogHost", "", conf, CONF_loghost);
+ gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc);
+ gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth);
+ gppi(sesskey, "SshBanner", 1, conf, CONF_ssh_show_banner);
+ gppi(sesskey, "AuthTIS", 0, conf, CONF_try_tis_auth);
+ gppi(sesskey, "AuthKI", 1, conf, CONF_try_ki_auth);
+ gppi(sesskey, "AuthGSSAPI", 1, conf, CONF_try_gssapi_auth);
#ifndef NO_GSSAPI
gprefs(sesskey, "GSSLibs", "\0",
- gsslibkeywords, ngsslibs, cfg->ssh_gsslist);
- gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom);
+ gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist);
+ gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom);
#endif
- gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell);
- gppfile(sesskey, "PublicKeyFile", &cfg->keyfile);
- gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd,
- sizeof(cfg->remote_cmd));
- gppi(sesskey, "RFCEnviron", 0, &cfg->rfc_environ);
- gppi(sesskey, "PassiveTelnet", 0, &cfg->passive_telnet);
- gppi(sesskey, "BackspaceIsDelete", 1, &cfg->bksp_is_delete);
- gppi(sesskey, "RXVTHomeEnd", 0, &cfg->rxvt_homeend);
- gppi(sesskey, "LinuxFunctionKeys", 0, &cfg->funky_type);
- gppi(sesskey, "NoApplicationKeys", 0, &cfg->no_applic_k);
- gppi(sesskey, "NoApplicationCursors", 0, &cfg->no_applic_c);
- gppi(sesskey, "NoMouseReporting", 0, &cfg->no_mouse_rep);
- gppi(sesskey, "NoRemoteResize", 0, &cfg->no_remote_resize);
- gppi(sesskey, "NoAltScreen", 0, &cfg->no_alt_screen);
- gppi(sesskey, "NoRemoteWinTitle", 0, &cfg->no_remote_wintitle);
+ gppi(sesskey, "SshNoShell", 0, conf, CONF_ssh_no_shell);
+ gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile);
+ gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd);
+ gppi(sesskey, "RFCEnviron", 0, conf, CONF_rfc_environ);
+ gppi(sesskey, "PassiveTelnet", 0, conf, CONF_passive_telnet);
+ gppi(sesskey, "BackspaceIsDelete", 1, conf, CONF_bksp_is_delete);
+ gppi(sesskey, "RXVTHomeEnd", 0, conf, CONF_rxvt_homeend);
+ gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type);
+ gppi(sesskey, "NoApplicationKeys", 0, conf, CONF_no_applic_k);
+ gppi(sesskey, "NoApplicationCursors", 0, conf, CONF_no_applic_c);
+ gppi(sesskey, "NoMouseReporting", 0, conf, CONF_no_mouse_rep);
+ gppi(sesskey, "NoRemoteResize", 0, conf, CONF_no_remote_resize);
+ gppi(sesskey, "NoAltScreen", 0, conf, CONF_no_alt_screen);
+ gppi(sesskey, "NoRemoteWinTitle", 0, conf, CONF_no_remote_wintitle);
{
/* Backward compatibility */
- int no_remote_qtitle;
- gppi(sesskey, "NoRemoteQTitle", 1, &no_remote_qtitle);
+ int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1);
/* We deliberately interpret the old setting of "no response" as
* "empty string". This changes the behaviour, but hopefully for
* the better; the user can always recover the old behaviour. */
gppi(sesskey, "RemoteQTitleAction",
no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL,
- &cfg->remote_qtitle_action);
+ conf, CONF_remote_qtitle_action);
}
- gppi(sesskey, "NoDBackspace", 0, &cfg->no_dbackspace);
- gppi(sesskey, "NoRemoteCharset", 0, &cfg->no_remote_charset);
- gppi(sesskey, "ApplicationCursorKeys", 0, &cfg->app_cursor);
- gppi(sesskey, "ApplicationKeypad", 0, &cfg->app_keypad);
- gppi(sesskey, "NetHackKeypad", 0, &cfg->nethack_keypad);
- gppi(sesskey, "AltF4", 1, &cfg->alt_f4);
- gppi(sesskey, "AltSpace", 0, &cfg->alt_space);
- gppi(sesskey, "AltOnly", 0, &cfg->alt_only);
- gppi(sesskey, "ComposeKey", 0, &cfg->compose_key);
- gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
- gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard);
- gppi(sesskey, "TelnetRet", 1, &cfg->telnet_newline);
- gppi(sesskey, "LocalEcho", AUTO, &cfg->localecho);
- gppi(sesskey, "LocalEdit", AUTO, &cfg->localedit);
- gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,
- sizeof(cfg->answerback));
- gppi(sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop);
- gppi(sesskey, "FullScreenOnAltEnter", 0, &cfg->fullscreenonaltenter);
- gppi(sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr);
- gppi(sesskey, "SunkenEdge", 0, &cfg->sunken_edge);
- gppi(sesskey, "WindowBorder", 1, &cfg->window_border);
- gppi(sesskey, "CurType", 0, &cfg->cursor_type);
- gppi(sesskey, "BlinkCur", 0, &cfg->blink_cur);
- /* pedantic compiler tells me I can't use &cfg->beep as an int * :-) */
- gppi(sesskey, "Beep", 1, &cfg->beep);
- gppi(sesskey, "BeepInd", 0, &cfg->beep_ind);
- gppfile(sesskey, "BellWaveFile", &cfg->bell_wavefile);
- gppi(sesskey, "BellOverload", 1, &cfg->bellovl);
- gppi(sesskey, "BellOverloadN", 5, &cfg->bellovl_n);
- gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC
+ gppi(sesskey, "NoDBackspace", 0, conf, CONF_no_dbackspace);
+ gppi(sesskey, "NoRemoteCharset", 0, conf, CONF_no_remote_charset);
+ gppi(sesskey, "ApplicationCursorKeys", 0, conf, CONF_app_cursor);
+ gppi(sesskey, "ApplicationKeypad", 0, conf, CONF_app_keypad);
+ gppi(sesskey, "NetHackKeypad", 0, conf, CONF_nethack_keypad);
+ gppi(sesskey, "AltF4", 1, conf, CONF_alt_f4);
+ gppi(sesskey, "AltSpace", 0, conf, CONF_alt_space);
+ gppi(sesskey, "AltOnly", 0, conf, CONF_alt_only);
+ gppi(sesskey, "ComposeKey", 0, conf, CONF_compose_key);
+ gppi(sesskey, "CtrlAltKeys", 1, conf, CONF_ctrlaltkeys);
+ gppi(sesskey, "TelnetKey", 0, conf, CONF_telnet_keyboard);
+ gppi(sesskey, "TelnetRet", 1, conf, CONF_telnet_newline);
+ gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho);
+ gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit);
+ gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback);
+ gppi(sesskey, "AlwaysOnTop", 0, conf, CONF_alwaysontop);
+ gppi(sesskey, "FullScreenOnAltEnter", 0, conf, CONF_fullscreenonaltenter);
+ gppi(sesskey, "HideMousePtr", 0, conf, CONF_hide_mouseptr);
+ gppi(sesskey, "SunkenEdge", 0, conf, CONF_sunken_edge);
+ gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border);
+ gppi(sesskey, "CurType", 0, conf, CONF_cursor_type);
+ gppi(sesskey, "BlinkCur", 0, conf, CONF_blink_cur);
+ /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */
+ gppi(sesskey, "Beep", 1, conf, CONF_beep);
+ gppi(sesskey, "BeepInd", 0, conf, CONF_beep_ind);
+ gppfile(sesskey, "BellWaveFile", conf, CONF_bell_wavefile);
+ gppi(sesskey, "BellOverload", 1, conf, CONF_bellovl);
+ gppi(sesskey, "BellOverloadN", 5, conf, CONF_bellovl_n);
+ i = gppi_raw(sesskey, "BellOverloadT", 2*TICKSPERSEC
#ifdef PUTTY_UNIX_H
*1000
#endif
- , &i);
- cfg->bellovl_t = i
+ );
+ conf_set_int(conf, CONF_bellovl_t, i
#ifdef PUTTY_UNIX_H
- / 1000
+ / 1000
#endif
- ;
- gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC
+ );
+ i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC
#ifdef PUTTY_UNIX_H
*1000
#endif
- , &i);
- cfg->bellovl_s = i
+ );
+ conf_set_int(conf, CONF_bellovl_s, i
#ifdef PUTTY_UNIX_H
- / 1000
+ / 1000
#endif
- ;
- gppi(sesskey, "ScrollbackLines", 200, &cfg->savelines);
- 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);
- gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle));
- gppi(sesskey, "TermWidth", 80, &cfg->width);
- gppi(sesskey, "TermHeight", 24, &cfg->height);
- gppfont(sesskey, "Font", &cfg->font);
- gppi(sesskey, "FontQuality", FQ_DEFAULT, &cfg->font_quality);
- gppi(sesskey, "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode);
- gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour);
- gppi(sesskey, "TryPalette", 0, &cfg->try_palette);
- gppi(sesskey, "ANSIColour", 1, &cfg->ansi_colour);
- gppi(sesskey, "Xterm256Colour", 1, &cfg->xterm_256_colour);
- gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour);
+ );
+ gppi(sesskey, "ScrollbackLines", 200, conf, CONF_savelines);
+ gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om);
+ gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode);
+ gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr);
+ gppi(sesskey, "CRImpliesLF", 0, conf, CONF_crhaslf);
+ gppi(sesskey, "DisableArabicShaping", 0, conf, CONF_arabicshaping);
+ gppi(sesskey, "DisableBidi", 0, conf, CONF_bidi);
+ gppi(sesskey, "WinNameAlways", 1, conf, CONF_win_name_always);
+ gpps(sesskey, "WinTitle", "", conf, CONF_wintitle);
+ gppi(sesskey, "TermWidth", 80, conf, CONF_width);
+ gppi(sesskey, "TermHeight", 24, conf, CONF_height);
+ gppfont(sesskey, "Font", conf, CONF_font);
+ gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality);
+ gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode);
+ gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour);
+ gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette);
+ gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour);
+ gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour);
+ gppi(sesskey, "BoldAsColour", 1, conf, CONF_bold_colour);
for (i = 0; i < 22; i++) {
static const char *const defaults[] = {
@@ -759,21 +875,22 @@ void load_open_settings(void *sesskey, Config *cfg)
"85,85,255", "187,0,187", "255,85,255", "0,187,187",
"85,255,255", "187,187,187", "255,255,255"
};
- char buf[20], buf2[30];
+ char buf[20], *buf2;
int c0, c1, c2;
sprintf(buf, "Colour%d", i);
- gpps(sesskey, buf, defaults[i], buf2, sizeof(buf2));
+ buf2 = gpps_raw(sesskey, buf, defaults[i]);
if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) {
- cfg->colours[i][0] = c0;
- cfg->colours[i][1] = c1;
- cfg->colours[i][2] = c2;
+ conf_set_int_int(conf, CONF_colours, i*3+0, c0);
+ conf_set_int_int(conf, CONF_colours, i*3+1, c1);
+ conf_set_int_int(conf, CONF_colours, i*3+2, c2);
}
+ sfree(buf2);
}
- gppi(sesskey, "RawCNP", 0, &cfg->rawcnp);
- gppi(sesskey, "PasteRTF", 0, &cfg->rtf_paste);
- gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm);
- gppi(sesskey, "RectSelect", 0, &cfg->rect_select);
- gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override);
+ gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp);
+ gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste);
+ gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm);
+ gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select);
+ gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override);
for (i = 0; i < 256; i += 32) {
static const char *const defaults[] = {
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
@@ -785,10 +902,10 @@ void load_open_settings(void *sesskey, Config *cfg)
"2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2",
"2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2"
};
- char buf[20], buf2[256], *p;
+ char buf[20], *buf2, *p;
int j;
sprintf(buf, "Wordness%d", i);
- gpps(sesskey, buf, defaults[i / 32], buf2, sizeof(buf2));
+ buf2 = gpps_raw(sesskey, buf, defaults[i / 32]);
p = buf2;
for (j = i; j < i + 32; j++) {
char *q = p;
@@ -796,75 +913,74 @@ void load_open_settings(void *sesskey, Config *cfg)
p++;
if (*p == ',')
*p++ = '\0';
- cfg->wordness[j] = atoi(q);
+ conf_set_int_int(conf, CONF_wordness, j, atoi(q));
}
+ sfree(buf2);
}
/*
* The empty default for LineCodePage will be converted later
* into a plausible default for the locale.
*/
- gpps(sesskey, "LineCodePage", "", cfg->line_codepage,
- sizeof(cfg->line_codepage));
- gppi(sesskey, "CJKAmbigWide", 0, &cfg->cjk_ambig_wide);
- gppi(sesskey, "UTF8Override", 1, &cfg->utf8_override);
- gpps(sesskey, "Printer", "", cfg->printer, sizeof(cfg->printer));
- gppi (sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr);
- gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar);
- gppi(sesskey, "ScrollBarFullScreen", 0, &cfg->scrollbar_in_fullscreen);
- gppi(sesskey, "ScrollOnKey", 0, &cfg->scroll_on_key);
- gppi(sesskey, "ScrollOnDisp", 1, &cfg->scroll_on_disp);
- gppi(sesskey, "EraseToScrollback", 1, &cfg->erase_to_scrollback);
- gppi(sesskey, "LockSize", 0, &cfg->resize_action);
- gppi(sesskey, "BCE", 1, &cfg->bce);
- gppi(sesskey, "BlinkText", 0, &cfg->blinktext);
- gppi(sesskey, "X11Forward", 0, &cfg->x11_forward);
- 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);
- gppmap(sesskey, "PortForwardings", "", cfg->portfwd, lenof(cfg->portfwd));
- 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;
+ gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage);
+ gppi(sesskey, "CJKAmbigWide", 0, conf, CONF_cjk_ambig_wide);
+ gppi(sesskey, "UTF8Override", 1, conf, CONF_utf8_override);
+ gpps(sesskey, "Printer", "", conf, CONF_printer);
+ gppi(sesskey, "CapsLockCyr", 0, conf, CONF_xlat_capslockcyr);
+ gppi(sesskey, "ScrollBar", 1, conf, CONF_scrollbar);
+ gppi(sesskey, "ScrollBarFullScreen", 0, conf, CONF_scrollbar_in_fullscreen);
+ gppi(sesskey, "ScrollOnKey", 0, conf, CONF_scroll_on_key);
+ gppi(sesskey, "ScrollOnDisp", 1, conf, CONF_scroll_on_disp);
+ gppi(sesskey, "EraseToScrollback", 1, conf, CONF_erase_to_scrollback);
+ gppi(sesskey, "LockSize", 0, conf, CONF_resize_action);
+ gppi(sesskey, "BCE", 1, conf, CONF_bce);
+ gppi(sesskey, "BlinkText", 0, conf, CONF_blinktext);
+ gppi(sesskey, "X11Forward", 0, conf, CONF_x11_forward);
+ gpps(sesskey, "X11Display", "", conf, CONF_x11_display);
+ gppi(sesskey, "X11AuthType", X11_MIT, conf, CONF_x11_auth);
+ gppfile(sesskey, "X11AuthFile", conf, CONF_xauthfile);
+
+ gppi(sesskey, "LocalPortAcceptAll", 0, conf, CONF_lport_acceptall);
+ gppi(sesskey, "RemotePortAcceptAll", 0, conf, CONF_rport_acceptall);
+ gppmap(sesskey, "PortForwardings", conf, CONF_portfwd);
+ i = gppi_raw(sesskey, "BugIgnore1", 0); conf_set_int(conf, CONF_sshbug_ignore1, 2-i);
+ i = gppi_raw(sesskey, "BugPlainPW1", 0); conf_set_int(conf, CONF_sshbug_plainpw1, 2-i);
+ i = gppi_raw(sesskey, "BugRSA1", 0); conf_set_int(conf, CONF_sshbug_rsa1, 2-i);
+ i = gppi_raw(sesskey, "BugIgnore2", 0); conf_set_int(conf, CONF_sshbug_ignore2, 2-i);
{
int i;
- gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
- if (cfg->sshbug_hmac2 == AUTO) {
- gppi(sesskey, "BuggyMAC", 0, &i);
+ i = gppi_raw(sesskey, "BugHMAC2", 0); conf_set_int(conf, CONF_sshbug_hmac2, 2-i);
+ if (2-i == AUTO) {
+ i = gppi_raw(sesskey, "BuggyMAC", 0);
if (i == 1)
- cfg->sshbug_hmac2 = FORCE_ON;
+ conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON);
}
}
- gppi(sesskey, "BugDeriveKey2", 0, &i); cfg->sshbug_derivekey2 = 2-i;
- 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);
- gppi(sesskey, "ShadowBold", 0, &cfg->shadowbold);
- gppfont(sesskey, "BoldFont", &cfg->boldfont);
- gppfont(sesskey, "WideFont", &cfg->widefont);
- gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont);
- gppi(sesskey, "ShadowBoldOffset", 1, &cfg->shadowboldoffset);
- gpps(sesskey, "SerialLine", "", cfg->serline, sizeof(cfg->serline));
- gppi(sesskey, "SerialSpeed", 9600, &cfg->serspeed);
- gppi(sesskey, "SerialDataBits", 8, &cfg->serdatabits);
- 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));
+ i = gppi_raw(sesskey, "BugDeriveKey2", 0); conf_set_int(conf, CONF_sshbug_derivekey2, 2-i);
+ i = gppi_raw(sesskey, "BugRSAPad2", 0); conf_set_int(conf, CONF_sshbug_rsapad2, 2-i);
+ i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i);
+ i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i);
+ i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i);
+ conf_set_int(conf, CONF_ssh_simple, FALSE);
+ gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp);
+ gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell);
+ gppi(sesskey, "ScrollbarOnLeft", 0, conf, CONF_scrollbar_on_left);
+ gppi(sesskey, "ShadowBold", 0, conf, CONF_shadowbold);
+ gppfont(sesskey, "BoldFont", conf, CONF_boldfont);
+ gppfont(sesskey, "WideFont", conf, CONF_widefont);
+ gppfont(sesskey, "WideBoldFont", conf, CONF_wideboldfont);
+ gppi(sesskey, "ShadowBoldOffset", 1, conf, CONF_shadowboldoffset);
+ gpps(sesskey, "SerialLine", "", conf, CONF_serline);
+ gppi(sesskey, "SerialSpeed", 9600, conf, CONF_serspeed);
+ gppi(sesskey, "SerialDataBits", 8, conf, CONF_serdatabits);
+ gppi(sesskey, "SerialStopHalfbits", 2, conf, CONF_serstopbits);
+ gppi(sesskey, "SerialParity", SER_PAR_NONE, conf, CONF_serparity);
+ gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, conf, CONF_serflow);
+ gpps(sesskey, "WindowClass", "", conf, CONF_winclass);
}
-void do_defaults(char *session, Config * cfg)
+void do_defaults(char *session, Conf *conf)
{
- load_settings(session, cfg);
+ load_settings(session, conf);
}
static int sessioncmp(const void *av, const void *bv)
diff --git a/tools/plink/ssh.c b/tools/plink/ssh.c
index 0982f84a4..8f1aa15de 100644
--- a/tools/plink/ssh.c
+++ b/tools/plink/ssh.c
@@ -473,6 +473,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin);
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin);
+static void ssh2_channel_check_close(struct ssh_channel *c);
+static void ssh_channel_destroy(struct ssh_channel *c);
/*
* Buffer management constants. There are several of these for
@@ -515,9 +517,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
#define OUR_V2_MAXPKT 0x4000UL
#define OUR_V2_PACKETLIMIT 0x9000UL
-/* Maximum length of passwords/passphrases (arbitrary) */
-#define SSH_MAX_PASSWORD_LEN 100
-
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
const static struct ssh_mac *macs[] = {
@@ -544,7 +543,7 @@ static int ssh_comp_none_disable(void *handle)
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
@@ -559,7 +558,15 @@ enum { /* channel types */
CHAN_X11,
CHAN_AGENT,
CHAN_SOCKDATA,
- CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */
+ CHAN_SOCKDATA_DORMANT, /* one the remote hasn't confirmed */
+ /*
+ * CHAN_ZOMBIE is used to indicate a channel for which we've
+ * already destroyed the local data source: for instance, if a
+ * forwarded port experiences a socket error on the local side, we
+ * immediately destroy its local socket and turn the SSH channel
+ * into CHAN_ZOMBIE.
+ */
+ CHAN_ZOMBIE
};
/*
@@ -588,18 +595,35 @@ struct ssh_channel {
* 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
*
* A channel is completely finished with when all four bits are set.
+ *
+ * In SSH-2, the four bits mean:
+ *
+ * 1 We have sent SSH2_MSG_CHANNEL_EOF.
+ * 2 We have sent SSH2_MSG_CHANNEL_CLOSE.
+ * 4 We have received SSH2_MSG_CHANNEL_EOF.
+ * 8 We have received SSH2_MSG_CHANNEL_CLOSE.
+ *
+ * A channel is completely finished with when we have both sent
+ * and received CLOSE.
+ *
+ * The symbolic constants below use the SSH-2 terminology, which
+ * is a bit confusing in SSH-1, but we have to use _something_.
*/
+#define CLOSES_SENT_EOF 1
+#define CLOSES_SENT_CLOSE 2
+#define CLOSES_RCVD_EOF 4
+#define CLOSES_RCVD_CLOSE 8
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.
+ * This flag indicates that an EOF 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 send the EOF until we've finished sending the data, so
+ * we set this flag instead to remind us to do so once our buffer
+ * is clear.
*/
- int pending_close;
+ int pending_eof;
/*
* True if this channel is causing the underlying connection to be
@@ -830,6 +854,8 @@ struct ssh_tag {
} state;
int size_needed, eof_needed;
+ int sent_console_eof;
+ int got_pty; /* affects EOF behaviour on main channel */
struct Packet **queue;
int queuelen, queuesize;
@@ -884,12 +910,26 @@ struct ssh_tag {
struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
/*
- * We maintain a full _copy_ of a Config structure here, not
- * merely a pointer to it. That way, when we're passed a new
- * one for reconfiguration, we can check the differences and
- * potentially reconfigure port forwardings etc in mid-session.
+ * We maintain our own copy of a Conf structure here. That way,
+ * when we're passed a new one for reconfiguration, we can check
+ * the differences and potentially reconfigure port forwardings
+ * etc in mid-session.
*/
- Config cfg;
+ Conf *conf;
+
+ /*
+ * Values cached out of conf so as to avoid the tree234 lookup
+ * cost every time they're used.
+ */
+ int logomitdata;
+
+ /*
+ * Dynamically allocated username string created during SSH
+ * login. Stored in here rather than in the coroutine state so
+ * that it'll be reliably freed if we shut down the SSH session
+ * at some unexpected moment.
+ */
+ char *username;
/*
* Used to transfer data back from async callbacks.
@@ -978,13 +1018,13 @@ static void logeventf(Ssh ssh, const char *fmt, ...)
static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype)
{
- if (ssh->cfg.logomitpass)
+ if (conf_get_int(ssh->conf, CONF_logomitpass))
pkt->logmode = blanktype;
}
static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype)
{
- if (ssh->cfg.logomitdata)
+ if (ssh->logomitdata)
pkt->logmode = blanktype;
}
@@ -993,26 +1033,27 @@ static void end_log_omission(Ssh ssh, struct Packet *pkt)
pkt->logmode = PKTLOG_EMIT;
}
-/* Helper function for common bits of parsing cfg.ttymodes. */
-static void parse_ttymodes(Ssh ssh, char *modes,
+/* Helper function for common bits of parsing ttymodes. */
+static void parse_ttymodes(Ssh ssh,
void (*do_mode)(void *data, char *mode, char *val),
void *data)
{
- while (*modes) {
- char *t = strchr(modes, '\t');
- char *m = snewn(t-modes+1, char);
- char *val;
- strncpy(m, modes, t-modes);
- m[t-modes] = '\0';
- if (*(t+1) == 'A')
- val = get_ttymode(ssh->frontend, m);
+ char *key, *val;
+
+ for (val = conf_get_str_strs(ssh->conf, CONF_ttymodes, NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(ssh->conf, CONF_ttymodes, key, &key)) {
+ /*
+ * val[0] is either 'V', indicating that an explicit value
+ * follows it, or 'A' indicating that we should pass the
+ * value through from the local environment via get_ttymode.
+ */
+ if (val[0] == 'A')
+ val = get_ttymode(ssh->frontend, key);
else
- val = dupstr(t+2);
+ val++; /* skip the 'V' */
if (val)
- do_mode(data, m, val);
- sfree(m);
- sfree(val);
- modes += strlen(modes) + 1;
+ do_mode(data, key, val);
}
}
@@ -1300,7 +1341,7 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
if (ssh->logctx) {
int nblanks = 0;
struct logblank_t blank;
- if (ssh->cfg.logomitdata) {
+ if (ssh->logomitdata) {
int do_blank = FALSE, blank_prefix = 0;
/* "Session data" packets - omit the data field */
if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||
@@ -1533,7 +1574,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
if (ssh->logctx) {
int nblanks = 0;
struct logblank_t blank;
- if (ssh->cfg.logomitdata) {
+ if (ssh->logomitdata) {
int do_blank = FALSE, blank_prefix = 0;
/* "Session data" packets - omit the data field */
if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) {
@@ -2417,8 +2458,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
* with SSH1_MSG_IGNOREs -- but this string never seems to change,
* so we can't distinguish them.
*/
- if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
- (ssh->cfg.sshbug_ignore1 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == AUTO &&
(!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
!strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
!strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
@@ -2432,8 +2473,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version has SSH-1 ignore bug");
}
- if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
- (ssh->cfg.sshbug_plainpw1 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == AUTO &&
(!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
/*
* These versions need a plain password sent; they can't
@@ -2444,8 +2485,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version needs a plain SSH-1 password");
}
- if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
- (ssh->cfg.sshbug_rsa1 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == AUTO &&
(!strcmp(imp, "Cisco-1.25")))) {
/*
* These versions apparently have no clue whatever about
@@ -2456,8 +2497,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version can't handle SSH-1 RSA authentication");
}
- if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
- (ssh->cfg.sshbug_hmac2 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == AUTO &&
!wc_match("* VShell", imp) &&
(wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
@@ -2469,8 +2510,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version has SSH-2 HMAC bug");
}
- if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
- (ssh->cfg.sshbug_derivekey2 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == AUTO &&
!wc_match("* VShell", imp) &&
(wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
/*
@@ -2482,8 +2523,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version has SSH-2 key-derivation bug");
}
- if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
- (ssh->cfg.sshbug_rsapad2 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == AUTO &&
(wc_match("OpenSSH_2.[5-9]*", imp) ||
wc_match("OpenSSH_3.[0-2]*", imp)))) {
/*
@@ -2493,8 +2534,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version has SSH-2 RSA padding bug");
}
- if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||
- (ssh->cfg.sshbug_pksessid2 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == AUTO &&
wc_match("OpenSSH_2.[0-2]*", imp))) {
/*
* These versions have the SSH-2 session-ID bug in
@@ -2504,8 +2545,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version has SSH-2 public-key-session-ID bug");
}
- if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||
- (ssh->cfg.sshbug_rekey2 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == AUTO &&
(wc_match("DigiSSH_2.0", imp) ||
wc_match("OpenSSH_2.[0-4]*", imp) ||
wc_match("OpenSSH_2.5.[0-3]*", imp) ||
@@ -2520,8 +2561,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version has SSH-2 rekey bug");
}
- if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON ||
- (ssh->cfg.sshbug_maxpkt2 == AUTO &&
+ if (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == AUTO &&
(wc_match("1.36_sshlib GlobalSCAPE", imp) ||
wc_match("1.36 sshlib: GlobalScape", imp)))) {
/*
@@ -2531,7 +2572,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
logevent("We believe remote version ignores SSH-2 maximum packet size");
}
- if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+ if (conf_get_int(ssh->conf, CONF_sshbug_ignore2) == FORCE_ON) {
/*
* Servers that don't support SSH2_MSG_IGNORE. Currently,
* none detected automatically.
@@ -2674,16 +2715,16 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
/* Anything greater or equal to "1.99" means protocol 2 is supported. */
s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0;
- if (ssh->cfg.sshprot == 0 && !s->proto1) {
+ if (conf_get_int(ssh->conf, CONF_sshprot) == 0 && !s->proto1) {
bombout(("SSH protocol version 1 required by user but not provided by server"));
crStop(0);
}
- if (ssh->cfg.sshprot == 3 && !s->proto2) {
+ if (conf_get_int(ssh->conf, CONF_sshprot) == 3 && !s->proto2) {
bombout(("SSH protocol version 2 required by user but not provided by server"));
crStop(0);
}
- if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1))
+ if (s->proto2 && (conf_get_int(ssh->conf, CONF_sshprot) >= 2 || !s->proto1))
ssh->version = 2;
else
ssh->version = 1;
@@ -2691,7 +2732,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
logeventf(ssh, "Using SSH protocol version %d", ssh->version);
/* Send the version string, if we haven't already */
- if (ssh->cfg.sshprot != 3)
+ if (conf_get_int(ssh->conf, CONF_sshprot) != 3)
ssh_send_verstring(ssh, s->version);
if (ssh->version == 2) {
@@ -2723,7 +2764,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
update_specials_menu(ssh->frontend);
ssh->state = SSH_STATE_BEFORE_SIZE;
- ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh);
+ ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh);
sfree(s->vstring);
@@ -2976,11 +3017,14 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
SockAddr addr;
const char *err;
-
- if (*ssh->cfg.loghost) {
+ char *loghost;
+ int addressfamily, sshprot;
+
+ loghost = conf_get_str(ssh->conf, CONF_loghost);
+ if (*loghost) {
char *colon;
- ssh->savedhost = dupstr(ssh->cfg.loghost);
+ ssh->savedhost = dupstr(loghost);
ssh->savedport = 22; /* default ssh port */
/*
@@ -3005,11 +3049,11 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
/*
* Try to find host.
*/
+ addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
logeventf(ssh, "Looking up host \"%s\"%s", host,
- (ssh->cfg.addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (ssh->cfg.addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
- addr = name_lookup(host, port, realhost, &ssh->cfg,
- ssh->cfg.addressfamily);
+ (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
+ addr = name_lookup(host, port, realhost, ssh->conf, addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
@@ -3021,7 +3065,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
*/
ssh->fn = &fn_table;
ssh->s = new_connection(addr, *realhost, port,
- 0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg);
+ 0, 1, nodelay, keepalive, (Plug) ssh, ssh->conf);
if ((err = sk_socket_error(ssh->s)) != NULL) {
ssh->s = NULL;
notify_remote_exit(ssh->frontend);
@@ -3032,9 +3076,10 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
* 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)
+ sshprot = conf_get_int(ssh->conf, CONF_sshprot);
+ if (sshprot == 0)
ssh->version = 1;
- if (ssh->cfg.sshprot == 3) {
+ if (sshprot == 3) {
ssh->version = 2;
ssh_send_verstring(ssh, NULL);
}
@@ -3042,9 +3087,9 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
/*
* loghost, if configured, overrides realhost.
*/
- if (*ssh->cfg.loghost) {
+ if (*loghost) {
sfree(*realhost);
- *realhost = dupstr(ssh->cfg.loghost);
+ *realhost = dupstr(loghost);
}
return NULL;
@@ -3209,7 +3254,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
int tis_auth_refused, ccard_auth_refused;
unsigned char session_id[16];
int cipher_type;
- char username[100];
void *publickey_blob;
int publickey_bloblen;
char *publickey_comment;
@@ -3226,6 +3270,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
char *commentp;
int commentlen;
int dlgret;
+ Filename *keyfile;
};
crState(do_ssh1_login_state);
@@ -3365,7 +3410,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
char *cipher_string = NULL;
int i;
for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
- int next_cipher = ssh->cfg.ssh_cipherlist[i];
+ int next_cipher = conf_get_int_int(ssh->conf,
+ CONF_ssh_cipherlist, i);
if (next_cipher == CIPHER_WARN) {
/* If/when we choose a cipher, warn about it */
warn = 1;
@@ -3480,14 +3526,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
fflush(stdout); /* FIXME eh? */
{
- if (!get_remote_username(&ssh->cfg, s->username,
- sizeof(s->username))) {
+ if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
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));
+ add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
@@ -3503,14 +3547,13 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStop(0);
}
- memcpy(s->username, s->cur_prompt->prompts[0]->result,
- lenof(s->username));
+ ssh->username = dupstr(s->cur_prompt->prompts[0]->result);
free_prompts(s->cur_prompt);
}
- send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);
+ send_packet(ssh, SSH1_CMSG_USER, PKT_STR, ssh->username, PKT_END);
{
- char *userlog = dupprintf("Sent username \"%s\"", s->username);
+ char *userlog = dupprintf("Sent username \"%s\"", ssh->username);
logevent(userlog);
if (flags & FLAG_INTERACTIVE &&
(!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) {
@@ -3533,24 +3576,25 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
/*
* Load the public half of any configured keyfile for later use.
*/
- if (!filename_is_null(ssh->cfg.keyfile)) {
+ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+ if (!filename_is_null(s->keyfile)) {
int keytype;
logeventf(ssh, "Reading private key file \"%.150s\"",
- filename_to_str(&ssh->cfg.keyfile));
- keytype = key_type(&ssh->cfg.keyfile);
+ filename_to_str(s->keyfile));
+ keytype = key_type(s->keyfile);
if (keytype == SSH_KEYTYPE_SSH1) {
const char *error;
- if (rsakey_pubblob(&ssh->cfg.keyfile,
+ if (rsakey_pubblob(s->keyfile,
&s->publickey_blob, &s->publickey_bloblen,
&s->publickey_comment, &error)) {
- s->publickey_encrypted = rsakey_encrypted(&ssh->cfg.keyfile,
+ s->publickey_encrypted = rsakey_encrypted(s->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),
+ filename_to_str(s->keyfile),
error);
c_write_str(ssh, msgbuf);
sfree(msgbuf);
@@ -3562,7 +3606,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
key_type_to_str(keytype));
msgbuf = dupprintf("Unable to use key file \"%.150s\""
" (%s)\r\n",
- filename_to_str(&ssh->cfg.keyfile),
+ filename_to_str(s->keyfile),
key_type_to_str(keytype));
c_write_str(ssh, msgbuf);
sfree(msgbuf);
@@ -3574,7 +3618,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
while (pktin->type == SSH1_SMSG_FAILURE) {
s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
- if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) {
+ if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists() && !s->tried_agent) {
/*
* Attempt RSA authentication using Pageant.
*/
@@ -3744,7 +3788,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
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;
}
@@ -3756,8 +3802,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
int got_passphrase; /* need not be kept over crReturn */
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Trying public key authentication.\r\n");
+ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
logeventf(ssh, "Trying public key \"%s\"",
- filename_to_str(&ssh->cfg.keyfile));
+ filename_to_str(s->keyfile));
s->tried_publickey = 1;
got_passphrase = FALSE;
while (!got_passphrase) {
@@ -3777,8 +3824,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
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);
+ s->publickey_comment), FALSE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
@@ -3799,7 +3845,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
/*
* Try decrypting key with passphrase.
*/
- ret = loadrsakey(&ssh->cfg.keyfile, &s->key, passphrase,
+ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+ ret = loadrsakey(s->keyfile, &s->key, passphrase,
&error);
if (passphrase) {
memset(passphrase, 0, strlen(passphrase));
@@ -3810,7 +3857,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
got_passphrase = TRUE;
} else if (ret == 0) {
c_write_str(ssh, "Couldn't load private key from ");
- c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile));
+ c_write_str(ssh, filename_to_str(s->keyfile));
c_write_str(ssh, " (");
c_write_str(ssh, error);
c_write_str(ssh, ").\r\n");
@@ -3893,7 +3940,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
*/
s->cur_prompt = new_prompts(ssh->frontend);
- if (ssh->cfg.try_tis_auth &&
+ if (conf_get_int(ssh->conf, CONF_try_tis_auth) &&
(s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
!s->tis_auth_refused) {
s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
@@ -3932,11 +3979,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
(*instr_suf) ? "\n" : "",
instr_suf);
s->cur_prompt->instr_reqd = TRUE;
- add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN);
+ add_prompt(s->cur_prompt, prompt, FALSE);
sfree(instr_suf);
}
}
- if (ssh->cfg.try_tis_auth &&
+ if (conf_get_int(ssh->conf, CONF_try_tis_auth) &&
(s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
!s->ccard_auth_refused) {
s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
@@ -3975,7 +4022,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
(*instr_suf) ? "\n" : "",
instr_suf);
s->cur_prompt->instr_reqd = TRUE;
- add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN);
+ add_prompt(s->cur_prompt, prompt, FALSE);
sfree(instr_suf);
}
}
@@ -3986,9 +4033,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
}
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);
+ add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
+ ssh->username, ssh->savedhost),
+ FALSE);
}
/*
@@ -4165,70 +4212,78 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
crFinish(1);
}
-void sshfwd_close(struct ssh_channel *c)
+static void ssh_channel_try_eof(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ assert(c->pending_eof); /* precondition for calling us */
+ if (c->halfopen)
+ return; /* can't close: not even opened yet */
+ if (ssh->version == 2 && bufchain_size(&c->v.v2.outbuffer) > 0)
+ return; /* can't send EOF: pending outgoing data */
+
+ if (ssh->version == 1) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
+ PKT_END);
+ c->closes |= CLOSES_SENT_EOF;
+ } else {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_EOF;
+ if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes)) {
+ /*
+ * Also send MSG_CLOSE.
+ */
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+ }
+ c->pending_eof = FALSE; /* we've sent it now */
+}
+
+void sshfwd_write_eof(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
if (ssh->state == SSH_STATE_CLOSED)
return;
- if (!c->closes) {
- /*
- * If halfopen is true, we have sent
- * CHANNEL_OPEN for this channel, but it hasn't even been
- * acknowledged by the server. So we must set a close flag
- * on it now, and then when the server acks the channel
- * open, we can close it then.
- */
- if (!c->halfopen) {
- if (ssh->version == 1) {
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
- PKT_END);
- c->closes = 1; /* sent MSG_CLOSE */
- } else {
- 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");
- }
- }
- }
+ if (c->closes & CLOSES_SENT_EOF)
+ return;
- if (c->type == CHAN_X11) {
- c->u.x11.s = NULL;
- logevent("Forwarded X11 connection terminated");
- } else if (c->type == CHAN_SOCKDATA ||
- c->type == CHAN_SOCKDATA_DORMANT) {
- c->u.pfd.s = NULL;
- logevent("Forwarded port closed");
- }
+ c->pending_eof = TRUE;
+ ssh_channel_try_eof(c);
+}
+
+void sshfwd_unclean_close(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ struct Packet *pktout;
+
+ if (ssh->state == SSH_STATE_CLOSED)
+ return;
+
+ if (c->closes & CLOSES_SENT_CLOSE)
+ return;
+
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
+ switch (c->type) {
+ case CHAN_X11:
+ x11_close(c->u.x11.s);
+ break;
+ case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
+ pfd_close(c->u.pfd.s);
+ break;
}
+ c->type = CHAN_ZOMBIE;
+ ssh2_channel_check_close(c);
}
int sshfwd_write(struct ssh_channel *c, char *buf, int len)
@@ -4363,11 +4418,11 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx)
}
}
-static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
+static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
{
- const char *portfwd_strptr = cfg->portfwd;
struct ssh_portfwd *epf;
int i;
+ char *key, *val;
if (!ssh->portfwds) {
ssh->portfwds = newtree234(ssh_portcmp);
@@ -4385,64 +4440,61 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
epf->status = DESTROY;
}
- while (*portfwd_strptr) {
+ for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
+ char *kp, *kp2, *vp, *vp2;
char address_family, type;
int sport,dport,sserv,dserv;
- char sports[256], dports[256], saddr[256], host[256];
- int n;
+ char *sports, *dports, *saddr, *host;
+
+ kp = key;
address_family = 'A';
type = 'L';
- if (*portfwd_strptr == 'A' ||
- *portfwd_strptr == '4' ||
- *portfwd_strptr == '6')
- address_family = *portfwd_strptr++;
- if (*portfwd_strptr == 'L' ||
- *portfwd_strptr == 'R' ||
- *portfwd_strptr == 'D')
- type = *portfwd_strptr++;
-
- saddr[0] = '\0';
-
- n = 0;
- while (*portfwd_strptr && *portfwd_strptr != '\t') {
- if (*portfwd_strptr == ':') {
- /*
- * We've seen a colon in the middle of the
- * source port number. This means that
- * everything we've seen until now is the
- * source _address_, so we'll move it into
- * saddr and start sports from the beginning
- * again.
- */
- portfwd_strptr++;
- sports[n] = '\0';
- if (ssh->version == 1 && type == 'R') {
- logeventf(ssh, "SSH-1 cannot handle remote source address "
- "spec \"%s\"; ignoring", sports);
- } else
- strcpy(saddr, sports);
- n = 0;
- }
- if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++;
+ if (*kp == 'A' || *kp == '4' || *kp == '6')
+ address_family = *kp++;
+ if (*kp == 'L' || *kp == 'R')
+ type = *kp++;
+
+ if ((kp2 = strchr(kp, ':')) != NULL) {
+ /*
+ * There's a colon in the middle of the source port
+ * string, which means that the part before it is
+ * actually a source address.
+ */
+ saddr = dupprintf("%.*s", (int)(kp2 - kp), kp);
+ sports = kp2+1;
+ } else {
+ saddr = NULL;
+ sports = kp;
}
- sports[n] = 0;
- if (type != 'D') {
- if (*portfwd_strptr == '\t')
- portfwd_strptr++;
- n = 0;
- while (*portfwd_strptr && *portfwd_strptr != ':') {
- if (n < lenof(host)-1) host[n++] = *portfwd_strptr++;
- }
- host[n] = 0;
- if (*portfwd_strptr == ':')
- portfwd_strptr++;
- n = 0;
- while (*portfwd_strptr) {
- if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++;
+ sport = atoi(sports);
+ sserv = 0;
+ if (sport == 0) {
+ sserv = 1;
+ sport = net_service_lookup(sports);
+ if (!sport) {
+ logeventf(ssh, "Service lookup failed for source"
+ " port \"%s\"", sports);
}
- dports[n] = 0;
- portfwd_strptr++;
+ }
+
+ if (type == 'L' && !strcmp(val, "D")) {
+ /* dynamic forwarding */
+ host = NULL;
+ dports = NULL;
+ dport = -1;
+ dserv = 0;
+ type = 'D';
+ } else {
+ /* ordinary forwarding */
+ vp = val;
+ vp2 = vp + strcspn(vp, ":");
+ host = dupprintf("%.*s", (int)(vp2 - vp), vp);
+ if (vp2)
+ vp2++;
+ dports = vp2;
dport = atoi(dports);
dserv = 0;
if (dport == 0) {
@@ -4453,33 +4505,18 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
" port \"%s\"", dports);
}
}
- } else {
- while (*portfwd_strptr) portfwd_strptr++;
- host[0] = 0;
- dports[0] = 0;
- dport = dserv = -1;
- portfwd_strptr++; /* eat the NUL and move to next one */
- }
- sport = atoi(sports);
- sserv = 0;
- if (sport == 0) {
- sserv = 1;
- sport = net_service_lookup(sports);
- if (!sport) {
- logeventf(ssh, "Service lookup failed for source"
- " port \"%s\"", sports);
- }
}
+
if (sport && dport) {
/* Set up a description of the source port. */
struct ssh_portfwd *pfrec, *epfrec;
pfrec = snew(struct ssh_portfwd);
pfrec->type = type;
- pfrec->saddr = *saddr ? dupstr(saddr) : NULL;
+ pfrec->saddr = saddr;
pfrec->sserv = sserv ? dupstr(sports) : NULL;
pfrec->sport = sport;
- pfrec->daddr = *host ? dupstr(host) : NULL;
+ pfrec->daddr = host;
pfrec->dserv = dserv ? dupstr(dports) : NULL;
pfrec->dport = dport;
pfrec->local = NULL;
@@ -4507,6 +4544,9 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
} else {
pfrec->status = CREATE;
}
+ } else {
+ sfree(saddr);
+ sfree(host);
}
}
@@ -4560,8 +4600,8 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */
if (epf->saddr) {
ssh2_pkt_addstring(pktout, epf->saddr);
- } else if (ssh->cfg.rport_acceptall) {
- /* XXX: ssh->cfg.rport_acceptall may not represent
+ } else if (conf_get_int(conf, CONF_rport_acceptall)) {
+ /* XXX: rport_acceptall may not represent
* what was used to open the original connection,
* since it's reconfigurable. */
ssh2_pkt_addstring(pktout, "0.0.0.0");
@@ -4610,7 +4650,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
if (epf->type == 'L') {
const char *err = pfd_addforward(epf->daddr, epf->dport,
epf->saddr, epf->sport,
- ssh, cfg,
+ ssh, conf,
&epf->local,
epf->addressfamily);
@@ -4622,7 +4662,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
} else if (epf->type == 'D') {
const char *err = pfd_addforward(NULL, -1,
epf->saddr, epf->sport,
- ssh, cfg,
+ ssh, conf,
&epf->local,
epf->addressfamily);
@@ -4678,7 +4718,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
ssh2_pkt_addbool(pktout, 1);/* want reply */
if (epf->saddr) {
ssh2_pkt_addstring(pktout, epf->saddr);
- } else if (cfg->rport_acceptall) {
+ } else if (conf_get_int(conf, CONF_rport_acceptall)) {
ssh2_pkt_addstring(pktout, "0.0.0.0");
} else {
ssh2_pkt_addstring(pktout, "127.0.0.1");
@@ -4734,7 +4774,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
c->ssh = ssh;
if (x11_init(&c->u.x11.s, ssh->x11disp, c,
- NULL, -1, &ssh->cfg) != NULL) {
+ NULL, -1, ssh->conf) != NULL) {
logevent("Opening X11 forward connection failed");
sfree(c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
@@ -4746,7 +4786,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = 0;
c->type = CHAN_X11; /* identify channel type */
add234(ssh->channels, c);
@@ -4776,10 +4816,11 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = 0;
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
+ c->u.a.message = NULL;
add234(ssh->channels, c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
PKT_INT, c->remoteid, PKT_INT, c->localid,
@@ -4820,7 +4861,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
logeventf(ssh, "Received remote port open request for %s:%d",
pf.dhost, port);
e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
- c, &ssh->cfg, pfp->pfrec->addressfamily);
+ c, ssh->conf, pfp->pfrec->addressfamily);
if (e != NULL) {
logeventf(ssh, "Port open failed: %s", e);
sfree(c);
@@ -4831,7 +4872,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = 0;
c->type = CHAN_SOCKDATA; /* identify channel type */
add234(ssh->channels, c);
@@ -4858,15 +4899,14 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
pfd_confirm(c->u.pfd.s);
}
- if (c && c->closes) {
+ if (c && c->pending_eof) {
/*
* We have a pending close on this channel,
* which we decided on before the server acked
* the channel open. So now we know the
* remoteid, we can close it again.
*/
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE,
- PKT_INT, c->remoteid, PKT_END);
+ ssh_channel_try_eof(c);
}
}
@@ -4891,34 +4931,59 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
if (c && !c->halfopen) {
- int closetype;
- closetype =
- (pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
-
- if ((c->closes == 0) && (c->type == CHAN_X11)) {
- logevent("Forwarded X11 connection terminated");
- assert(c->u.x11.s != NULL);
- x11_close(c->u.x11.s);
- c->u.x11.s = NULL;
- }
- if ((c->closes == 0) && (c->type == CHAN_SOCKDATA)) {
- logevent("Forwarded port closed");
- assert(c->u.pfd.s != NULL);
- pfd_close(c->u.pfd.s);
- c->u.pfd.s = NULL;
- }
- c->closes |= (closetype << 2); /* seen this message */
- if (!(c->closes & closetype)) {
- send_packet(ssh, pktin->type, PKT_INT, c->remoteid,
- PKT_END);
- c->closes |= closetype; /* sent it too */
- }
+ if (pktin->type == SSH1_MSG_CHANNEL_CLOSE &&
+ !(c->closes & CLOSES_RCVD_EOF)) {
+ /*
+ * Received CHANNEL_CLOSE, which we translate into
+ * outgoing EOF.
+ */
+ int send_close = FALSE;
+
+ c->closes |= CLOSES_RCVD_EOF;
+
+ switch (c->type) {
+ case CHAN_X11:
+ if (c->u.x11.s)
+ x11_send_eof(c->u.x11.s);
+ else
+ send_close = TRUE;
+ case CHAN_SOCKDATA:
+ if (c->u.pfd.s)
+ x11_send_eof(c->u.pfd.s);
+ else
+ send_close = TRUE;
+ case CHAN_AGENT:
+ send_close = TRUE;
+ }
- if (c->closes == 15) {
- del234(ssh->channels, c);
- sfree(c);
- }
+ if (send_close && !(c->closes & CLOSES_SENT_EOF)) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
+ PKT_END);
+ c->closes |= CLOSES_SENT_EOF;
+ }
+ }
+
+ if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION &&
+ !(c->closes & CLOSES_RCVD_CLOSE)) {
+
+ if (!(c->closes & CLOSES_SENT_EOF)) {
+ bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d"
+ " for which we never sent CHANNEL_CLOSE\n", i));
+ }
+
+ c->closes |= CLOSES_RCVD_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) &&
+ !(c->closes & CLOSES_SENT_CLOSE)) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_END);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes))
+ ssh_channel_destroy(c);
} else {
bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" :
@@ -5052,7 +5117,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data;
ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status;
- if (ssh->cfg.agentfwd && agent_exists()) {
+ if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) {
logevent("Requesting agent forwarding");
send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
do {
@@ -5071,9 +5136,9 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
}
}
- if (ssh->cfg.x11_forward &&
- (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
- ssh->cfg.x11_auth, &ssh->cfg))) {
+ if (conf_get_int(ssh->conf, CONF_x11_forward) &&
+ (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+ conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
logevent("Requesting X11 forwarding");
/*
* Note that while we blank the X authentication data here, we don't
@@ -5114,24 +5179,23 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
}
}
- ssh_setup_portfwd(ssh, &ssh->cfg);
+ ssh_setup_portfwd(ssh, ssh->conf);
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
- if (!ssh->cfg.nopty) {
+ if (!conf_get_int(ssh->conf, CONF_nopty)) {
struct Packet *pkt;
/* 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);
+ sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
/* Send the pty request. */
pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY);
- ssh_pkt_addstring(pkt, ssh->cfg.termtype);
+ ssh_pkt_addstring(pkt, conf_get_str(ssh->conf, CONF_termtype));
ssh_pkt_adduint32(pkt, ssh->term_height);
ssh_pkt_adduint32(pkt, ssh->term_width);
ssh_pkt_adduint32(pkt, 0); /* width in pixels */
ssh_pkt_adduint32(pkt, 0); /* height in pixels */
- parse_ttymodes(ssh, ssh->cfg.ttymodes,
- ssh1_send_ttymode, (void *)pkt);
+ parse_ttymodes(ssh, ssh1_send_ttymode, (void *)pkt);
ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED);
ssh_pkt_adduint32(pkt, ssh->ispeed);
ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED);
@@ -5149,14 +5213,16 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
} else if (pktin->type == SSH1_SMSG_FAILURE) {
c_write_str(ssh, "Server refused to allocate pty\r\n");
ssh->editing = ssh->echoing = 1;
- }
- logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
- ssh->ospeed, ssh->ispeed);
+ } else {
+ logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
+ ssh->ospeed, ssh->ispeed);
+ ssh->got_pty = TRUE;
+ }
} else {
ssh->editing = ssh->echoing = 1;
}
- if (ssh->cfg.compression) {
+ if (conf_get_int(ssh->conf, CONF_compression)) {
send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END);
do {
crReturnV;
@@ -5184,12 +5250,11 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
* exists, we fall straight back to that.
*/
{
- char *cmd = ssh->cfg.remote_cmd_ptr;
-
- if (!cmd) cmd = ssh->cfg.remote_cmd;
+ char *cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
- if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) {
- cmd = ssh->cfg.remote_cmd_ptr2;
+ if (conf_get_int(ssh->conf, CONF_ssh_subsys) &&
+ conf_get_str(ssh->conf, CONF_remote_cmd2)) {
+ cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
ssh->fallback_cmd = TRUE;
}
if (*cmd)
@@ -5422,6 +5487,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
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;
@@ -5437,6 +5504,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
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.
@@ -5456,7 +5525,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
s->n_preferred_kex = 0;
for (i = 0; i < KEX_MAX; i++) {
- switch (ssh->cfg.ssh_kexlist[i]) {
+ switch (conf_get_int_int(ssh->conf, CONF_ssh_kexlist, i)) {
case KEX_DHGEX:
s->preferred_kex[s->n_preferred_kex++] =
&ssh_diffiehellman_gex;
@@ -5488,12 +5557,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
s->n_preferred_ciphers = 0;
for (i = 0; i < CIPHER_MAX; i++) {
- switch (ssh->cfg.ssh_cipherlist[i]) {
+ switch (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i)) {
case CIPHER_BLOWFISH:
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish;
break;
case CIPHER_DES:
- if (ssh->cfg.ssh2_des_cbc) {
+ if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc)) {
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des;
}
break;
@@ -5519,7 +5588,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
/*
* Set up preferred compression.
*/
- if (ssh->cfg.compression)
+ if (conf_get_int(ssh->conf, CONF_compression))
s->preferred_comp = &ssh_zlib;
else
s->preferred_comp = &ssh_comp_none;
@@ -5601,26 +5670,32 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
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. */
@@ -5769,6 +5844,13 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
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 */
@@ -5778,8 +5860,19 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
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;
@@ -6291,8 +6384,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
ssh->kex_in_progress = FALSE;
ssh->last_rekey = GETTICKCOUNT();
- if (ssh->cfg.ssh_rekey_time != 0)
- ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+ if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0)
+ ssh->next_rekey = schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC,
ssh2_timer, ssh);
/*
@@ -6315,19 +6408,52 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
* 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,
@@ -6340,12 +6466,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
* hit the event log _too_ often. */
ssh->outgoing_data_size = 0;
ssh->incoming_data_size = 0;
- if (ssh->cfg.ssh_rekey_time != 0) {
+ if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) {
ssh->next_rekey =
- schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+ schedule_timer(conf_get_int(ssh->conf, CONF_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);
}
@@ -6371,6 +6497,7 @@ static int ssh2_try_send(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
struct Packet *pktout;
+ int ret;
while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {
int len;
@@ -6395,14 +6522,23 @@ static int ssh2_try_send(struct ssh_channel *c)
* After having sent as much data as we can, return the amount
* still buffered.
*/
- return bufchain_size(&c->v.v2.outbuffer);
+ ret = bufchain_size(&c->v.v2.outbuffer);
+
+ /*
+ * And if there's no data pending but we need to send an EOF, send
+ * it.
+ */
+ if (!ret && c->pending_eof)
+ ssh_channel_try_eof(c);
+
+ return ret;
}
static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
{
int bufsize;
- if (c->closes)
- return; /* don't send on closing channels */
+ if (c->closes & CLOSES_SENT_EOF)
+ return; /* don't send on channels we've EOFed */
bufsize = ssh2_try_send(c);
if (bufsize == 0) {
switch (c->type) {
@@ -6422,19 +6558,6 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
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;
- }
}
/*
@@ -6445,10 +6568,10 @@ 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->pending_eof = 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;
+ conf_get_int(ssh->conf, CONF_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);
@@ -6462,11 +6585,12 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
Ssh ssh = c->ssh;
/*
- * Never send WINDOW_ADJUST for a channel that the remote side
- * already thinks it's closed; there's no point, since it won't
- * be sending any more data anyway.
+ * Never send WINDOW_ADJUST for a channel that the remote side has
+ * already sent EOF on; there's no point, since it won't be
+ * sending any more data anyway. Ditto if _we've_ already sent
+ * CLOSE.
*/
- if (c->closes != 0)
+ if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
return;
/*
@@ -6476,7 +6600,6 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
*/
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
@@ -6564,25 +6687,49 @@ static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)
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;
+ /*
+ * We may now initiate channel-closing procedures, if that winadj
+ * was the last thing outstanding before we send CHANNEL_CLOSE.
+ */
+ ssh2_channel_check_close(c);
+ 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 or are sent before this handler gets
- * installed.
+ * sent with want_reply false, are sent before this handler gets
+ * installed, or are "winadj@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@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;
- struct winadj *wa;
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- wa = c->v.v2.winadj_head;
- if (wa)
- ssh_disconnect(ssh, NULL, "Received SSH_MSG_CHANNEL_SUCCESS for "
- "\"winadj@putty.projects.tartarus.org\"",
- SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
- else
+ if (!ssh2_handle_winadj_response(c))
ssh_disconnect(ssh, NULL,
"Received unsolicited SSH_MSG_CHANNEL_SUCCESS",
SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
@@ -6597,28 +6744,14 @@ static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin)
* installed.
*/
struct ssh_channel *c;
- struct winadj *wa;
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- wa = c->v.v2.winadj_head;
- if (!wa) {
+ if (!ssh2_handle_winadj_response(c))
ssh_disconnect(ssh, NULL,
"Received unsolicited SSH_MSG_CHANNEL_FAILURE",
SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
- return;
- }
- 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;
}
static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
@@ -6627,7 +6760,7 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- if (!c->closes) {
+ if (!(c->closes & CLOSES_SENT_EOF)) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
ssh2_try_send_and_unthrottle(ssh, c);
}
@@ -6699,6 +6832,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
ssh_agentf_callback, c))
ssh_agentf_callback(c, reply, replylen);
sfree(c->u.a.message);
+ c->u.a.message = NULL;
c->u.a.lensofar = 0;
}
}
@@ -6728,7 +6862,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
* throttle the whole channel.
*/
if ((bufsize > c->v.v2.locmaxwin ||
- (ssh->cfg.ssh_simple && bufsize > 0)) &&
+ (conf_get_int(ssh->conf, CONF_ssh_simple) && bufsize > 0)) &&
!c->throttling_conn) {
c->throttling_conn = 1;
ssh_throttle_conn(ssh, +1);
@@ -6736,93 +6870,186 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
+static void ssh_channel_destroy(struct ssh_channel *c)
{
- struct ssh_channel *c;
+ Ssh ssh = c->ssh;
- c = ssh2_channel_msg(ssh, pktin);
- if (!c)
- return;
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ ssh->mainchan = NULL;
+ update_specials_menu(ssh->frontend);
+ break;
+ case CHAN_X11:
+ if (c->u.x11.s != NULL)
+ x11_close(c->u.x11.s);
+ logevent("Forwarded X11 connection terminated");
+ break;
+ case CHAN_AGENT:
+ sfree(c->u.a.message);
+ break;
+ case CHAN_SOCKDATA:
+ if (c->u.pfd.s != NULL)
+ pfd_close(c->u.pfd.s);
+ logevent("Forwarded port closed");
+ break;
+ }
+
+ del234(ssh->channels, c);
+ if (ssh->version == 2)
+ bufchain_clear(&c->v.v2.outbuffer);
+ sfree(c);
+
+ /*
+ * See if that was the last channel left open.
+ * (This is only our termination condition if we're
+ * not running in -N mode.)
+ */
+ if (ssh->version == 2 &&
+ !conf_get_int(ssh->conf, CONF_ssh_no_shell) &&
+ count234(ssh->channels) == 0) {
+ /*
+ * We used to send SSH_MSG_DISCONNECT here,
+ * because I'd believed that _every_ conforming
+ * SSH-2 connection had to end with a disconnect
+ * being sent by at least one side; apparently
+ * I was wrong and it's perfectly OK to
+ * unceremoniously slam the connection shut
+ * when you're done, and indeed OpenSSH feels
+ * this is more polite than sending a
+ * DISCONNECT. So now we don't.
+ */
+ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
+ }
+}
+
+static void ssh2_channel_check_close(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ struct Packet *pktout;
+
+ if ((c->closes & (CLOSES_SENT_EOF | CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
+ == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.winadj_head) {
+ /*
+ * We have both sent and received EOF, and we have no
+ * outstanding winadj channel requests, which means the
+ * channel is in final wind-up. But we haven't sent CLOSE, so
+ * let's do so now.
+ */
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) {
+ /*
+ * We have both sent and received CLOSE, which means we're
+ * completely done with the channel.
+ */
+ ssh_channel_destroy(c);
+ }
+}
+
+static void ssh2_channel_got_eof(struct ssh_channel *c)
+{
+ if (c->closes & CLOSES_RCVD_EOF)
+ return; /* already seen EOF */
+ c->closes |= CLOSES_RCVD_EOF;
if (c->type == CHAN_X11) {
- /*
- * Remote EOF on an X11 channel means we should
- * wrap up and close the channel ourselves.
- */
- x11_close(c->u.x11.s);
- c->u.x11.s = NULL;
- sshfwd_close(c);
+ x11_send_eof(c->u.x11.s);
} else if (c->type == CHAN_AGENT) {
- sshfwd_close(c);
+ /* Manufacture an outgoing EOF in response to the incoming one. */
+ sshfwd_write_eof(c);
} else if (c->type == CHAN_SOCKDATA) {
- pfd_close(c->u.pfd.s);
- c->u.pfd.s = NULL;
- sshfwd_close(c);
+ pfd_send_eof(c->u.pfd.s);
+ } else if (c->type == CHAN_MAINSESSION) {
+ Ssh ssh = c->ssh;
+
+ if (!ssh->sent_console_eof &&
+ (from_backend_eof(ssh->frontend) || ssh->got_pty)) {
+ /*
+ * Either from_backend_eof told us that the front end
+ * wants us to close the outgoing side of the connection
+ * as soon as we see EOF from the far end, or else we've
+ * unilaterally decided to do that because we've allocated
+ * a remote pty and hence EOF isn't a particularly
+ * meaningful concept.
+ */
+ sshfwd_write_eof(c);
+ }
+ ssh->sent_console_eof = TRUE;
}
+
+ ssh2_channel_check_close(c);
+}
+
+static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
+{
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ ssh2_channel_got_eof(c);
}
static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- struct Packet *pktout;
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- /* Do pre-close processing on the channel. */
- switch (c->type) {
- case CHAN_MAINSESSION:
- ssh->mainchan = NULL;
- update_specials_menu(ssh->frontend);
- break;
- case CHAN_X11:
- if (c->u.x11.s != NULL)
- x11_close(c->u.x11.s);
- sshfwd_close(c);
- break;
- case CHAN_AGENT:
- sshfwd_close(c);
- break;
- case CHAN_SOCKDATA:
- if (c->u.pfd.s != NULL)
- pfd_close(c->u.pfd.s);
- sshfwd_close(c);
- break;
- }
- if (c->closes == 0) {
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
+
+ /*
+ * When we receive CLOSE on a channel, we assume it comes with an
+ * implied EOF if we haven't seen EOF yet.
+ */
+ ssh2_channel_got_eof(c);
+
+ /*
+ * And we also send an outgoing EOF, if we haven't already, on the
+ * assumption that CLOSE is a pretty forceful announcement that
+ * the remote side is doing away with the entire channel. (If it
+ * had wanted to send us EOF and continue receiving data from us,
+ * it would have just sent CHANNEL_EOF.)
+ */
+ if (!(c->closes & CLOSES_SENT_EOF)) {
+ /*
+ * Make sure we don't read any more from whatever our local
+ * data source is for this channel.
+ */
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ ssh->send_ok = 0; /* stop trying to read from stdin */
+ break;
+ case CHAN_X11:
+ x11_override_throttle(c->u.x11.s, 1);
+ break;
+ case CHAN_SOCKDATA:
+ pfd_override_throttle(c->u.pfd.s, 1);
+ break;
+ }
+
+ /*
+ * Send outgoing EOF.
+ */
+ sshfwd_write_eof(c);
}
- del234(ssh->channels, c);
- bufchain_clear(&c->v.v2.outbuffer);
- sfree(c);
/*
- * See if that was the last channel left open.
- * (This is only our termination condition if we're
- * not running in -N mode.)
+ * Now process the actual close.
*/
- if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
- /*
- * We used to send SSH_MSG_DISCONNECT here,
- * because I'd believed that _every_ conforming
- * SSH-2 connection had to end with a disconnect
- * being sent by at least one side; apparently
- * I was wrong and it's perfectly OK to
- * unceremoniously slam the connection shut
- * when you're done, and indeed OpenSSH feels
- * this is more polite than sending a
- * DISCONNECT. So now we don't.
- */
- ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
+ if (!(c->closes & CLOSES_RCVD_CLOSE)) {
+ c->closes |= CLOSES_RCVD_CLOSE;
+ ssh2_channel_check_close(c);
}
}
static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- struct Packet *pktout;
c = ssh2_channel_msg(ssh, pktin);
if (!c)
@@ -6836,17 +7063,8 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
if (c->u.pfd.s)
pfd_confirm(c->u.pfd.s);
- if (c->closes) {
- /*
- * We have a pending close on this channel,
- * which we decided on before the server acked
- * the channel open. So now we know the
- * remoteid, we can close it again.
- */
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
- }
+ if (c->pending_eof)
+ ssh_channel_try_eof(c);
}
static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
@@ -7118,7 +7336,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
if (!ssh->X11_fwd_enabled)
error = "X11 forwarding is not enabled";
else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,
- addrstr, peerport, &ssh->cfg)) != NULL) {
+ addrstr, peerport, ssh->conf)) != NULL) {
logeventf(ssh, "Local X11 connection failed: %s", x11err);
error = "Unable to open an X11 connection";
} else {
@@ -7145,7 +7363,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
const char *e = pfd_newconnect(&c->u.pfd.s,
realpf->dhost,
realpf->dport, c,
- &ssh->cfg,
+ ssh->conf,
realpf->pfrec->addressfamily);
logeventf(ssh, "Attempting to forward remote port to "
"%s:%d", realpf->dhost, realpf->dport);
@@ -7201,7 +7419,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
{
/* Arbitrary limit to prevent unbounded inflation of buffer */
- if (ssh->cfg.ssh_show_banner &&
+ if (conf_get_int(ssh->conf, CONF_ssh_show_banner) &&
bufchain_size(&ssh->banner) <= 131072) {
char *banner = NULL;
int size = 0;
@@ -7244,7 +7462,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
AUTH_TYPE_PASSWORD,
- AUTH_TYPE_GSSAPI,
+ AUTH_TYPE_GSSAPI, /* always QUIET */
AUTH_TYPE_KEYBOARD_INTERACTIVE,
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
} type;
@@ -7256,10 +7474,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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];
+ char *username;
char *password;
int got_username;
void *publickey_blob;
@@ -7278,6 +7496,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
int try_send;
int num_env, env_left, env_ok;
struct Packet *pktout;
+ Filename *keyfile;
#ifndef NO_GSSAPI
struct ssh_gss_library *gsslib;
Ssh_gss_ctx gss_ctx;
@@ -7292,12 +7511,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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) {
+ if (!conf_get_int(ssh->conf, CONF_ssh_no_userauth)) {
/*
* Request userauth protocol, and await a response to it.
*/
@@ -7340,28 +7559,29 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* Load the public half of any configured public key file
* for later use.
*/
- if (!filename_is_null(ssh->cfg.keyfile)) {
+ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+ if (!filename_is_null(s->keyfile)) {
int keytype;
logeventf(ssh, "Reading private key file \"%.150s\"",
- filename_to_str(&ssh->cfg.keyfile));
- keytype = key_type(&ssh->cfg.keyfile);
+ filename_to_str(s->keyfile));
+ keytype = key_type(s->keyfile);
if (keytype == SSH_KEYTYPE_SSH2) {
const char *error;
s->publickey_blob =
- ssh2_userkey_loadpub(&ssh->cfg.keyfile,
+ ssh2_userkey_loadpub(s->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);
+ ssh2_userkey_encrypted(s->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),
+ filename_to_str(s->keyfile),
error);
c_write_str(ssh, msgbuf);
sfree(msgbuf);
@@ -7372,7 +7592,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
key_type_to_str(keytype));
msgbuf = dupprintf("Unable to use key file \"%.150s\""
" (%s)\r\n",
- filename_to_str(&ssh->cfg.keyfile),
+ filename_to_str(s->keyfile),
key_type_to_str(keytype));
c_write_str(ssh, msgbuf);
sfree(msgbuf);
@@ -7387,7 +7607,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
s->nkeys = 0;
s->agent_response = NULL;
s->pkblob_in_agent = NULL;
- if (ssh->cfg.tryagent && agent_exists()) {
+ if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) {
void *r;
@@ -7439,6 +7659,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
s->nkeys = 0;
}
}
+ } else {
+ logevent("Failed to get reply from Pageant");
}
}
@@ -7468,26 +7690,23 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* the username they will want to be able to get back and
* retype it!
*/
- s->username[0] = '\0';
s->got_username = FALSE;
while (!s->we_are_in) {
/*
* Get a username.
*/
- if (s->got_username && !ssh->cfg.change_username) {
+ if (s->got_username && !conf_get_int(ssh->conf, CONF_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 (!get_remote_username(&ssh->cfg, s->username,
- sizeof(s->username))) {
+ } else if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
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));
+ add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
@@ -7504,13 +7723,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStopV;
}
- memcpy(s->username, s->cur_prompt->prompts[0]->result,
- lenof(s->username));
+ ssh->username = dupstr(s->cur_prompt->prompts[0]->result);
free_prompts(s->cur_prompt);
} else {
char *stuff;
if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
- stuff = dupprintf("Using username \"%s\".\r\n", s->username);
+ stuff = dupprintf("Using username \"%s\".\r\n", ssh->username);
c_write_str(ssh, stuff);
sfree(stuff);
}
@@ -7525,7 +7743,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, ssh->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */
ssh2_pkt_addstring(s->pktout, "none"); /* method */
ssh2_pkt_send(ssh, s->pktout);
@@ -7582,7 +7800,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
- s->we_are_in = TRUE;
+ s->we_are_in = s->userauth_success = TRUE;
break;
}
@@ -7605,19 +7823,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/*
* 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
@@ -7630,14 +7849,31 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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");
+ } else {
+ assert(s->type == AUTH_TYPE_PASSWORD);
+ logevent("Password authentication failed");
c_write_str(ssh, "Access denied\r\n");
- logevent("Access denied");
- if (s->type == AUTH_TYPE_PASSWORD &&
- ssh->cfg.change_username) {
+
+ if (conf_get_int(ssh->conf, CONF_change_username)) {
/* XXX perhaps we should allow
* keyboard-interactive to do this too? */
s->we_are_in = FALSE;
@@ -7653,12 +7889,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
in_commasep_string("publickey", methods, methlen);
s->can_passwd =
in_commasep_string("password", methods, methlen);
- s->can_keyb_inter = ssh->cfg.try_ki_auth &&
+ s->can_keyb_inter = conf_get_int(ssh->conf, CONF_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 &&
+ ssh->gsslibs = ssh_gss_setup(ssh->conf);
+ s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
in_commasep_string("gssapi-with-mic", methods, methlen) &&
ssh->gsslibs->nlibraries > 0;
#endif
@@ -7691,7 +7927,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/* See if server will accept it */
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, ssh->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "publickey");
@@ -7726,7 +7962,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* 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->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "publickey");
@@ -7830,7 +8066,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* 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->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "publickey"); /* method */
@@ -7876,7 +8112,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
add_prompt(s->cur_prompt,
dupprintf("Passphrase for key \"%.100s\": ",
s->publickey_comment),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
@@ -7904,8 +8140,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/*
* Try decrypting the key.
*/
- key = ssh2_load_userkey(&ssh->cfg.keyfile, passphrase,
- &error);
+ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+ key = ssh2_load_userkey(s->keyfile, passphrase, &error);
if (passphrase) {
/* burn the evidence */
memset(passphrase, 0, strlen(passphrase));
@@ -7938,7 +8174,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* 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->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "publickey");
@@ -7986,6 +8222,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
sfree(sigdata);
ssh2_pkt_send(ssh, s->pktout);
+ logevent("Sent public key signature");
s->type = AUTH_TYPE_PUBLICKEY;
key->alg->freekey(key->data);
}
@@ -8011,7 +8248,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
int i, j;
s->gsslib = NULL;
for (i = 0; i < ngsslibs; i++) {
- int want_id = ssh->cfg.ssh_gsslist[i];
+ int want_id = conf_get_int_int(ssh->conf,
+ CONF_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];
@@ -8034,9 +8272,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/* 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->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);
@@ -8105,7 +8344,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
(s->gsslib,
&s->gss_ctx,
s->gss_srv_name,
- ssh->cfg.gssapifwd,
+ conf_get_int(ssh->conf, CONF_gssapifwd),
&s->gss_rcvtok,
&s->gss_sndtok);
@@ -8161,7 +8400,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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->username);
ssh_pkt_addstring(s->pktout, "ssh-connection");
ssh_pkt_addstring(s->pktout, "gssapi-with-mic");
@@ -8192,7 +8431,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh->pkt_actx = 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->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "keyboard-interactive");
@@ -8200,6 +8439,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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) {
@@ -8208,8 +8449,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* 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;
@@ -8253,7 +8492,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
}
add_prompt(s->cur_prompt,
dupprintf("%.*s", prompt_len, prompt),
- echo, SSH_MAX_PASSWORD_LEN);
+ echo);
}
if (name_len) {
@@ -8321,6 +8560,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
}
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.
@@ -8347,10 +8593,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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,
+ add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
+ ssh->username,
ssh->savedhost),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
@@ -8388,7 +8634,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* people who find out how long their password is!
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, ssh->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "password");
@@ -8452,11 +8698,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
*/
add_prompt(s->cur_prompt,
dupstr("Current password (blank for previously entered password): "),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
add_prompt(s->cur_prompt, dupstr("Enter new password: "),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
/*
* Loop until the user manages to enter the same
@@ -8517,7 +8763,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* (see above for padding rationale)
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, ssh->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
ssh2_pkt_addstring(s->pktout, "password");
@@ -8590,6 +8836,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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.
*/
@@ -8608,9 +8868,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/*
* Create the main session channel.
*/
- if (ssh->cfg.ssh_no_shell) {
+ if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) {
ssh->mainchan = NULL;
- } else if (*ssh->cfg.ssh_nc_host) {
+ } else if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
/*
* Just start a direct-tcpip channel and use it as the main
* channel.
@@ -8620,14 +8880,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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);
+ conf_get_str(ssh->conf, CONF_ssh_nc_host),
+ conf_get_int(ssh->conf, CONF_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);
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);
- ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);
+ ssh2_pkt_addstring(s->pktout, conf_get_str(ssh->conf, CONF_ssh_nc_host));
+ ssh2_pkt_adduint32(s->pktout, conf_get_int(ssh->conf, CONF_ssh_nc_port));
/*
* There's nothing meaningful to put in the originator
* fields, but some servers insist on syntactically correct
@@ -8705,7 +8966,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =
ssh2_msg_channel_open;
- if (ssh->mainchan && ssh->cfg.ssh_simple) {
+ if (ssh->mainchan && conf_get_int(ssh->conf, CONF_ssh_simple)) {
/*
* This message indicates to the server that we promise
* not to try to run any other channel in parallel with
@@ -8722,9 +8983,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/*
* Potentially enable X11 forwarding.
*/
- if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward &&
- (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
- ssh->cfg.x11_auth, &ssh->cfg))) {
+ if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) &&
+ (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+ conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
logevent("Requesting X11 forwarding");
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
@@ -8763,12 +9024,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/*
* Enable port forwardings.
*/
- ssh_setup_portfwd(ssh, &ssh->cfg);
+ ssh_setup_portfwd(ssh, ssh->conf);
/*
* Potentially enable agent forwarding.
*/
- if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {
+ if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_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);
@@ -8794,24 +9055,23 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/*
* Now allocate a pty for the session.
*/
- if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {
+ if (ssh->mainchan && !ssh->ncmode && !conf_get_int(ssh->conf, CONF_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);
+ sscanf(conf_get_str(ssh->conf, CONF_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_addstring(s->pktout, conf_get_str(ssh->conf, CONF_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);
+ parse_ttymodes(ssh, 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);
@@ -8833,6 +9093,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
} else {
logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
ssh->ospeed, ssh->ispeed);
+ ssh->got_pty = TRUE;
}
} else {
ssh->editing = ssh->echoing = 1;
@@ -8844,63 +9105,57 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* 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->ncmode && *ssh->cfg.environmt) {
- char *e = ssh->cfg.environmt;
- char *var, *varend, *val;
+ if (ssh->mainchan && !ssh->ncmode) {
+ char *key, *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++;
-
+ for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) {
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, key);
ssh2_pkt_addstring(s->pktout, val);
ssh2_pkt_send(ssh, s->pktout);
s->num_env++;
}
- logeventf(ssh, "Sent %d environment variables", s->num_env);
+ if (s->num_env) {
+ logeventf(ssh, "Sent %d environment variables", s->num_env);
- s->env_ok = 0;
- s->env_left = s->num_env;
+ s->env_ok = 0;
+ s->env_left = s->num_env;
- while (s->env_left > 0) {
- crWaitUntilV(pktin);
+ 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;
+ 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++;
}
- } else {
- s->env_ok++;
- }
- s->env_left--;
- }
+ 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");
+ 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");
+ }
}
}
@@ -8914,12 +9169,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
char *cmd;
if (ssh->fallback_cmd) {
- subsys = ssh->cfg.ssh_subsys2;
- cmd = ssh->cfg.remote_cmd_ptr2;
+ subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
+ cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
} else {
- subsys = ssh->cfg.ssh_subsys;
- cmd = ssh->cfg.remote_cmd_ptr;
- if (!cmd) cmd = ssh->cfg.remote_cmd;
+ subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
+ cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
}
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
@@ -8952,7 +9206,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* not, and if the fallback command exists, try falling
* back to it before complaining.
*/
- if (!ssh->fallback_cmd && ssh->cfg.remote_cmd_ptr2 != NULL) {
+ if (!ssh->fallback_cmd &&
+ *conf_get_str(ssh->conf, CONF_remote_cmd2)) {
logevent("Primary command failed; attempting fallback");
ssh->fallback_cmd = TRUE;
continue;
@@ -9056,10 +9311,9 @@ static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
/* 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);
@@ -9143,7 +9397,7 @@ static void ssh2_timer(void *ctx, long now)
if (ssh->state == SSH_STATE_CLOSED)
return;
- if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 &&
+ if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 &&
now - ssh->next_rekey >= 0) {
do_ssh2_transport(ssh, "timeout", -1, NULL);
}
@@ -9184,21 +9438,26 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
}
}
+static void ssh_cache_conf_values(Ssh ssh)
+{
+ ssh->logomitdata = conf_get_int(ssh->conf, CONF_logomitdata);
+}
+
/*
* Called to set up the connection.
*
* Returns an error message, or NULL on success.
*/
static const char *ssh_init(void *frontend_handle, void **backend_handle,
- Config *cfg,
- char *host, int port, char **realhost, int nodelay,
- int keepalive)
+ Conf *conf, char *host, int port, char **realhost,
+ int nodelay, int keepalive)
{
const char *p;
Ssh ssh;
ssh = snew(struct ssh_tag);
- ssh->cfg = *cfg; /* STRUCTURE COPY */
+ ssh->conf = conf_copy(conf);
+ ssh_cache_conf_values(ssh);
ssh->version = 0; /* when not ready yet */
ssh->s = NULL;
ssh->cipher = NULL;
@@ -9260,6 +9519,9 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->deferred_rekey_reason = NULL;
bufchain_init(&ssh->queued_incoming_data);
ssh->frozen = FALSE;
+ ssh->username = NULL;
+ ssh->sent_console_eof = FALSE;
+ ssh->got_pty = FALSE;
*backend_handle = ssh;
@@ -9269,8 +9531,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
#endif
ssh->frontend = frontend_handle;
- ssh->term_width = ssh->cfg.width;
- ssh->term_height = ssh->cfg.height;
+ ssh->term_width = conf_get_int(ssh->conf, CONF_width);
+ ssh->term_height = conf_get_int(ssh->conf, CONF_height);
ssh->channels = NULL;
ssh->rportfwds = NULL;
@@ -9291,7 +9553,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->incoming_data_size = ssh->outgoing_data_size =
ssh->deferred_data_size = 0L;
- ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
+ ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
+ CONF_ssh_rekey_data));
ssh->kex_in_progress = FALSE;
#ifndef NO_GSSAPI
@@ -9395,6 +9658,8 @@ static void ssh_free(void *handle)
if (ssh->pinger)
pinger_free(ssh->pinger);
bufchain_clear(&ssh->queued_incoming_data);
+ sfree(ssh->username);
+ conf_free(ssh->conf);
#ifndef NO_GSSAPI
if (ssh->gsslibs)
ssh_gss_cleanup(ssh->gsslibs);
@@ -9407,19 +9672,21 @@ static void ssh_free(void *handle)
/*
* Reconfigure the SSH backend.
*/
-static void ssh_reconfig(void *handle, Config *cfg)
+static void ssh_reconfig(void *handle, Conf *conf)
{
Ssh ssh = (Ssh) handle;
char *rekeying = NULL, rekey_mandatory = FALSE;
unsigned long old_max_data_size;
+ int i, rekey_time;
- pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
+ pinger_reconfig(ssh->pinger, ssh->conf, conf);
if (ssh->portfwds)
- ssh_setup_portfwd(ssh, cfg);
+ ssh_setup_portfwd(ssh, conf);
- if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&
- cfg->ssh_rekey_time != 0) {
- long new_next = ssh->last_rekey + cfg->ssh_rekey_time*60*TICKSPERSEC;
+ rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
+ if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time &&
+ rekey_time != 0) {
+ long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC;
long now = GETTICKCOUNT();
if (new_next - now < 0) {
@@ -9430,7 +9697,8 @@ static void ssh_reconfig(void *handle, Config *cfg)
}
old_max_data_size = ssh->max_data_size;
- ssh->max_data_size = parse_blocksize(cfg->ssh_rekey_data);
+ ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
+ CONF_ssh_rekey_data));
if (old_max_data_size != ssh->max_data_size &&
ssh->max_data_size != 0) {
if (ssh->outgoing_data_size > ssh->max_data_size ||
@@ -9438,19 +9706,27 @@ static void ssh_reconfig(void *handle, Config *cfg)
rekeying = "data limit lowered";
}
- if (ssh->cfg.compression != cfg->compression) {
+ if (conf_get_int(ssh->conf, CONF_compression) !=
+ conf_get_int(conf, CONF_compression)) {
rekeying = "compression setting changed";
rekey_mandatory = TRUE;
}
- if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc ||
- memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist,
- sizeof(ssh->cfg.ssh_cipherlist))) {
+ for (i = 0; i < CIPHER_MAX; i++)
+ if (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i) !=
+ conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
+ rekeying = "cipher settings changed";
+ rekey_mandatory = TRUE;
+ }
+ if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc) !=
+ conf_get_int(conf, CONF_ssh2_des_cbc)) {
rekeying = "cipher settings changed";
rekey_mandatory = TRUE;
}
- ssh->cfg = *cfg; /* STRUCTURE COPY */
+ conf_free(ssh->conf);
+ ssh->conf = conf_copy(conf);
+ ssh_cache_conf_values(ssh);
if (rekeying) {
if (!ssh->kex_in_progress) {
@@ -9498,7 +9774,7 @@ static int ssh_sendbuffer(void *handle)
if (ssh->version == 1) {
return override_value;
} else if (ssh->version == 2) {
- if (!ssh->mainchan || ssh->mainchan->closes > 0)
+ if (!ssh->mainchan)
return override_value;
else
return (override_value +
@@ -9528,7 +9804,7 @@ static void ssh_size(void *handle, int width, int height)
ssh->size_needed = TRUE; /* buffer for later */
break;
case SSH_STATE_SESSION:
- if (!ssh->cfg.nopty) {
+ if (!conf_get_int(ssh->conf, CONF_nopty)) {
if (ssh->version == 1) {
send_packet(ssh, SSH1_CMSG_WINDOW_SIZE,
PKT_INT, ssh->term_height,
@@ -9647,9 +9923,7 @@ static void ssh_special(void *handle, Telnet_Special code)
if (ssh->version == 1) {
send_packet(ssh, SSH1_CMSG_EOF, PKT_END);
} else if (ssh->mainchan) {
- struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
- ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);
- ssh2_pkt_send(ssh, pktout);
+ sshfwd_write_eof(ssh->mainchan);
ssh->send_ok = 0; /* now stop trying to read from stdin */
}
logevent("Sent EOF message");
@@ -9752,7 +10026,7 @@ static void ssh_unthrottle(void *handle, int bufsize)
ssh2_set_window(ssh->mainchan,
bufsize < ssh->mainchan->v.v2.locmaxwin ?
ssh->mainchan->v.v2.locmaxwin - bufsize : 0);
- if (ssh->cfg.ssh_simple)
+ if (conf_get_int(ssh->conf, CONF_ssh_simple))
buflimit = 0;
else
buflimit = ssh->mainchan->v.v2.locmaxwin;
diff --git a/tools/plink/ssh.h b/tools/plink/ssh.h
index 86c402965..3431842aa 100644
--- a/tools/plink/ssh.h
+++ b/tools/plink/ssh.h
@@ -9,8 +9,9 @@
struct ssh_channel;
-extern void sshfwd_close(struct ssh_channel *c);
extern int sshfwd_write(struct ssh_channel *c, char *, int);
+extern void sshfwd_write_eof(struct ssh_channel *c);
+extern void sshfwd_unclean_close(struct ssh_channel *c);
extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
/*
@@ -251,6 +252,9 @@ struct ssh_signkey {
struct ssh_compress {
char *name;
+ /* For zlib@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,
@@ -331,16 +335,15 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
/* Exports from portfwd.c */
extern const char *pfd_newconnect(Socket * s, char *hostname, int port,
- void *c, const Config *cfg,
- int addressfamily);
+ void *c, Conf *conf, int addressfamily);
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
- int port, void *backhandle,
- const Config *cfg, void **sockdata,
- int address_family);
+ int port, void *backhandle, Conf *conf,
+ void **sockdata, int address_family);
extern void pfd_close(Socket s);
extern void pfd_terminate(void *sockdata);
extern int pfd_send(Socket s, char *data, int len);
+extern void pfd_send_eof(Socket s);
extern void pfd_confirm(Socket s);
extern void pfd_unthrottle(Socket s);
extern void pfd_override_throttle(Socket s, int enable);
@@ -390,18 +393,18 @@ struct X11Display {
* details are looked up by calling platform_get_x11_auth.
*/
extern struct X11Display *x11_setup_display(char *display, int authtype,
- const Config *);
+ Conf *);
void x11_free_display(struct X11Display *disp);
extern const char *x11_init(Socket *, struct X11Display *, void *,
- const char *, int, const Config *);
+ const char *, int, Conf *);
extern void x11_close(Socket);
extern int x11_send(Socket, char *, int);
+extern void x11_send_eof(Socket s);
extern void x11_unthrottle(Socket s);
extern void x11_override_throttle(Socket s, int enable);
char *x11_display(const char *display);
/* Platform-dependent X11 functions */
-extern void platform_get_x11_auth(struct X11Display *display,
- const Config *);
+extern void platform_get_x11_auth(struct X11Display *display, Conf *);
/* 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 */
@@ -447,6 +450,8 @@ int ssh1_write_bignum(void *data, Bignum bn);
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);
diff --git a/tools/plink/sshbn.c b/tools/plink/sshbn.c
index e9ff0cde4..51cecdf2b 100644
--- a/tools/plink/sshbn.c
+++ b/tools/plink/sshbn.c
@@ -51,7 +51,34 @@ typedef unsigned __int64 BignumDblInt;
__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 @@ Bignum bn_power_2(int n)
}
/*
+ * 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.)
*/
-static void internal_mul(BignumInt *a, BignumInt *b,
- BignumInt *c, int len)
+#define KARATSUBA_THRESHOLD 50
+static int mul_compute_scratch(int len)
{
- int i, j;
- BignumDblInt t;
-
- for (j = 0; j < 2 * len; j++)
- c[j] = 0;
-
- 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;
+ 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 monty_reduce(BignumInt *x, const BignumInt *n,
+ const BignumInt *mninv, BignumInt *tmp, int len)
+{
+ int i;
+ BignumInt carry;
+
+ /*
+ * 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);
+
+ /*
+ * 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 @@ static void internal_mod(BignumInt *a, int alen,
}
/*
- * 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 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)
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 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)
/* 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 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)
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 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)
}
/*
+ * 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 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod)
/* 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 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod)
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 @@ Bignum bigmuladd(Bignum a, Bignum b, Bignum addend)
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 @@ Bignum bigmuladd(Bignum a, Bignum b, Bignum addend)
}
ret[0] = maxspot;
+ for (i = 0; i < wslen; i++)
+ workspace[i] = 0;
sfree(workspace);
return ret;
}
@@ -813,6 +1413,69 @@ Bignum bigmul(Bignum a, Bignum b)
}
/*
+ * 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 @@ char *bignum_decimal(Bignum x)
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
diff --git a/tools/plink/sshgss.h b/tools/plink/sshgss.h
index 5d8fca1b7..61f35a299 100644
--- a/tools/plink/sshgss.h
+++ b/tools/plink/sshgss.h
@@ -47,7 +47,7 @@ struct ssh_gss_liblist {
struct ssh_gss_library *libraries;
int nlibraries;
};
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg);
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf);
void ssh_gss_cleanup(struct ssh_gss_liblist *list);
/*
diff --git a/tools/plink/sshpubk.c b/tools/plink/sshpubk.c
index 7b5a69071..c1386108a 100644
--- a/tools/plink/sshpubk.c
+++ b/tools/plink/sshpubk.c
@@ -162,7 +162,7 @@ int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase,
int ret = 0;
const char *error = NULL;
- fp = f_open(*filename, "rb", FALSE);
+ fp = f_open(filename, "rb", FALSE);
if (!fp) {
error = "can't open file";
goto end;
@@ -203,7 +203,7 @@ int rsakey_encrypted(const Filename *filename, char **comment)
FILE *fp;
char buf[64];
- fp = f_open(*filename, "rb", FALSE);
+ fp = f_open(filename, "rb", FALSE);
if (!fp)
return 0; /* doesn't even exist */
@@ -241,7 +241,7 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
*bloblen = 0;
ret = 0;
- fp = f_open(*filename, "rb", FALSE);
+ fp = f_open(filename, "rb", FALSE);
if (!fp) {
error = "can't open file";
goto end;
@@ -364,7 +364,7 @@ int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase)
/*
* Done. Write the result to the file.
*/
- fp = f_open(*filename, "wb", TRUE);
+ fp = f_open(filename, "wb", TRUE);
if (fp) {
int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf));
if (fclose(fp))
@@ -632,7 +632,7 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
encryption = comment = mac = NULL;
public_blob = private_blob = NULL;
- fp = f_open(*filename, "rb", FALSE);
+ fp = f_open(filename, "rb", FALSE);
if (!fp) {
error = "can't open file";
goto error;
@@ -881,7 +881,7 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
public_blob = NULL;
- fp = f_open(*filename, "rb", FALSE);
+ fp = f_open(filename, "rb", FALSE);
if (!fp) {
error = "can't open file";
goto error;
@@ -962,7 +962,7 @@ int ssh2_userkey_encrypted(const Filename *filename, char **commentptr)
if (commentptr)
*commentptr = NULL;
- fp = f_open(*filename, "rb", FALSE);
+ fp = f_open(filename, "rb", FALSE);
if (!fp)
return 0;
if (!read_header(fp, header)
@@ -1143,7 +1143,7 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
memset(&s, 0, sizeof(s));
}
- fp = f_open(*filename, "w", TRUE);
+ fp = f_open(filename, "w", TRUE);
if (!fp)
return 0;
fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name);
@@ -1179,7 +1179,7 @@ int key_type(const Filename *filename)
const char openssh_sig[] = "-----BEGIN ";
int i;
- fp = f_open(*filename, "r", FALSE);
+ fp = f_open(filename, "r", FALSE);
if (!fp)
return SSH_KEYTYPE_UNOPENABLE;
i = fread(buf, 1, sizeof(buf), fp);
diff --git a/tools/plink/sshrsa.c b/tools/plink/sshrsa.c
index d06e9d6f4..ea6440bc5 100644
--- a/tools/plink/sshrsa.c
+++ b/tools/plink/sshrsa.c
@@ -114,9 +114,83 @@ static void sha512_mpint(SHA512_State * s, Bignum b)
}
/*
- * 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 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key)
* _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);
diff --git a/tools/plink/sshzlib.c b/tools/plink/sshzlib.c
index 7d37141c7..9c780a41f 100644
--- a/tools/plink/sshzlib.c
+++ b/tools/plink/sshzlib.c
@@ -1259,6 +1259,8 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
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 @@ int main(int argc, char **argv)
const struct ssh_compress ssh_zlib = {
"zlib",
+ "zlib@openssh.com", /* delayed version */
zlib_compress_init,
zlib_compress_cleanup,
zlib_compress_block,
diff --git a/tools/plink/storage.h b/tools/plink/storage.h
index 0e0a7c0bd..e7963ece3 100644
--- a/tools/plink/storage.h
+++ b/tools/plink/storage.h
@@ -9,9 +9,9 @@
/* ----------------------------------------------------------------------
* Functions to save and restore PuTTY sessions. Note that this is
* only the low-level code to do the reading and writing. The
- * higher-level code that translates a Config structure into a set
- * of (key,value) pairs is elsewhere, since it doesn't (mostly)
- * change between platforms.
+ * higher-level code that translates an internal Conf structure into
+ * a set of (key,value) pairs in their external storage format is
+ * elsewhere, since it doesn't (mostly) change between platforms.
*/
/*
@@ -31,8 +31,8 @@
void *open_settings_w(const char *sessionname, char **errmsg);
void write_setting_s(void *handle, const char *key, const char *value);
void write_setting_i(void *handle, const char *key, int value);
-void write_setting_filename(void *handle, const char *key, Filename value);
-void write_setting_fontspec(void *handle, const char *key, FontSpec font);
+void write_setting_filename(void *handle, const char *key, Filename *value);
+void write_setting_fontspec(void *handle, const char *key, FontSpec *font);
void close_settings_w(void *handle);
/*
@@ -41,22 +41,21 @@ void close_settings_w(void *handle);
* number of calls to read_setting_s() and read_setting_i(), and
* then close it using close_settings_r().
*
- * read_setting_s() writes into the provided buffer and returns a
- * pointer to the same buffer.
+ * read_setting_s() returns a dynamically allocated string which the
+ * caller must free. read_setting_filename() and
+ * read_setting_fontspec() likewise return dynamically allocated
+ * structures.
*
* If a particular string setting is not present in the session,
* read_setting_s() can return NULL, in which case the caller
* should invent a sensible default. If an integer setting is not
* present, read_setting_i() returns its provided default.
- *
- * read_setting_filename() and read_setting_fontspec() each read into
- * the provided buffer, and return zero if they failed to.
*/
void *open_settings_r(const char *sessionname);
-char *read_setting_s(void *handle, const char *key, char *buffer, int buflen);
+char *read_setting_s(void *handle, const char *key);
int read_setting_i(void *handle, const char *key, int defvalue);
-int read_setting_filename(void *handle, const char *key, Filename *value);
-int read_setting_fontspec(void *handle, const char *key, FontSpec *font);
+Filename *read_setting_filename(void *handle, const char *key);
+FontSpec *read_setting_fontspec(void *handle, const char *key);
void close_settings_r(void *handle);
/*
diff --git a/tools/plink/telnet.c b/tools/plink/telnet.c
index 8fbe88679..c024538b9 100644
--- a/tools/plink/telnet.c
+++ b/tools/plink/telnet.c
@@ -201,7 +201,7 @@ typedef struct telnet_tag {
SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
} state;
- Config cfg;
+ Conf *conf;
Pinger pinger;
} *Telnet;
@@ -363,42 +363,46 @@ static void proc_rec_opt(Telnet telnet, int cmd, int option)
static void process_subneg(Telnet telnet)
{
- unsigned char b[2048], *p, *q;
- int var, value, n;
- char *e;
+ unsigned char *b, *p, *q;
+ int var, value, n, bsize;
+ char *e, *eval, *ekey, *user;
switch (telnet->sb_opt) {
case TELOPT_TSPEED:
if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
char *logbuf;
+ char *termspeed = conf_get_str(telnet->conf, CONF_termspeed);
+ b = snewn(20 + strlen(termspeed), unsigned char);
b[0] = IAC;
b[1] = SB;
b[2] = TELOPT_TSPEED;
b[3] = TELQUAL_IS;
- strcpy((char *)(b + 4), telnet->cfg.termspeed);
- n = 4 + strlen(telnet->cfg.termspeed);
+ strcpy((char *)(b + 4), termspeed);
+ n = 4 + strlen(termspeed);
b[n] = IAC;
b[n + 1] = SE;
telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2);
logevent(telnet->frontend, "server:\tSB TSPEED SEND");
- logbuf = dupprintf("client:\tSB TSPEED IS %s", telnet->cfg.termspeed);
+ logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed);
logevent(telnet->frontend, logbuf);
sfree(logbuf);
+ sfree(b);
} else
logevent(telnet->frontend, "server:\tSB TSPEED <something weird>");
break;
case TELOPT_TTYPE:
if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
char *logbuf;
+ char *termtype = conf_get_str(telnet->conf, CONF_termtype);
+ b = snewn(20 + strlen(termtype), unsigned char);
b[0] = IAC;
b[1] = SB;
b[2] = TELOPT_TTYPE;
b[3] = TELQUAL_IS;
- for (n = 0; telnet->cfg.termtype[n]; n++)
- b[n + 4] = (telnet->cfg.termtype[n] >= 'a'
- && telnet->cfg.termtype[n] <=
- 'z' ? telnet->cfg.termtype[n] + 'A' -
- 'a' : telnet->cfg.termtype[n]);
+ for (n = 0; termtype[n]; n++)
+ b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ?
+ termtype[n] + 'A' - 'a' :
+ termtype[n]);
b[n + 4] = IAC;
b[n + 5] = SE;
telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6);
@@ -407,6 +411,7 @@ static void process_subneg(Telnet telnet)
logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4);
logevent(telnet->frontend, logbuf);
sfree(logbuf);
+ sfree(b);
} else
logevent(telnet->frontend, "server:\tSB TTYPE <something weird>\r\n");
break;
@@ -421,7 +426,7 @@ static void process_subneg(Telnet telnet)
logevent(telnet->frontend, logbuf);
sfree(logbuf);
if (telnet->sb_opt == TELOPT_OLD_ENVIRON) {
- if (telnet->cfg.rfc_environ) {
+ if (conf_get_int(telnet->conf, CONF_rfc_environ)) {
value = RFC_VALUE;
var = RFC_VAR;
} else {
@@ -449,50 +454,75 @@ static void process_subneg(Telnet telnet)
value = RFC_VALUE;
var = RFC_VAR;
}
+ bsize = 20;
+ for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+ NULL, &ekey);
+ eval != NULL;
+ eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+ ekey, &ekey))
+ bsize += strlen(ekey) + strlen(eval) + 2;
+ user = get_remote_username(telnet->conf);
+ if (user)
+ bsize += 6 + strlen(user);
+
+ b = snewn(bsize, unsigned char);
b[0] = IAC;
b[1] = SB;
b[2] = telnet->sb_opt;
b[3] = TELQUAL_IS;
n = 4;
- e = telnet->cfg.environmt;
- while (*e) {
+ for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+ NULL, &ekey);
+ eval != NULL;
+ eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+ ekey, &ekey)) {
b[n++] = var;
- while (*e && *e != '\t')
- b[n++] = *e++;
- if (*e == '\t')
- e++;
+ for (e = ekey; *e; e++)
+ b[n++] = *e;
b[n++] = value;
- while (*e)
- b[n++] = *e++;
- e++;
+ for (e = eval; *e; 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>" : ""));
+ if (user) {
+ b[n++] = var;
+ b[n++] = 'U';
+ b[n++] = 'S';
+ b[n++] = 'E';
+ b[n++] = 'R';
+ b[n++] = value;
+ for (e = user; *e; e++)
+ b[n++] = *e;
+ }
+ b[n++] = IAC;
+ b[n++] = SE;
+ telnet->bufsize = sk_write(telnet->s, (char *)b, n);
+ if (n == 6) {
+ logbuf = dupprintf("client:\tSB %s IS <nothing>",
+ telopt(telnet->sb_opt));
+ logevent(telnet->frontend, logbuf);
+ sfree(logbuf);
+ } else {
+ logbuf = dupprintf("client:\tSB %s IS:",
+ telopt(telnet->sb_opt));
logevent(telnet->frontend, logbuf);
sfree(logbuf);
+ for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+ NULL, &ekey);
+ eval != NULL;
+ eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+ ekey, &ekey)) {
+ logbuf = dupprintf("\t%s=%s", ekey, eval);
+ logevent(telnet->frontend, logbuf);
+ sfree(logbuf);
+ }
+ if (user) {
+ logbuf = dupprintf("\tUSER=%s", user);
+ logevent(telnet->frontend, logbuf);
+ sfree(logbuf);
+ }
}
+ sfree(b);
+ sfree(user);
}
break;
}
@@ -637,6 +667,12 @@ static int telnet_closing(Plug plug, const char *error_msg, int error_code,
{
Telnet telnet = (Telnet) plug;
+ /*
+ * We don't implement independent EOF in each direction for Telnet
+ * connections; as soon as we get word that the remote side has
+ * sent us EOF, we wind up the whole connection.
+ */
+
if (telnet->s) {
sk_close(telnet->s);
telnet->s = NULL;
@@ -674,9 +710,8 @@ static void telnet_sent(Plug plug, int bufsize)
* freed by the caller.
*/
static const char *telnet_init(void *frontend_handle, void **backend_handle,
- Config *cfg,
- char *host, int port, char **realhost,
- int nodelay, int keepalive)
+ Conf *conf, char *host, int port,
+ char **realhost, int nodelay, int keepalive)
{
static const struct plug_function_table fn_table = {
telnet_log,
@@ -687,10 +722,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
SockAddr addr;
const char *err;
Telnet telnet;
+ char *loghost;
+ int addressfamily;
telnet = snew(struct telnet_tag);
telnet->fn = &fn_table;
- telnet->cfg = *cfg; /* STRUCTURE COPY */
+ telnet->conf = conf_copy(conf);
telnet->s = NULL;
telnet->echoing = TRUE;
telnet->editing = TRUE;
@@ -698,8 +735,8 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
telnet->sb_buf = NULL;
telnet->sb_size = 0;
telnet->frontend = frontend_handle;
- telnet->term_width = telnet->cfg.width;
- telnet->term_height = telnet->cfg.height;
+ telnet->term_width = conf_get_int(telnet->conf, CONF_width);
+ telnet->term_height = conf_get_int(telnet->conf, CONF_height);
telnet->state = TOP_LEVEL;
telnet->ldisc = NULL;
telnet->pinger = NULL;
@@ -710,14 +747,15 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
*/
{
char *buf;
+ addressfamily = conf_get_int(telnet->conf, CONF_addressfamily);
buf = dupprintf("Looking up host \"%s\"%s", host,
- (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
"")));
logevent(telnet->frontend, buf);
sfree(buf);
}
- addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily);
+ addr = name_lookup(host, port, realhost, telnet->conf, addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
@@ -730,16 +768,16 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
* Open socket.
*/
telnet->s = new_connection(addr, *realhost, port, 0, 1,
- nodelay, keepalive, (Plug) telnet, &telnet->cfg);
+ nodelay, keepalive, (Plug) telnet, telnet->conf);
if ((err = sk_socket_error(telnet->s)) != NULL)
return err;
- telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet);
+ telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet);
/*
* Initialise option states.
*/
- if (telnet->cfg.passive_telnet) {
+ if (conf_get_int(telnet->conf, CONF_passive_telnet)) {
const struct Opt *const *o;
for (o = opts; *o; o++)
@@ -768,11 +806,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
/*
* loghost overrides realhost, if specified.
*/
- if (*telnet->cfg.loghost) {
+ loghost = conf_get_str(telnet->conf, CONF_loghost);
+ if (*loghost) {
char *colon;
sfree(*realhost);
- *realhost = dupstr(telnet->cfg.loghost);
+ *realhost = dupstr(loghost);
colon = strrchr(*realhost, ':');
if (colon) {
/*
@@ -796,6 +835,7 @@ static void telnet_free(void *handle)
sk_close(telnet->s);
if (telnet->pinger)
pinger_free(telnet->pinger);
+ conf_free(telnet->conf);
sfree(telnet);
}
/*
@@ -803,11 +843,12 @@ static void telnet_free(void *handle)
* necessary, in this backend: we just save the fresh config for
* any subsequent negotiations.
*/
-static void telnet_reconfig(void *handle, Config *cfg)
+static void telnet_reconfig(void *handle, Conf *conf)
{
Telnet telnet = (Telnet) handle;
- pinger_reconfig(telnet->pinger, &telnet->cfg, cfg);
- telnet->cfg = *cfg; /* STRUCTURE COPY */
+ pinger_reconfig(telnet->pinger, telnet->conf, conf);
+ conf_free(telnet->conf);
+ telnet->conf = conf_copy(conf);
}
/*
diff --git a/tools/plink/terminal.h b/tools/plink/terminal.h
index 6d3b1c544..924cf56b9 100644
--- a/tools/plink/terminal.h
+++ b/tools/plink/terminal.h
@@ -233,13 +233,13 @@ struct terminal_tag {
struct unicode_data *ucsdata;
/*
- * We maintain a full _copy_ of a Config structure here, not
- * merely a pointer to it. That way, when we're passed a new
- * one for reconfiguration, we can check the differences and
- * adjust the _current_ setting of (e.g.) auto wrap mode rather
- * than only the default.
+ * We maintain a full copy of a Conf here, not merely a pointer
+ * to it. That way, when we're passed a new one for
+ * reconfiguration, we can check the differences and adjust the
+ * _current_ setting of (e.g.) auto wrap mode rather than only
+ * the default.
*/
- Config cfg;
+ Conf *conf;
/*
* from_backend calls term_out, but it can also be called from
@@ -273,6 +273,52 @@ struct terminal_tag {
int wcFromTo_size;
struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache;
int bidi_cache_size;
+
+ /*
+ * We copy a bunch of stuff out of the Conf structure into local
+ * fields in the Terminal structure, to avoid the repeated
+ * tree234 lookups which would be involved in fetching them from
+ * the former every time.
+ */
+ int ansi_colour;
+ char *answerback;
+ int answerbacklen;
+ int arabicshaping;
+ int beep;
+ int bellovl;
+ int bellovl_n;
+ int bellovl_s;
+ int bellovl_t;
+ int bidi;
+ int bksp_is_delete;
+ int blink_cur;
+ int blinktext;
+ int cjk_ambig_wide;
+ int conf_height;
+ int conf_width;
+ int crhaslf;
+ int erase_to_scrollback;
+ int funky_type;
+ int lfhascr;
+ int logflush;
+ int logtype;
+ int mouse_override;
+ int nethack_keypad;
+ int no_alt_screen;
+ int no_applic_c;
+ int no_applic_k;
+ int no_dbackspace;
+ int no_mouse_rep;
+ int no_remote_charset;
+ int no_remote_resize;
+ int no_remote_wintitle;
+ int rawcnp;
+ int rect_select;
+ int remote_qtitle_action;
+ int rxvt_homeend;
+ int scroll_on_disp;
+ int scroll_on_key;
+ int xterm_256_colour;
};
#define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
diff --git a/tools/plink/version.c b/tools/plink/version.c
index bcb233272..16347ca19 100644
--- a/tools/plink/version.c
+++ b/tools/plink/version.c
@@ -5,7 +5,21 @@
#define STR1(x) #x
#define STR(x) STR1(x)
-#define SVN_REV 9080
+#ifdef INCLUDE_EMPTY_H
+/*
+ * Horrible hack to force version.o to be rebuilt unconditionally in
+ * the automake world: empty.h is an empty header file, created by the
+ * makefile and forcibly updated every time make is run. Including it
+ * here causes automake to track it as a dependency, which will cause
+ * version.o to be rebuilt too.
+ *
+ * The space between # and include causes mkfiles.pl's dependency
+ * scanner (for all other makefile types) to ignore this include,
+ * which is correct because only the automake makefile passes
+ * -DINCLUDE_EMPTY_H to enable it.
+ */
+# include "empty.h"
+#endif
#if defined SNAPSHOT
@@ -25,6 +39,11 @@ char sshver[] = "PuTTY-Snapshot-" SNAPSHOT_TEXT;
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) ", " __DATE__ " " __TIME__;
diff --git a/tools/plink/wincons.c b/tools/plink/wincons.c
index 4f984d958..41bbc130d 100644
--- a/tools/plink/wincons.c
+++ b/tools/plink/wincons.c
@@ -201,7 +201,7 @@ int askalg(void *frontend, const char *algtype, const char *algname,
* Ask whether to wipe a session log file before writing to it.
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
*/
-int askappend(void *frontend, Filename filename,
+int askappend(void *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx)
{
HANDLE hin;
@@ -223,11 +223,11 @@ int askappend(void *frontend, Filename filename,
char line[32];
if (console_batch_mode) {
- fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);
+ fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
fflush(stderr);
return 0;
}
- fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);
+ fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
fflush(stderr);
hin = GetStdHandle(STD_INPUT_HANDLE);
@@ -315,7 +315,7 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
int i;
for (i = 0; i < (int)p->n_prompts; i++)
- memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
+ prompt_set_result(p->prompts[i], "");
}
/*
@@ -365,9 +365,9 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
- DWORD savemode, newmode, i = 0;
+ DWORD savemode, newmode;
+ int len;
prompt_t *pr = p->prompts[curr_prompt];
- BOOL r;
GetConsoleMode(hin, &savemode);
newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
@@ -379,25 +379,44 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));
- r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL);
+ len = 0;
+ while (1) {
+ DWORD ret = 0;
+ BOOL r;
+
+ prompt_ensure_result_size(pr, len * 5 / 4 + 512);
+
+ r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1,
+ &ret, NULL);
+
+ if (!r || ret == 0) {
+ len = -1;
+ break;
+ }
+ len += ret;
+ if (pr->result[len - 1] == '\n') {
+ len--;
+ if (pr->result[len - 1] == '\r')
+ len--;
+ break;
+ }
+ }
SetConsoleMode(hin, savemode);
- if ((int) i > pr->result_len)
- i = pr->result_len - 1;
- else
- i = i - 2;
- pr->result[i] = '\0';
-
if (!pr->echo) {
DWORD dummy;
WriteFile(hout, "\r\n", 2, &dummy, NULL);
}
+ if (len < 0) {
+ return 0; /* failure due to read error */
+ }
+
+ pr->result[len] = '\0';
}
return 1; /* success */
-
}
void frontend_keypress(void *handle)
diff --git a/tools/plink/windefs.c b/tools/plink/windefs.c
index de01dafaa..e2f04ac70 100644
--- a/tools/plink/windefs.c
+++ b/tools/plink/windefs.c
@@ -6,28 +6,20 @@
#include <commctrl.h>
-FontSpec platform_default_fontspec(const char *name)
+FontSpec *platform_default_fontspec(const char *name)
{
- FontSpec ret;
- if (!strcmp(name, "Font")) {
- strcpy(ret.name, "Courier New");
- ret.isbold = 0;
- ret.charset = ANSI_CHARSET;
- ret.height = 10;
- } else {
- ret.name[0] = '\0';
- }
- return ret;
+ if (!strcmp(name, "Font"))
+ return fontspec_new("Courier New", 0, 10, ANSI_CHARSET);
+ else
+ return fontspec_new("", 0, 0, 0);
}
-Filename platform_default_filename(const char *name)
+Filename *platform_default_filename(const char *name)
{
- Filename ret;
if (!strcmp(name, "LogFileName"))
- strcpy(ret.path, "putty.log");
+ return filename_from_str("putty.log");
else
- *ret.path = '\0';
- return ret;
+ return filename_from_str("");
}
char *platform_default_s(const char *name)
diff --git a/tools/plink/wingss.c b/tools/plink/wingss.c
index 4efad5d58..91d2d45b5 100644
--- a/tools/plink/wingss.c
+++ b/tools/plink/wingss.c
@@ -18,10 +18,10 @@ const char *const gsslibnames[3] = {
"Microsoft SSPI SECUR32.DLL",
"User-specified GSSAPI DLL",
};
-const struct keyval gsslibkeywords[] = {
- { "gssapi32", 0 },
- { "sspi", 1 },
- { "custom", 2 },
+const struct keyvalwhere gsslibkeywords[] = {
+ { "gssapi32", 0, -1, -1 },
+ { "sspi", 1, -1, -1 },
+ { "custom", 2, -1, -1 },
};
DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
@@ -65,11 +65,12 @@ const char *gsslogmsg = NULL;
static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
{
HMODULE module;
HKEY regkey;
struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
+ char *path;
list->libraries = snewn(3, struct ssh_gss_library);
list->nlibraries = 0;
@@ -148,8 +149,9 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
* Custom GSSAPI DLL.
*/
module = NULL;
- if (cfg->ssh_gss_custom.path[0]) {
- module = LoadLibrary(cfg->ssh_gss_custom.path);
+ path = conf_get_filename(conf, CONF_ssh_gss_custom)->path;
+ if (*path) {
+ module = LoadLibrary(path);
}
if (module) {
struct ssh_gss_library *lib =
@@ -157,7 +159,7 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
lib->id = 2;
lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified"
- " library '%s'", cfg->ssh_gss_custom.path);
+ " library '%s'", path);
lib->handle = (void *)module;
#define BIND_GSS_FN(name) \
diff --git a/tools/plink/winhandl.c b/tools/plink/winhandl.c
index dbcab2b2a..06c2a6a07 100644
--- a/tools/plink/winhandl.c
+++ b/tools/plink/winhandl.c
@@ -250,6 +250,7 @@ struct handle_output {
* Data only ever read or written by the main thread.
*/
bufchain queued_data; /* data still waiting to be written */
+ enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
/*
* Callback function called when the backlog in the bufchain
@@ -320,6 +321,11 @@ static void handle_try_output(struct handle_output *ctx)
ctx->len = sendlen;
SetEvent(ctx->ev_from_main);
ctx->busy = TRUE;
+ } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 &&
+ ctx->outgoingeof == EOF_PENDING) {
+ CloseHandle(ctx->h);
+ ctx->h = INVALID_HANDLE_VALUE;
+ ctx->outgoingeof = EOF_SENT;
}
}
@@ -408,6 +414,7 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
h->u.o.done = FALSE;
h->u.o.privdata = privdata;
bufchain_init(&h->u.o.queued_data);
+ h->u.o.outgoingeof = EOF_NO;
h->u.o.sentdata = sentdata;
h->u.o.flags = flags;
@@ -424,11 +431,28 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
int handle_write(struct handle *h, const void *data, int len)
{
assert(h->output);
+ assert(h->u.o.outgoingeof == EOF_NO);
bufchain_add(&h->u.o.queued_data, data, len);
handle_try_output(&h->u.o);
return bufchain_size(&h->u.o.queued_data);
}
+void handle_write_eof(struct handle *h)
+{
+ /*
+ * This function is called when we want to proactively send an
+ * end-of-file notification on the handle. We can only do this by
+ * actually closing the handle - so never call this on a
+ * bidirectional handle if we're still interested in its incoming
+ * direction!
+ */
+ assert(h->output);
+ if (!h->u.o.outgoingeof == EOF_NO) {
+ h->u.o.outgoingeof = EOF_PENDING;
+ handle_try_output(&h->u.o);
+ }
+}
+
HANDLE *handle_get_events(int *nevents)
{
HANDLE *ret;
diff --git a/tools/plink/winmisc.c b/tools/plink/winmisc.c
index e70e77efa..e8a35ee1d 100644
--- a/tools/plink/winmisc.c
+++ b/tools/plink/winmisc.c
@@ -14,27 +14,58 @@ char *platform_get_x_display(void) {
return dupstr(getenv("DISPLAY"));
}
-Filename filename_from_str(const char *str)
+Filename *filename_from_str(const char *str)
{
- Filename ret;
- strncpy(ret.path, str, sizeof(ret.path));
- ret.path[sizeof(ret.path)-1] = '\0';
+ Filename *ret = snew(Filename);
+ ret->path = dupstr(str);
return ret;
}
+Filename *filename_copy(const Filename *fn)
+{
+ return filename_from_str(fn->path);
+}
+
const char *filename_to_str(const Filename *fn)
{
return fn->path;
}
-int filename_equal(Filename f1, Filename f2)
+int filename_equal(const Filename *f1, const Filename *f2)
+{
+ return !strcmp(f1->path, f2->path);
+}
+
+int filename_is_null(const Filename *fn)
{
- return !strcmp(f1.path, f2.path);
+ return !*fn->path;
}
-int filename_is_null(Filename fn)
+void filename_free(Filename *fn)
+{
+ sfree(fn->path);
+ sfree(fn);
+}
+
+int filename_serialise(const Filename *f, void *vdata)
+{
+ char *data = (char *)vdata;
+ int len = strlen(f->path) + 1; /* include trailing NUL */
+ if (data) {
+ strcpy(data, f->path);
+ }
+ return len;
+}
+Filename *filename_deserialise(void *vdata, int maxsize, int *used)
{
- return !*fn.path;
+ char *data = (char *)vdata;
+ char *end;
+ end = memchr(data, '\0', maxsize);
+ if (!end)
+ return NULL;
+ end++;
+ *used = end - data;
+ return filename_from_str(data);
}
char *get_username(void)
@@ -379,3 +410,51 @@ void *minefield_c_realloc(void *p, size_t size)
}
#endif /* MINEFIELD */
+
+FontSpec *fontspec_new(const char *name,
+ int bold, int height, int charset)
+{
+ FontSpec *f = snew(FontSpec);
+ f->name = dupstr(name);
+ f->isbold = bold;
+ f->height = height;
+ f->charset = charset;
+ return f;
+}
+FontSpec *fontspec_copy(const FontSpec *f)
+{
+ return fontspec_new(f->name, f->isbold, f->height, f->charset);
+}
+void fontspec_free(FontSpec *f)
+{
+ sfree(f->name);
+ sfree(f);
+}
+int fontspec_serialise(FontSpec *f, void *vdata)
+{
+ char *data = (char *)vdata;
+ int len = strlen(f->name) + 1; /* include trailing NUL */
+ if (data) {
+ strcpy(data, f->name);
+ PUT_32BIT_MSB_FIRST(data + len, f->isbold);
+ PUT_32BIT_MSB_FIRST(data + len + 4, f->height);
+ PUT_32BIT_MSB_FIRST(data + len + 8, f->charset);
+ }
+ return len + 12; /* also include three 4-byte ints */
+}
+FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
+{
+ char *data = (char *)vdata;
+ char *end;
+ if (maxsize < 13)
+ return NULL;
+ end = memchr(data, '\0', maxsize-12);
+ if (!end)
+ return NULL;
+ end++;
+ *used = end - data + 12;
+ return fontspec_new(data,
+ GET_32BIT_MSB_FIRST(end),
+ GET_32BIT_MSB_FIRST(end + 4),
+ GET_32BIT_MSB_FIRST(end + 8));
+}
diff --git a/tools/plink/winnet.c b/tools/plink/winnet.c
index 8da79dbe7..11f9ba52e 100644
--- a/tools/plink/winnet.c
+++ b/tools/plink/winnet.c
@@ -64,6 +64,7 @@ struct Socket_tag {
char oobdata[1];
int sending_oob;
int oobinline, nodelay, keepalive, privport;
+ enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
SockAddr addr;
SockAddrStep step;
int port;
@@ -167,6 +168,7 @@ DECL_WINDOWS_FUNCTION(static, int, setsockopt,
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, shutdown, (SOCKET, int));
DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,
(SOCKET, long, u_long FAR *));
DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
@@ -291,6 +293,7 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, socket);
GET_WINDOWS_FUNCTION(winsock_module, listen);
GET_WINDOWS_FUNCTION(winsock_module, send);
+ GET_WINDOWS_FUNCTION(winsock_module, shutdown);
GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);
GET_WINDOWS_FUNCTION(winsock_module, accept);
GET_WINDOWS_FUNCTION(winsock_module, recv);
@@ -745,6 +748,7 @@ static void sk_tcp_flush(Socket s)
static void sk_tcp_close(Socket s);
static int sk_tcp_write(Socket s, const char *data, int len);
static int sk_tcp_write_oob(Socket s, const char *data, int len);
+static void sk_tcp_write_eof(Socket s);
static void sk_tcp_set_private_ptr(Socket s, void *ptr);
static void *sk_tcp_get_private_ptr(Socket s);
static void sk_tcp_set_frozen(Socket s, int is_frozen);
@@ -759,6 +763,7 @@ Socket sk_register(void *sock, Plug plug)
sk_tcp_close,
sk_tcp_write,
sk_tcp_write_oob,
+ sk_tcp_write_eof,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
@@ -780,6 +785,7 @@ Socket sk_register(void *sock, Plug plug)
bufchain_init(&ret->output_data);
ret->writable = 1; /* to start with */
ret->sending_oob = 0;
+ ret->outgoingeof = EOF_NO;
ret->frozen = 1;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
@@ -1007,6 +1013,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
sk_tcp_close,
sk_tcp_write,
sk_tcp_write_oob,
+ sk_tcp_write_eof,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
@@ -1028,6 +1035,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
ret->connected = 0; /* to start with */
ret->writable = 0; /* to start with */
ret->sending_oob = 0;
+ ret->outgoingeof = EOF_NO;
ret->frozen = 0;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
@@ -1058,6 +1066,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
sk_tcp_close,
sk_tcp_write,
sk_tcp_write_oob,
+ sk_tcp_write_eof,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
@@ -1089,6 +1098,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
bufchain_init(&ret->output_data);
ret->writable = 0; /* to start with */
ret->sending_oob = 0;
+ ret->outgoingeof = EOF_NO;
ret->frozen = 0;
ret->frozen_readable = 0;
ret->localhost_only = local_host_only;
@@ -1325,12 +1335,23 @@ void try_send(Actual_Socket s)
}
}
}
+
+ /*
+ * If we reach here, we've finished sending everything we might
+ * have needed to send. Send EOF, if we need to.
+ */
+ if (s->outgoingeof == EOF_PENDING) {
+ p_shutdown(s->s, SD_SEND);
+ s->outgoingeof = EOF_SENT;
+ }
}
static int sk_tcp_write(Socket sock, const char *buf, int len)
{
Actual_Socket s = (Actual_Socket) sock;
+ assert(s->outgoingeof == EOF_NO);
+
/*
* Add the data to the buffer list on the socket.
*/
@@ -1349,6 +1370,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
{
Actual_Socket s = (Actual_Socket) sock;
+ assert(s->outgoingeof == EOF_NO);
+
/*
* Replace the buffer list on the socket with the data.
*/
@@ -1366,6 +1389,24 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
return s->sending_oob;
}
+static void sk_tcp_write_eof(Socket sock)
+{
+ Actual_Socket s = (Actual_Socket) sock;
+
+ assert(s->outgoingeof == EOF_NO);
+
+ /*
+ * Mark the socket as pending outgoing EOF.
+ */
+ s->outgoingeof = EOF_PENDING;
+
+ /*
+ * Now try sending from the start of the buffer list.
+ */
+ if (s->writable)
+ try_send(s);
+}
+
int select_result(WPARAM wParam, LPARAM lParam)
{
int ret, open;
@@ -1691,7 +1732,7 @@ char *get_hostname(void)
hostname = NULL;
break;
}
- } while (strlen(hostname) >= len-1);
+ } while (strlen(hostname) >= (size_t)(len-1));
return hostname;
}
diff --git a/tools/plink/winpgntc.c b/tools/plink/winpgntc.c
index 2a5aa734f..0dabe7167 100644
--- a/tools/plink/winpgntc.c
+++ b/tools/plink/winpgntc.c
@@ -86,15 +86,70 @@ DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor,
(PSECURITY_DESCRIPTOR, DWORD));
DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner,
(PSECURITY_DESCRIPTOR, PSID, BOOL));
-static int init_advapi(void)
+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,
@@ -108,8 +163,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
COPYDATASTRUCT cds;
SECURITY_ATTRIBUTES sa, *psa;
PSECURITY_DESCRIPTOR psd = NULL;
- HANDLE proc, tok;
- TOKEN_USER *user = NULL;
+ PSID usersid = NULL;
*out = NULL;
*outlen = 0;
@@ -130,31 +184,16 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
* run PSFTPs which refer back to the owning user's
* unprivileged Pageant.
*/
-
- if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
- GetCurrentProcessId())) != NULL) {
- if (p_OpenProcessToken(proc, TOKEN_QUERY, &tok)) {
- DWORD retlen;
- p_GetTokenInformation(tok, TokenUser, NULL, 0, &retlen);
- user = (TOKEN_USER *)LocalAlloc(LPTR, retlen);
- if (!p_GetTokenInformation(tok, TokenUser,
- user, retlen, &retlen)) {
- LocalFree(user);
- user = NULL;
- }
- CloseHandle(tok);
- }
- CloseHandle(proc);
- }
+ usersid = get_user_sid();
psa = NULL;
- if (user) {
+ if (usersid) {
psd = (PSECURITY_DESCRIPTOR)
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (psd) {
if (p_InitializeSecurityDescriptor
(psd, SECURITY_DESCRIPTOR_REVISION) &&
- p_SetSecurityDescriptorOwner(psd, user->User.Sid, FALSE)) {
+ p_SetSecurityDescriptorOwner(psd, usersid, FALSE)) {
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = psd;
@@ -221,7 +260,6 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
CloseHandle(filemap);
if (psd)
LocalFree(psd);
- if (user)
- LocalFree(user);
+ sfree(usersid);
return 1;
}
diff --git a/tools/plink/winplink.c b/tools/plink/winplink.c
index 7af9e1b4f..bd65f7825 100644
--- a/tools/plink/winplink.c
+++ b/tools/plink/winplink.c
@@ -83,7 +83,7 @@ WSAEVENT netevent;
static Backend *back;
static void *backhandle;
-static Config cfg;
+static Conf *conf;
int term_ldisc(Terminal *term, int mode)
{
@@ -130,6 +130,12 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len)
return 0; /* not reached */
}
+int from_backend_eof(void *frontend_handle)
+{
+ handle_write_eof(stdout_handle);
+ return FALSE; /* do not respond to incoming EOF with outgoing */
+}
+
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
int ret;
@@ -298,10 +304,11 @@ int main(int argc, char **argv)
/*
* Process the command line.
*/
- do_defaults(NULL, &cfg);
+ conf = conf_new();
+ do_defaults(NULL, conf);
loaded_session = FALSE;
- default_protocol = cfg.protocol;
- default_port = cfg.port;
+ default_protocol = conf_get_int(conf, CONF_protocol);
+ default_port = conf_get_int(conf, CONF_port);
errors = 0;
{
/*
@@ -311,8 +318,10 @@ int main(int argc, char **argv)
if (p) {
const Backend *b = backend_from_name(p);
if (b) {
- default_protocol = cfg.protocol = b->protocol;
- default_port = cfg.port = b->default_port;
+ default_protocol = b->protocol;
+ default_port = b->default_port;
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ conf_set_int(conf, CONF_port, default_port);
}
}
}
@@ -320,7 +329,7 @@ int main(int argc, char **argv)
char *p = *++argv;
if (*p == '-') {
int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
- 1, &cfg);
+ 1, conf);
if (ret == -2) {
fprintf(stderr,
"plink: option \"%s\" requires an argument\n", p);
@@ -332,7 +341,7 @@ int main(int argc, char **argv)
} else if (!strcmp(p, "-batch")) {
console_batch_mode = 1;
} else if (!strcmp(p, "-s")) {
- /* Save status to write to cfg later. */
+ /* Save status to write to conf later. */
use_subsystem = 1;
} else if (!strcmp(p, "-V")) {
version();
@@ -344,7 +353,7 @@ int main(int argc, char **argv)
errors = 1;
}
} else if (*p) {
- if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
+ if (!conf_launchable(conf) || !(got_host || loaded_session)) {
char *q = p;
/*
* If the hostname starts with "telnet:", set the
@@ -357,7 +366,7 @@ int main(int argc, char **argv)
q += 7;
if (q[0] == '/' && q[1] == '/')
q += 2;
- cfg.protocol = PROT_TELNET;
+ conf_set_int(conf, CONF_protocol, PROT_TELNET);
p = q;
while (*p && *p != ':' && *p != '/')
p++;
@@ -365,11 +374,10 @@ int main(int argc, char **argv)
if (*p)
*p++ = '\0';
if (c == ':')
- cfg.port = atoi(p);
+ conf_set_int(conf, CONF_port, atoi(p));
else
- cfg.port = -1;
- strncpy(cfg.host, q, sizeof(cfg.host) - 1);
- cfg.host[sizeof(cfg.host) - 1] = '\0';
+ conf_set_int(conf, CONF_port, -1);
+ conf_set_str(conf, CONF_host, q);
got_host = TRUE;
} else {
char *r, *user, *host;
@@ -384,7 +392,9 @@ int main(int argc, char **argv)
*r = '\0';
b = backend_from_name(p);
if (b) {
- default_protocol = cfg.protocol = b->protocol;
+ default_protocol = b->protocol;
+ conf_set_int(conf, CONF_protocol,
+ default_protocol);
portnumber = b->default_port;
}
p = r + 1;
@@ -411,26 +421,24 @@ int main(int argc, char **argv)
* same name as the hostname.
*/
{
- Config cfg2;
- do_defaults(host, &cfg2);
- if (loaded_session || !cfg_launchable(&cfg2)) {
+ Conf *conf2 = conf_new();
+ do_defaults(host, conf2);
+ if (loaded_session || !conf_launchable(conf2)) {
/* No settings for this host; use defaults */
/* (or session was already loaded with -load) */
- strncpy(cfg.host, host, sizeof(cfg.host) - 1);
- cfg.host[sizeof(cfg.host) - 1] = '\0';
- cfg.port = default_port;
+ conf_set_str(conf, CONF_host, host);
+ conf_set_int(conf, CONF_port, default_port);
got_host = TRUE;
} else {
- cfg = cfg2;
+ conf_copy_into(conf, conf2);
loaded_session = TRUE;
}
+ conf_free(conf2);
}
if (user) {
/* Patch in specified username. */
- strncpy(cfg.username, user,
- sizeof(cfg.username) - 1);
- cfg.username[sizeof(cfg.username) - 1] = '\0';
+ conf_set_str(conf, CONF_username, user);
}
}
@@ -457,9 +465,9 @@ int main(int argc, char **argv)
}
if (cmdlen) command[--cmdlen]='\0';
/* change trailing blank to NUL */
- cfg.remote_cmd_ptr = command;
- cfg.remote_cmd_ptr2 = NULL;
- cfg.nopty = TRUE; /* command => no terminal */
+ conf_set_str(conf, CONF_remote_cmd, command);
+ conf_set_str(conf, CONF_remote_cmd2, "");
+ conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */
break; /* done with cmdline */
}
@@ -469,70 +477,78 @@ int main(int argc, char **argv)
if (errors)
return 1;
- if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
+ if (!conf_launchable(conf) || !(got_host || loaded_session)) {
usage();
}
/*
- * Trim leading whitespace off the hostname if it's there.
+ * Muck about with the hostname in various ways.
*/
{
- int space = strspn(cfg.host, " \t");
- memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
- }
+ char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+ char *host = hostbuf;
+ char *p, *q;
+
+ /*
+ * Trim leading whitespace.
+ */
+ host += strspn(host, " \t");
- /* See if host is of the form user@host */
- if (cfg_launchable(&cfg)) {
- char *atsign = strrchr(cfg.host, '@');
- /* Make sure we're not overflowing the user field */
- if (atsign) {
- if (atsign - cfg.host < sizeof cfg.username) {
- strncpy(cfg.username, cfg.host, atsign - cfg.host);
- cfg.username[atsign - cfg.host] = '\0';
+ /*
+ * See if host is of the form user@host, and separate out
+ * the username if so.
+ */
+ if (host[0] != '\0') {
+ char *atsign = strrchr(host, '@');
+ if (atsign) {
+ *atsign = '\0';
+ conf_set_str(conf, CONF_username, host);
+ host = atsign + 1;
}
- memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
}
+
+ /*
+ * Trim off a colon suffix if it's there.
+ */
+ host[strcspn(host, ":")] = '\0';
+
+ /*
+ * Remove any remaining whitespace.
+ */
+ p = hostbuf;
+ q = host;
+ while (*q) {
+ if (*q != ' ' && *q != '\t')
+ *p++ = *q;
+ q++;
+ }
+ *p = '\0';
+
+ conf_set_str(conf, CONF_host, hostbuf);
+ sfree(hostbuf);
}
/*
* Perform command-line overrides on session configuration.
*/
- cmdline_run_saved(&cfg);
+ cmdline_run_saved(conf);
/*
* Apply subsystem status.
*/
if (use_subsystem)
- cfg.ssh_subsys = TRUE;
-
- /*
- * Trim a colon suffix off the hostname if it's there.
- */
- cfg.host[strcspn(cfg.host, ":")] = '\0';
-
- /*
- * Remove any remaining whitespace from the hostname.
- */
- {
- int p1 = 0, p2 = 0;
- while (cfg.host[p2] != '\0') {
- if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
- cfg.host[p1] = cfg.host[p2];
- p1++;
- }
- p2++;
- }
- cfg.host[p1] = '\0';
- }
+ conf_set_int(conf, CONF_ssh_subsys, TRUE);
- if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)
+ if (!*conf_get_str(conf, CONF_remote_cmd) &&
+ !*conf_get_str(conf, CONF_remote_cmd2) &&
+ !*conf_get_str(conf, CONF_ssh_nc_host))
flags |= FLAG_INTERACTIVE;
/*
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
- back = backend_from_proto(cfg.protocol);
+ back = backend_from_proto(conf_get_int(conf, CONF_protocol));
if (back == NULL) {
fprintf(stderr,
"Internal fault: Unsupported protocol found\n");
@@ -543,7 +559,7 @@ int main(int argc, char **argv)
* Select port.
*/
if (portnumber != -1)
- cfg.port = portnumber;
+ conf_set_int(conf, CONF_port, portnumber);
sk_init();
if (p_WSAEventSelect == NULL) {
@@ -551,7 +567,7 @@ int main(int argc, char **argv)
return 1;
}
- logctx = log_init(NULL, &cfg);
+ logctx = log_init(NULL, conf);
console_provide_logctx(logctx);
/*
@@ -562,11 +578,14 @@ int main(int argc, char **argv)
const char *error;
char *realhost;
/* nodelay is only useful if stdin is a character device (console) */
- int nodelay = cfg.tcp_nodelay &&
+ int nodelay = conf_get_int(conf, CONF_tcp_nodelay) &&
(GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
- error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
- &realhost, nodelay, cfg.tcp_keepalives);
+ error = back->init(NULL, &backhandle, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost, nodelay,
+ conf_get_int(conf, CONF_tcp_keepalives));
if (error) {
fprintf(stderr, "Unable to open connection:\n%s", error);
return 1;
diff --git a/tools/plink/winproxy.c b/tools/plink/winproxy.c
index 4da4d2e01..7a8c7b69b 100644
--- a/tools/plink/winproxy.c
+++ b/tools/plink/winproxy.c
@@ -87,6 +87,13 @@ static int sk_localproxy_write_oob(Socket s, const char *data, int len)
return sk_localproxy_write(s, data, len);
}
+static void sk_localproxy_write_eof(Socket s)
+{
+ Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
+
+ handle_write_eof(ps->to_cmd_h);
+}
+
static void sk_localproxy_flush(Socket s)
{
/* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
@@ -123,7 +130,7 @@ static const char *sk_localproxy_socket_error(Socket s)
Socket platform_new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, int keepalive,
- Plug plug, const Config *cfg)
+ Plug plug, Conf *conf)
{
char *cmd;
@@ -132,6 +139,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
sk_localproxy_close,
sk_localproxy_write,
sk_localproxy_write_oob,
+ sk_localproxy_write_eof,
sk_localproxy_flush,
sk_localproxy_set_private_ptr,
sk_localproxy_get_private_ptr,
@@ -145,10 +153,10 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
STARTUPINFO si;
PROCESS_INFORMATION pi;
- if (cfg->proxy_type != PROXY_CMD)
+ if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
return NULL;
- cmd = format_telnet_command(addr, port, cfg);
+ cmd = format_telnet_command(addr, port, conf);
{
char *msg = dupprintf("Starting local proxy command: %s", cmd);
diff --git a/tools/plink/winstore.c b/tools/plink/winstore.c
index 13ee184a5..f152b8f69 100644
--- a/tools/plink/winstore.c
+++ b/tools/plink/winstore.c
@@ -150,17 +150,26 @@ void *open_settings_r(const char *sessionname)
return (void *) sesskey;
}
-char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
+char *read_setting_s(void *handle, const char *key)
{
DWORD type, size;
- size = buflen;
+ char *ret;
- if (!handle ||
- RegQueryValueEx((HKEY) handle, key, 0,
- &type, buffer, &size) != ERROR_SUCCESS ||
+ if (!handle)
+ return NULL;
+
+ /* Find out the type and size of the data. */
+ if (RegQueryValueEx((HKEY) handle, key, 0,
+ &type, NULL, &size) != ERROR_SUCCESS ||
+ type != REG_SZ)
+ return NULL;
+
+ ret = snewn(size+1, char);
+ if (RegQueryValueEx((HKEY) handle, key, 0,
+ &type, ret, &size) != ERROR_SUCCESS ||
type != REG_SZ) return NULL;
- else
- return buffer;
+
+ return ret;
}
int read_setting_i(void *handle, const char *key, int defvalue)
@@ -177,53 +186,64 @@ int read_setting_i(void *handle, const char *key, int defvalue)
return val;
}
-int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
+FontSpec *read_setting_fontspec(void *handle, const char *name)
{
char *settingname;
- FontSpec ret;
+ char *fontname;
+ int isbold, height, charset;
+
+ fontname = read_setting_s(handle, name);
+ if (!fontname)
+ return NULL;
- if (!read_setting_s(handle, name, ret.name, sizeof(ret.name)))
- return 0;
settingname = dupcat(name, "IsBold", NULL);
- ret.isbold = read_setting_i(handle, settingname, -1);
+ isbold = read_setting_i(handle, settingname, -1);
sfree(settingname);
- if (ret.isbold == -1) return 0;
+ if (isbold == -1) return NULL;
+
settingname = dupcat(name, "CharSet", NULL);
- ret.charset = read_setting_i(handle, settingname, -1);
+ charset = read_setting_i(handle, settingname, -1);
sfree(settingname);
- if (ret.charset == -1) return 0;
+ if (charset == -1) return NULL;
+
settingname = dupcat(name, "Height", NULL);
- ret.height = read_setting_i(handle, settingname, INT_MIN);
+ height = read_setting_i(handle, settingname, INT_MIN);
sfree(settingname);
- if (ret.height == INT_MIN) return 0;
- *result = ret;
- return 1;
+ if (height == INT_MIN) return NULL;
+
+ return fontspec_new(fontname, isbold, height, charset);
}
-void write_setting_fontspec(void *handle, const char *name, FontSpec font)
+void write_setting_fontspec(void *handle, const char *name, FontSpec *font)
{
char *settingname;
- write_setting_s(handle, name, font.name);
+ write_setting_s(handle, name, font->name);
settingname = dupcat(name, "IsBold", NULL);
- write_setting_i(handle, settingname, font.isbold);
+ write_setting_i(handle, settingname, font->isbold);
sfree(settingname);
settingname = dupcat(name, "CharSet", NULL);
- write_setting_i(handle, settingname, font.charset);
+ write_setting_i(handle, settingname, font->charset);
sfree(settingname);
settingname = dupcat(name, "Height", NULL);
- write_setting_i(handle, settingname, font.height);
+ write_setting_i(handle, settingname, font->height);
sfree(settingname);
}
-int read_setting_filename(void *handle, const char *name, Filename *result)
+Filename *read_setting_filename(void *handle, const char *name)
{
- return !!read_setting_s(handle, name, result->path, sizeof(result->path));
+ char *tmp = read_setting_s(handle, name);
+ if (tmp) {
+ Filename *ret = filename_from_str(tmp);
+ sfree(tmp);
+ return ret;
+ } else
+ return NULL;
}
-void write_setting_filename(void *handle, const char *name, Filename result)
+void write_setting_filename(void *handle, const char *name, Filename *result)
{
- write_setting_s(handle, name, result.path);
+ write_setting_s(handle, name, result->path);
}
void close_settings_r(void *handle)
diff --git a/tools/plink/winstuff.h b/tools/plink/winstuff.h
index 1cc48348e..d2d65a47f 100644
--- a/tools/plink/winstuff.h
+++ b/tools/plink/winstuff.h
@@ -16,16 +16,18 @@
#include "winhelp.h"
struct Filename {
- char path[FILENAME_MAX];
+ char *path;
};
-#define f_open(filename, mode, isprivate) ( fopen((filename).path, (mode)) )
+#define f_open(filename, mode, isprivate) ( fopen((filename)->path, (mode)) )
struct FontSpec {
- char name[64];
+ char *name;
int isbold;
int height;
int charset;
};
+struct FontSpec *fontspec_new(const char *name,
+ int bold, int height, int charset);
#ifndef CLEARTYPE_QUALITY
#define CLEARTYPE_QUALITY 5
@@ -115,7 +117,7 @@ struct FontSpec {
#ifndef DONE_TYPEDEFS
#define DONE_TYPEDEFS
-typedef struct config_tag Config;
+typedef struct conf_tag Conf;
typedef struct backend_tag Backend;
typedef struct terminal_tag Terminal;
#endif
@@ -285,6 +287,7 @@ BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save);
filereq *filereq_new(void);
void filereq_free(filereq *state);
int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid);
+char *GetDlgItemText_alloc(HWND hwnd, int id);
void split_into_argv(char *, int *, char ***, char ***);
/*
@@ -473,7 +476,7 @@ void EnableSizeTip(int bEnable);
* Exports from unicode.c.
*/
struct unicode_data;
-void init_ucs(Config *, struct unicode_data *);
+void init_ucs(Conf *, struct unicode_data *);
/*
* Exports from winhandl.c.
@@ -489,6 +492,7 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,
struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
void *privdata, int flags);
int handle_write(struct handle *h, const void *data, int len);
+void handle_write_eof(struct handle *h);
HANDLE *handle_get_events(int *nevents);
void handle_free(struct handle *h);
void handle_got_event(HANDLE event);
@@ -497,7 +501,7 @@ int handle_backlog(struct handle *h);
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.
*
@@ -509,6 +513,14 @@ void agent_schedule_callback(void (*callback)(void *, void *, int),
#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;
diff --git a/tools/plink/winx11.c b/tools/plink/winx11.c
index c8951b086..630fac765 100644
--- a/tools/plink/winx11.c
+++ b/tools/plink/winx11.c
@@ -9,10 +9,11 @@
#include "putty.h"
#include "ssh.h"
-void platform_get_x11_auth(struct X11Display *disp, const Config *cfg)
+void platform_get_x11_auth(struct X11Display *disp, Conf *conf)
{
- if (cfg->xauthfile.path[0])
- x11_get_auth_from_authfile(disp, cfg->xauthfile.path);
+ char *xauthpath = conf_get_filename(conf, CONF_xauthfile)->path;
+ if (xauthpath[0])
+ x11_get_auth_from_authfile(disp, xauthpath);
}
const int platform_uses_x11_unix_by_default = FALSE;
diff --git a/tools/plink/x11fwd.c b/tools/plink/x11fwd.c
index 9f22a2364..d98908a77 100644
--- a/tools/plink/x11fwd.c
+++ b/tools/plink/x11fwd.c
@@ -68,8 +68,7 @@ static const struct plug_function_table dummy_plug = {
dummy_plug_sent, dummy_plug_accepting
};
-struct X11Display *x11_setup_display(char *display, int authtype,
- const Config *cfg)
+struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf)
{
struct X11Display *disp = snew(struct X11Display);
char *localcopy;
@@ -166,7 +165,7 @@ struct X11Display *x11_setup_display(char *display, int authtype,
disp->port = 6000 + disp->displaynum;
disp->addr = name_lookup(disp->hostname, disp->port,
- &disp->realhost, cfg, ADDRTYPE_UNSPEC);
+ &disp->realhost, conf, ADDRTYPE_UNSPEC);
if ((err = sk_addr_error(disp->addr)) != NULL) {
sk_addr_free(disp->addr);
@@ -249,7 +248,7 @@ struct X11Display *x11_setup_display(char *display, int authtype,
disp->localauthproto = X11_NO_AUTH;
disp->localauthdata = NULL;
disp->localauthdatalen = 0;
- platform_get_x11_auth(disp, cfg);
+ platform_get_x11_auth(disp, conf);
return disp;
}
@@ -508,9 +507,12 @@ static int x11_closing(Plug plug, const char *error_msg, int error_code,
* We have no way to communicate down the forwarded connection,
* so if an error occurred on the socket, we just ignore it
* and treat it like a proper close.
+ *
+ * FIXME: except we could initiate a full close here instead of
+ * just an outgoing EOF? ssh.c currently has no API for that, but
+ * it could.
*/
- sshfwd_close(pr->c);
- x11_close(pr->s);
+ sshfwd_write_eof(pr->c);
return 1;
}
@@ -558,8 +560,7 @@ int x11_get_screen_number(char *display)
* also, fills the SocketsStructure
*/
extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
- const char *peeraddr, int peerport,
- const Config *cfg)
+ const char *peeraddr, int peerport, Conf *conf)
{
static const struct plug_function_table fn_table = {
x11_log,
@@ -586,7 +587,7 @@ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
pr->s = *s = new_connection(sk_addr_dup(disp->addr),
disp->realhost, disp->port,
- 0, 1, 0, 0, (Plug) pr, cfg);
+ 0, 1, 0, 0, (Plug) pr, conf);
if ((err = sk_socket_error(*s)) != NULL) {
sfree(pr);
return err;
@@ -723,8 +724,7 @@ int x11_send(Socket s, char *data, int len)
memset(reply + 8, 0, msgsize);
memcpy(reply + 8, message, msglen);
sshfwd_write(pr->c, (char *)reply, 8 + msgsize);
- sshfwd_close(pr->c);
- x11_close(s);
+ sshfwd_write_eof(pr->c);
sfree(reply);
sfree(message);
return 0;
@@ -789,3 +789,8 @@ int x11_send(Socket s, char *data, int len)
return sk_write(s, data, len);
}
+
+void x11_send_eof(Socket s)
+{
+ sk_write_eof(s);
+}