1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2005 by Dominic Rath *
5 * Dominic.Rath@gmx.de *
7 * Copyright (C) 2007-2010 Øyvind Harboe *
8 * oyvind.harboe@zylin.com *
10 * Copyright (C) 2008 by Spencer Oliver *
11 * spen@spen-soft.co.uk *
12 ***************************************************************************/
18 #include "telnet_server.h"
19 #include <target/target_request.h>
20 #include <helper/configuration.h>
21 #include <helper/list.h>
23 static char *telnet_port
;
25 static char *negotiate
=
26 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
27 "\xFF\xFB\x01" /* IAC WILL Echo */
28 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
29 "\xFF\xFE\x01"; /* IAC DON'T Echo */
31 #define CTRL(c) (c - '@')
32 #define TELNET_HISTORY ".openocd_history"
34 /* The only way we can detect that the socket is closed is the first time
35 * we write to it, we will fail. Subsequent write operations will
38 static int telnet_write(struct connection
*connection
, const void *data
,
41 struct telnet_connection
*t_con
= connection
->priv
;
43 return ERROR_SERVER_REMOTE_CLOSED
;
45 if (connection_write(connection
, data
, len
) == len
)
48 return ERROR_SERVER_REMOTE_CLOSED
;
51 /* output an audible bell */
52 static int telnet_bell(struct connection
*connection
)
54 /* ("\a" does not work, at least on windows) */
55 return telnet_write(connection
, "\x07", 1);
58 static int telnet_prompt(struct connection
*connection
)
60 struct telnet_connection
*t_con
= connection
->priv
;
62 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
65 static int telnet_outputline(struct connection
*connection
, const char *line
)
69 /* process lines in buffer */
71 char *line_end
= strchr(line
, '\n');
78 telnet_write(connection
, line
, len
);
80 telnet_write(connection
, "\r\n", 2);
89 static int telnet_output(struct command_context
*cmd_ctx
, const char *line
)
91 struct connection
*connection
= cmd_ctx
->output_handler_priv
;
93 return telnet_outputline(connection
, line
);
96 static void telnet_log_callback(void *priv
, const char *file
, unsigned line
,
97 const char *function
, const char *string
)
99 struct connection
*connection
= priv
;
100 struct telnet_connection
*t_con
= connection
->priv
;
104 /* If the prompt is not visible, simply output the message. */
105 if (!t_con
->prompt_visible
) {
106 telnet_outputline(connection
, string
);
110 /* Clear the command line. */
111 tmp
= strlen(t_con
->prompt
) + t_con
->line_size
;
113 for (i
= 0; i
< tmp
; i
+= 16)
114 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
117 for (i
= 0; i
< tmp
; i
+= 16)
118 telnet_write(connection
, " ", MIN(tmp
- i
, 16));
120 for (i
= 0; i
< tmp
; i
+= 16)
121 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
124 telnet_outputline(connection
, string
);
126 /* Put the command line to its previous state. */
127 telnet_prompt(connection
);
128 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
130 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
131 telnet_write(connection
, "\b", 1);
134 static void telnet_load_history(struct telnet_connection
*t_con
)
137 char buffer
[TELNET_BUFFER_SIZE
];
140 char *history
= get_home_dir(TELNET_HISTORY
);
143 LOG_INFO("unable to get user home directory, telnet history will be disabled");
147 histfp
= fopen(history
, "rb");
151 while (fgets(buffer
, sizeof(buffer
), histfp
)) {
153 char *p
= strchr(buffer
, '\n');
156 if (buffer
[0] && i
< TELNET_LINE_HISTORY_SIZE
)
157 t_con
->history
[i
++] = strdup(buffer
);
160 t_con
->next_history
= i
;
161 t_con
->next_history
%= TELNET_LINE_HISTORY_SIZE
;
162 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
163 t_con
->current_history
= t_con
->next_history
> 0 ? i
- 1 : 0;
170 static void telnet_save_history(struct telnet_connection
*t_con
)
176 char *history
= get_home_dir(TELNET_HISTORY
);
179 LOG_INFO("unable to get user home directory, telnet history will be disabled");
183 histfp
= fopen(history
, "wb");
187 num
= TELNET_LINE_HISTORY_SIZE
;
188 i
= t_con
->current_history
+ 1;
189 i
%= TELNET_LINE_HISTORY_SIZE
;
191 while (!t_con
->history
[i
] && num
> 0) {
193 i
%= TELNET_LINE_HISTORY_SIZE
;
198 for (; num
> 0; num
--) {
199 fprintf(histfp
, "%s\n", t_con
->history
[i
]);
201 i
%= TELNET_LINE_HISTORY_SIZE
;
210 static int telnet_new_connection(struct connection
*connection
)
212 struct telnet_connection
*telnet_connection
;
213 struct telnet_service
*telnet_service
= connection
->service
->priv
;
215 telnet_connection
= calloc(1, sizeof(struct telnet_connection
));
217 if (!telnet_connection
) {
218 LOG_ERROR("Failed to allocate telnet connection.");
222 connection
->priv
= telnet_connection
;
224 /* initialize telnet connection information */
225 telnet_connection
->prompt
= strdup("> ");
226 telnet_connection
->prompt_visible
= true;
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 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 void telnet_history_go(struct connection
*connection
, int idx
)
271 struct telnet_connection
*t_con
= connection
->priv
;
273 if (t_con
->history
[idx
]) {
274 telnet_clear_line(connection
, t_con
);
275 t_con
->line_size
= strlen(t_con
->history
[idx
]);
276 t_con
->line_cursor
= t_con
->line_size
;
277 memcpy(t_con
->line
, t_con
->history
[idx
], t_con
->line_size
);
278 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
279 t_con
->current_history
= idx
;
281 t_con
->state
= TELNET_STATE_DATA
;
284 static void telnet_history_up(struct connection
*connection
)
286 struct telnet_connection
*t_con
= connection
->priv
;
288 size_t last_history
= (t_con
->current_history
> 0) ?
289 t_con
->current_history
- 1 :
290 TELNET_LINE_HISTORY_SIZE
-1;
291 telnet_history_go(connection
, last_history
);
294 static void telnet_history_down(struct connection
*connection
)
296 struct telnet_connection
*t_con
= connection
->priv
;
299 next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
300 telnet_history_go(connection
, next_history
);
303 static void telnet_history_add(struct connection
*connection
)
305 struct telnet_connection
*t_con
= connection
->priv
;
307 /* save only non-blank not repeating lines in the history */
308 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ?
309 t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
311 if (*t_con
->line
&& (!prev_line
|| strcmp(t_con
->line
, prev_line
))) {
312 /* if the history slot is already taken, free it */
313 free(t_con
->history
[t_con
->next_history
]);
315 /* add line to history */
316 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
318 /* wrap history at TELNET_LINE_HISTORY_SIZE */
319 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
321 /* current history line starts at the new entry */
322 t_con
->current_history
= t_con
->next_history
;
324 free(t_con
->history
[t_con
->current_history
]);
325 t_con
->history
[t_con
->current_history
] = strdup("");
329 static int telnet_history_print(struct connection
*connection
)
331 struct telnet_connection
*tc
;
333 tc
= connection
->priv
;
335 for (size_t i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
339 * The tc->next_history line contains empty string (unless NULL), thus
342 line
= tc
->history
[(tc
->next_history
+ i
) % TELNET_LINE_HISTORY_SIZE
];
345 telnet_write(connection
, line
, strlen(line
));
346 telnet_write(connection
, "\r\n\x00", 3);
353 /* The prompt is always placed at the line beginning. */
354 telnet_write(connection
, "\r", 1);
356 return telnet_prompt(connection
);
359 static void telnet_move_cursor(struct connection
*connection
, size_t pos
)
361 struct telnet_connection
*tc
= connection
->priv
;
364 if (pos
== tc
->line_cursor
) /* nothing to do */
367 if (pos
> tc
->line_size
) /* out of bounds */
370 if (pos
< tc
->line_cursor
) {
371 tmp
= tc
->line_cursor
- pos
;
373 for (size_t i
= 0; i
< tmp
; i
+= 16)
374 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
377 tmp
= pos
- tc
->line_cursor
;
379 for (size_t i
= 0; i
< tmp
; i
+= 16)
380 telnet_write(connection
, tc
->line
+ tc
->line_cursor
+ i
,
384 tc
->line_cursor
= pos
;
387 /* check buffer size leaving one spare character for string null termination */
388 static inline bool telnet_can_insert(struct connection
*connection
, size_t len
)
390 struct telnet_connection
*t_con
= connection
->priv
;
392 return t_con
->line_size
+ len
< TELNET_LINE_MAX_SIZE
;
395 /* write to telnet console, and update the telnet_connection members
396 * this function is capable of inserting in the middle of a line
397 * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
399 * returns false when it fails to insert the requested data
401 static bool telnet_insert(struct connection
*connection
, const void *data
, size_t len
)
403 struct telnet_connection
*t_con
= connection
->priv
;
405 if (!telnet_can_insert(connection
, len
)) {
406 telnet_bell(connection
);
410 if (t_con
->line_cursor
< t_con
->line_size
) {
411 /* we have some content after the cursor */
412 memmove(t_con
->line
+ t_con
->line_cursor
+ len
,
413 t_con
->line
+ t_con
->line_cursor
,
414 t_con
->line_size
- t_con
->line_cursor
);
417 strncpy(t_con
->line
+ t_con
->line_cursor
, data
, len
);
419 telnet_write(connection
,
420 t_con
->line
+ t_con
->line_cursor
,
421 t_con
->line_size
+ len
- t_con
->line_cursor
);
423 t_con
->line_size
+= len
;
424 t_con
->line_cursor
+= len
;
426 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
427 telnet_write(connection
, "\b", 1);
432 static void telnet_delete_character(struct connection
*connection
)
434 struct telnet_connection
*t_con
= connection
->priv
;
436 if (t_con
->line_cursor
== 0)
439 if (t_con
->line_cursor
!= t_con
->line_size
) {
441 telnet_write(connection
, "\b", 1);
442 t_con
->line_cursor
--;
444 memmove(t_con
->line
+ t_con
->line_cursor
,
445 t_con
->line
+ t_con
->line_cursor
+ 1,
449 telnet_write(connection
,
450 t_con
->line
+ t_con
->line_cursor
,
453 telnet_write(connection
, " \b", 2);
454 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
455 telnet_write(connection
, "\b", 1);
458 t_con
->line_cursor
--;
459 /* back space: move the 'printer' head one char
460 * back, overwrite with space, move back again */
461 telnet_write(connection
, "\b \b", 3);
465 static void telnet_remove_character(struct connection
*connection
)
467 struct telnet_connection
*t_con
= connection
->priv
;
469 if (t_con
->line_cursor
< t_con
->line_size
) {
472 /* remove char from line buffer */
473 memmove(t_con
->line
+ t_con
->line_cursor
,
474 t_con
->line
+ t_con
->line_cursor
+ 1,
475 t_con
->line_size
- t_con
->line_cursor
);
477 /* print remainder of buffer */
478 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
,
479 t_con
->line_size
- t_con
->line_cursor
);
480 /* overwrite last char with whitespace */
481 telnet_write(connection
, " \b", 2);
483 /* move back to cursor position*/
484 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
485 telnet_write(connection
, "\b", 1);
489 static int telnet_exec_line(struct connection
*connection
)
491 struct telnet_connection
*t_con
= connection
->priv
;
492 struct command_context
*command_context
= connection
->cmd_ctx
;
495 telnet_write(connection
, "\r\n\x00", 3);
497 if (strcmp(t_con
->line
, "history") == 0) {
498 retval
= telnet_history_print(connection
);
500 if (retval
!= ERROR_OK
)
506 telnet_history_add(connection
);
508 t_con
->line_size
= 0;
510 /* to suppress prompt in log callback during command execution */
511 t_con
->prompt_visible
= false;
513 if (strcmp(t_con
->line
, "shutdown") == 0)
514 telnet_save_history(t_con
);
516 retval
= command_run_line(command_context
, t_con
->line
);
518 t_con
->line_cursor
= 0;
519 t_con
->prompt_visible
= true;
521 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
522 return ERROR_SERVER_REMOTE_CLOSED
;
524 /* the prompt is always placed at the line beginning */
525 telnet_write(connection
, "\r", 1);
527 retval
= telnet_prompt(connection
);
528 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
529 return ERROR_SERVER_REMOTE_CLOSED
;
534 static void telnet_cut_line_to_end(struct connection
*connection
)
536 struct telnet_connection
*t_con
= connection
->priv
;
538 /* FIXME: currently this function does not save to clipboard */
540 if (t_con
->line_cursor
< t_con
->line_size
) {
541 /* overwrite with space, until end of line, move back */
542 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
543 telnet_write(connection
, " ", 1);
544 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
545 telnet_write(connection
, "\b", 1);
546 t_con
->line
[t_con
->line_cursor
] = '\0';
547 t_con
->line_size
= t_con
->line_cursor
;
551 static void telnet_interrupt(struct connection
*connection
)
553 struct telnet_connection
*t_con
= connection
->priv
;
555 /* print '^C' at line end, and display a new command prompt */
556 telnet_move_cursor(connection
, t_con
->line_size
);
557 telnet_write(connection
, "^C\n\r", 4);
558 t_con
->line_cursor
= 0;
559 t_con
->line_size
= 0;
560 telnet_prompt(connection
);
563 static void telnet_auto_complete(struct connection
*connection
)
565 struct telnet_connection
*t_con
= connection
->priv
;
566 struct command_context
*command_context
= connection
->cmd_ctx
;
575 /* - user command sequence, either at line beginning
576 * or we start over after these characters ';', '[', '{'
577 * - user variable sequence, start after the character '$'
578 * and do not contain white spaces */
579 bool is_variable_auto_completion
= false;
580 bool have_spaces
= false;
581 size_t seq_start
= (t_con
->line_cursor
== 0) ? 0 : (t_con
->line_cursor
- 1);
583 char c
= t_con
->line
[seq_start
];
585 if (c
== ';' || c
== '[' || c
== '{') {
588 } else if (c
== ' ') {
590 } else if (c
== '$' && !have_spaces
) {
591 is_variable_auto_completion
= true;
602 /* user command position in the line, ignore leading spaces */
603 size_t usr_cmd_pos
= seq_start
;
604 while ((usr_cmd_pos
< t_con
->line_cursor
) && isspace(t_con
->line
[usr_cmd_pos
]))
607 /* check user command length */
608 if (t_con
->line_cursor
< usr_cmd_pos
) {
609 telnet_bell(connection
);
612 size_t usr_cmd_len
= t_con
->line_cursor
- usr_cmd_pos
;
614 /* optimize multiple spaces in the user command,
615 * because info commands does not tolerate multiple spaces */
616 size_t optimized_spaces
= 0;
617 char query
[usr_cmd_len
+ 1];
618 for (size_t i
= 0; i
< usr_cmd_len
; i
++) {
619 if ((i
< usr_cmd_len
- 1) && isspace(t_con
->line
[usr_cmd_pos
+ i
])
620 && isspace(t_con
->line
[usr_cmd_pos
+ i
+ 1])) {
625 query
[i
- optimized_spaces
] = t_con
->line
[usr_cmd_pos
+ i
];
628 usr_cmd_len
-= optimized_spaces
;
629 query
[usr_cmd_len
] = '\0';
631 /* filter commands */
634 if (is_variable_auto_completion
)
635 query_cmd
= alloc_printf("lsort [info vars {%s*}]", query
);
637 query_cmd
= alloc_printf("_telnet_autocomplete_helper {%s*}", query
);
640 LOG_ERROR("Out of memory");
644 int retval
= Jim_EvalSource(command_context
->interp
, __FILE__
, __LINE__
, query_cmd
);
646 if (retval
!= JIM_OK
)
649 Jim_Obj
*list
= Jim_GetResult(command_context
->interp
);
650 Jim_IncrRefCount(list
);
652 /* common prefix length of the matched commands */
653 size_t common_len
= 0;
654 char *first_match
= NULL
; /* used to compute the common prefix length */
656 int len
= Jim_ListLength(command_context
->interp
, list
);
657 for (int i
= 0; i
< len
; i
++) {
658 Jim_Obj
*elem
= Jim_ListGetIndex(command_context
->interp
, list
, i
);
659 Jim_IncrRefCount(elem
);
661 char *name
= (char *)Jim_GetString(elem
, NULL
);
663 /* validate the command */
664 bool ignore_cmd
= false;
665 if (!is_variable_auto_completion
) {
666 Jim_Cmd
*jim_cmd
= Jim_GetCommand(command_context
->interp
, elem
, JIM_NONE
);
669 /* Why we are here? Let's ignore it! */
671 } else if (jimcmd_is_oocd_command(jim_cmd
)) {
672 struct command
*cmd
= jimcmd_privdata(jim_cmd
);
674 if (cmd
&& !cmd
->handler
&& !cmd
->jim_handler
) {
675 /* Initial part of a multi-word command. Ignore it! */
677 } else if (cmd
&& cmd
->mode
== COMMAND_CONFIG
) {
678 /* Not executable after config phase. Ignore it! */
684 /* save the command in the prediction list */
686 struct cmd_match
*match
= calloc(1, sizeof(struct cmd_match
));
688 LOG_ERROR("Out of memory");
689 Jim_DecrRefCount(command_context
->interp
, elem
);
690 break; /* break the for loop */
693 if (list_empty(&matches
)) {
694 common_len
= strlen(name
);
697 size_t new_common_len
= usr_cmd_len
; /* save some loops */
699 while (new_common_len
< common_len
&& first_match
[new_common_len
] == name
[new_common_len
])
702 common_len
= new_common_len
;
706 list_add_tail(&match
->lh
, &matches
);
709 Jim_DecrRefCount(command_context
->interp
, elem
);
711 /* end of command filtering */
713 /* proceed with auto-completion */
714 if (list_empty(&matches
))
715 telnet_bell(connection
);
716 else if (common_len
== usr_cmd_len
&& list_is_singular(&matches
) && t_con
->line_cursor
== t_con
->line_size
)
717 telnet_insert(connection
, " ", 1);
718 else if (common_len
> usr_cmd_len
) {
719 int completion_size
= common_len
- usr_cmd_len
;
720 if (telnet_insert(connection
, first_match
+ usr_cmd_len
, completion_size
)) {
721 /* in bash this extra space is only added when the cursor in at the end of line */
722 if (list_is_singular(&matches
) && t_con
->line_cursor
== t_con
->line_size
)
723 telnet_insert(connection
, " ", 1);
725 } else if (!list_is_singular(&matches
)) {
726 telnet_write(connection
, "\n\r", 2);
728 struct cmd_match
*match
;
729 list_for_each_entry(match
, &matches
, lh
) {
730 telnet_write(connection
, match
->cmd
, strlen(match
->cmd
));
731 telnet_write(connection
, "\n\r", 2);
734 telnet_prompt(connection
);
735 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
737 /* restore the terminal visible cursor location */
738 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
739 telnet_write(connection
, "\b", 1);
742 /* destroy the command_list */
743 struct cmd_match
*tmp
, *match
;
744 list_for_each_entry_safe(match
, tmp
, &matches
, lh
)
747 Jim_DecrRefCount(command_context
->interp
, list
);
750 static int telnet_input(struct connection
*connection
)
753 unsigned char buffer
[TELNET_BUFFER_SIZE
];
754 unsigned char *buf_p
;
755 struct telnet_connection
*t_con
= connection
->priv
;
757 bytes_read
= connection_read(connection
, buffer
, TELNET_BUFFER_SIZE
);
760 return ERROR_SERVER_REMOTE_CLOSED
;
761 else if (bytes_read
== -1) {
762 LOG_ERROR("error during read: %s", strerror(errno
));
763 return ERROR_SERVER_REMOTE_CLOSED
;
768 switch (t_con
->state
) {
769 case TELNET_STATE_DATA
:
770 if (*buf_p
== 0xff) {
771 t_con
->state
= TELNET_STATE_IAC
;
773 if (isprint(*buf_p
)) { /* printable character */
774 telnet_insert(connection
, buf_p
, 1);
775 } else { /* non-printable */
776 if (*buf_p
== 0x1b) { /* escape */
777 t_con
->state
= TELNET_STATE_ESCAPE
;
778 t_con
->last_escape
= '\x00';
779 } else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) { /* CR/LF */
782 /* skip over combinations with CR/LF and NUL characters */
783 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) ||
784 (*(buf_p
+ 1) == 0xd))) {
788 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0)) {
792 t_con
->line
[t_con
->line_size
] = 0;
794 retval
= telnet_exec_line(connection
);
795 if (retval
!= ERROR_OK
)
797 } else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) { /* delete character */
798 telnet_delete_character(connection
);
799 } else if (*buf_p
== 0x15) { /* clear line */
800 telnet_clear_line(connection
, t_con
);
801 } else if (*buf_p
== CTRL('B')) { /* cursor left */
802 telnet_move_cursor(connection
, t_con
->line_cursor
- 1);
803 t_con
->state
= TELNET_STATE_DATA
;
804 } else if (*buf_p
== CTRL('C')) { /* interrupt */
805 telnet_interrupt(connection
);
806 } else if (*buf_p
== CTRL('F')) { /* cursor right */
807 telnet_move_cursor(connection
, t_con
->line_cursor
+ 1);
808 t_con
->state
= TELNET_STATE_DATA
;
809 } else if (*buf_p
== CTRL('P')) { /* cursor up */
810 telnet_history_up(connection
);
811 } else if (*buf_p
== CTRL('N')) { /* cursor down */
812 telnet_history_down(connection
);
813 } else if (*buf_p
== CTRL('A')) { /* move the cursor to the beginning of the line */
814 telnet_move_cursor(connection
, 0);
815 } else if (*buf_p
== CTRL('E')) { /* move the cursor to the end of the line */
816 telnet_move_cursor(connection
, t_con
->line_size
);
817 } else if (*buf_p
== CTRL('K')) { /* kill line to end */
818 telnet_cut_line_to_end(connection
);
819 } else if (*buf_p
== '\t') {
820 telnet_auto_complete(connection
);
822 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
827 case TELNET_STATE_IAC
:
830 t_con
->state
= TELNET_STATE_DONT
;
833 t_con
->state
= TELNET_STATE_DO
;
836 t_con
->state
= TELNET_STATE_WONT
;
839 t_con
->state
= TELNET_STATE_WILL
;
843 case TELNET_STATE_SB
:
845 case TELNET_STATE_SE
:
847 case TELNET_STATE_WILL
:
848 case TELNET_STATE_WONT
:
849 case TELNET_STATE_DO
:
850 case TELNET_STATE_DONT
:
851 t_con
->state
= TELNET_STATE_DATA
;
853 case TELNET_STATE_ESCAPE
:
854 if (t_con
->last_escape
== '[') {
855 if (*buf_p
== 'D') { /* cursor left */
856 telnet_move_cursor(connection
, t_con
->line_cursor
- 1);
857 t_con
->state
= TELNET_STATE_DATA
;
858 } else if (*buf_p
== 'C') { /* cursor right */
859 telnet_move_cursor(connection
, t_con
->line_cursor
+ 1);
860 t_con
->state
= TELNET_STATE_DATA
;
861 } else if (*buf_p
== 'A') { /* cursor up */
862 telnet_history_up(connection
);
863 } else if (*buf_p
== 'B') { /* cursor down */
864 telnet_history_down(connection
);
865 } else if (*buf_p
== 'F') { /* end key */
866 telnet_move_cursor(connection
, t_con
->line_size
);
867 t_con
->state
= TELNET_STATE_DATA
;
868 } else if (*buf_p
== 'H') { /* home key */
869 telnet_move_cursor(connection
, 0);
870 t_con
->state
= TELNET_STATE_DATA
;
871 } else if (*buf_p
== '3') {
872 t_con
->last_escape
= *buf_p
;
874 t_con
->state
= TELNET_STATE_DATA
;
876 } else if (t_con
->last_escape
== '3') {
877 /* Remove character */
879 telnet_remove_character(connection
);
880 t_con
->state
= TELNET_STATE_DATA
;
882 t_con
->state
= TELNET_STATE_DATA
;
883 } else if (t_con
->last_escape
== '\x00') {
885 t_con
->last_escape
= *buf_p
;
887 t_con
->state
= TELNET_STATE_DATA
;
889 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
890 t_con
->state
= TELNET_STATE_DATA
;
895 LOG_ERROR("unknown telnet state");
906 static int telnet_connection_closed(struct connection
*connection
)
908 struct telnet_connection
*t_con
= connection
->priv
;
911 log_remove_callback(telnet_log_callback
, connection
);
914 t_con
->prompt
= NULL
;
916 /* save telnet history */
917 telnet_save_history(t_con
);
919 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
920 free(t_con
->history
[i
]);
921 t_con
->history
[i
] = NULL
;
924 /* if this connection registered a debug-message receiver delete it */
925 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
927 free(connection
->priv
);
928 connection
->priv
= NULL
;
933 static const struct service_driver telnet_service_driver
= {
935 .new_connection_during_keep_alive_handler
= NULL
,
936 .new_connection_handler
= telnet_new_connection
,
937 .input_handler
= telnet_input
,
938 .connection_closed_handler
= telnet_connection_closed
,
939 .keep_client_alive_handler
= NULL
,
942 int telnet_init(char *banner
)
944 if (strcmp(telnet_port
, "disabled") == 0) {
945 LOG_INFO("telnet server disabled");
949 struct telnet_service
*telnet_service
=
950 malloc(sizeof(struct telnet_service
));
952 if (!telnet_service
) {
953 LOG_ERROR("Failed to allocate telnet service.");
957 telnet_service
->banner
= banner
;
959 int ret
= add_service(&telnet_service_driver
, telnet_port
, CONNECTION_LIMIT_UNLIMITED
,
962 if (ret
!= ERROR_OK
) {
963 free(telnet_service
);
970 /* daemon configuration command telnet_port */
971 COMMAND_HANDLER(handle_telnet_port_command
)
973 return CALL_COMMAND_HANDLER(server_pipe_command
, &telnet_port
);
976 COMMAND_HANDLER(handle_exit_command
)
978 return ERROR_COMMAND_CLOSE_CONNECTION
;
981 static const struct command_registration telnet_command_handlers
[] = {
984 .handler
= handle_exit_command
,
985 .mode
= COMMAND_EXEC
,
987 .help
= "exit telnet session",
990 .name
= "telnet_port",
991 .handler
= handle_telnet_port_command
,
992 .mode
= COMMAND_CONFIG
,
993 .help
= "Specify port on which to listen "
994 "for incoming telnet connections. "
995 "Read help on 'gdb_port'.",
996 .usage
= "[port_num]",
998 COMMAND_REGISTRATION_DONE
1001 int telnet_register_commands(struct command_context
*cmd_ctx
)
1003 telnet_port
= strdup("4444");
1004 return register_commands(cmd_ctx
, NULL
, telnet_command_handlers
);
1007 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)