1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2007-2010 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
8 * Copyright (C) 2008 by Spencer Oliver *
9 * spen@spen-soft.co.uk *
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. *
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. *
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 ***************************************************************************/
31 #include "telnet_server.h"
32 #include <target/target_request.h>
33 #include <helper/configuration.h>
35 static const char *telnet_port;
37 static char *negotiate =
38 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
39 "\xFF\xFB\x01" /* IAC WILL Echo */
40 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
41 "\xFF\xFE\x01"; /* IAC DON'T Echo */
43 #define CTRL(c) (c - '@')
44 #define TELNET_HISTORY ".openocd_history"
46 /* The only way we can detect that the socket is closed is the first time
47 * we write to it, we will fail. Subsequent write operations will
50 static int telnet_write(struct connection *connection, const void *data,
53 struct telnet_connection *t_con = connection->priv;
55 return ERROR_SERVER_REMOTE_CLOSED;
57 if (connection_write(connection, data, len) == len)
60 return ERROR_SERVER_REMOTE_CLOSED;
63 static int telnet_prompt(struct connection *connection)
65 struct telnet_connection *t_con = connection->priv;
67 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
70 static int telnet_outputline(struct connection *connection, const char *line)
74 /* process lines in buffer */
76 char *line_end = strchr(line, '\n');
83 telnet_write(connection, line, len);
85 telnet_write(connection, "\r\n", 2);
94 static int telnet_output(struct command_context *cmd_ctx, const char *line)
96 struct connection *connection = cmd_ctx->output_handler_priv;
98 return telnet_outputline(connection, line);
101 static void telnet_log_callback(void *priv, const char *file, unsigned line,
102 const char *function, const char *string)
104 struct connection *connection = priv;
105 struct telnet_connection *t_con = connection->priv;
108 /* if there is no prompt, simply output the message */
109 if (t_con->line_cursor < 0) {
110 telnet_outputline(connection, string);
114 /* clear the command line */
115 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
116 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
117 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
118 telnet_write(connection, " ", i > 16 ? 16 : i);
119 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
120 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
122 /* output the message */
123 telnet_outputline(connection, string);
125 /* put the command line to its previous state */
126 telnet_prompt(connection);
127 telnet_write(connection, t_con->line, t_con->line_size);
128 for (i = t_con->line_size; i > t_con->line_cursor; i--)
129 telnet_write(connection, "\b", 1);
132 static void telnet_load_history(struct telnet_connection *t_con)
135 char buffer[TELNET_BUFFER_SIZE];
138 char *history = get_home_dir(TELNET_HISTORY);
140 if (history == NULL) {
141 LOG_INFO("unable to get user home directory, telnet history will be disabled");
145 histfp = fopen(history, "rb");
149 while (fgets(buffer, sizeof(buffer), histfp) != NULL) {
151 char *p = strchr(buffer, '\n');
154 if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
155 t_con->history[i++] = strdup(buffer);
158 t_con->next_history = i;
159 t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
160 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
161 t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
168 static void telnet_save_history(struct telnet_connection *t_con)
174 char *history = get_home_dir(TELNET_HISTORY);
176 if (history == NULL) {
177 LOG_INFO("unable to get user home directory, telnet history will be disabled");
181 histfp = fopen(history, "wb");
185 num = TELNET_LINE_HISTORY_SIZE;
186 i = t_con->current_history + 1;
187 i %= TELNET_LINE_HISTORY_SIZE;
189 while (t_con->history[i] == NULL && num > 0) {
191 i %= TELNET_LINE_HISTORY_SIZE;
196 for (; num > 0; num--) {
197 fprintf(histfp, "%s\n", t_con->history[i]);
199 i %= TELNET_LINE_HISTORY_SIZE;
208 static int telnet_new_connection(struct connection *connection)
210 struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection));
211 struct telnet_service *telnet_service = connection->service->priv;
214 connection->priv = telnet_connection;
216 /* initialize telnet connection information */
217 telnet_connection->closed = 0;
218 telnet_connection->line_size = 0;
219 telnet_connection->line_cursor = 0;
220 telnet_connection->option_size = 0;
221 telnet_connection->prompt = strdup("> ");
222 telnet_connection->state = TELNET_STATE_DATA;
224 /* output goes through telnet connection */
225 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
227 /* negotiate telnet options */
228 telnet_write(connection, negotiate, strlen(negotiate));
230 /* print connection banner */
231 if (telnet_service->banner) {
232 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
233 telnet_write(connection, "\r\n", 2);
236 /* the prompt is always placed at the line beginning */
237 telnet_write(connection, "\r", 1);
238 telnet_prompt(connection);
240 /* initialize history */
241 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
242 telnet_connection->history[i] = NULL;
243 telnet_connection->next_history = 0;
244 telnet_connection->current_history = 0;
245 telnet_load_history(telnet_connection);
247 log_add_callback(telnet_log_callback, connection);
252 static void telnet_clear_line(struct connection *connection,
253 struct telnet_connection *t_con)
255 /* move to end of line */
256 if (t_con->line_cursor < t_con->line_size)
257 telnet_write(connection,
258 t_con->line + t_con->line_cursor,
259 t_con->line_size - t_con->line_cursor);
261 /* backspace, overwrite with space, backspace */
262 while (t_con->line_size > 0) {
263 telnet_write(connection, "\b \b", 3);
266 t_con->line_cursor = 0;
269 static int telnet_input(struct connection *connection)
272 unsigned char buffer[TELNET_BUFFER_SIZE];
273 unsigned char *buf_p;
274 struct telnet_connection *t_con = connection->priv;
275 struct command_context *command_context = connection->cmd_ctx;
277 bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
280 return ERROR_SERVER_REMOTE_CLOSED;
281 else if (bytes_read == -1) {
282 LOG_ERROR("error during read: %s", strerror(errno));
283 return ERROR_SERVER_REMOTE_CLOSED;
288 switch (t_con->state) {
289 case TELNET_STATE_DATA:
291 t_con->state = TELNET_STATE_IAC;
293 if (isprint(*buf_p)) { /* printable character */
294 /* watch buffer size leaving one spare character for
295 * string null termination */
296 if (t_con->line_size == TELNET_LINE_MAX_SIZE-1) {
297 /* output audible bell if buffer is full
298 * "\a" does not work, at least on windows */
299 telnet_write(connection, "\x07", 1);
300 } else if (t_con->line_cursor == t_con->line_size) {
301 telnet_write(connection, buf_p, 1);
302 t_con->line[t_con->line_size++] = *buf_p;
303 t_con->line_cursor++;
306 memmove(t_con->line + t_con->line_cursor + 1,
307 t_con->line + t_con->line_cursor,
308 t_con->line_size - t_con->line_cursor);
309 t_con->line[t_con->line_cursor] = *buf_p;
311 telnet_write(connection,
312 t_con->line + t_con->line_cursor,
313 t_con->line_size - t_con->line_cursor);
314 t_con->line_cursor++;
315 for (i = t_con->line_cursor; i < t_con->line_size; i++)
316 telnet_write(connection, "\b", 1);
318 } else { /* non-printable */
319 if (*buf_p == 0x1b) { /* escape */
320 t_con->state = TELNET_STATE_ESCAPE;
321 t_con->last_escape = '\x00';
322 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
325 /* skip over combinations with CR/LF and NUL characters */
326 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
327 (*(buf_p + 1) == 0xd))) {
331 if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
335 t_con->line[t_con->line_size] = 0;
337 telnet_write(connection, "\r\n\x00", 3);
339 if (strcmp(t_con->line, "history") == 0) {
341 for (i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
342 /* the t_con->next_history line contains empty string
343 * (unless NULL), thus it is not printed */
344 char *history_line = t_con->history[(t_con->
346 TELNET_LINE_HISTORY_SIZE];
348 telnet_write(connection, history_line,
349 strlen(history_line));
350 telnet_write(connection, "\r\n\x00", 3);
353 t_con->line_size = 0;
354 t_con->line_cursor = 0;
358 /* save only non-blank not repeating lines in the history */
359 char *prev_line = t_con->history[(t_con->current_history > 0) ?
360 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
361 if (*t_con->line && (prev_line == NULL ||
362 strcmp(t_con->line, prev_line))) {
363 /* if the history slot is already taken, free it */
364 if (t_con->history[t_con->next_history])
365 free(t_con->history[t_con->next_history]);
367 /* add line to history */
368 t_con->history[t_con->next_history] = strdup(t_con->line);
370 /* wrap history at TELNET_LINE_HISTORY_SIZE */
371 t_con->next_history = (t_con->next_history + 1) %
372 TELNET_LINE_HISTORY_SIZE;
374 /* current history line starts at the new entry */
375 t_con->current_history =
378 if (t_con->history[t_con->current_history])
379 free(t_con->history[t_con->current_history]);
380 t_con->history[t_con->current_history] = strdup("");
383 t_con->line_size = 0;
385 /* to suppress prompt in log callback during command execution */
386 t_con->line_cursor = -1;
388 if (strcmp(t_con->line, "shutdown") == 0)
389 telnet_save_history(t_con);
391 retval = command_run_line(command_context, t_con->line);
393 t_con->line_cursor = 0;
395 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
396 return ERROR_SERVER_REMOTE_CLOSED;
398 /* the prompt is always * placed at the line beginning */
399 telnet_write(connection, "\r", 1);
401 retval = telnet_prompt(connection);
402 if (retval == ERROR_SERVER_REMOTE_CLOSED)
403 return ERROR_SERVER_REMOTE_CLOSED;
405 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */
406 if (t_con->line_cursor > 0) {
407 if (t_con->line_cursor != t_con->line_size) {
409 telnet_write(connection, "\b", 1);
410 t_con->line_cursor--;
412 memmove(t_con->line + t_con->line_cursor,
413 t_con->line + t_con->line_cursor + 1,
417 telnet_write(connection,
418 t_con->line + t_con->line_cursor,
421 telnet_write(connection, " \b", 2);
422 for (i = t_con->line_cursor; i < t_con->line_size; i++)
423 telnet_write(connection, "\b", 1);
426 t_con->line_cursor--;
427 /* back space: move the 'printer' head one char
428 * back, overwrite with space, move back again */
429 telnet_write(connection, "\b \b", 3);
432 } else if (*buf_p == 0x15) /* clear line */
433 telnet_clear_line(connection, t_con);
434 else if (*buf_p == CTRL('B')) { /* cursor left */
435 if (t_con->line_cursor > 0) {
436 telnet_write(connection, "\b", 1);
437 t_con->line_cursor--;
439 t_con->state = TELNET_STATE_DATA;
440 } else if (*buf_p == CTRL('F')) { /* cursor right */
441 if (t_con->line_cursor < t_con->line_size)
442 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
443 t_con->state = TELNET_STATE_DATA;
445 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
449 case TELNET_STATE_IAC:
452 t_con->state = TELNET_STATE_DONT;
455 t_con->state = TELNET_STATE_DO;
458 t_con->state = TELNET_STATE_WONT;
461 t_con->state = TELNET_STATE_WILL;
465 case TELNET_STATE_SB:
467 case TELNET_STATE_SE:
469 case TELNET_STATE_WILL:
470 case TELNET_STATE_WONT:
471 case TELNET_STATE_DO:
472 case TELNET_STATE_DONT:
473 t_con->state = TELNET_STATE_DATA;
475 case TELNET_STATE_ESCAPE:
476 if (t_con->last_escape == '[') {
477 if (*buf_p == 'D') { /* cursor left */
478 if (t_con->line_cursor > 0) {
479 telnet_write(connection, "\b", 1);
480 t_con->line_cursor--;
482 t_con->state = TELNET_STATE_DATA;
483 } else if (*buf_p == 'C') { /* cursor right */
484 if (t_con->line_cursor < t_con->line_size)
485 telnet_write(connection,
486 t_con->line + t_con->line_cursor++, 1);
487 t_con->state = TELNET_STATE_DATA;
488 } else if (*buf_p == 'A') { /* cursor up */
489 int last_history = (t_con->current_history > 0) ?
490 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
491 if (t_con->history[last_history]) {
492 telnet_clear_line(connection, t_con);
493 t_con->line_size = strlen(t_con->history[last_history]);
494 t_con->line_cursor = t_con->line_size;
495 memcpy(t_con->line, t_con->history[last_history], t_con->line_size);
496 telnet_write(connection, t_con->line, t_con->line_size);
497 t_con->current_history = last_history;
499 t_con->state = TELNET_STATE_DATA;
500 } else if (*buf_p == 'B') { /* cursor down */
501 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
502 if (t_con->history[next_history]) {
503 telnet_clear_line(connection, t_con);
504 t_con->line_size = strlen(t_con->history[next_history]);
505 t_con->line_cursor = t_con->line_size;
506 memcpy(t_con->line, t_con->history[next_history], t_con->line_size);
507 telnet_write(connection, t_con->line, t_con->line_size);
508 t_con->current_history = next_history;
510 t_con->state = TELNET_STATE_DATA;
511 } else if (*buf_p == '3')
512 t_con->last_escape = *buf_p;
514 t_con->state = TELNET_STATE_DATA;
515 } else if (t_con->last_escape == '3') {
516 /* Remove character */
518 if (t_con->line_cursor < t_con->line_size) {
521 /* remove char from line buffer */
522 memmove(t_con->line + t_con->line_cursor,
523 t_con->line + t_con->line_cursor + 1,
524 t_con->line_size - t_con->line_cursor);
526 /* print remainder of buffer */
527 telnet_write(connection, t_con->line + t_con->line_cursor,
528 t_con->line_size - t_con->line_cursor);
529 /* overwrite last char with whitespace */
530 telnet_write(connection, " \b", 2);
532 /* move back to cursor position*/
533 for (i = t_con->line_cursor; i < t_con->line_size; i++)
534 telnet_write(connection, "\b", 1);
537 t_con->state = TELNET_STATE_DATA;
539 t_con->state = TELNET_STATE_DATA;
540 } else if (t_con->last_escape == '\x00') {
542 t_con->last_escape = *buf_p;
544 t_con->state = TELNET_STATE_DATA;
546 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
547 t_con->state = TELNET_STATE_DATA;
552 LOG_ERROR("unknown telnet state");
563 static int telnet_connection_closed(struct connection *connection)
565 struct telnet_connection *t_con = connection->priv;
568 log_remove_callback(telnet_log_callback, connection);
572 t_con->prompt = NULL;
575 /* save telnet history */
576 telnet_save_history(t_con);
578 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
579 if (t_con->history[i]) {
580 free(t_con->history[i]);
581 t_con->history[i] = NULL;
585 /* if this connection registered a debug-message receiver delete it */
586 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
588 if (connection->priv) {
589 free(connection->priv);
590 connection->priv = NULL;
592 LOG_ERROR("BUG: connection->priv == NULL");
597 int telnet_init(char *banner)
599 if (strcmp(telnet_port, "disabled") == 0) {
600 LOG_INFO("telnet server disabled");
604 struct telnet_service *telnet_service = malloc(sizeof(struct telnet_service));
606 telnet_service->banner = banner;
608 return add_service("telnet",
611 telnet_new_connection,
613 telnet_connection_closed,
617 /* daemon configuration command telnet_port */
618 COMMAND_HANDLER(handle_telnet_port_command)
620 return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
623 COMMAND_HANDLER(handle_exit_command)
625 return ERROR_COMMAND_CLOSE_CONNECTION;
628 static const struct command_registration telnet_command_handlers[] = {
631 .handler = handle_exit_command,
632 .mode = COMMAND_EXEC,
634 .help = "exit telnet session",
637 .name = "telnet_port",
638 .handler = handle_telnet_port_command,
640 .help = "Specify port on which to listen "
641 "for incoming telnet connections. "
642 "Read help on 'gdb_port'.",
643 .usage = "[port_num]",
645 COMMAND_REGISTRATION_DONE
648 int telnet_register_commands(struct command_context *cmd_ctx)
650 telnet_port = strdup("4444");
651 return register_commands(cmd_ctx, NULL, telnet_command_handlers);