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, see <http://www.gnu.org/licenses/>. *
23 ***************************************************************************/
29 #include "telnet_server.h"
30 #include <target/target_request.h>
31 #include <helper/configuration.h>
33 static char *telnet_port
;
35 static char *negotiate
=
36 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
37 "\xFF\xFB\x01" /* IAC WILL Echo */
38 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
39 "\xFF\xFE\x01"; /* IAC DON'T Echo */
41 #define CTRL(c) (c - '@')
42 #define TELNET_HISTORY ".openocd_history"
44 /* The only way we can detect that the socket is closed is the first time
45 * we write to it, we will fail. Subsequent write operations will
48 static int telnet_write(struct connection
*connection
, const void *data
,
51 struct telnet_connection
*t_con
= connection
->priv
;
53 return ERROR_SERVER_REMOTE_CLOSED
;
55 if (connection_write(connection
, data
, len
) == len
)
58 return ERROR_SERVER_REMOTE_CLOSED
;
61 static int telnet_prompt(struct connection
*connection
)
63 struct telnet_connection
*t_con
= connection
->priv
;
65 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
68 static int telnet_outputline(struct connection
*connection
, const char *line
)
72 /* process lines in buffer */
74 char *line_end
= strchr(line
, '\n');
81 telnet_write(connection
, line
, len
);
83 telnet_write(connection
, "\r\n", 2);
92 static int telnet_output(struct command_context
*cmd_ctx
, const char *line
)
94 struct connection
*connection
= cmd_ctx
->output_handler_priv
;
96 return telnet_outputline(connection
, line
);
99 static void telnet_log_callback(void *priv
, const char *file
, unsigned line
,
100 const char *function
, const char *string
)
102 struct connection
*connection
= priv
;
103 struct telnet_connection
*t_con
= connection
->priv
;
106 /* if there is no prompt, simply output the message */
107 if (t_con
->line_cursor
< 0) {
108 telnet_outputline(connection
, string
);
112 /* clear the command line */
113 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
114 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i
> 16 ? 16 : i
);
115 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
116 telnet_write(connection
, " ", i
> 16 ? 16 : i
);
117 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
118 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i
> 16 ? 16 : i
);
120 /* output the message */
121 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);
130 static void telnet_load_history(struct telnet_connection
*t_con
)
133 char buffer
[TELNET_BUFFER_SIZE
];
136 char *history
= get_home_dir(TELNET_HISTORY
);
138 if (history
== NULL
) {
139 LOG_INFO("unable to get user home directory, telnet history will be disabled");
143 histfp
= fopen(history
, "rb");
147 while (fgets(buffer
, sizeof(buffer
), histfp
) != NULL
) {
149 char *p
= strchr(buffer
, '\n');
152 if (buffer
[0] && i
< TELNET_LINE_HISTORY_SIZE
)
153 t_con
->history
[i
++] = strdup(buffer
);
156 t_con
->next_history
= i
;
157 t_con
->next_history
%= TELNET_LINE_HISTORY_SIZE
;
158 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
159 t_con
->current_history
= t_con
->next_history
> 0 ? i
- 1 : 0;
166 static void telnet_save_history(struct telnet_connection
*t_con
)
172 char *history
= get_home_dir(TELNET_HISTORY
);
174 if (history
== NULL
) {
175 LOG_INFO("unable to get user home directory, telnet history will be disabled");
179 histfp
= fopen(history
, "wb");
183 num
= TELNET_LINE_HISTORY_SIZE
;
184 i
= t_con
->current_history
+ 1;
185 i
%= TELNET_LINE_HISTORY_SIZE
;
187 while (t_con
->history
[i
] == NULL
&& num
> 0) {
189 i
%= TELNET_LINE_HISTORY_SIZE
;
194 for (; num
> 0; num
--) {
195 fprintf(histfp
, "%s\n", t_con
->history
[i
]);
197 i
%= TELNET_LINE_HISTORY_SIZE
;
206 static int telnet_new_connection(struct connection
*connection
)
208 struct telnet_connection
*telnet_connection
;
209 struct telnet_service
*telnet_service
= connection
->service
->priv
;
212 telnet_connection
= malloc(sizeof(struct telnet_connection
));
214 if (!telnet_connection
) {
215 LOG_ERROR("Failed to allocate telnet connection.");
219 connection
->priv
= telnet_connection
;
221 /* initialize telnet connection information */
222 telnet_connection
->closed
= 0;
223 telnet_connection
->line_size
= 0;
224 telnet_connection
->line_cursor
= 0;
225 telnet_connection
->option_size
= 0;
226 telnet_connection
->prompt
= strdup("> ");
227 telnet_connection
->state
= TELNET_STATE_DATA
;
229 /* output goes through telnet connection */
230 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
232 /* negotiate telnet options */
233 telnet_write(connection
, negotiate
, strlen(negotiate
));
235 /* print connection banner */
236 if (telnet_service
->banner
) {
237 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
238 telnet_write(connection
, "\r\n", 2);
241 /* the prompt is always placed at the line beginning */
242 telnet_write(connection
, "\r", 1);
243 telnet_prompt(connection
);
245 /* initialize history */
246 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
247 telnet_connection
->history
[i
] = NULL
;
248 telnet_connection
->next_history
= 0;
249 telnet_connection
->current_history
= 0;
250 telnet_load_history(telnet_connection
);
252 log_add_callback(telnet_log_callback
, connection
);
257 static void telnet_clear_line(struct connection
*connection
,
258 struct telnet_connection
*t_con
)
260 /* move to end of line */
261 if (t_con
->line_cursor
< t_con
->line_size
)
262 telnet_write(connection
,
263 t_con
->line
+ t_con
->line_cursor
,
264 t_con
->line_size
- t_con
->line_cursor
);
266 /* backspace, overwrite with space, backspace */
267 while (t_con
->line_size
> 0) {
268 telnet_write(connection
, "\b \b", 3);
271 t_con
->line_cursor
= 0;
274 static void telnet_history_go(struct connection
*connection
, int idx
)
276 struct telnet_connection
*t_con
= connection
->priv
;
278 if (t_con
->history
[idx
]) {
279 telnet_clear_line(connection
, t_con
);
280 t_con
->line_size
= strlen(t_con
->history
[idx
]);
281 t_con
->line_cursor
= t_con
->line_size
;
282 memcpy(t_con
->line
, t_con
->history
[idx
], t_con
->line_size
);
283 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
284 t_con
->current_history
= idx
;
286 t_con
->state
= TELNET_STATE_DATA
;
289 static void telnet_history_up(struct connection
*connection
)
291 struct telnet_connection
*t_con
= connection
->priv
;
293 int last_history
= (t_con
->current_history
> 0) ?
294 t_con
->current_history
- 1 :
295 TELNET_LINE_HISTORY_SIZE
-1;
296 telnet_history_go(connection
, last_history
);
299 static void telnet_history_down(struct connection
*connection
)
301 struct telnet_connection
*t_con
= connection
->priv
;
303 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
304 telnet_history_go(connection
, next_history
);
307 static int telnet_input(struct connection
*connection
)
310 unsigned char buffer
[TELNET_BUFFER_SIZE
];
311 unsigned char *buf_p
;
312 struct telnet_connection
*t_con
= connection
->priv
;
313 struct command_context
*command_context
= connection
->cmd_ctx
;
315 bytes_read
= connection_read(connection
, buffer
, TELNET_BUFFER_SIZE
);
318 return ERROR_SERVER_REMOTE_CLOSED
;
319 else if (bytes_read
== -1) {
320 LOG_ERROR("error during read: %s", strerror(errno
));
321 return ERROR_SERVER_REMOTE_CLOSED
;
326 switch (t_con
->state
) {
327 case TELNET_STATE_DATA
:
329 t_con
->state
= TELNET_STATE_IAC
;
331 if (isprint(*buf_p
)) { /* printable character */
332 /* watch buffer size leaving one spare character for
333 * string null termination */
334 if (t_con
->line_size
== TELNET_LINE_MAX_SIZE
-1) {
335 /* output audible bell if buffer is full
336 * "\a" does not work, at least on windows */
337 telnet_write(connection
, "\x07", 1);
338 } else if (t_con
->line_cursor
== t_con
->line_size
) {
339 telnet_write(connection
, buf_p
, 1);
340 t_con
->line
[t_con
->line_size
++] = *buf_p
;
341 t_con
->line_cursor
++;
344 memmove(t_con
->line
+ t_con
->line_cursor
+ 1,
345 t_con
->line
+ t_con
->line_cursor
,
346 t_con
->line_size
- t_con
->line_cursor
);
347 t_con
->line
[t_con
->line_cursor
] = *buf_p
;
349 telnet_write(connection
,
350 t_con
->line
+ t_con
->line_cursor
,
351 t_con
->line_size
- t_con
->line_cursor
);
352 t_con
->line_cursor
++;
353 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
354 telnet_write(connection
, "\b", 1);
356 } else { /* non-printable */
357 if (*buf_p
== 0x1b) { /* escape */
358 t_con
->state
= TELNET_STATE_ESCAPE
;
359 t_con
->last_escape
= '\x00';
360 } else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) { /* CR/LF */
363 /* skip over combinations with CR/LF and NUL characters */
364 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) ||
365 (*(buf_p
+ 1) == 0xd))) {
369 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0)) {
373 t_con
->line
[t_con
->line_size
] = 0;
375 telnet_write(connection
, "\r\n\x00", 3);
377 if (strcmp(t_con
->line
, "history") == 0) {
379 for (i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
380 /* the t_con->next_history line contains empty string
381 * (unless NULL), thus it is not printed */
382 char *history_line
= t_con
->history
[(t_con
->
384 TELNET_LINE_HISTORY_SIZE
];
386 telnet_write(connection
, history_line
,
387 strlen(history_line
));
388 telnet_write(connection
, "\r\n\x00", 3);
391 t_con
->line_size
= 0;
392 t_con
->line_cursor
= 0;
396 /* save only non-blank not repeating lines in the history */
397 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ?
398 t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
399 if (*t_con
->line
&& (prev_line
== NULL
||
400 strcmp(t_con
->line
, prev_line
))) {
401 /* if the history slot is already taken, free it */
402 if (t_con
->history
[t_con
->next_history
])
403 free(t_con
->history
[t_con
->next_history
]);
405 /* add line to history */
406 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
408 /* wrap history at TELNET_LINE_HISTORY_SIZE */
409 t_con
->next_history
= (t_con
->next_history
+ 1) %
410 TELNET_LINE_HISTORY_SIZE
;
412 /* current history line starts at the new entry */
413 t_con
->current_history
=
416 if (t_con
->history
[t_con
->current_history
])
417 free(t_con
->history
[t_con
->current_history
]);
418 t_con
->history
[t_con
->current_history
] = strdup("");
421 t_con
->line_size
= 0;
423 /* to suppress prompt in log callback during command execution */
424 t_con
->line_cursor
= -1;
426 if (strcmp(t_con
->line
, "shutdown") == 0)
427 telnet_save_history(t_con
);
429 retval
= command_run_line(command_context
, t_con
->line
);
431 t_con
->line_cursor
= 0;
433 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
434 return ERROR_SERVER_REMOTE_CLOSED
;
436 /* the prompt is always * placed at the line beginning */
437 telnet_write(connection
, "\r", 1);
439 retval
= telnet_prompt(connection
);
440 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
441 return ERROR_SERVER_REMOTE_CLOSED
;
443 } else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) { /* delete character */
444 if (t_con
->line_cursor
> 0) {
445 if (t_con
->line_cursor
!= t_con
->line_size
) {
447 telnet_write(connection
, "\b", 1);
448 t_con
->line_cursor
--;
450 memmove(t_con
->line
+ t_con
->line_cursor
,
451 t_con
->line
+ t_con
->line_cursor
+ 1,
455 telnet_write(connection
,
456 t_con
->line
+ t_con
->line_cursor
,
459 telnet_write(connection
, " \b", 2);
460 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
461 telnet_write(connection
, "\b", 1);
464 t_con
->line_cursor
--;
465 /* back space: move the 'printer' head one char
466 * back, overwrite with space, move back again */
467 telnet_write(connection
, "\b \b", 3);
470 } else if (*buf_p
== 0x15) /* clear line */
471 telnet_clear_line(connection
, t_con
);
472 else if (*buf_p
== CTRL('B')) { /* cursor left */
473 if (t_con
->line_cursor
> 0) {
474 telnet_write(connection
, "\b", 1);
475 t_con
->line_cursor
--;
477 t_con
->state
= TELNET_STATE_DATA
;
478 } else if (*buf_p
== CTRL('F')) { /* cursor right */
479 if (t_con
->line_cursor
< t_con
->line_size
)
480 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
481 t_con
->state
= TELNET_STATE_DATA
;
482 } else if (*buf_p
== CTRL('P')) /* cursor up */
483 telnet_history_up(connection
);
484 else if (*buf_p
== CTRL('N')) /* cursor down */
485 telnet_history_down(connection
);
487 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
491 case TELNET_STATE_IAC
:
494 t_con
->state
= TELNET_STATE_DONT
;
497 t_con
->state
= TELNET_STATE_DO
;
500 t_con
->state
= TELNET_STATE_WONT
;
503 t_con
->state
= TELNET_STATE_WILL
;
507 case TELNET_STATE_SB
:
509 case TELNET_STATE_SE
:
511 case TELNET_STATE_WILL
:
512 case TELNET_STATE_WONT
:
513 case TELNET_STATE_DO
:
514 case TELNET_STATE_DONT
:
515 t_con
->state
= TELNET_STATE_DATA
;
517 case TELNET_STATE_ESCAPE
:
518 if (t_con
->last_escape
== '[') {
519 if (*buf_p
== 'D') { /* cursor left */
520 if (t_con
->line_cursor
> 0) {
521 telnet_write(connection
, "\b", 1);
522 t_con
->line_cursor
--;
524 t_con
->state
= TELNET_STATE_DATA
;
525 } else if (*buf_p
== 'C') { /* cursor right */
526 if (t_con
->line_cursor
< t_con
->line_size
)
527 telnet_write(connection
,
528 t_con
->line
+ t_con
->line_cursor
++, 1);
529 t_con
->state
= TELNET_STATE_DATA
;
530 } else if (*buf_p
== 'A') { /* cursor up */
531 telnet_history_up(connection
);
532 } else if (*buf_p
== 'B') { /* cursor down */
533 telnet_history_down(connection
);
534 } else if (*buf_p
== '3')
535 t_con
->last_escape
= *buf_p
;
537 t_con
->state
= TELNET_STATE_DATA
;
538 } else if (t_con
->last_escape
== '3') {
539 /* Remove character */
541 if (t_con
->line_cursor
< t_con
->line_size
) {
544 /* remove char from line buffer */
545 memmove(t_con
->line
+ t_con
->line_cursor
,
546 t_con
->line
+ t_con
->line_cursor
+ 1,
547 t_con
->line_size
- t_con
->line_cursor
);
549 /* print remainder of buffer */
550 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
,
551 t_con
->line_size
- t_con
->line_cursor
);
552 /* overwrite last char with whitespace */
553 telnet_write(connection
, " \b", 2);
555 /* move back to cursor position*/
556 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
557 telnet_write(connection
, "\b", 1);
560 t_con
->state
= TELNET_STATE_DATA
;
562 t_con
->state
= TELNET_STATE_DATA
;
563 } else if (t_con
->last_escape
== '\x00') {
565 t_con
->last_escape
= *buf_p
;
567 t_con
->state
= TELNET_STATE_DATA
;
569 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
570 t_con
->state
= TELNET_STATE_DATA
;
575 LOG_ERROR("unknown telnet state");
586 static int telnet_connection_closed(struct connection
*connection
)
588 struct telnet_connection
*t_con
= connection
->priv
;
591 log_remove_callback(telnet_log_callback
, connection
);
595 t_con
->prompt
= NULL
;
598 /* save telnet history */
599 telnet_save_history(t_con
);
601 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
602 if (t_con
->history
[i
]) {
603 free(t_con
->history
[i
]);
604 t_con
->history
[i
] = NULL
;
608 /* if this connection registered a debug-message receiver delete it */
609 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
611 if (connection
->priv
) {
612 free(connection
->priv
);
613 connection
->priv
= NULL
;
615 LOG_ERROR("BUG: connection->priv == NULL");
620 int telnet_init(char *banner
)
622 if (strcmp(telnet_port
, "disabled") == 0) {
623 LOG_INFO("telnet server disabled");
627 struct telnet_service
*telnet_service
=
628 malloc(sizeof(struct telnet_service
));
630 if (!telnet_service
) {
631 LOG_ERROR("Failed to allocate telnet service.");
635 telnet_service
->banner
= banner
;
637 int ret
= add_service("telnet", telnet_port
, CONNECTION_LIMIT_UNLIMITED
,
638 telnet_new_connection
, telnet_input
, telnet_connection_closed
,
641 if (ret
!= ERROR_OK
) {
642 free(telnet_service
);
649 /* daemon configuration command telnet_port */
650 COMMAND_HANDLER(handle_telnet_port_command
)
652 return CALL_COMMAND_HANDLER(server_pipe_command
, &telnet_port
);
655 COMMAND_HANDLER(handle_exit_command
)
657 return ERROR_COMMAND_CLOSE_CONNECTION
;
660 static const struct command_registration telnet_command_handlers
[] = {
663 .handler
= handle_exit_command
,
664 .mode
= COMMAND_EXEC
,
666 .help
= "exit telnet session",
669 .name
= "telnet_port",
670 .handler
= handle_telnet_port_command
,
672 .help
= "Specify port on which to listen "
673 "for incoming telnet connections. "
674 "Read help on 'gdb_port'.",
675 .usage
= "[port_num]",
677 COMMAND_REGISTRATION_DONE
680 int telnet_register_commands(struct command_context
*cmd_ctx
)
682 telnet_port
= strdup("4444");
683 return register_commands(cmd_ctx
, NULL
, telnet_command_handlers
);