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.
 
 
 
 
 
 

663 lines
18 KiB

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