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>
32 #include <helper/list.h>
34 static char *telnet_port
;
36 static char *negotiate
=
37 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
38 "\xFF\xFB\x01" /* IAC WILL Echo */
39 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
40 "\xFF\xFE\x01"; /* IAC DON'T Echo */
42 #define CTRL(c) (c - '@')
43 #define TELNET_HISTORY ".openocd_history"
45 /* The only way we can detect that the socket is closed is the first time
46 * we write to it, we will fail. Subsequent write operations will
49 static int telnet_write(struct connection
*connection
, const void *data
,
52 struct telnet_connection
*t_con
= connection
->priv
;
54 return ERROR_SERVER_REMOTE_CLOSED
;
56 if (connection_write(connection
, data
, len
) == len
)
59 return ERROR_SERVER_REMOTE_CLOSED
;
62 /* output an audible bell */
63 static int telnet_bell(struct connection
*connection
)
65 /* ("\a" does not work, at least on windows) */
66 return telnet_write(connection
, "\x07", 1);
69 static int telnet_prompt(struct connection
*connection
)
71 struct telnet_connection
*t_con
= connection
->priv
;
73 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
76 static int telnet_outputline(struct connection
*connection
, const char *line
)
80 /* process lines in buffer */
82 char *line_end
= strchr(line
, '\n');
89 telnet_write(connection
, line
, len
);
91 telnet_write(connection
, "\r\n", 2);
100 static int telnet_output(struct command_context
*cmd_ctx
, const char *line
)
102 struct connection
*connection
= cmd_ctx
->output_handler_priv
;
104 return telnet_outputline(connection
, line
);
107 static void telnet_log_callback(void *priv
, const char *file
, unsigned line
,
108 const char *function
, const char *string
)
110 struct connection
*connection
= priv
;
111 struct telnet_connection
*t_con
= connection
->priv
;
115 /* If the prompt is not visible, simply output the message. */
116 if (!t_con
->prompt_visible
) {
117 telnet_outputline(connection
, string
);
121 /* Clear the command line. */
122 tmp
= strlen(t_con
->prompt
) + t_con
->line_size
;
124 for (i
= 0; i
< tmp
; i
+= 16)
125 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
128 for (i
= 0; i
< tmp
; i
+= 16)
129 telnet_write(connection
, " ", MIN(tmp
- i
, 16));
131 for (i
= 0; i
< tmp
; i
+= 16)
132 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
135 telnet_outputline(connection
, string
);
137 /* Put the command line to its previous state. */
138 telnet_prompt(connection
);
139 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
141 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
142 telnet_write(connection
, "\b", 1);
145 static void telnet_load_history(struct telnet_connection
*t_con
)
148 char buffer
[TELNET_BUFFER_SIZE
];
151 char *history
= get_home_dir(TELNET_HISTORY
);
154 LOG_INFO("unable to get user home directory, telnet history will be disabled");
158 histfp
= fopen(history
, "rb");
162 while (fgets(buffer
, sizeof(buffer
), histfp
)) {
164 char *p
= strchr(buffer
, '\n');
167 if (buffer
[0] && i
< TELNET_LINE_HISTORY_SIZE
)
168 t_con
->history
[i
++] = strdup(buffer
);
171 t_con
->next_history
= i
;
172 t_con
->next_history
%= TELNET_LINE_HISTORY_SIZE
;
173 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
174 t_con
->current_history
= t_con
->next_history
> 0 ? i
- 1 : 0;
181 static void telnet_save_history(struct telnet_connection
*t_con
)
187 char *history
= get_home_dir(TELNET_HISTORY
);
190 LOG_INFO("unable to get user home directory, telnet history will be disabled");
194 histfp
= fopen(history
, "wb");
198 num
= TELNET_LINE_HISTORY_SIZE
;
199 i
= t_con
->current_history
+ 1;
200 i
%= TELNET_LINE_HISTORY_SIZE
;
202 while (!t_con
->history
[i
] && num
> 0) {
204 i
%= TELNET_LINE_HISTORY_SIZE
;
209 for (; num
> 0; num
--) {
210 fprintf(histfp
, "%s\n", t_con
->history
[i
]);
212 i
%= TELNET_LINE_HISTORY_SIZE
;
221 static int telnet_new_connection(struct connection
*connection
)
223 struct telnet_connection
*telnet_connection
;
224 struct telnet_service
*telnet_service
= connection
->service
->priv
;
226 telnet_connection
= calloc(1, sizeof(struct telnet_connection
));
228 if (!telnet_connection
) {
229 LOG_ERROR("Failed to allocate telnet connection.");
233 connection
->priv
= telnet_connection
;
235 /* initialize telnet connection information */
236 telnet_connection
->prompt
= strdup("> ");
237 telnet_connection
->prompt_visible
= true;
238 telnet_connection
->state
= TELNET_STATE_DATA
;
240 /* output goes through telnet connection */
241 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
243 /* negotiate telnet options */
244 telnet_write(connection
, negotiate
, strlen(negotiate
));
246 /* print connection banner */
247 if (telnet_service
->banner
) {
248 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
249 telnet_write(connection
, "\r\n", 2);
252 /* the prompt is always placed at the line beginning */
253 telnet_write(connection
, "\r", 1);
254 telnet_prompt(connection
);
256 telnet_load_history(telnet_connection
);
258 log_add_callback(telnet_log_callback
, connection
);
263 static void telnet_clear_line(struct connection
*connection
,
264 struct telnet_connection
*t_con
)
266 /* move to end of line */
267 if (t_con
->line_cursor
< t_con
->line_size
)
268 telnet_write(connection
,
269 t_con
->line
+ t_con
->line_cursor
,
270 t_con
->line_size
- t_con
->line_cursor
);
272 /* backspace, overwrite with space, backspace */
273 while (t_con
->line_size
> 0) {
274 telnet_write(connection
, "\b \b", 3);
277 t_con
->line_cursor
= 0;
280 static void telnet_history_go(struct connection
*connection
, int idx
)
282 struct telnet_connection
*t_con
= connection
->priv
;
284 if (t_con
->history
[idx
]) {
285 telnet_clear_line(connection
, t_con
);
286 t_con
->line_size
= strlen(t_con
->history
[idx
]);
287 t_con
->line_cursor
= t_con
->line_size
;
288 memcpy(t_con
->line
, t_con
->history
[idx
], t_con
->line_size
);
289 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
290 t_con
->current_history
= idx
;
292 t_con
->state
= TELNET_STATE_DATA
;
295 static void telnet_history_up(struct connection
*connection
)
297 struct telnet_connection
*t_con
= connection
->priv
;
299 size_t last_history
= (t_con
->current_history
> 0) ?
300 t_con
->current_history
- 1 :
301 TELNET_LINE_HISTORY_SIZE
-1;
302 telnet_history_go(connection
, last_history
);
305 static void telnet_history_down(struct connection
*connection
)
307 struct telnet_connection
*t_con
= connection
->priv
;
310 next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
311 telnet_history_go(connection
, next_history
);
314 static void telnet_history_add(struct connection
*connection
)
316 struct telnet_connection
*t_con
= connection
->priv
;
318 /* save only non-blank not repeating lines in the history */
319 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ?
320 t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
322 if (*t_con
->line
&& (!prev_line
|| strcmp(t_con
->line
, prev_line
))) {
323 /* if the history slot is already taken, free it */
324 free(t_con
->history
[t_con
->next_history
]);
326 /* add line to history */
327 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
329 /* wrap history at TELNET_LINE_HISTORY_SIZE */
330 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
332 /* current history line starts at the new entry */
333 t_con
->current_history
= t_con
->next_history
;
335 free(t_con
->history
[t_con
->current_history
]);
336 t_con
->history
[t_con
->current_history
] = strdup("");
340 static int telnet_history_print(struct connection
*connection
)
342 struct telnet_connection
*tc
;
344 tc
= connection
->priv
;
346 for (size_t i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
350 * The tc->next_history line contains empty string (unless NULL), thus
353 line
= tc
->history
[(tc
->next_history
+ i
) % TELNET_LINE_HISTORY_SIZE
];
356 telnet_write(connection
, line
, strlen(line
));
357 telnet_write(connection
, "\r\n\x00", 3);
364 /* The prompt is always placed at the line beginning. */
365 telnet_write(connection
, "\r", 1);
367 return telnet_prompt(connection
);
370 static void telnet_move_cursor(struct connection
*connection
, size_t pos
)
372 struct telnet_connection
*tc
= connection
->priv
;
375 if (pos
== tc
->line_cursor
) /* nothing to do */
378 if (pos
> tc
->line_size
) /* out of bounds */
381 if (pos
< tc
->line_cursor
) {
382 tmp
= tc
->line_cursor
- pos
;
384 for (size_t i
= 0; i
< tmp
; i
+= 16)
385 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
388 tmp
= pos
- tc
->line_cursor
;
390 for (size_t i
= 0; i
< tmp
; i
+= 16)
391 telnet_write(connection
, tc
->line
+ tc
->line_cursor
+ i
,
395 tc
->line_cursor
= pos
;
398 /* check buffer size leaving one spare character for string null termination */
399 static inline bool telnet_can_insert(struct connection
*connection
, size_t len
)
401 struct telnet_connection
*t_con
= connection
->priv
;
403 return t_con
->line_size
+ len
< TELNET_LINE_MAX_SIZE
;
406 /* write to telnet console, and update the telnet_connection members
407 * this function is capable of inserting in the middle of a line
408 * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
410 * returns false when it fails to insert the requested data
412 static bool telnet_insert(struct connection
*connection
, const void *data
, size_t len
)
414 struct telnet_connection
*t_con
= connection
->priv
;
416 if (!telnet_can_insert(connection
, len
)) {
417 telnet_bell(connection
);
421 if (t_con
->line_cursor
< t_con
->line_size
) {
422 /* we have some content after the cursor */
423 memmove(t_con
->line
+ t_con
->line_cursor
+ len
,
424 t_con
->line
+ t_con
->line_cursor
,
425 t_con
->line_size
- t_con
->line_cursor
);
428 strncpy(t_con
->line
+ t_con
->line_cursor
, data
, len
);
430 telnet_write(connection
,
431 t_con
->line
+ t_con
->line_cursor
,
432 t_con
->line_size
+ len
- t_con
->line_cursor
);
434 t_con
->line_size
+= len
;
435 t_con
->line_cursor
+= len
;
437 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
438 telnet_write(connection
, "\b", 1);
443 static void telnet_delete_character(struct connection
*connection
)
445 struct telnet_connection
*t_con
= connection
->priv
;
447 if (t_con
->line_cursor
== 0)
450 if (t_con
->line_cursor
!= t_con
->line_size
) {
452 telnet_write(connection
, "\b", 1);
453 t_con
->line_cursor
--;
455 memmove(t_con
->line
+ t_con
->line_cursor
,
456 t_con
->line
+ t_con
->line_cursor
+ 1,
460 telnet_write(connection
,
461 t_con
->line
+ t_con
->line_cursor
,
464 telnet_write(connection
, " \b", 2);
465 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
466 telnet_write(connection
, "\b", 1);
469 t_con
->line_cursor
--;
470 /* back space: move the 'printer' head one char
471 * back, overwrite with space, move back again */
472 telnet_write(connection
, "\b \b", 3);
476 static void telnet_remove_character(struct connection
*connection
)
478 struct telnet_connection
*t_con
= connection
->priv
;
480 if (t_con
->line_cursor
< t_con
->line_size
) {
483 /* remove char from line buffer */
484 memmove(t_con
->line
+ t_con
->line_cursor
,
485 t_con
->line
+ t_con
->line_cursor
+ 1,
486 t_con
->line_size
- t_con
->line_cursor
);
488 /* print remainder of buffer */
489 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
,
490 t_con
->line_size
- t_con
->line_cursor
);
491 /* overwrite last char with whitespace */
492 telnet_write(connection
, " \b", 2);
494 /* move back to cursor position*/
495 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
496 telnet_write(connection
, "\b", 1);
500 static int telnet_exec_line(struct connection
*connection
)
502 struct telnet_connection
*t_con
= connection
->priv
;
503 struct command_context
*command_context
= connection
->cmd_ctx
;
506 telnet_write(connection
, "\r\n\x00", 3);
508 if (strcmp(t_con
->line
, "history") == 0) {
509 retval
= telnet_history_print(connection
);
511 if (retval
!= ERROR_OK
)
517 telnet_history_add(connection
);
519 t_con
->line_size
= 0;
521 /* to suppress prompt in log callback during command execution */
522 t_con
->prompt_visible
= false;
524 if (strcmp(t_con
->line
, "shutdown") == 0)
525 telnet_save_history(t_con
);
527 retval
= command_run_line(command_context
, t_con
->line
);
529 t_con
->line_cursor
= 0;
530 t_con
->prompt_visible
= true;
532 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
533 return ERROR_SERVER_REMOTE_CLOSED
;
535 /* the prompt is always placed at the line beginning */
536 telnet_write(connection
, "\r", 1);
538 retval
= telnet_prompt(connection
);
539 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
540 return ERROR_SERVER_REMOTE_CLOSED
;
545 static void telnet_cut_line_to_end(struct connection
*connection
)
547 struct telnet_connection
*t_con
= connection
->priv
;
549 /* FIXME: currently this function does not save to clipboard */
551 if (t_con
->line_cursor
< t_con
->line_size
) {
552 /* overwrite with space, until end of line, move back */
553 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
554 telnet_write(connection
, " ", 1);
555 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
556 telnet_write(connection
, "\b", 1);
557 t_con
->line
[t_con
->line_cursor
] = '\0';
558 t_con
->line_size
= t_con
->line_cursor
;
562 static void telnet_interrupt(struct connection
*connection
)
564 struct telnet_connection
*t_con
= connection
->priv
;
566 /* print '^C' at line end, and display a new command prompt */
567 telnet_move_cursor(connection
, t_con
->line_size
);
568 telnet_write(connection
, "^C\n\r", 4);
569 t_con
->line_cursor
= 0;
570 t_con
->line_size
= 0;
571 telnet_prompt(connection
);
574 static void telnet_auto_complete(struct connection
*connection
)
576 struct telnet_connection
*t_con
= connection
->priv
;
577 struct command_context
*command_context
= connection
->cmd_ctx
;
586 /* - user command sequence, either at line beginning
587 * or we start over after these characters ';', '[', '{'
588 * - user variable sequence, start after the character '$'
589 * and do not contain white spaces */
590 bool is_variable_auto_completion
= false;
591 bool have_spaces
= false;
592 size_t seq_start
= (t_con
->line_cursor
== 0) ? 0 : (t_con
->line_cursor
- 1);
594 char c
= t_con
->line
[seq_start
];
596 if (c
== ';' || c
== '[' || c
== '{') {
599 } else if (c
== ' ') {
601 } else if (c
== '$' && !have_spaces
) {
602 is_variable_auto_completion
= true;
613 /* user command position in the line, ignore leading spaces */
614 size_t usr_cmd_pos
= seq_start
;
615 while ((usr_cmd_pos
< t_con
->line_cursor
) && isspace(t_con
->line
[usr_cmd_pos
]))
618 /* user command length */
619 size_t usr_cmd_len
= t_con
->line_cursor
- usr_cmd_pos
;
621 /* optimize multiple spaces in the user command,
622 * because info commands does not tolerate multiple spaces */
623 size_t optimized_spaces
= 0;
624 char query
[usr_cmd_len
+ 1];
625 for (size_t i
= 0; i
< usr_cmd_len
; i
++) {
626 if ((i
< usr_cmd_len
- 1) && isspace(t_con
->line
[usr_cmd_pos
+ i
])
627 && isspace(t_con
->line
[usr_cmd_pos
+ i
+ 1])) {
632 query
[i
- optimized_spaces
] = t_con
->line
[usr_cmd_pos
+ i
];
635 usr_cmd_len
-= optimized_spaces
;
636 query
[usr_cmd_len
] = '\0';
638 /* filter commands */
641 if (is_variable_auto_completion
)
642 query_cmd
= alloc_printf("lsort [info vars {%s*}]", query
);
644 query_cmd
= alloc_printf("_telnet_autocomplete_helper {%s*}", query
);
647 LOG_ERROR("Out of memory");
651 int retval
= Jim_EvalSource(command_context
->interp
, __FILE__
, __LINE__
, query_cmd
);
653 if (retval
!= JIM_OK
)
656 Jim_Obj
*list
= Jim_GetResult(command_context
->interp
);
657 Jim_IncrRefCount(list
);
659 /* common prefix length of the matched commands */
660 size_t common_len
= 0;
661 char *first_match
= NULL
; /* used to compute the common prefix length */
663 int len
= Jim_ListLength(command_context
->interp
, list
);
664 for (int i
= 0; i
< len
; i
++) {
665 Jim_Obj
*elem
= Jim_ListGetIndex(command_context
->interp
, list
, i
);
666 Jim_IncrRefCount(elem
);
668 char *name
= (char *)Jim_GetString(elem
, NULL
);
670 /* validate the command */
671 bool ignore_cmd
= false;
672 if (!is_variable_auto_completion
) {
673 Jim_Cmd
*jim_cmd
= Jim_GetCommand(command_context
->interp
, elem
, JIM_NONE
);
676 /* Why we are here? Let's ignore it! */
678 } else if (jimcmd_is_oocd_command(jim_cmd
)) {
679 struct command
*cmd
= jimcmd_privdata(jim_cmd
);
681 if (cmd
&& !cmd
->handler
&& !cmd
->jim_handler
) {
682 /* Initial part of a multi-word command. Ignore it! */
684 } else if (cmd
&& cmd
->mode
== COMMAND_CONFIG
) {
685 /* Not executable after config phase. Ignore it! */
691 /* save the command in the prediction list */
693 struct cmd_match
*match
= calloc(1, sizeof(struct cmd_match
));
695 LOG_ERROR("Out of memory");
696 Jim_DecrRefCount(command_context
->interp
, elem
);
697 break; /* break the for loop */
700 if (list_empty(&matches
)) {
701 common_len
= strlen(name
);
704 size_t new_common_len
= usr_cmd_len
; /* save some loops */
706 while (new_common_len
< common_len
&& first_match
[new_common_len
] == name
[new_common_len
])
709 common_len
= new_common_len
;
713 list_add_tail(&match
->lh
, &matches
);
716 Jim_DecrRefCount(command_context
->interp
, elem
);
718 /* end of command filtering */
720 /* proceed with auto-completion */
721 if (list_empty(&matches
))
722 telnet_bell(connection
);
723 else if (common_len
== usr_cmd_len
&& list_is_singular(&matches
) && t_con
->line_cursor
== t_con
->line_size
)
724 telnet_insert(connection
, " ", 1);
725 else if (common_len
> usr_cmd_len
) {
726 int completion_size
= common_len
- usr_cmd_len
;
727 if (telnet_insert(connection
, first_match
+ usr_cmd_len
, completion_size
)) {
728 /* in bash this extra space is only added when the cursor in at the end of line */
729 if (list_is_singular(&matches
) && t_con
->line_cursor
== t_con
->line_size
)
730 telnet_insert(connection
, " ", 1);
732 } else if (!list_is_singular(&matches
)) {
733 telnet_write(connection
, "\n\r", 2);
735 struct cmd_match
*match
;
736 list_for_each_entry(match
, &matches
, lh
) {
737 telnet_write(connection
, match
->cmd
, strlen(match
->cmd
));
738 telnet_write(connection
, "\n\r", 2);
741 telnet_prompt(connection
);
742 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
744 /* restore the terminal visible cursor location */
745 for (size_t i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
746 telnet_write(connection
, "\b", 1);
749 /* destroy the command_list */
750 struct cmd_match
*tmp
, *match
;
751 list_for_each_entry_safe(match
, tmp
, &matches
, lh
)
754 Jim_DecrRefCount(command_context
->interp
, list
);
757 static int telnet_input(struct connection
*connection
)
760 unsigned char buffer
[TELNET_BUFFER_SIZE
];
761 unsigned char *buf_p
;
762 struct telnet_connection
*t_con
= connection
->priv
;
764 bytes_read
= connection_read(connection
, buffer
, TELNET_BUFFER_SIZE
);
767 return ERROR_SERVER_REMOTE_CLOSED
;
768 else if (bytes_read
== -1) {
769 LOG_ERROR("error during read: %s", strerror(errno
));
770 return ERROR_SERVER_REMOTE_CLOSED
;
775 switch (t_con
->state
) {
776 case TELNET_STATE_DATA
:
777 if (*buf_p
== 0xff) {
778 t_con
->state
= TELNET_STATE_IAC
;
780 if (isprint(*buf_p
)) { /* printable character */
781 telnet_insert(connection
, buf_p
, 1);
782 } else { /* non-printable */
783 if (*buf_p
== 0x1b) { /* escape */
784 t_con
->state
= TELNET_STATE_ESCAPE
;
785 t_con
->last_escape
= '\x00';
786 } else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) { /* CR/LF */
789 /* skip over combinations with CR/LF and NUL characters */
790 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) ||
791 (*(buf_p
+ 1) == 0xd))) {
795 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0)) {
799 t_con
->line
[t_con
->line_size
] = 0;
801 retval
= telnet_exec_line(connection
);
802 if (retval
!= ERROR_OK
)
804 } else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) { /* delete character */
805 telnet_delete_character(connection
);
806 } else if (*buf_p
== 0x15) { /* clear line */
807 telnet_clear_line(connection
, t_con
);
808 } else if (*buf_p
== CTRL('B')) { /* cursor left */
809 telnet_move_cursor(connection
, t_con
->line_cursor
- 1);
810 t_con
->state
= TELNET_STATE_DATA
;
811 } else if (*buf_p
== CTRL('C')) { /* interrupt */
812 telnet_interrupt(connection
);
813 } else if (*buf_p
== CTRL('F')) { /* cursor right */
814 telnet_move_cursor(connection
, t_con
->line_cursor
+ 1);
815 t_con
->state
= TELNET_STATE_DATA
;
816 } else if (*buf_p
== CTRL('P')) { /* cursor up */
817 telnet_history_up(connection
);
818 } else if (*buf_p
== CTRL('N')) { /* cursor down */
819 telnet_history_down(connection
);
820 } else if (*buf_p
== CTRL('A')) { /* move the cursor to the beginning of the line */
821 telnet_move_cursor(connection
, 0);
822 } else if (*buf_p
== CTRL('E')) { /* move the cursor to the end of the line */
823 telnet_move_cursor(connection
, t_con
->line_size
);
824 } else if (*buf_p
== CTRL('K')) { /* kill line to end */
825 telnet_cut_line_to_end(connection
);
826 } else if (*buf_p
== '\t') {
827 telnet_auto_complete(connection
);
829 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
834 case TELNET_STATE_IAC
:
837 t_con
->state
= TELNET_STATE_DONT
;
840 t_con
->state
= TELNET_STATE_DO
;
843 t_con
->state
= TELNET_STATE_WONT
;
846 t_con
->state
= TELNET_STATE_WILL
;
850 case TELNET_STATE_SB
:
852 case TELNET_STATE_SE
:
854 case TELNET_STATE_WILL
:
855 case TELNET_STATE_WONT
:
856 case TELNET_STATE_DO
:
857 case TELNET_STATE_DONT
:
858 t_con
->state
= TELNET_STATE_DATA
;
860 case TELNET_STATE_ESCAPE
:
861 if (t_con
->last_escape
== '[') {
862 if (*buf_p
== 'D') { /* cursor left */
863 telnet_move_cursor(connection
, t_con
->line_cursor
- 1);
864 t_con
->state
= TELNET_STATE_DATA
;
865 } else if (*buf_p
== 'C') { /* cursor right */
866 telnet_move_cursor(connection
, t_con
->line_cursor
+ 1);
867 t_con
->state
= TELNET_STATE_DATA
;
868 } else if (*buf_p
== 'A') { /* cursor up */
869 telnet_history_up(connection
);
870 } else if (*buf_p
== 'B') { /* cursor down */
871 telnet_history_down(connection
);
872 } else if (*buf_p
== 'F') { /* end key */
873 telnet_move_cursor(connection
, t_con
->line_size
);
874 t_con
->state
= TELNET_STATE_DATA
;
875 } else if (*buf_p
== 'H') { /* home key */
876 telnet_move_cursor(connection
, 0);
877 t_con
->state
= TELNET_STATE_DATA
;
878 } else if (*buf_p
== '3') {
879 t_con
->last_escape
= *buf_p
;
881 t_con
->state
= TELNET_STATE_DATA
;
883 } else if (t_con
->last_escape
== '3') {
884 /* Remove character */
886 telnet_remove_character(connection
);
887 t_con
->state
= TELNET_STATE_DATA
;
889 t_con
->state
= TELNET_STATE_DATA
;
890 } else if (t_con
->last_escape
== '\x00') {
892 t_con
->last_escape
= *buf_p
;
894 t_con
->state
= TELNET_STATE_DATA
;
896 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
897 t_con
->state
= TELNET_STATE_DATA
;
902 LOG_ERROR("unknown telnet state");
913 static int telnet_connection_closed(struct connection
*connection
)
915 struct telnet_connection
*t_con
= connection
->priv
;
918 log_remove_callback(telnet_log_callback
, connection
);
921 t_con
->prompt
= NULL
;
923 /* save telnet history */
924 telnet_save_history(t_con
);
926 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
927 free(t_con
->history
[i
]);
928 t_con
->history
[i
] = NULL
;
931 /* if this connection registered a debug-message receiver delete it */
932 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
934 free(connection
->priv
);
935 connection
->priv
= NULL
;
940 static const struct service_driver telnet_service_driver
= {
942 .new_connection_during_keep_alive_handler
= NULL
,
943 .new_connection_handler
= telnet_new_connection
,
944 .input_handler
= telnet_input
,
945 .connection_closed_handler
= telnet_connection_closed
,
946 .keep_client_alive_handler
= NULL
,
949 int telnet_init(char *banner
)
951 if (strcmp(telnet_port
, "disabled") == 0) {
952 LOG_INFO("telnet server disabled");
956 struct telnet_service
*telnet_service
=
957 malloc(sizeof(struct telnet_service
));
959 if (!telnet_service
) {
960 LOG_ERROR("Failed to allocate telnet service.");
964 telnet_service
->banner
= banner
;
966 int ret
= add_service(&telnet_service_driver
, telnet_port
, CONNECTION_LIMIT_UNLIMITED
,
969 if (ret
!= ERROR_OK
) {
970 free(telnet_service
);
977 /* daemon configuration command telnet_port */
978 COMMAND_HANDLER(handle_telnet_port_command
)
980 return CALL_COMMAND_HANDLER(server_pipe_command
, &telnet_port
);
983 COMMAND_HANDLER(handle_exit_command
)
985 return ERROR_COMMAND_CLOSE_CONNECTION
;
988 static const struct command_registration telnet_command_handlers
[] = {
991 .handler
= handle_exit_command
,
992 .mode
= COMMAND_EXEC
,
994 .help
= "exit telnet session",
997 .name
= "telnet_port",
998 .handler
= handle_telnet_port_command
,
999 .mode
= COMMAND_CONFIG
,
1000 .help
= "Specify port on which to listen "
1001 "for incoming telnet connections. "
1002 "Read help on 'gdb_port'.",
1003 .usage
= "[port_num]",
1005 COMMAND_REGISTRATION_DONE
1008 int telnet_register_commands(struct command_context
*cmd_ctx
)
1010 telnet_port
= strdup("4444");
1011 return register_commands(cmd_ctx
, NULL
, telnet_command_handlers
);
1014 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)