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
;
107 /* If the prompt is not visible, simply output the message. */
108 if (!t_con
->prompt_visible
) {
109 telnet_outputline(connection
, string
);
113 /* Clear the command line. */
114 tmp
= strlen(t_con
->prompt
) + t_con
->line_size
;
116 for (i
= 0; i
< tmp
; i
+= 16)
117 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
120 for (i
= 0; i
< tmp
; i
+= 16)
121 telnet_write(connection
, " ", MIN(tmp
- i
, 16));
123 for (i
= 0; i
< tmp
; i
+= 16)
124 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
127 telnet_outputline(connection
, string
);
129 /* Put the command line to its previous state. */
130 telnet_prompt(connection
);
131 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
133 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
134 telnet_write(connection
, "\b", 1);
137 static void telnet_load_history(struct telnet_connection
*t_con
)
140 char buffer
[TELNET_BUFFER_SIZE
];
143 char *history
= get_home_dir(TELNET_HISTORY
);
145 if (history
== NULL
) {
146 LOG_INFO("unable to get user home directory, telnet history will be disabled");
150 histfp
= fopen(history
, "rb");
154 while (fgets(buffer
, sizeof(buffer
), histfp
) != NULL
) {
156 char *p
= strchr(buffer
, '\n');
159 if (buffer
[0] && i
< TELNET_LINE_HISTORY_SIZE
)
160 t_con
->history
[i
++] = strdup(buffer
);
163 t_con
->next_history
= i
;
164 t_con
->next_history
%= TELNET_LINE_HISTORY_SIZE
;
165 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
166 t_con
->current_history
= t_con
->next_history
> 0 ? i
- 1 : 0;
173 static void telnet_save_history(struct telnet_connection
*t_con
)
179 char *history
= get_home_dir(TELNET_HISTORY
);
181 if (history
== NULL
) {
182 LOG_INFO("unable to get user home directory, telnet history will be disabled");
186 histfp
= fopen(history
, "wb");
190 num
= TELNET_LINE_HISTORY_SIZE
;
191 i
= t_con
->current_history
+ 1;
192 i
%= TELNET_LINE_HISTORY_SIZE
;
194 while (t_con
->history
[i
] == NULL
&& num
> 0) {
196 i
%= TELNET_LINE_HISTORY_SIZE
;
201 for (; num
> 0; num
--) {
202 fprintf(histfp
, "%s\n", t_con
->history
[i
]);
204 i
%= TELNET_LINE_HISTORY_SIZE
;
213 static int telnet_new_connection(struct connection
*connection
)
215 struct telnet_connection
*telnet_connection
;
216 struct telnet_service
*telnet_service
= connection
->service
->priv
;
219 telnet_connection
= malloc(sizeof(struct telnet_connection
));
221 if (!telnet_connection
) {
222 LOG_ERROR("Failed to allocate telnet connection.");
226 connection
->priv
= telnet_connection
;
228 /* initialize telnet connection information */
229 telnet_connection
->closed
= false;
230 telnet_connection
->line_size
= 0;
231 telnet_connection
->line_cursor
= 0;
232 telnet_connection
->prompt
= strdup("> ");
233 telnet_connection
->prompt_visible
= true;
234 telnet_connection
->state
= TELNET_STATE_DATA
;
236 /* output goes through telnet connection */
237 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
239 /* negotiate telnet options */
240 telnet_write(connection
, negotiate
, strlen(negotiate
));
242 /* print connection banner */
243 if (telnet_service
->banner
) {
244 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
245 telnet_write(connection
, "\r\n", 2);
248 /* the prompt is always placed at the line beginning */
249 telnet_write(connection
, "\r", 1);
250 telnet_prompt(connection
);
252 /* initialize history */
253 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
254 telnet_connection
->history
[i
] = NULL
;
255 telnet_connection
->next_history
= 0;
256 telnet_connection
->current_history
= 0;
257 telnet_load_history(telnet_connection
);
259 log_add_callback(telnet_log_callback
, connection
);
264 static void telnet_clear_line(struct connection
*connection
,
265 struct telnet_connection
*t_con
)
267 /* move to end of line */
268 if (t_con
->line_cursor
< t_con
->line_size
)
269 telnet_write(connection
,
270 t_con
->line
+ t_con
->line_cursor
,
271 t_con
->line_size
- t_con
->line_cursor
);
273 /* backspace, overwrite with space, backspace */
274 while (t_con
->line_size
> 0) {
275 telnet_write(connection
, "\b \b", 3);
278 t_con
->line_cursor
= 0;
281 static void telnet_history_go(struct connection
*connection
, int idx
)
283 struct telnet_connection
*t_con
= connection
->priv
;
285 if (t_con
->history
[idx
]) {
286 telnet_clear_line(connection
, t_con
);
287 t_con
->line_size
= strlen(t_con
->history
[idx
]);
288 t_con
->line_cursor
= t_con
->line_size
;
289 memcpy(t_con
->line
, t_con
->history
[idx
], t_con
->line_size
);
290 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
291 t_con
->current_history
= idx
;
293 t_con
->state
= TELNET_STATE_DATA
;
296 static void telnet_history_up(struct connection
*connection
)
298 struct telnet_connection
*t_con
= connection
->priv
;
300 size_t last_history
= (t_con
->current_history
> 0) ?
301 t_con
->current_history
- 1 :
302 TELNET_LINE_HISTORY_SIZE
-1;
303 telnet_history_go(connection
, last_history
);
306 static void telnet_history_down(struct connection
*connection
)
308 struct telnet_connection
*t_con
= connection
->priv
;
311 next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
312 telnet_history_go(connection
, next_history
);
315 static void telnet_move_cursor(struct connection
*connection
, size_t pos
)
317 struct telnet_connection
*tc
;
320 tc
= connection
->priv
;
322 if (pos
< tc
->line_cursor
) {
323 tmp
= tc
->line_cursor
- pos
;
325 for (size_t i
= 0; i
< tmp
; i
+= 16)
326 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
329 tmp
= pos
- tc
->line_cursor
;
331 for (size_t i
= 0; i
< tmp
; i
+= 16)
332 telnet_write(connection
, tc
->line
+ tc
->line_cursor
+ i
,
336 tc
->line_cursor
= pos
;
339 static int telnet_input(struct connection
*connection
)
342 unsigned char buffer
[TELNET_BUFFER_SIZE
];
343 unsigned char *buf_p
;
344 struct telnet_connection
*t_con
= connection
->priv
;
345 struct command_context
*command_context
= connection
->cmd_ctx
;
347 bytes_read
= connection_read(connection
, buffer
, TELNET_BUFFER_SIZE
);
350 return ERROR_SERVER_REMOTE_CLOSED
;
351 else if (bytes_read
== -1) {
352 LOG_ERROR("error during read: %s", strerror(errno
));
353 return ERROR_SERVER_REMOTE_CLOSED
;
358 switch (t_con
->state
) {
359 case TELNET_STATE_DATA
:
361 t_con
->state
= TELNET_STATE_IAC
;
363 if (isprint(*buf_p
)) { /* printable character */
364 /* watch buffer size leaving one spare character for
365 * string null termination */
366 if (t_con
->line_size
== TELNET_LINE_MAX_SIZE
-1) {
367 /* output audible bell if buffer is full
368 * "\a" does not work, at least on windows */
369 telnet_write(connection
, "\x07", 1);
370 } else if (t_con
->line_cursor
== t_con
->line_size
) {
371 telnet_write(connection
, buf_p
, 1);
372 t_con
->line
[t_con
->line_size
++] = *buf_p
;
373 t_con
->line_cursor
++;
376 memmove(t_con
->line
+ t_con
->line_cursor
+ 1,
377 t_con
->line
+ t_con
->line_cursor
,
378 t_con
->line_size
- t_con
->line_cursor
);
379 t_con
->line
[t_con
->line_cursor
] = *buf_p
;
381 telnet_write(connection
,
382 t_con
->line
+ t_con
->line_cursor
,
383 t_con
->line_size
- t_con
->line_cursor
);
384 t_con
->line_cursor
++;
385 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
386 telnet_write(connection
, "\b", 1);
388 } else { /* non-printable */
389 if (*buf_p
== 0x1b) { /* escape */
390 t_con
->state
= TELNET_STATE_ESCAPE
;
391 t_con
->last_escape
= '\x00';
392 } else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) { /* CR/LF */
395 /* skip over combinations with CR/LF and NUL characters */
396 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) ||
397 (*(buf_p
+ 1) == 0xd))) {
401 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0)) {
405 t_con
->line
[t_con
->line_size
] = 0;
407 telnet_write(connection
, "\r\n\x00", 3);
409 if (strcmp(t_con
->line
, "history") == 0) {
411 for (i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
412 /* the t_con->next_history line contains empty string
413 * (unless NULL), thus it is not printed */
414 char *history_line
= t_con
->history
[(t_con
->
416 TELNET_LINE_HISTORY_SIZE
];
418 telnet_write(connection
, history_line
,
419 strlen(history_line
));
420 telnet_write(connection
, "\r\n\x00", 3);
423 t_con
->line_size
= 0;
424 t_con
->line_cursor
= 0;
428 /* save only non-blank not repeating lines in the history */
429 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ?
430 t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
431 if (*t_con
->line
&& (prev_line
== NULL
||
432 strcmp(t_con
->line
, prev_line
))) {
433 /* if the history slot is already taken, free it */
434 if (t_con
->history
[t_con
->next_history
])
435 free(t_con
->history
[t_con
->next_history
]);
437 /* add line to history */
438 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
440 /* wrap history at TELNET_LINE_HISTORY_SIZE */
441 t_con
->next_history
= (t_con
->next_history
+ 1) %
442 TELNET_LINE_HISTORY_SIZE
;
444 /* current history line starts at the new entry */
445 t_con
->current_history
=
448 if (t_con
->history
[t_con
->current_history
])
449 free(t_con
->history
[t_con
->current_history
]);
450 t_con
->history
[t_con
->current_history
] = strdup("");
453 t_con
->line_size
= 0;
455 /* to suppress prompt in log callback during command execution */
456 t_con
->prompt_visible
= false;
458 if (strcmp(t_con
->line
, "shutdown") == 0)
459 telnet_save_history(t_con
);
461 retval
= command_run_line(command_context
, t_con
->line
);
463 t_con
->line_cursor
= 0;
464 t_con
->prompt_visible
= true;
466 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
467 return ERROR_SERVER_REMOTE_CLOSED
;
469 /* the prompt is always * placed at the line beginning */
470 telnet_write(connection
, "\r", 1);
472 retval
= telnet_prompt(connection
);
473 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
474 return ERROR_SERVER_REMOTE_CLOSED
;
476 } else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) { /* delete character */
477 if (t_con
->line_cursor
> 0) {
478 if (t_con
->line_cursor
!= t_con
->line_size
) {
480 telnet_write(connection
, "\b", 1);
481 t_con
->line_cursor
--;
483 memmove(t_con
->line
+ t_con
->line_cursor
,
484 t_con
->line
+ t_con
->line_cursor
+ 1,
488 telnet_write(connection
,
489 t_con
->line
+ t_con
->line_cursor
,
492 telnet_write(connection
, " \b", 2);
493 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
494 telnet_write(connection
, "\b", 1);
497 t_con
->line_cursor
--;
498 /* back space: move the 'printer' head one char
499 * back, overwrite with space, move back again */
500 telnet_write(connection
, "\b \b", 3);
503 } else if (*buf_p
== 0x15) /* clear line */
504 telnet_clear_line(connection
, t_con
);
505 else if (*buf_p
== CTRL('B')) { /* cursor left */
506 if (t_con
->line_cursor
> 0) {
507 telnet_write(connection
, "\b", 1);
508 t_con
->line_cursor
--;
510 t_con
->state
= TELNET_STATE_DATA
;
511 } else if (*buf_p
== CTRL('F')) { /* cursor right */
512 if (t_con
->line_cursor
< t_con
->line_size
)
513 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
514 t_con
->state
= TELNET_STATE_DATA
;
515 } else if (*buf_p
== CTRL('P')) /* cursor up */
516 telnet_history_up(connection
);
517 else if (*buf_p
== CTRL('N')) /* cursor down */
518 telnet_history_down(connection
);
519 else if (*buf_p
== CTRL('A'))
520 telnet_move_cursor(connection
, 0);
521 else if (*buf_p
== CTRL('E'))
522 telnet_move_cursor(connection
, t_con
->line_size
);
524 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
528 case TELNET_STATE_IAC
:
531 t_con
->state
= TELNET_STATE_DONT
;
534 t_con
->state
= TELNET_STATE_DO
;
537 t_con
->state
= TELNET_STATE_WONT
;
540 t_con
->state
= TELNET_STATE_WILL
;
544 case TELNET_STATE_SB
:
546 case TELNET_STATE_SE
:
548 case TELNET_STATE_WILL
:
549 case TELNET_STATE_WONT
:
550 case TELNET_STATE_DO
:
551 case TELNET_STATE_DONT
:
552 t_con
->state
= TELNET_STATE_DATA
;
554 case TELNET_STATE_ESCAPE
:
555 if (t_con
->last_escape
== '[') {
556 if (*buf_p
== 'D') { /* cursor left */
557 if (t_con
->line_cursor
> 0) {
558 telnet_write(connection
, "\b", 1);
559 t_con
->line_cursor
--;
561 t_con
->state
= TELNET_STATE_DATA
;
562 } else if (*buf_p
== 'C') { /* cursor right */
563 if (t_con
->line_cursor
< t_con
->line_size
)
564 telnet_write(connection
,
565 t_con
->line
+ t_con
->line_cursor
++, 1);
566 t_con
->state
= TELNET_STATE_DATA
;
567 } else if (*buf_p
== 'A') { /* cursor up */
568 telnet_history_up(connection
);
569 } else if (*buf_p
== 'B') { /* cursor down */
570 telnet_history_down(connection
);
571 } else if (*buf_p
== '3')
572 t_con
->last_escape
= *buf_p
;
574 t_con
->state
= TELNET_STATE_DATA
;
575 } else if (t_con
->last_escape
== '3') {
576 /* Remove character */
578 if (t_con
->line_cursor
< t_con
->line_size
) {
581 /* remove char from line buffer */
582 memmove(t_con
->line
+ t_con
->line_cursor
,
583 t_con
->line
+ t_con
->line_cursor
+ 1,
584 t_con
->line_size
- t_con
->line_cursor
);
586 /* print remainder of buffer */
587 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
,
588 t_con
->line_size
- t_con
->line_cursor
);
589 /* overwrite last char with whitespace */
590 telnet_write(connection
, " \b", 2);
592 /* move back to cursor position*/
593 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
594 telnet_write(connection
, "\b", 1);
597 t_con
->state
= TELNET_STATE_DATA
;
599 t_con
->state
= TELNET_STATE_DATA
;
600 } else if (t_con
->last_escape
== '\x00') {
602 t_con
->last_escape
= *buf_p
;
604 t_con
->state
= TELNET_STATE_DATA
;
606 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
607 t_con
->state
= TELNET_STATE_DATA
;
612 LOG_ERROR("unknown telnet state");
623 static int telnet_connection_closed(struct connection
*connection
)
625 struct telnet_connection
*t_con
= connection
->priv
;
628 log_remove_callback(telnet_log_callback
, connection
);
632 t_con
->prompt
= NULL
;
635 /* save telnet history */
636 telnet_save_history(t_con
);
638 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
639 if (t_con
->history
[i
]) {
640 free(t_con
->history
[i
]);
641 t_con
->history
[i
] = NULL
;
645 /* if this connection registered a debug-message receiver delete it */
646 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
648 if (connection
->priv
) {
649 free(connection
->priv
);
650 connection
->priv
= NULL
;
652 LOG_ERROR("BUG: connection->priv == NULL");
657 int telnet_init(char *banner
)
659 if (strcmp(telnet_port
, "disabled") == 0) {
660 LOG_INFO("telnet server disabled");
664 struct telnet_service
*telnet_service
=
665 malloc(sizeof(struct telnet_service
));
667 if (!telnet_service
) {
668 LOG_ERROR("Failed to allocate telnet service.");
672 telnet_service
->banner
= banner
;
674 int ret
= add_service("telnet", telnet_port
, CONNECTION_LIMIT_UNLIMITED
,
675 telnet_new_connection
, telnet_input
, telnet_connection_closed
,
678 if (ret
!= ERROR_OK
) {
679 free(telnet_service
);
686 /* daemon configuration command telnet_port */
687 COMMAND_HANDLER(handle_telnet_port_command
)
689 return CALL_COMMAND_HANDLER(server_pipe_command
, &telnet_port
);
692 COMMAND_HANDLER(handle_exit_command
)
694 return ERROR_COMMAND_CLOSE_CONNECTION
;
697 static const struct command_registration telnet_command_handlers
[] = {
700 .handler
= handle_exit_command
,
701 .mode
= COMMAND_EXEC
,
703 .help
= "exit telnet session",
706 .name
= "telnet_port",
707 .handler
= handle_telnet_port_command
,
709 .help
= "Specify port on which to listen "
710 "for incoming telnet connections. "
711 "Read help on 'gdb_port'.",
712 .usage
= "[port_num]",
714 COMMAND_REGISTRATION_DONE
717 int telnet_register_commands(struct command_context
*cmd_ctx
)
719 telnet_port
= strdup("4444");
720 return register_commands(cmd_ctx
, NULL
, telnet_command_handlers
);
723 void telnet_service_free(void)
Linking to existing account procedure
If you already have an account and want to add another login method
you
MUST first sign in with your existing account and
then change URL to read
https://review.openocd.org/login/?link
to get to this page again but this time it'll work for linking. Thank you.
SSH host keys fingerprints
1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=.. |
|+o.. . |
|*.o . . |
|+B . . . |
|Bo. = o S |
|Oo.+ + = |
|oB=.* = . o |
| =+=.+ + E |
|. .=o . o |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)