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