cmdline.c 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. /*
  2. * cmdline.c - command-line parsing shared between many of the
  3. * PuTTY applications
  4. */
  5. #include <stdio.h>
  6. #include <assert.h>
  7. #include <stdlib.h>
  8. #include "putty.h"
  9. /*
  10. * Some command-line parameters need to be saved up until after
  11. * we've loaded the saved session which will form the basis of our
  12. * eventual running configuration. For this we use the macro
  13. * SAVEABLE, which notices if the `need_save' parameter is set and
  14. * saves the parameter and value on a list.
  15. *
  16. * We also assign priorities to saved parameters, just to slightly
  17. * ameliorate silly ordering problems. For example, if you specify
  18. * a saved session to load, it will be loaded _before_ all your
  19. * local modifications such as -L are evaluated; and if you specify
  20. * a protocol and a port, the protocol is set up first so that the
  21. * port can override its choice of port number.
  22. *
  23. * (In fact -load is not saved at all, since in at least Plink the
  24. * processing of further command-line options depends on whether or
  25. * not the loaded session contained a hostname. So it must be
  26. * executed immediately.)
  27. */
  28. #define NPRIORITIES 2
  29. struct cmdline_saved_param {
  30. CmdlineArg *p, *value;
  31. };
  32. struct cmdline_saved_param_set {
  33. struct cmdline_saved_param *params;
  34. size_t nsaved, savesize;
  35. };
  36. /*
  37. * C guarantees this structure will be initialised to all zero at
  38. * program start, which is exactly what we want.
  39. */
  40. static struct cmdline_saved_param_set saves[NPRIORITIES];
  41. static void cmdline_save_param(CmdlineArg *p, CmdlineArg *value, int pri)
  42. {
  43. sgrowarray(saves[pri].params, saves[pri].savesize, saves[pri].nsaved);
  44. saves[pri].params[saves[pri].nsaved].p = p;
  45. saves[pri].params[saves[pri].nsaved].value = value;
  46. saves[pri].nsaved++;
  47. }
  48. static char *cmdline_password = NULL;
  49. void cmdline_cleanup(void)
  50. {
  51. int pri;
  52. if (cmdline_password) {
  53. smemclr(cmdline_password, strlen(cmdline_password));
  54. sfree(cmdline_password);
  55. cmdline_password = NULL;
  56. }
  57. for (pri = 0; pri < NPRIORITIES; pri++) {
  58. sfree(saves[pri].params);
  59. saves[pri].params = NULL;
  60. saves[pri].savesize = 0;
  61. saves[pri].nsaved = 0;
  62. }
  63. }
  64. #define SAVEABLE(pri) do { \
  65. if (need_save) { cmdline_save_param(arg, nextarg, pri); return ret; } \
  66. } while (0)
  67. /*
  68. * Similar interface to seat_get_userpass_input(), except that here a
  69. * SPR(K)_INCOMPLETE return means that we aren't capable of processing
  70. * the prompt and someone else should do it.
  71. */
  72. SeatPromptResult cmdline_get_passwd_input(
  73. prompts_t *p, cmdline_get_passwd_input_state *state, bool restartable)
  74. {
  75. /*
  76. * We only handle prompts which don't echo (which we assume to be
  77. * passwords), and (currently) we only cope with a password prompt
  78. * that comes in a prompt-set on its own. Also, we don't use a
  79. * command-line password for any kind of prompt which is destined
  80. * for local use rather than to be sent to the server: the idea is
  81. * to pre-fill _passwords_, not private-key passphrases (for which
  82. * there are better alternatives available).
  83. */
  84. if (p->n_prompts != 1 || p->prompts[0]->echo || !p->to_server) {
  85. return SPR_INCOMPLETE;
  86. }
  87. /*
  88. * If we've tried once, return utter failure (no more passwords left
  89. * to try).
  90. */
  91. if (state->tried)
  92. return SPR_SW_ABORT("Configured password was not accepted");
  93. /*
  94. * If we never had a password available in the first place, we
  95. * can't do anything in any case. (But we delay this test until
  96. * after trying once, so that even if we free cmdline_password
  97. * below, we'll still remember that we _used_ to have one.)
  98. */
  99. if (!cmdline_password)
  100. return SPR_INCOMPLETE;
  101. prompt_set_result(p->prompts[0], cmdline_password);
  102. state->tried = true;
  103. if (!restartable) {
  104. /*
  105. * If there's no possibility of needing to do this again after
  106. * a 'Restart Session' event, then wipe our copy of the
  107. * password out of memory.
  108. */
  109. smemclr(cmdline_password, strlen(cmdline_password));
  110. sfree(cmdline_password);
  111. cmdline_password = NULL;
  112. }
  113. return SPR_OK;
  114. }
  115. static void cmdline_report_unavailable(const char *p)
  116. {
  117. cmdline_error("option \"%s\" not available in this tool", p);
  118. }
  119. static bool cmdline_check_unavailable(int flag, const char *p)
  120. {
  121. if (cmdline_tooltype & flag) {
  122. cmdline_report_unavailable(p);
  123. return true;
  124. }
  125. return false;
  126. }
  127. #define UNAVAILABLE_IN(flag) do { \
  128. if (cmdline_check_unavailable(flag, p)) return ret; \
  129. } while (0)
  130. /*
  131. * Process a standard command-line parameter. `p' is the parameter
  132. * in question; `value' is the subsequent element of argv, which
  133. * may or may not be required as an operand to the parameter.
  134. * If `need_save' is 1, arguments which need to be saved as
  135. * described at this top of this file are, for later execution;
  136. * if 0, they are processed normally. (-1 is a special value used
  137. * by pterm to count arguments for a preliminary pass through the
  138. * argument list; it causes immediate return with an appropriate
  139. * value with no action taken.)
  140. * Return value is 2 if both arguments were used; 1 if only p was
  141. * used; 0 if the parameter wasn't one we recognised; -2 if it
  142. * should have been 2 but value was NULL.
  143. */
  144. #define RETURN(x) do { \
  145. if ((x) == 2 && !value) return -2; \
  146. ret = x; \
  147. if (need_save < 0) return x; \
  148. } while (0)
  149. static bool seen_hostname_argument = false;
  150. static bool seen_port_argument = false;
  151. static bool seen_verbose_option = false;
  152. static bool loaded_session = false;
  153. bool cmdline_verbose(void) { return seen_verbose_option; }
  154. bool cmdline_seat_verbose(Seat *seat) { return cmdline_verbose(); }
  155. bool cmdline_lp_verbose(LogPolicy *lp) { return cmdline_verbose(); }
  156. bool cmdline_loaded_session(void) { return loaded_session; }
  157. static void set_protocol(Conf *conf, int protocol)
  158. {
  159. settings_set_default_protocol(protocol);
  160. conf_set_int(conf, CONF_protocol, protocol);
  161. }
  162. static void set_port(Conf *conf, int port)
  163. {
  164. settings_set_default_port(port);
  165. conf_set_int(conf, CONF_port, port);
  166. }
  167. int cmdline_process_param(CmdlineArg *arg, CmdlineArg *nextarg,
  168. int need_save, Conf *conf)
  169. {
  170. int ret = 0;
  171. const char *p = cmdline_arg_to_str(arg);
  172. const char *value_utf8 = cmdline_arg_to_utf8(nextarg);
  173. const char *value = cmdline_arg_to_str(nextarg);
  174. if (p[0] != '-') {
  175. if (need_save < 0)
  176. return 0;
  177. /*
  178. * Common handling for the tools whose initial command-line
  179. * arguments specify a hostname to connect to, i.e. PuTTY and
  180. * Plink. Doesn't count the file transfer tools, because their
  181. * hostname specification appears as part of a more
  182. * complicated scheme.
  183. */
  184. if ((cmdline_tooltype & TOOLTYPE_HOST_ARG) &&
  185. !seen_hostname_argument &&
  186. (!(cmdline_tooltype & TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) ||
  187. !loaded_session || !conf_launchable(conf))) {
  188. /*
  189. * Treat this argument as a host name, if we have not yet
  190. * seen a host name argument or -load.
  191. *
  192. * Exception, in some tools (Plink): if we have seen -load
  193. * but it didn't create a launchable session, then we
  194. * still accept a hostname argument following that -load.
  195. * This allows you to make saved sessions that configure
  196. * lots of other stuff (colour schemes, terminal settings
  197. * etc) and then say 'putty -load sessionname hostname'.
  198. *
  199. * Also, we carefully _don't_ test conf for launchability
  200. * if we haven't been explicitly told to load a session
  201. * (otherwise saving a host name into Default Settings
  202. * would cause 'putty' on its own to immediately launch
  203. * the default session and never be able to do anything
  204. * else).
  205. */
  206. if (!strncmp(p, "telnet:", 7)) {
  207. /*
  208. * If the argument starts with "telnet:", set the
  209. * protocol to Telnet and process the string as a
  210. * Telnet URL.
  211. */
  212. /*
  213. * Skip the "telnet:" or "telnet://" prefix.
  214. */
  215. p += 7;
  216. if (p[0] == '/' && p[1] == '/')
  217. p += 2;
  218. conf_set_int(conf, CONF_protocol, PROT_TELNET);
  219. /*
  220. * The next thing we expect is a host name.
  221. */
  222. {
  223. const char *host = p;
  224. char *buf;
  225. p += host_strcspn(p, ":/");
  226. buf = dupprintf("%.*s", (int)(p - host), host);
  227. conf_set_str(conf, CONF_host, buf);
  228. sfree(buf);
  229. seen_hostname_argument = true;
  230. }
  231. /*
  232. * If the host name is followed by a colon, then
  233. * expect a port number after it.
  234. */
  235. if (*p == ':') {
  236. p++;
  237. conf_set_int(conf, CONF_port, atoi(p));
  238. /*
  239. * Set the flag that will stop us from treating
  240. * the next argument as a separate port; this one
  241. * counts as explicitly provided.
  242. */
  243. seen_port_argument = true;
  244. } else {
  245. conf_set_int(conf, CONF_port, -1);
  246. }
  247. } else {
  248. char *user = NULL, *hostname = NULL;
  249. const char *hostname_after_user;
  250. int port_override = -1;
  251. size_t len;
  252. /*
  253. * Otherwise, treat it as a bare host name.
  254. */
  255. if (cmdline_tooltype & TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) {
  256. /*
  257. * Here Plink checks for a comma-separated
  258. * protocol prefix, e.g. 'ssh,hostname' or
  259. * 'ssh,user@hostname'.
  260. *
  261. * I'm not entirely sure why; this behaviour dates
  262. * from 2000 and isn't explained. But I _think_ it
  263. * has to do with CVS transport or similar use
  264. * cases, in which the end user invokes the SSH
  265. * client indirectly, via some means that only
  266. * lets them pass a single string argument, and it
  267. * was occasionally useful to shoehorn the choice
  268. * of protocol into that argument.
  269. */
  270. const char *comma = strchr(p, ',');
  271. if (comma) {
  272. char *prefix = dupprintf("%.*s", (int)(comma - p), p);
  273. const struct BackendVtable *vt =
  274. backend_vt_from_name(prefix);
  275. if (vt) {
  276. set_protocol(conf, vt->protocol);
  277. port_override = vt->default_port;
  278. } else {
  279. cmdline_error("unrecognised protocol prefix '%s'",
  280. prefix);
  281. }
  282. sfree(prefix);
  283. p = comma + 1;
  284. }
  285. }
  286. hostname_after_user = p;
  287. if (cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) {
  288. /*
  289. * If the hostname argument can also be a saved
  290. * session (see below), then here we also check
  291. * for a user@ prefix, which will override the
  292. * username from the saved session.
  293. *
  294. * (If the hostname argument _isn't_ a saved
  295. * session, we don't do this.)
  296. */
  297. const char *at = strrchr(p, '@');
  298. if (at) {
  299. user = dupprintf("%.*s", (int)(at - p), p);
  300. hostname_after_user = at + 1;
  301. }
  302. }
  303. /*
  304. * Write the whole hostname argument (minus only that
  305. * optional protocol prefix) into the existing Conf,
  306. * for tools that don't treat it as a saved session
  307. * and as a fallback for those that do.
  308. */
  309. hostname = dupstr(p + strspn(p, " \t"));
  310. len = strlen(hostname);
  311. while (len > 0 && (hostname[len-1] == ' ' ||
  312. hostname[len-1] == '\t'))
  313. hostname[--len] = '\0';
  314. seen_hostname_argument = true;
  315. conf_set_str(conf, CONF_host, hostname);
  316. if ((cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) &&
  317. !loaded_session) {
  318. /*
  319. * For some tools, we equivocate between a
  320. * hostname argument and an argument naming a
  321. * saved session. Here we attempt to load a
  322. * session with the specified name, and if that
  323. * session exists and is launchable, we overwrite
  324. * the entire Conf with it.
  325. *
  326. * We skip this check if a -load option has
  327. * already happened, so that
  328. *
  329. * plink -load non-launchable-session hostname
  330. *
  331. * will treat 'hostname' as a hostname _even_ if a
  332. * saved session called 'hostname' exists. (This
  333. * doesn't lose any functionality someone could
  334. * have needed, because if 'hostname' did cause a
  335. * session to be loaded, then it would overwrite
  336. * everything from the previously loaded session.
  337. * So if that was the behaviour someone wanted,
  338. * then they could get it by leaving off the
  339. * -load completely.)
  340. */
  341. Conf *conf2 = conf_new();
  342. if (do_defaults(hostname_after_user, conf2) &&
  343. conf_launchable(conf2)) {
  344. conf_copy_into(conf, conf2);
  345. loaded_session = true;
  346. /* And override the username if one was given. */
  347. if (user)
  348. conf_set_str(conf, CONF_username, user);
  349. }
  350. conf_free(conf2);
  351. }
  352. sfree(hostname);
  353. sfree(user);
  354. if (port_override >= 0)
  355. conf_set_int(conf, CONF_port, port_override);
  356. }
  357. return 1;
  358. } else if ((cmdline_tooltype & TOOLTYPE_PORT_ARG) &&
  359. !seen_port_argument) {
  360. /*
  361. * If we've already got a host name from the command line
  362. * (either as a hostname argument or a qualifying -load),
  363. * but not a port number, then treat the next argument as
  364. * a port number.
  365. *
  366. * We handle this by calling ourself recursively to
  367. * pretend we received a -P argument, so that it will be
  368. * deferred until it's a good moment to run it.
  369. */
  370. int retd = cmdline_process_param(
  371. cmdline_arg_from_str(arg->list, "-P"), arg, 1, conf);
  372. assert(retd == 2);
  373. seen_port_argument = true;
  374. return 1;
  375. } else {
  376. /*
  377. * Refuse to recognise this argument, and give it back to
  378. * the tool's own command-line processing.
  379. */
  380. return 0;
  381. }
  382. }
  383. if (!strcmp(p, "-load")) {
  384. RETURN(2);
  385. /* This parameter must be processed immediately rather than being
  386. * saved. */
  387. do_defaults(value, conf);
  388. loaded_session = true;
  389. return 2;
  390. }
  391. for (size_t i = 0; backends[i]; i++) {
  392. if (p[0] == '-' && !strcmp(p+1, backends[i]->id)) {
  393. RETURN(1);
  394. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  395. SAVEABLE(0);
  396. set_protocol(conf, backends[i]->protocol);
  397. if (backends[i]->default_port)
  398. set_port(conf, backends[i]->default_port);
  399. if (backends[i]->protocol == PROT_SERIAL) {
  400. /* Special handling: the 'where to connect to' argument will
  401. * have been placed into CONF_host, but for this protocol, it
  402. * needs to be in CONF_serline */
  403. conf_set_str(conf, CONF_serline,
  404. conf_get_str(conf, CONF_host));
  405. }
  406. return 1;
  407. }
  408. }
  409. if (!strcmp(p, "-v")) {
  410. RETURN(1);
  411. UNAVAILABLE_IN(TOOLTYPE_NO_VERBOSE_OPTION);
  412. seen_verbose_option = true;
  413. }
  414. if (!strcmp(p, "-l")) {
  415. RETURN(2);
  416. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  417. SAVEABLE(0);
  418. if (value_utf8)
  419. conf_set_utf8(conf, CONF_username, value_utf8);
  420. else
  421. conf_set_str(conf, CONF_username, value);
  422. }
  423. if (!strcmp(p, "-loghost")) {
  424. RETURN(2);
  425. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  426. SAVEABLE(0);
  427. conf_set_str(conf, CONF_loghost, value);
  428. }
  429. if (!strcmp(p, "-hostkey")) {
  430. char *dup;
  431. RETURN(2);
  432. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  433. SAVEABLE(0);
  434. dup = dupstr(value);
  435. if (!validate_manual_hostkey(dup)) {
  436. cmdline_error("'%s' is not a valid format for a manual host "
  437. "key specification", value);
  438. sfree(dup);
  439. return ret;
  440. }
  441. conf_set_str_str(conf, CONF_ssh_manual_hostkeys, dup, "");
  442. sfree(dup);
  443. }
  444. if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {
  445. char type, *q, *qq, *key, *val;
  446. RETURN(2);
  447. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  448. SAVEABLE(0);
  449. if (strcmp(p, "-D")) {
  450. /*
  451. * For -L or -R forwarding types:
  452. *
  453. * We expect _at least_ two colons in this string. The
  454. * possible formats are `sourceport:desthost:destport',
  455. * or `sourceip:sourceport:desthost:destport' if you're
  456. * specifying a particular loopback address. We need to
  457. * replace the one between source and dest with a \t;
  458. * this means we must find the second-to-last colon in
  459. * the string.
  460. *
  461. * (This looks like a foolish way of doing it given the
  462. * existence of strrchr, but it's more efficient than
  463. * two strrchrs - not to mention that the second strrchr
  464. * would require us to modify the input string!)
  465. */
  466. type = p[1]; /* 'L' or 'R' */
  467. q = qq = host_strchr(value, ':');
  468. while (qq) {
  469. char *qqq = host_strchr(qq+1, ':');
  470. if (qqq)
  471. q = qq;
  472. qq = qqq;
  473. }
  474. if (!q) {
  475. cmdline_error("-%c expects at least two colons in its"
  476. " argument", type);
  477. return ret;
  478. }
  479. key = dupprintf("%c%.*s", type, (int)(q - value), value);
  480. val = dupstr(q+1);
  481. } else {
  482. /*
  483. * Dynamic port forwardings are entered under the same key
  484. * as if they were local (because they occupy the same
  485. * port space - a local and a dynamic forwarding on the
  486. * same local port are mutually exclusive), with the
  487. * special value "D" (which can be distinguished from
  488. * anything in the ordinary -L case by containing no
  489. * colon).
  490. */
  491. key = dupprintf("L%s", value);
  492. val = dupstr("D");
  493. }
  494. conf_set_str_str(conf, CONF_portfwd, key, val);
  495. sfree(key);
  496. sfree(val);
  497. }
  498. if ((!strcmp(p, "-nc"))) {
  499. char *host, *portp;
  500. RETURN(2);
  501. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  502. SAVEABLE(0);
  503. portp = host_strchr(value, ':');
  504. if (!portp) {
  505. cmdline_error("-nc expects argument of form 'host:port'");
  506. return ret;
  507. }
  508. host = dupprintf("%.*s", (int)(portp - value), value);
  509. conf_set_str(conf, CONF_ssh_nc_host, host);
  510. conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1));
  511. sfree(host);
  512. }
  513. if (!strcmp(p, "-m")) {
  514. Filename *filename;
  515. FILE *fp;
  516. RETURN(2);
  517. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  518. SAVEABLE(0);
  519. filename = cmdline_arg_to_filename(nextarg);
  520. fp = f_open(filename, "r", false);
  521. if (!fp) {
  522. cmdline_error("unable to open command file \"%s\"",
  523. filename_to_str(filename));
  524. filename_free(filename);
  525. return ret;
  526. }
  527. filename_free(filename);
  528. strbuf *command = strbuf_new();
  529. char readbuf[4096];
  530. while (1) {
  531. size_t nread = fread(readbuf, 1, sizeof(readbuf), fp);
  532. if (nread == 0)
  533. break;
  534. put_data(command, readbuf, nread);
  535. }
  536. fclose(fp);
  537. conf_set_str(conf, CONF_remote_cmd, command->s);
  538. conf_set_str(conf, CONF_remote_cmd2, "");
  539. conf_set_bool(conf, CONF_nopty, true); /* command => no terminal */
  540. strbuf_free(command);
  541. }
  542. if (!strcmp(p, "-P")) {
  543. RETURN(2);
  544. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  545. SAVEABLE(1); /* lower priority than -ssh, -telnet, etc */
  546. conf_set_int(conf, CONF_port, atoi(value));
  547. }
  548. if (!strcmp(p, "-pw")) {
  549. RETURN(2);
  550. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  551. SAVEABLE(1);
  552. /* We delay evaluating this until after the protocol is decided,
  553. * so that we can warn if it's of no use with the selected protocol */
  554. if (conf_get_int(conf, CONF_protocol) != PROT_SSH)
  555. cmdline_error("the -pw option can only be used with the "
  556. "SSH protocol");
  557. else {
  558. if (cmdline_password) {
  559. smemclr(cmdline_password, strlen(cmdline_password));
  560. sfree(cmdline_password);
  561. }
  562. cmdline_password = dupstr(value);
  563. }
  564. cmdline_arg_wipe(nextarg);
  565. }
  566. if (!strcmp(p, "-pwfile")) {
  567. RETURN(2);
  568. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  569. SAVEABLE(1);
  570. /* We delay evaluating this until after the protocol is decided,
  571. * so that we can warn if it's of no use with the selected protocol */
  572. if (conf_get_int(conf, CONF_protocol) != PROT_SSH)
  573. cmdline_error("the -pwfile option can only be used with the "
  574. "SSH protocol");
  575. else {
  576. Filename *fn = cmdline_arg_to_filename(nextarg);
  577. FILE *fp = f_open(fn, "r", false);
  578. if (!fp) {
  579. cmdline_error("unable to open password file '%s'", value);
  580. } else {
  581. if (cmdline_password) {
  582. smemclr(cmdline_password, strlen(cmdline_password));
  583. sfree(cmdline_password);
  584. }
  585. cmdline_password = chomp(fgetline(fp));
  586. if (!cmdline_password) {
  587. cmdline_error("unable to read a password from file '%s'",
  588. value);
  589. }
  590. fclose(fp);
  591. }
  592. filename_free(fn);
  593. }
  594. }
  595. if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") ||
  596. !strcmp(p, "-pageant")) {
  597. RETURN(1);
  598. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  599. SAVEABLE(0);
  600. conf_set_bool(conf, CONF_tryagent, true);
  601. }
  602. if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||
  603. !strcmp(p, "-nopageant")) {
  604. RETURN(1);
  605. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  606. SAVEABLE(0);
  607. conf_set_bool(conf, CONF_tryagent, false);
  608. }
  609. if (!strcmp(p, "-no-trivial-auth")) {
  610. RETURN(1);
  611. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  612. SAVEABLE(0);
  613. conf_set_bool(conf, CONF_ssh_no_trivial_userauth, true);
  614. }
  615. if (!strcmp(p, "-share")) {
  616. RETURN(1);
  617. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  618. SAVEABLE(0);
  619. conf_set_bool(conf, CONF_ssh_connection_sharing, true);
  620. }
  621. if (!strcmp(p, "-noshare")) {
  622. RETURN(1);
  623. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  624. SAVEABLE(0);
  625. conf_set_bool(conf, CONF_ssh_connection_sharing, false);
  626. }
  627. if (!strcmp(p, "-A")) {
  628. RETURN(1);
  629. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  630. SAVEABLE(0);
  631. conf_set_bool(conf, CONF_agentfwd, true);
  632. }
  633. if (!strcmp(p, "-a")) {
  634. RETURN(1);
  635. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  636. SAVEABLE(0);
  637. conf_set_bool(conf, CONF_agentfwd, false);
  638. }
  639. if (!strcmp(p, "-X")) {
  640. RETURN(1);
  641. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  642. SAVEABLE(0);
  643. conf_set_bool(conf, CONF_x11_forward, true);
  644. }
  645. if (!strcmp(p, "-x")) {
  646. RETURN(1);
  647. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  648. SAVEABLE(0);
  649. conf_set_bool(conf, CONF_x11_forward, false);
  650. }
  651. if (!strcmp(p, "-t")) {
  652. RETURN(1);
  653. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  654. SAVEABLE(1); /* lower priority than -m */
  655. conf_set_bool(conf, CONF_nopty, false);
  656. }
  657. if (!strcmp(p, "-T")) {
  658. RETURN(1);
  659. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  660. SAVEABLE(1);
  661. conf_set_bool(conf, CONF_nopty, true);
  662. }
  663. if (!strcmp(p, "-N")) {
  664. RETURN(1);
  665. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  666. SAVEABLE(0);
  667. conf_set_bool(conf, CONF_ssh_no_shell, true);
  668. }
  669. if (!strcmp(p, "-C")) {
  670. RETURN(1);
  671. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  672. SAVEABLE(0);
  673. conf_set_bool(conf, CONF_compression, true);
  674. }
  675. if (!strcmp(p, "-1")) {
  676. RETURN(1);
  677. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  678. SAVEABLE(0);
  679. conf_set_int(conf, CONF_sshprot, 0); /* ssh protocol 1 only */
  680. }
  681. if (!strcmp(p, "-2")) {
  682. RETURN(1);
  683. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  684. SAVEABLE(0);
  685. conf_set_int(conf, CONF_sshprot, 3); /* ssh protocol 2 only */
  686. }
  687. if (!strcmp(p, "-i")) {
  688. RETURN(2);
  689. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  690. SAVEABLE(0);
  691. Filename *fn = cmdline_arg_to_filename(nextarg);
  692. conf_set_filename(conf, CONF_keyfile, fn);
  693. filename_free(fn);
  694. }
  695. if (!strcmp(p, "-cert")) {
  696. RETURN(2);
  697. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  698. SAVEABLE(0);
  699. Filename *fn = cmdline_arg_to_filename(nextarg);
  700. conf_set_filename(conf, CONF_detached_cert, fn);
  701. filename_free(fn);
  702. }
  703. if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {
  704. RETURN(1);
  705. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  706. SAVEABLE(1);
  707. conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4);
  708. }
  709. if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {
  710. RETURN(1);
  711. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  712. SAVEABLE(1);
  713. conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6);
  714. }
  715. if (!strcmp(p, "-sercfg")) {
  716. const char *nextitem;
  717. RETURN(2);
  718. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
  719. SAVEABLE(1);
  720. if (conf_get_int(conf, CONF_protocol) != PROT_SERIAL)
  721. cmdline_error("the -sercfg option can only be used with the "
  722. "serial protocol");
  723. /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */
  724. nextitem = value;
  725. while (nextitem[0] != '\0') {
  726. int length, skip;
  727. char *end = strchr(nextitem, ',');
  728. if (!end) {
  729. length = strlen(nextitem);
  730. skip = 0;
  731. } else {
  732. length = end - nextitem;
  733. skip = 1;
  734. }
  735. if (length == 1) {
  736. switch (*nextitem) {
  737. case '1':
  738. case '2':
  739. conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0'));
  740. break;
  741. case '5':
  742. case '6':
  743. case '7':
  744. case '8':
  745. case '9':
  746. conf_set_int(conf, CONF_serdatabits, *nextitem-'0');
  747. break;
  748. case 'n':
  749. conf_set_int(conf, CONF_serparity, SER_PAR_NONE);
  750. break;
  751. case 'o':
  752. conf_set_int(conf, CONF_serparity, SER_PAR_ODD);
  753. break;
  754. case 'e':
  755. conf_set_int(conf, CONF_serparity, SER_PAR_EVEN);
  756. break;
  757. case 'm':
  758. conf_set_int(conf, CONF_serparity, SER_PAR_MARK);
  759. break;
  760. case 's':
  761. conf_set_int(conf, CONF_serparity, SER_PAR_SPACE);
  762. break;
  763. case 'N':
  764. conf_set_int(conf, CONF_serflow, SER_FLOW_NONE);
  765. break;
  766. case 'X':
  767. conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF);
  768. break;
  769. case 'R':
  770. conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS);
  771. break;
  772. case 'D':
  773. conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR);
  774. break;
  775. default:
  776. cmdline_error("Unrecognised suboption \"-sercfg %c\"",
  777. *nextitem);
  778. }
  779. } else if (length == 3 && !strncmp(nextitem,"1.5",3)) {
  780. /* Messy special case */
  781. conf_set_int(conf, CONF_serstopbits, 3);
  782. } else {
  783. char *speedstr = dupprintf("%.*s", length, nextitem);
  784. int serspeed = atoi(speedstr);
  785. sfree(speedstr);
  786. if (serspeed != 0) {
  787. conf_set_int(conf, CONF_serspeed, serspeed);
  788. } else {
  789. cmdline_error("Unrecognised suboption \"-sercfg %s\"",
  790. nextitem);
  791. }
  792. }
  793. nextitem += length + skip;
  794. }
  795. }
  796. if (!strcmp(p, "-sessionlog")) {
  797. RETURN(2);
  798. UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER);
  799. /* but available even in TOOLTYPE_NONNETWORK, cf pterm "-log" */
  800. SAVEABLE(0);
  801. Filename *fn = cmdline_arg_to_filename(nextarg);
  802. conf_set_filename(conf, CONF_logfilename, fn);
  803. conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
  804. filename_free(fn);
  805. }
  806. if (!strcmp(p, "-sshlog") ||
  807. !strcmp(p, "-sshrawlog")) {
  808. RETURN(2);
  809. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  810. SAVEABLE(0);
  811. Filename *fn = cmdline_arg_to_filename(nextarg);
  812. conf_set_filename(conf, CONF_logfilename, fn);
  813. conf_set_int(conf, CONF_logtype,
  814. !strcmp(p, "-sshlog") ? LGTYP_PACKETS :
  815. /* !strcmp(p, "-sshrawlog") ? */ LGTYP_SSHRAW);
  816. filename_free(fn);
  817. }
  818. if (!strcmp(p, "-logoverwrite")) {
  819. RETURN(1);
  820. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  821. SAVEABLE(0);
  822. conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
  823. }
  824. if (!strcmp(p, "-logappend")) {
  825. RETURN(1);
  826. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  827. SAVEABLE(0);
  828. conf_set_int(conf, CONF_logxfovr, LGXF_APN);
  829. }
  830. if (!strcmp(p, "-proxycmd")) {
  831. RETURN(2);
  832. UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
  833. SAVEABLE(0);
  834. conf_set_int(conf, CONF_proxy_type, PROXY_CMD);
  835. conf_set_str(conf, CONF_proxy_telnet_command, value);
  836. }
  837. if (!strcmp(p, "-batch")) {
  838. RETURN(1);
  839. SAVEABLE(0);
  840. if (!console_set_batch_mode(true)) {
  841. cmdline_report_unavailable(p);
  842. return ret;
  843. }
  844. }
  845. if (!strcmp(p, "-legacy-stdio-prompts") ||
  846. !strcmp(p, "-legacy_stdio_prompts")) {
  847. RETURN(1);
  848. SAVEABLE(0);
  849. if (!console_set_stdio_prompts(true)) {
  850. cmdline_report_unavailable(p);
  851. return ret;
  852. }
  853. }
  854. if (!strcmp(p, "-legacy-charset-handling") ||
  855. !strcmp(p, "-legacy_charset_handling")) {
  856. RETURN(1);
  857. SAVEABLE(0);
  858. if (!set_legacy_charset_handling(true)) {
  859. cmdline_report_unavailable(p);
  860. return ret;
  861. }
  862. }
  863. #ifdef _WINDOWS
  864. /*
  865. * Cross-tool options only available on Windows.
  866. */
  867. if (!strcmp(p, "-restrict-acl") || !strcmp(p, "-restrict_acl") ||
  868. !strcmp(p, "-restrictacl")) {
  869. RETURN(1);
  870. restrict_process_acl();
  871. }
  872. #endif
  873. return ret; /* unrecognised */
  874. }
  875. void cmdline_run_saved(Conf *conf)
  876. {
  877. for (size_t pri = 0; pri < NPRIORITIES; pri++) {
  878. for (size_t i = 0; i < saves[pri].nsaved; i++)
  879. cmdline_process_param(saves[pri].params[i].p,
  880. saves[pri].params[i].value, 0, conf);
  881. saves[pri].nsaved = 0;
  882. }
  883. }
  884. bool cmdline_host_ok(Conf *conf)
  885. {
  886. /*
  887. * Return true if the command-line arguments we've processed in
  888. * TOOLTYPE_HOST_ARG mode are sufficient to justify launching a
  889. * session.
  890. */
  891. assert(cmdline_tooltype & TOOLTYPE_HOST_ARG);
  892. /*
  893. * Of course, if we _can't_ launch a session, the answer is
  894. * clearly no.
  895. */
  896. if (!conf_launchable(conf))
  897. return false;
  898. /*
  899. * But also, if we haven't seen either a -load option or a
  900. * hostname argument, i.e. the only saved settings we've loaded
  901. * are Default Settings plus any non-hostname-based stuff from the
  902. * command line, then the answer is still no, _even_ if this Conf
  903. * is launchable. Otherwise, if you saved your favourite hostname
  904. * into Default Settings, then just running 'putty' without
  905. * arguments would connect to it without ever offering you the
  906. * option to connect to something else or change the setting.
  907. */
  908. if (!seen_hostname_argument && !loaded_session)
  909. return false;
  910. return true;
  911. }