You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

653 lines
20 KiB

  1. /***************************************************************************
  2. * Copyright (C) 2005 by Dominic Rath *
  3. * Dominic.Rath@gmx.de *
  4. * *
  5. * Copyright (C) 2007-2010 √ėyvind Harboe *
  6. * oyvind.harboe@zylin.com *
  7. * *
  8. * Copyright (C) 2008 by Spencer Oliver *
  9. * spen@spen-soft.co.uk *
  10. * *
  11. * This program is free software; you can redistribute it and/or modify *
  12. * it under the terms of the GNU General Public License as published by *
  13. * the Free Software Foundation; either version 2 of the License, or *
  14. * (at your option) any later version. *
  15. * *
  16. * This program is distributed in the hope that it will be useful, *
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  19. * GNU General Public License for more details. *
  20. * *
  21. * You should have received a copy of the GNU General Public License *
  22. * along with this program; if not, write to the *
  23. * Free Software Foundation, Inc., *
  24. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  25. ***************************************************************************/
  26. #ifdef HAVE_CONFIG_H
  27. #include "config.h"
  28. #endif
  29. #include "telnet_server.h"
  30. #include <target/target_request.h>
  31. #include <helper/configuration.h>
  32. static const char *telnet_port;
  33. static char *negotiate =
  34. "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
  35. "\xFF\xFB\x01" /* IAC WILL Echo */
  36. "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
  37. "\xFF\xFE\x01"; /* IAC DON'T Echo */
  38. #define CTRL(c) (c - '@')
  39. #define TELNET_HISTORY ".openocd_history"
  40. /* The only way we can detect that the socket is closed is the first time
  41. * we write to it, we will fail. Subsequent write operations will
  42. * succeed. Shudder!
  43. */
  44. static int telnet_write(struct connection *connection, const void *data,
  45. int len)
  46. {
  47. struct telnet_connection *t_con = connection->priv;
  48. if (t_con->closed)
  49. return ERROR_SERVER_REMOTE_CLOSED;
  50. if (connection_write(connection, data, len) == len)
  51. return ERROR_OK;
  52. t_con->closed = 1;
  53. return ERROR_SERVER_REMOTE_CLOSED;
  54. }
  55. static int telnet_prompt(struct connection *connection)
  56. {
  57. struct telnet_connection *t_con = connection->priv;
  58. return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
  59. }
  60. static int telnet_outputline(struct connection *connection, const char *line)
  61. {
  62. int len;
  63. /* process lines in buffer */
  64. while (*line) {
  65. char *line_end = strchr(line, '\n');
  66. if (line_end)
  67. len = line_end-line;
  68. else
  69. len = strlen(line);
  70. telnet_write(connection, line, len);
  71. if (line_end) {
  72. telnet_write(connection, "\r\n", 2);
  73. line += len + 1;
  74. } else
  75. line += len;
  76. }
  77. return ERROR_OK;
  78. }
  79. static int telnet_output(struct command_context *cmd_ctx, const char *line)
  80. {
  81. struct connection *connection = cmd_ctx->output_handler_priv;
  82. return telnet_outputline(connection, line);
  83. }
  84. static void telnet_log_callback(void *priv, const char *file, unsigned line,
  85. const char *function, const char *string)
  86. {
  87. struct connection *connection = priv;
  88. struct telnet_connection *t_con = connection->priv;
  89. int i;
  90. /* if there is no prompt, simply output the message */
  91. if (t_con->line_cursor < 0) {
  92. telnet_outputline(connection, string);
  93. return;
  94. }
  95. /* clear the command line */
  96. for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
  97. telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
  98. for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
  99. telnet_write(connection, " ", i > 16 ? 16 : i);
  100. for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
  101. telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
  102. /* output the message */
  103. telnet_outputline(connection, string);
  104. /* put the command line to its previous state */
  105. telnet_prompt(connection);
  106. telnet_write(connection, t_con->line, t_con->line_size);
  107. for (i = t_con->line_size; i > t_con->line_cursor; i--)
  108. telnet_write(connection, "\b", 1);
  109. }
  110. static void telnet_load_history(struct telnet_connection *t_con)
  111. {
  112. FILE *histfp;
  113. char buffer[TELNET_BUFFER_SIZE];
  114. int i = 0;
  115. char *history = get_home_dir(TELNET_HISTORY);
  116. if (history == NULL) {
  117. LOG_INFO("unable to get user home directory, telnet history will be disabled");
  118. return;
  119. }
  120. histfp = fopen(history, "rb");
  121. if (histfp) {
  122. while (fgets(buffer, sizeof(buffer), histfp) != NULL) {
  123. char *p = strchr(buffer, '\n');
  124. if (p)
  125. *p = '\0';
  126. if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
  127. t_con->history[i++] = strdup(buffer);
  128. }
  129. t_con->next_history = i;
  130. t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
  131. /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
  132. t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
  133. fclose(histfp);
  134. }
  135. free(history);
  136. }
  137. static void telnet_save_history(struct telnet_connection *t_con)
  138. {
  139. FILE *histfp;
  140. int i;
  141. int num;
  142. char *history = get_home_dir(TELNET_HISTORY);
  143. if (history == NULL) {
  144. LOG_INFO("unable to get user home directory, telnet history will be disabled");
  145. return;
  146. }
  147. histfp = fopen(history, "wb");
  148. if (histfp) {
  149. num = TELNET_LINE_HISTORY_SIZE;
  150. i = t_con->current_history + 1;
  151. i %= TELNET_LINE_HISTORY_SIZE;
  152. while (t_con->history[i] == NULL && num > 0) {
  153. i++;
  154. i %= TELNET_LINE_HISTORY_SIZE;
  155. num--;
  156. }
  157. if (num > 0) {
  158. for (; num > 0; num--) {
  159. fprintf(histfp, "%s\n", t_con->history[i]);
  160. i++;
  161. i %= TELNET_LINE_HISTORY_SIZE;
  162. }
  163. }
  164. fclose(histfp);
  165. }
  166. free(history);
  167. }
  168. static int telnet_new_connection(struct connection *connection)
  169. {
  170. struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection));
  171. struct telnet_service *telnet_service = connection->service->priv;
  172. int i;
  173. connection->priv = telnet_connection;
  174. /* initialize telnet connection information */
  175. telnet_connection->closed = 0;
  176. telnet_connection->line_size = 0;
  177. telnet_connection->line_cursor = 0;
  178. telnet_connection->option_size = 0;
  179. telnet_connection->prompt = strdup("> ");
  180. telnet_connection->state = TELNET_STATE_DATA;
  181. /* output goes through telnet connection */
  182. command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
  183. /* negotiate telnet options */
  184. telnet_write(connection, negotiate, strlen(negotiate));
  185. /* print connection banner */
  186. if (telnet_service->banner) {
  187. telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
  188. telnet_write(connection, "\r\n", 2);
  189. }
  190. /* the prompt is always placed at the line beginning */
  191. telnet_write(connection, "\r", 1);
  192. telnet_prompt(connection);
  193. /* initialize history */
  194. for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
  195. telnet_connection->history[i] = NULL;
  196. telnet_connection->next_history = 0;
  197. telnet_connection->current_history = 0;
  198. telnet_load_history(telnet_connection);
  199. log_add_callback(telnet_log_callback, connection);
  200. return ERROR_OK;
  201. }
  202. static void telnet_clear_line(struct connection *connection,
  203. struct telnet_connection *t_con)
  204. {
  205. /* move to end of line */
  206. if (t_con->line_cursor < t_con->line_size)
  207. telnet_write(connection,
  208. t_con->line + t_con->line_cursor,
  209. t_con->line_size - t_con->line_cursor);
  210. /* backspace, overwrite with space, backspace */
  211. while (t_con->line_size > 0) {
  212. telnet_write(connection, "\b \b", 3);
  213. t_con->line_size--;
  214. }
  215. t_con->line_cursor = 0;
  216. }
  217. static int telnet_input(struct connection *connection)
  218. {
  219. int bytes_read;
  220. unsigned char buffer[TELNET_BUFFER_SIZE];
  221. unsigned char *buf_p;
  222. struct telnet_connection *t_con = connection->priv;
  223. struct command_context *command_context = connection->cmd_ctx;
  224. bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
  225. if (bytes_read == 0)
  226. return ERROR_SERVER_REMOTE_CLOSED;
  227. else if (bytes_read == -1) {
  228. LOG_ERROR("error during read: %s", strerror(errno));
  229. return ERROR_SERVER_REMOTE_CLOSED;
  230. }
  231. buf_p = buffer;
  232. while (bytes_read) {
  233. switch (t_con->state) {
  234. case TELNET_STATE_DATA:
  235. if (*buf_p == 0xff)
  236. t_con->state = TELNET_STATE_IAC;
  237. else {
  238. if (isprint(*buf_p)) { /* printable character */
  239. /* watch buffer size leaving one spare character for
  240. * string null termination */
  241. if (t_con->line_size == TELNET_LINE_MAX_SIZE-1) {
  242. /* output audible bell if buffer is full
  243. * "\a" does not work, at least on windows */
  244. telnet_write(connection, "\x07", 1);
  245. } else if (t_con->line_cursor == t_con->line_size) {
  246. telnet_write(connection, buf_p, 1);
  247. t_con->line[t_con->line_size++] = *buf_p;
  248. t_con->line_cursor++;
  249. } else {
  250. int i;
  251. memmove(t_con->line + t_con->line_cursor + 1,
  252. t_con->line + t_con->line_cursor,
  253. t_con->line_size - t_con->line_cursor);
  254. t_con->line[t_con->line_cursor] = *buf_p;
  255. t_con->line_size++;
  256. telnet_write(connection,
  257. t_con->line + t_con->line_cursor,
  258. t_con->line_size - t_con->line_cursor);
  259. t_con->line_cursor++;
  260. for (i = t_con->line_cursor; i < t_con->line_size; i++)
  261. telnet_write(connection, "\b", 1);
  262. }
  263. } else { /* non-printable */
  264. if (*buf_p == 0x1b) { /* escape */
  265. t_con->state = TELNET_STATE_ESCAPE;
  266. t_con->last_escape = '\x00';
  267. } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
  268. int retval;
  269. /* skip over combinations with CR/LF and NUL characters */
  270. if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
  271. (*(buf_p + 1) == 0xd))) {
  272. buf_p++;
  273. bytes_read--;
  274. }
  275. if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
  276. buf_p++;
  277. bytes_read--;
  278. }
  279. t_con->line[t_con->line_size] = 0;
  280. telnet_write(connection, "\r\n\x00", 3);
  281. if (strcmp(t_con->line, "history") == 0) {
  282. int i;
  283. for (i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
  284. /* the t_con->next_history line contains empty string
  285. * (unless NULL), thus it is not printed */
  286. char *history_line = t_con->history[(t_con->
  287. next_history + i) %
  288. TELNET_LINE_HISTORY_SIZE];
  289. if (history_line) {
  290. telnet_write(connection, history_line,
  291. strlen(history_line));
  292. telnet_write(connection, "\r\n\x00", 3);
  293. }
  294. }
  295. t_con->line_size = 0;
  296. t_con->line_cursor = 0;
  297. continue;
  298. }
  299. /* save only non-blank not repeating lines in the history */
  300. char *prev_line = t_con->history[(t_con->current_history > 0) ?
  301. t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
  302. if (*t_con->line && (prev_line == NULL ||
  303. strcmp(t_con->line, prev_line))) {
  304. /* if the history slot is already taken, free it */
  305. if (t_con->history[t_con->next_history])
  306. free(t_con->history[t_con->next_history]);
  307. /* add line to history */
  308. t_con->history[t_con->next_history] = strdup(t_con->line);
  309. /* wrap history at TELNET_LINE_HISTORY_SIZE */
  310. t_con->next_history = (t_con->next_history + 1) %
  311. TELNET_LINE_HISTORY_SIZE;
  312. /* current history line starts at the new entry */
  313. t_con->current_history =
  314. t_con->next_history;
  315. if (t_con->history[t_con->current_history])
  316. free(t_con->history[t_con->current_history]);
  317. t_con->history[t_con->current_history] = strdup("");
  318. }
  319. t_con->line_size = 0;
  320. /* to suppress prompt in log callback during command execution */
  321. t_con->line_cursor = -1;
  322. if (strcmp(t_con->line, "shutdown") == 0)
  323. telnet_save_history(t_con);
  324. retval = command_run_line(command_context, t_con->line);
  325. t_con->line_cursor = 0;
  326. if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
  327. return ERROR_SERVER_REMOTE_CLOSED;
  328. /* the prompt is always * placed at the line beginning */
  329. telnet_write(connection, "\r", 1);
  330. retval = telnet_prompt(connection);
  331. if (retval == ERROR_SERVER_REMOTE_CLOSED)
  332. return ERROR_SERVER_REMOTE_CLOSED;
  333. } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
  334. if (t_con->line_cursor > 0) {
  335. if (t_con->line_cursor != t_con->line_size) {
  336. int i;
  337. telnet_write(connection, "\b", 1);
  338. t_con->line_cursor--;
  339. t_con->line_size--;
  340. memmove(t_con->line + t_con->line_cursor,
  341. t_con->line + t_con->line_cursor + 1,
  342. t_con->line_size -
  343. t_con->line_cursor);
  344. telnet_write(connection,
  345. t_con->line + t_con->line_cursor,
  346. t_con->line_size -
  347. t_con->line_cursor);
  348. telnet_write(connection, " \b", 2);
  349. for (i = t_con->line_cursor; i < t_con->line_size; i++)
  350. telnet_write(connection, "\b", 1);
  351. } else {
  352. t_con->line_size--;
  353. t_con->line_cursor--;
  354. /* back space: move the 'printer' head one char
  355. * back, overwrite with space, move back again */
  356. telnet_write(connection, "\b \b", 3);
  357. }
  358. }
  359. } else if (*buf_p == 0x15) /* clear line */
  360. telnet_clear_line(connection, t_con);
  361. else if (*buf_p == CTRL('B')) { /* cursor left */
  362. if (t_con->line_cursor > 0) {
  363. telnet_write(connection, "\b", 1);
  364. t_con->line_cursor--;
  365. }
  366. t_con->state = TELNET_STATE_DATA;
  367. } else if (*buf_p == CTRL('F')) { /* cursor right */
  368. if (t_con->line_cursor < t_con->line_size)
  369. telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
  370. t_con->state = TELNET_STATE_DATA;
  371. } else
  372. LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
  373. }
  374. }
  375. break;
  376. case TELNET_STATE_IAC:
  377. switch (*buf_p) {
  378. case 0xfe:
  379. t_con->state = TELNET_STATE_DONT;
  380. break;
  381. case 0xfd:
  382. t_con->state = TELNET_STATE_DO;
  383. break;
  384. case 0xfc:
  385. t_con->state = TELNET_STATE_WONT;
  386. break;
  387. case 0xfb:
  388. t_con->state = TELNET_STATE_WILL;
  389. break;
  390. }
  391. break;
  392. case TELNET_STATE_SB:
  393. break;
  394. case TELNET_STATE_SE:
  395. break;
  396. case TELNET_STATE_WILL:
  397. case TELNET_STATE_WONT:
  398. case TELNET_STATE_DO:
  399. case TELNET_STATE_DONT:
  400. t_con->state = TELNET_STATE_DATA;
  401. break;
  402. case TELNET_STATE_ESCAPE:
  403. if (t_con->last_escape == '[') {
  404. if (*buf_p == 'D') { /* cursor left */
  405. if (t_con->line_cursor > 0) {
  406. telnet_write(connection, "\b", 1);
  407. t_con->line_cursor--;
  408. }
  409. t_con->state = TELNET_STATE_DATA;
  410. } else if (*buf_p == 'C') { /* cursor right */
  411. if (t_con->line_cursor < t_con->line_size)
  412. telnet_write(connection,
  413. t_con->line + t_con->line_cursor++, 1);
  414. t_con->state = TELNET_STATE_DATA;
  415. } else if (*buf_p == 'A') { /* cursor up */
  416. int last_history = (t_con->current_history > 0) ?
  417. t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
  418. if (t_con->history[last_history]) {
  419. telnet_clear_line(connection, t_con);
  420. t_con->line_size = strlen(t_con->history[last_history]);
  421. t_con->line_cursor = t_con->line_size;
  422. memcpy(t_con->line, t_con->history[last_history], t_con->line_size);
  423. telnet_write(connection, t_con->line, t_con->line_size);
  424. t_con->current_history = last_history;
  425. }
  426. t_con->state = TELNET_STATE_DATA;
  427. } else if (*buf_p == 'B') { /* cursor down */
  428. int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
  429. if (t_con->history[next_history]) {
  430. telnet_clear_line(connection, t_con);
  431. t_con->line_size = strlen(t_con->history[next_history]);
  432. t_con->line_cursor = t_con->line_size;
  433. memcpy(t_con->line, t_con->history[next_history], t_con->line_size);
  434. telnet_write(connection, t_con->line, t_con->line_size);
  435. t_con->current_history = next_history;
  436. }
  437. t_con->state = TELNET_STATE_DATA;
  438. } else if (*buf_p == '3')
  439. t_con->last_escape = *buf_p;
  440. else
  441. t_con->state = TELNET_STATE_DATA;
  442. } else if (t_con->last_escape == '3') {
  443. /* Remove character */
  444. if (*buf_p == '~') {
  445. if (t_con->line_cursor < t_con->line_size) {
  446. int i;
  447. t_con->line_size--;
  448. /* remove char from line buffer */
  449. memmove(t_con->line + t_con->line_cursor,
  450. t_con->line + t_con->line_cursor + 1,
  451. t_con->line_size - t_con->line_cursor);
  452. /* print remainder of buffer */
  453. telnet_write(connection, t_con->line + t_con->line_cursor,
  454. t_con->line_size - t_con->line_cursor);
  455. /* overwrite last char with whitespace */
  456. telnet_write(connection, " \b", 2);
  457. /* move back to cursor position*/
  458. for (i = t_con->line_cursor; i < t_con->line_size; i++)
  459. telnet_write(connection, "\b", 1);
  460. }
  461. t_con->state = TELNET_STATE_DATA;
  462. } else
  463. t_con->state = TELNET_STATE_DATA;
  464. } else if (t_con->last_escape == '\x00') {
  465. if (*buf_p == '[')
  466. t_con->last_escape = *buf_p;
  467. else
  468. t_con->state = TELNET_STATE_DATA;
  469. } else {
  470. LOG_ERROR("BUG: unexpected value in t_con->last_escape");
  471. t_con->state = TELNET_STATE_DATA;
  472. }
  473. break;
  474. default:
  475. LOG_ERROR("unknown telnet state");
  476. exit(-1);
  477. }
  478. bytes_read--;
  479. buf_p++;
  480. }
  481. return ERROR_OK;
  482. }
  483. static int telnet_connection_closed(struct connection *connection)
  484. {
  485. struct telnet_connection *t_con = connection->priv;
  486. int i;
  487. log_remove_callback(telnet_log_callback, connection);
  488. if (t_con->prompt) {
  489. free(t_con->prompt);
  490. t_con->prompt = NULL;
  491. }
  492. /* save telnet history */
  493. telnet_save_history(t_con);
  494. for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
  495. if (t_con->history[i]) {
  496. free(t_con->history[i]);
  497. t_con->history[i] = NULL;
  498. }
  499. }
  500. /* if this connection registered a debug-message receiver delete it */
  501. delete_debug_msg_receiver(connection->cmd_ctx, NULL);
  502. if (connection->priv) {
  503. free(connection->priv);
  504. connection->priv = NULL;
  505. } else
  506. LOG_ERROR("BUG: connection->priv == NULL");
  507. return ERROR_OK;
  508. }
  509. int telnet_init(char *banner)
  510. {
  511. if (strcmp(telnet_port, "disabled") == 0) {
  512. LOG_INFO("telnet server disabled");
  513. return ERROR_OK;
  514. }
  515. struct telnet_service *telnet_service = malloc(sizeof(struct telnet_service));
  516. telnet_service->banner = banner;
  517. return add_service("telnet",
  518. telnet_port,
  519. 1,
  520. telnet_new_connection,
  521. telnet_input,
  522. telnet_connection_closed,
  523. telnet_service);
  524. }
  525. /* daemon configuration command telnet_port */
  526. COMMAND_HANDLER(handle_telnet_port_command)
  527. {
  528. return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
  529. }
  530. COMMAND_HANDLER(handle_exit_command)
  531. {
  532. return ERROR_COMMAND_CLOSE_CONNECTION;
  533. }
  534. static const struct command_registration telnet_command_handlers[] = {
  535. {
  536. .name = "exit",
  537. .handler = handle_exit_command,
  538. .mode = COMMAND_EXEC,
  539. .usage = "",
  540. .help = "exit telnet session",
  541. },
  542. {
  543. .name = "telnet_port",
  544. .handler = handle_telnet_port_command,
  545. .mode = COMMAND_ANY,
  546. .help = "Specify port on which to listen "
  547. "for incoming telnet connections. "
  548. "Read help on 'gdb_port'.",
  549. .usage = "[port_num]",
  550. },
  551. COMMAND_REGISTRATION_DONE
  552. };
  553. int telnet_register_commands(struct command_context *cmd_ctx)
  554. {
  555. telnet_port = strdup("4444");
  556. return register_commands(cmd_ctx, NULL, telnet_command_handlers);
  557. }