int cmdline_process_param(const char *p, char *value, int need_save, Conf *conf) { int ret = 0; if (p[0] != '-') { if (need_save < 0) return 0; /* * Common handling for the tools whose initial command-line * arguments specify a hostname to connect to, i.e. PuTTY and * Plink. Doesn't count the file transfer tools, because their * hostname specification appears as part of a more * complicated scheme. */ if ((cmdline_tooltype & TOOLTYPE_HOST_ARG) && !seen_hostname_argument && (!(cmdline_tooltype & TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) || !loaded_session || !conf_launchable(conf))) { /* * Treat this argument as a host name, if we have not yet * seen a host name argument or -load. * * Exception, in some tools (Plink): if we have seen -load * but it didn't create a launchable session, then we * still accept a hostname argument following that -load. * This allows you to make saved sessions that configure * lots of other stuff (colour schemes, terminal settings * etc) and then say 'putty -load sessionname hostname'. * * Also, we carefully _don't_ test conf for launchability * if we haven't been explicitly told to load a session * (otherwise saving a host name into Default Settings * would cause 'putty' on its own to immediately launch * the default session and never be able to do anything * else). */ if (!strncmp(p, "telnet:", 7)) { /* * If the argument starts with "telnet:", set the * protocol to Telnet and process the string as a * Telnet URL. */ /* * Skip the "telnet:" or "telnet://" prefix. */ p += 7; if (p[0] == '/' && p[1] == '/') p += 2; conf_set_int(conf, CONF_protocol, PROT_TELNET); /* * The next thing we expect is a host name. */ { const char *host = p; char *buf; p += host_strcspn(p, ":/"); buf = dupprintf("%.*s", (int)(p - host), host); conf_set_str(conf, CONF_host, buf); sfree(buf); seen_hostname_argument = true; } /* * If the host name is followed by a colon, then * expect a port number after it. */ if (*p == ':') { p++; conf_set_int(conf, CONF_port, atoi(p)); /* * Set the flag that will stop us from treating * the next argument as a separate port; this one * counts as explicitly provided. */ seen_port_argument = true; } else { conf_set_int(conf, CONF_port, -1); } } else { char *user = NULL, *hostname = NULL; const char *hostname_after_user; int port_override = -1; size_t len; /* * Otherwise, treat it as a bare host name. */ if (cmdline_tooltype & TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) { /* * Here Plink checks for a comma-separated * protocol prefix, e.g. 'ssh,hostname' or * 'ssh,user@hostname'. * * I'm not entirely sure why; this behaviour dates * from 2000 and isn't explained. But I _think_ it * has to do with CVS transport or similar use * cases, in which the end user invokes the SSH * client indirectly, via some means that only * lets them pass a single string argument, and it * was occasionally useful to shoehorn the choice * of protocol into that argument. */ const char *comma = strchr(p, ','); if (comma) { char *prefix = dupprintf("%.*s", (int)(comma - p), p); const struct BackendVtable *vt = backend_vt_from_name(prefix); if (vt) { default_protocol = vt->protocol; conf_set_int(conf, CONF_protocol, default_protocol); port_override = vt->default_port; } else { cmdline_error("unrecognised protocol prefix '%s'", prefix); } sfree(prefix); p = comma + 1; } } hostname_after_user = p; if (cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) { /* * If the hostname argument can also be a saved * session (see below), then here we also check * for a user@ prefix, which will override the * username from the saved session. * * (If the hostname argument _isn't_ a saved * session, we don't do this.) */ const char *at = strrchr(p, '@'); if (at) { user = dupprintf("%.*s", (int)(at - p), p); hostname_after_user = at + 1; } } /* * Write the whole hostname argument (minus only that * optional protocol prefix) into the existing Conf, * for tools that don't treat it as a saved session * and as a fallback for those that do. */ hostname = dupstr(p + strspn(p, " \t")); len = strlen(hostname); while (len > 0 && (hostname[len-1] == ' ' || hostname[len-1] == '\t')) hostname[--len] = '\0'; seen_hostname_argument = true; conf_set_str(conf, CONF_host, hostname); if ((cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) && !loaded_session) { /* * For some tools, we equivocate between a * hostname argument and an argument naming a * saved session. Here we attempt to load a * session with the specified name, and if that * session exists and is launchable, we overwrite * the entire Conf with it. * * We skip this check if a -load option has * already happened, so that * * plink -load non-launchable-session hostname * * will treat 'hostname' as a hostname _even_ if a * saved session called 'hostname' exists. (This * doesn't lose any functionality someone could * have needed, because if 'hostname' did cause a * session to be loaded, then it would overwrite * everything from the previously loaded session. * So if that was the behaviour someone wanted, * then they could get it by leaving off the * -load completely.) */ Conf *conf2 = conf_new(); if (do_defaults(hostname_after_user, conf2) && conf_launchable(conf2)) { conf_copy_into(conf, conf2); loaded_session = true; /* And override the username if one was given. */ if (user) conf_set_str(conf, CONF_username, user); } conf_free(conf2); } sfree(hostname); sfree(user); if (port_override >= 0) conf_set_int(conf, CONF_port, port_override); } return 1; } else if ((cmdline_tooltype & TOOLTYPE_PORT_ARG) && !seen_port_argument) { /* * If we've already got a host name from the command line * (either as a hostname argument or a qualifying -load), * but not a port number, then treat the next argument as * a port number. * * We handle this by calling ourself recursively to * pretend we received a -P argument, so that it will be * deferred until it's a good moment to run it. */ char *dup = dupstr(p); /* 'value' is not a const char * */ int retd = cmdline_process_param("-P", dup, 1, conf); sfree(dup); assert(retd == 2); seen_port_argument = true; return 1; } else { /* * Refuse to recognise this argument, and give it back to * the tool's own command-line processing. */ return 0; } } #ifdef PUTTYNG if (!stricmp(p, "-hwndparent")) { RETURN(2); hwnd_parent = atoi(value); return 2; } #endif if (!strcmp(p, "-load")) { RETURN(2); /* This parameter must be processed immediately rather than being * saved. */ do_defaults(value, conf); loaded_session = true; cmdline_session_name = dupstr(value); return 2; } if (!strcmp(p, "-ssh")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); 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 = 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 = 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 = 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 = 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); flags |= FLAG_VERBOSE; } if (!strcmp(p, "-l")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_str(conf, CONF_username, value); } if (!strcmp(p, "-loghost")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_str(conf, CONF_loghost, value); } if (!strcmp(p, "-hostkey")) { char *dup; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); dup = dupstr(value); if (!validate_manual_hostkey(dup)) { cmdline_error("'%s' is not a valid format for a manual host " "key specification", value); sfree(dup); return ret; } conf_set_str_str(conf, CONF_ssh_manual_hostkeys, dup, ""); sfree(dup); } if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) { char type, *q, *qq, *key, *val; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); 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 * specifying a particular loopback address. We need to * 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!) */ type = p[1]; /* 'L' or 'R' */ q = qq = host_strchr(value, ':'); while (qq) { char *qqq = host_strchr(qq+1, ':'); if (qqq) q = qq; qq = qqq; } if (!q) { cmdline_error("-%c expects at least two colons in its" " argument", type); return ret; } key = dupprintf("%c%.*s", type, (int)(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"); } conf_set_str_str(conf, CONF_portfwd, key, val); sfree(key); sfree(val); } if ((!strcmp(p, "-nc"))) { char *host, *portp; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); portp = host_strchr(value, ':'); if (!portp) { cmdline_error("-nc expects argument of form 'host:port'"); return ret; } host = dupprintf("%.*s", (int)(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")) { const char *filename; FILE *fp; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); filename = value; fp = fopen(filename, "r"); if (!fp) { cmdline_error("unable to open command file \"%s\"", filename); return ret; } strbuf *command = strbuf_new(); char readbuf[4096]; while (1) { size_t nread = fread(readbuf, 1, sizeof(readbuf), fp); if (nread == 0) break; put_data(command, readbuf, nread); } fclose(fp); conf_set_str(conf, CONF_remote_cmd, command->s); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_bool(conf, CONF_nopty, true); /* command => no terminal */ strbuf_free(command); } if (!strcmp(p, "-P")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -ssh,-telnet */ conf_set_int(conf, CONF_port, atoi(value)); } if (!strcmp(p, "-pw")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); 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 (conf_get_int(conf, CONF_protocol) != PROT_SSH) cmdline_error("the -pw option can only be used with the " "SSH protocol"); else { cmdline_password = dupstr(value); /* Assuming that `value' is directly from argv, make a good faith * attempt to trample it, to stop it showing up in `ps' output * on Unix-like systems. Not guaranteed, of course. */ smemclr(value, strlen(value)); } } if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") || !strcmp(p, "-pageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_tryagent, true); } if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") || !strcmp(p, "-nopageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_tryagent, false); } if (!strcmp(p, "-share")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_connection_sharing, true); } if (!strcmp(p, "-noshare")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_connection_sharing, false); } if (!strcmp(p, "-A")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_agentfwd, true); } if (!strcmp(p, "-a")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_agentfwd, false); } if (!strcmp(p, "-X")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_x11_forward, true); } if (!strcmp(p, "-x")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_x11_forward, false); } if (!strcmp(p, "-t")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -m */ conf_set_bool(conf, CONF_nopty, false); } if (!strcmp(p, "-T")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); conf_set_bool(conf, CONF_nopty, true); } if (!strcmp(p, "-N")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_no_shell, true); } if (!strcmp(p, "-C")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_compression, true); } if (!strcmp(p, "-1")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_sshprot, 0); /* ssh protocol 1 only */ } if (!strcmp(p, "-2")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); 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); 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); conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4); } if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) { RETURN(1); SAVEABLE(1); 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 (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 */ nextitem = value; while (nextitem[0] != '\0') { int length, skip; char *end = strchr(nextitem, ','); if (!end) { length = strlen(nextitem); skip = 0; } else { length = end - nextitem; nextitem[length] = '\0'; skip = 1; } if (length == 1) { switch (*nextitem) { case '1': case '2': conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0')); break; case '5': case '6': case '7': case '8': case '9': conf_set_int(conf, CONF_serdatabits, *nextitem-'0'); break; case 'n': conf_set_int(conf, CONF_serparity, SER_PAR_NONE); break; case 'o': conf_set_int(conf, CONF_serparity, SER_PAR_ODD); break; case 'e': conf_set_int(conf, CONF_serparity, SER_PAR_EVEN); break; case 'm': conf_set_int(conf, CONF_serparity, SER_PAR_MARK); break; case 's': conf_set_int(conf, CONF_serparity, SER_PAR_SPACE); break; case 'N': conf_set_int(conf, CONF_serflow, SER_FLOW_NONE); break; case 'X': conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF); break; case 'R': conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS); break; case 'D': conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR); break; default: cmdline_error("Unrecognised suboption \"-sercfg %c\"", *nextitem); } } else if (length == 3 && !strncmp(nextitem,"1.5",3)) { /* Messy special case */ conf_set_int(conf, CONF_serstopbits, 3); } else { int serspeed = atoi(nextitem); if (serspeed != 0) { conf_set_int(conf, CONF_serspeed, serspeed); } else { cmdline_error("Unrecognised suboption \"-sercfg %s\"", nextitem); } } nextitem += length + skip; } } if (!strcmp(p, "-sessionlog")) { Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER); /* but available even in TOOLTYPE_NONNETWORK, cf pterm "-log" */ SAVEABLE(0); fn = filename_from_str(value); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); filename_free(fn); } if (!strcmp(p, "-sshlog") || !strcmp(p, "-sshrawlog")) { Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); fn = filename_from_str(value); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, !strcmp(p, "-sshlog") ? LGTYP_PACKETS : /* !strcmp(p, "-sshrawlog") ? */ LGTYP_SSHRAW); filename_free(fn); } if (!strcmp(p, "-proxycmd")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_proxy_type, PROXY_CMD); conf_set_str(conf, CONF_proxy_telnet_command, value); } #ifdef _WINDOWS /* * Cross-tool options only available on Windows. */ if (!strcmp(p, "-restrict-acl") || !strcmp(p, "-restrict_acl") || !strcmp(p, "-restrictacl")) { RETURN(1); restrict_process_acl(); restricted_acl = true; } #endif return ret; /* unrecognised */ }
/* * Read a set of name-value pairs in the format we occasionally use: * NAME\tVALUE\0NAME\tVALUE\0\0 in memory * NAME=VALUE,NAME=VALUE, in storage * If there's no "=VALUE" (e.g. just NAME,NAME,NAME) then those keys * are mapped to the empty string. */ static int gppmap(void *handle, char *name, Conf *conf, int primary) { 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; while (*p) { q = buf; val = NULL; while (*p && *p != ',') { int c = *p++; if (c == '=') c = '\0'; if (c == '\\') c = *p++; *q++ = c; if (!c) val = q; } if (*p == ',') p++; if (!val) val = q; *q = '\0'; if (primary == CONF_portfwd && strchr(buf, 'D') != NULL) { /* * 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, by finding the D and turning it into a L. */ char *newkey = dupstr(buf); *strchr(newkey, 'D') = 'L'; conf_set_str_str(conf, primary, newkey, "D"); sfree(newkey); } else { conf_set_str_str(conf, primary, buf, val); } } sfree(buf); return TRUE; }
void load_open_settings(void *sesskey, Conf *conf) { int i; 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) { 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, conf, CONF_addressfamily); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ 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; 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, 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. */ for (i = 0; ttymodes[i]; i++) conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A"); } /* proxy settings */ 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; i = gppi_raw(sesskey, "ProxyType", 0); if (i == 0) conf_set_int(conf, CONF_proxy_type, PROXY_NONE); else if (i == 1) conf_set_int(conf, CONF_proxy_type, PROXY_HTTP); else if (i == 3) conf_set_int(conf, CONF_proxy_type, PROXY_TELNET); else if (i == 4) conf_set_int(conf, CONF_proxy_type, PROXY_CMD); else { i = gppi_raw(sesskey, "ProxySOCKSVersion", 5); if (i == 5) conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5); else conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4); } } 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", 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, 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; 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, conf, CONF_ssh_kexlist); } gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); /* SSH-2 only by default */ gppi(sesskey, "SshProt", 3, 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, conf, CONF_ssh_gsslist); gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); #endif 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); /* PuTTY CAPI start */ #ifdef _WINDOWS gppi(sesskey, "AuthCAPI", 0, conf, CONF_try_capi_auth); gpps(sesskey, "CAPICertID", "", conf, CONF_capi_certID); #endif /* PuTTY CAPI end */ 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_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, conf, CONF_remote_qtitle_action); } 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 ); conf_set_int(conf, CONF_bellovl_t, i #ifdef PUTTY_UNIX_H / 1000 #endif ); i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif ); conf_set_int(conf, CONF_bellovl_s, i #ifdef PUTTY_UNIX_H / 1000 #endif ); gppi(sesskey, "ScrollbackLines", 2000, 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); i = gppi_raw(sesskey, "BoldAsColour", 1); conf_set_int(conf, CONF_bold_style, i+1); for (i = 0; i < 22; i++) { static const char *const defaults[] = { "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0", "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85", "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187", "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; int c0, c1, c2; sprintf(buf, "Colour%d", i); buf2 = gpps_raw(sesskey, buf, defaults[i]); if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) { 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, 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", "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1", "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,1,1,1,1,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,1,1,1,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", "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, *p; int j; sprintf(buf, "Wordness%d", i); buf2 = gpps_raw(sesskey, buf, defaults[i / 32]); p = buf2; for (j = i; j < i + 32; j++) { char *q = p; while (*p && *p != ',') p++; if (*p == ',') *p++ = '\0'; 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", "", 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; 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) conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON); } } 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); i = gppi_raw(sesskey, "BugOldGex2", 0); conf_set_int(conf, CONF_sshbug_oldgex2, 2-i); i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); i = gppi_raw(sesskey, "BugChanReq", 0); conf_set_int(conf, CONF_sshbug_chanreq, 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); gppi(sesskey, "ConnectionSharing", 0, conf, CONF_ssh_connection_sharing); gppi(sesskey, "ConnectionSharingUpstream", 1, conf, CONF_ssh_connection_sharing_upstream); gppi(sesskey, "ConnectionSharingDownstream", 1, conf, CONF_ssh_connection_sharing_downstream); gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys); }
int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, const char *screen_number_suffix, ptrlen authproto, ptrlen authdata, Socket **sockets, Conf *conf) { char *tmpdir; char *authfilename = NULL; strbuf *authfiledata = NULL; char *unix_path = NULL; SockAddr *a_tcp = NULL, *a_unix = NULL; int authfd; FILE *authfp; int displayno; authfiledata = strbuf_new_nm(); int nsockets = 0; /* * Look for a free TCP port to run our server on. */ for (displayno = mindisp;; displayno++) { const char *err; int tcp_port = displayno + 6000; int addrtype = ADDRTYPE_IPV4; sockets[nsockets] = new_listener( NULL, tcp_port, plug, false, conf, addrtype); err = sk_socket_error(sockets[nsockets]); if (!err) { char *hostname = get_hostname(); if (hostname) { char *canonicalname = NULL; a_tcp = name_lookup(hostname, tcp_port, &canonicalname, conf, addrtype, NULL, ""); sfree(canonicalname); } sfree(hostname); nsockets++; break; /* success! */ } else { sk_close(sockets[nsockets]); } if (!strcmp(err, strerror(EADDRINUSE))) /* yuck! */ goto out; } if (a_tcp) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_tcp, displayno, authproto, authdata); } /* * Try to establish the Unix-domain analogue. That may or may not * work - file permissions in /tmp may prevent it, for example - * but it's worth a try, and we don't consider it a fatal error if * it doesn't work. */ unix_path = dupprintf("/tmp/.X11-unix/X%d", displayno); a_unix = unix_sock_addr(unix_path); sockets[nsockets] = new_unix_listener(a_unix, plug); if (!sk_socket_error(sockets[nsockets])) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_unix, displayno, authproto, authdata); nsockets++; } else { sk_close(sockets[nsockets]); sfree(unix_path); unix_path = NULL; } /* * Decide where the authority data will be written. */ tmpdir = getenv("TMPDIR"); if (!tmpdir || !*tmpdir) tmpdir = "/tmp"; authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX", NULL); { int oldumask = umask(077); authfd = mkstemp(authfilename); umask(oldumask); } if (authfd < 0) { while (nsockets-- > 0) sk_close(sockets[nsockets]); goto out; } /* * Spawn a subprocess which will try to reliably delete our * auth file when we terminate, in case we die unexpectedly. */ { int cleanup_pipe[2]; pid_t pid; /* Don't worry if pipe or fork fails; it's not _that_ critical. */ if (!pipe(cleanup_pipe)) { if ((pid = fork()) == 0) { int buf[1024]; /* * Our parent process holds the writing end of * this pipe, and writes nothing to it. Hence, * we expect read() to return EOF as soon as * that process terminates. */ close(0); close(1); close(2); setpgid(0, 0); close(cleanup_pipe[1]); close(authfd); while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0); unlink(authfilename); if (unix_path) unlink(unix_path); _exit(0); } else if (pid < 0) { close(cleanup_pipe[0]); close(cleanup_pipe[1]); } else { close(cleanup_pipe[0]); cloexec(cleanup_pipe[1]); } } } authfp = fdopen(authfd, "wb"); fwrite(authfiledata->u, 1, authfiledata->len, authfp); fclose(authfp); { char *display = dupprintf(":%d%s", displayno, screen_number_suffix); conf_set_str_str(conf, CONF_environmt, "DISPLAY", display); sfree(display); } conf_set_str_str(conf, CONF_environmt, "XAUTHORITY", authfilename); /* * FIXME: return at least the DISPLAY and XAUTHORITY env settings, * and perhaps also the display number */ out: if (a_tcp) sk_addr_free(a_tcp); /* a_unix doesn't need freeing, because new_unix_listener took it over */ sfree(authfilename); strbuf_free(authfiledata); sfree(unix_path); return nsockets; }