1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2007-2010 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
8 * Copyright (C) 2008 by Spencer Oliver *
9 * spen@spen-soft.co.uk *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
25 ***************************************************************************/
31 #include "telnet_server.h"
32 #include <target/target_request.h>
33 #include <helper/configuration.h>
35 static const char *telnet_port
;
37 static char *negotiate
=
38 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
39 "\xFF\xFB\x01" /* IAC WILL Echo */
40 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
41 "\xFF\xFE\x01"; /* IAC DON'T Echo */
43 #define CTRL(c) (c - '@')
44 #define TELNET_HISTORY ".openocd_history"
46 /* The only way we can detect that the socket is closed is the first time
47 * we write to it, we will fail. Subsequent write operations will
50 static int telnet_write(struct connection
*connection
, const void *data
,
53 struct telnet_connection
*t_con
= connection
->priv
;
55 return ERROR_SERVER_REMOTE_CLOSED
;
57 if (connection_write(connection
, data
, len
) == len
)
60 return ERROR_SERVER_REMOTE_CLOSED
;
63 static int telnet_prompt(struct connection
*connection
)
65 struct telnet_connection
*t_con
= connection
->priv
;
67 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
70 static int telnet_outputline(struct connection
*connection
, const char *line
)
74 /* process lines in buffer */
76 char *line_end
= strchr(line
, '\n');
83 telnet_write(connection
, line
, len
);
85 telnet_write(connection
, "\r\n", 2);
94 static int telnet_output(struct command_context
*cmd_ctx
, const char *line
)
96 struct connection
*connection
= cmd_ctx
->output_handler_priv
;
98 return telnet_outputline(connection
, line
);
101 static void telnet_log_callback(void *priv
, const char *file
, unsigned line
,
102 const char *function
, const char *string
)
104 struct connection
*connection
= priv
;
105 struct telnet_connection
*t_con
= connection
->priv
;
108 /* if there is no prompt, simply output the message */
109 if (t_con
->line_cursor
< 0) {
110 telnet_outputline(connection
, string
);
114 /* clear the command line */
115 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
116 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i
> 16 ? 16 : i
);
117 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
118 telnet_write(connection
, " ", i
> 16 ? 16 : i
);
119 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
120 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i
> 16 ? 16 : i
);
122 /* output the message */
123 telnet_outputline(connection
, string
);
125 /* put the command line to its previous state */
126 telnet_prompt(connection
);
127 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
128 for (i
= t_con
->line_size
; i
> t_con
->line_cursor
; i
--)
129 telnet_write(connection
, "\b", 1);
132 static void telnet_load_history(struct telnet_connection
*t_con
)
135 char buffer
[TELNET_BUFFER_SIZE
];
138 char *history
= get_home_dir(TELNET_HISTORY
);
140 if (history
== NULL
) {
141 LOG_INFO("unable to get user home directory, telnet history will be disabled");
145 histfp
= fopen(history
, "rb");
149 while (fgets(buffer
, sizeof(buffer
), histfp
) != NULL
) {
151 char *p
= strchr(buffer
, '\n');
154 if (buffer
[0] && i
< TELNET_LINE_HISTORY_SIZE
)
155 t_con
->history
[i
++] = strdup(buffer
);
158 t_con
->next_history
= i
;
159 t_con
->next_history
%= TELNET_LINE_HISTORY_SIZE
;
160 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
161 t_con
->current_history
= t_con
->next_history
> 0 ? i
- 1 : 0;
168 static void telnet_save_history(struct telnet_connection
*t_con
)
174 char *history
= get_home_dir(TELNET_HISTORY
);
176 if (history
== NULL
) {
177 LOG_INFO("unable to get user home directory, telnet history will be disabled");
181 histfp
= fopen(history
, "wb");
185 num
= TELNET_LINE_HISTORY_SIZE
;
186 i
= t_con
->current_history
+ 1;
187 i
%= TELNET_LINE_HISTORY_SIZE
;
189 while (t_con
->history
[i
] == NULL
&& num
> 0) {
191 i
%= TELNET_LINE_HISTORY_SIZE
;
196 for (; num
> 0; num
--) {
197 fprintf(histfp
, "%s\n", t_con
->history
[i
]);
199 i
%= TELNET_LINE_HISTORY_SIZE
;
208 static int telnet_new_connection(struct connection
*connection
)
210 struct telnet_connection
*telnet_connection
= malloc(sizeof(struct telnet_connection
));
211 struct telnet_service
*telnet_service
= connection
->service
->priv
;
214 connection
->priv
= telnet_connection
;
216 /* initialize telnet connection information */
217 telnet_connection
->closed
= 0;
218 telnet_connection
->line_size
= 0;
219 telnet_connection
->line_cursor
= 0;
220 telnet_connection
->option_size
= 0;
221 telnet_connection
->prompt
= strdup("> ");
222 telnet_connection
->state
= TELNET_STATE_DATA
;
224 /* output goes through telnet connection */
225 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
227 /* negotiate telnet options */
228 telnet_write(connection
, negotiate
, strlen(negotiate
));
230 /* print connection banner */
231 if (telnet_service
->banner
) {
232 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
233 telnet_write(connection
, "\r\n", 2);
236 /* the prompt is always placed at the line beginning */
237 telnet_write(connection
, "\r", 1);
238 telnet_prompt(connection
);
240 /* initialize history */
241 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
242 telnet_connection
->history
[i
] = NULL
;
243 telnet_connection
->next_history
= 0;
244 telnet_connection
->current_history
= 0;
245 telnet_load_history(telnet_connection
);
247 log_add_callback(telnet_log_callback
, connection
);
252 static void telnet_clear_line(struct connection
*connection
,
253 struct telnet_connection
*t_con
)
255 /* move to end of line */
256 if (t_con
->line_cursor
< t_con
->line_size
)
257 telnet_write(connection
,
258 t_con
->line
+ t_con
->line_cursor
,
259 t_con
->line_size
- t_con
->line_cursor
);
261 /* backspace, overwrite with space, backspace */
262 while (t_con
->line_size
> 0) {
263 telnet_write(connection
, "\b \b", 3);
266 t_con
->line_cursor
= 0;
269 static 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 int 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
;
298 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
299 telnet_history_go(connection
, next_history
);
302 static int telnet_input(struct connection
*connection
)
305 unsigned char buffer
[TELNET_BUFFER_SIZE
];
306 unsigned char *buf_p
;
307 struct telnet_connection
*t_con
= connection
->priv
;
308 struct command_context
*command_context
= connection
->cmd_ctx
;
310 bytes_read
= connection_read(connection
, buffer
, TELNET_BUFFER_SIZE
);
313 return ERROR_SERVER_REMOTE_CLOSED
;
314 else if (bytes_read
== -1) {
315 LOG_ERROR("error during read: %s", strerror(errno
));
316 return ERROR_SERVER_REMOTE_CLOSED
;
321 switch (t_con
->state
) {
322 case TELNET_STATE_DATA
:
324 t_con
->state
= TELNET_STATE_IAC
;
326 if (isprint(*buf_p
)) { /* printable character */
327 /* watch buffer size leaving one spare character for
328 * string null termination */
329 if (t_con
->line_size
== TELNET_LINE_MAX_SIZE
-1) {
330 /* output audible bell if buffer is full
331 * "\a" does not work, at least on windows */
332 telnet_write(connection
, "\x07", 1);
333 } else if (t_con
->line_cursor
== t_con
->line_size
) {
334 telnet_write(connection
, buf_p
, 1);
335 t_con
->line
[t_con
->line_size
++] = *buf_p
;
336 t_con
->line_cursor
++;
339 memmove(t_con
->line
+ t_con
->line_cursor
+ 1,
340 t_con
->line
+ t_con
->line_cursor
,
341 t_con
->line_size
- t_con
->line_cursor
);
342 t_con
->line
[t_con
->line_cursor
] = *buf_p
;
344 telnet_write(connection
,
345 t_con
->line
+ t_con
->line_cursor
,
346 t_con
->line_size
- t_con
->line_cursor
);
347 t_con
->line_cursor
++;
348 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
349 telnet_write(connection
, "\b", 1);
351 } else { /* non-printable */
352 if (*buf_p
== 0x1b) { /* escape */
353 t_con
->state
= TELNET_STATE_ESCAPE
;
354 t_con
->last_escape
= '\x00';
355 } else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) { /* CR/LF */
358 /* skip over combinations with CR/LF and NUL characters */
359 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) ||
360 (*(buf_p
+ 1) == 0xd))) {
364 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0)) {
368 t_con
->line
[t_con
->line_size
] = 0;
370 telnet_write(connection
, "\r\n\x00", 3);
372 if (strcmp(t_con
->line
, "history") == 0) {
374 for (i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
375 /* the t_con->next_history line contains empty string
376 * (unless NULL), thus it is not printed */
377 char *history_line
= t_con
->history
[(t_con
->
379 TELNET_LINE_HISTORY_SIZE
];
381 telnet_write(connection
, history_line
,
382 strlen(history_line
));
383 telnet_write(connection
, "\r\n\x00", 3);
386 t_con
->line_size
= 0;
387 t_con
->line_cursor
= 0;
391 /* save only non-blank not repeating lines in the history */
392 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ?
393 t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
394 if (*t_con
->line
&& (prev_line
== NULL
||
395 strcmp(t_con
->line
, prev_line
))) {
396 /* if the history slot is already taken, free it */
397 if (t_con
->history
[t_con
->next_history
])
398 free(t_con
->history
[t_con
->next_history
]);
400 /* add line to history */
401 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
403 /* wrap history at TELNET_LINE_HISTORY_SIZE */
404 t_con
->next_history
= (t_con
->next_history
+ 1) %
405 TELNET_LINE_HISTORY_SIZE
;
407 /* current history line starts at the new entry */
408 t_con
->current_history
=
411 if (t_con
->history
[t_con
->current_history
])
412 free(t_con
->history
[t_con
->current_history
]);
413 t_con
->history
[t_con
->current_history
] = strdup("");
416 t_con
->line_size
= 0;
418 /* to suppress prompt in log callback during command execution */
419 t_con
->line_cursor
= -1;
421 if (strcmp(t_con
->line
, "shutdown") == 0)
422 telnet_save_history(t_con
);
424 retval
= command_run_line(command_context
, t_con
->line
);
426 t_con
->line_cursor
= 0;
428 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
429 return ERROR_SERVER_REMOTE_CLOSED
;
431 /* the prompt is always * placed at the line beginning */
432 telnet_write(connection
, "\r", 1);
434 retval
= telnet_prompt(connection
);
435 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
436 return ERROR_SERVER_REMOTE_CLOSED
;
438 } else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) { /* delete character */
439 if (t_con
->line_cursor
> 0) {
440 if (t_con
->line_cursor
!= t_con
->line_size
) {
442 telnet_write(connection
, "\b", 1);
443 t_con
->line_cursor
--;
445 memmove(t_con
->line
+ t_con
->line_cursor
,
446 t_con
->line
+ t_con
->line_cursor
+ 1,
450 telnet_write(connection
,
451 t_con
->line
+ t_con
->line_cursor
,
454 telnet_write(connection
, " \b", 2);
455 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
456 telnet_write(connection
, "\b", 1);
459 t_con
->line_cursor
--;
460 /* back space: move the 'printer' head one char
461 * back, overwrite with space, move back again */
462 telnet_write(connection
, "\b \b", 3);
465 } else if (*buf_p
== 0x15) /* clear line */
466 telnet_clear_line(connection
, t_con
);
467 else if (*buf_p
== CTRL('B')) { /* cursor left */
468 if (t_con
->line_cursor
> 0) {
469 telnet_write(connection
, "\b", 1);
470 t_con
->line_cursor
--;
472 t_con
->state
= TELNET_STATE_DATA
;
473 } else if (*buf_p
== CTRL('F')) { /* cursor right */
474 if (t_con
->line_cursor
< t_con
->line_size
)
475 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
476 t_con
->state
= TELNET_STATE_DATA
;
477 } else if (*buf_p
== CTRL('P')) /* cursor up */
478 telnet_history_up(connection
);
479 else if (*buf_p
== CTRL('N')) /* cursor down */
480 telnet_history_down(connection
);
482 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
486 case TELNET_STATE_IAC
:
489 t_con
->state
= TELNET_STATE_DONT
;
492 t_con
->state
= TELNET_STATE_DO
;
495 t_con
->state
= TELNET_STATE_WONT
;
498 t_con
->state
= TELNET_STATE_WILL
;
502 case TELNET_STATE_SB
:
504 case TELNET_STATE_SE
:
506 case TELNET_STATE_WILL
:
507 case TELNET_STATE_WONT
:
508 case TELNET_STATE_DO
:
509 case TELNET_STATE_DONT
:
510 t_con
->state
= TELNET_STATE_DATA
;
512 case TELNET_STATE_ESCAPE
:
513 if (t_con
->last_escape
== '[') {
514 if (*buf_p
== 'D') { /* cursor left */
515 if (t_con
->line_cursor
> 0) {
516 telnet_write(connection
, "\b", 1);
517 t_con
->line_cursor
--;
519 t_con
->state
= TELNET_STATE_DATA
;
520 } else if (*buf_p
== 'C') { /* cursor right */
521 if (t_con
->line_cursor
< t_con
->line_size
)
522 telnet_write(connection
,
523 t_con
->line
+ t_con
->line_cursor
++, 1);
524 t_con
->state
= TELNET_STATE_DATA
;
525 } else if (*buf_p
== 'A') { /* cursor up */
526 telnet_history_up(connection
);
527 } else if (*buf_p
== 'B') { /* cursor down */
528 telnet_history_down(connection
);
529 } else if (*buf_p
== '3')
530 t_con
->last_escape
= *buf_p
;
532 t_con
->state
= TELNET_STATE_DATA
;
533 } else if (t_con
->last_escape
== '3') {
534 /* Remove character */
536 if (t_con
->line_cursor
< t_con
->line_size
) {
539 /* remove char from line buffer */
540 memmove(t_con
->line
+ t_con
->line_cursor
,
541 t_con
->line
+ t_con
->line_cursor
+ 1,
542 t_con
->line_size
- t_con
->line_cursor
);
544 /* print remainder of buffer */
545 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
,
546 t_con
->line_size
- t_con
->line_cursor
);
547 /* overwrite last char with whitespace */
548 telnet_write(connection
, " \b", 2);
550 /* move back to cursor position*/
551 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
552 telnet_write(connection
, "\b", 1);
555 t_con
->state
= TELNET_STATE_DATA
;
557 t_con
->state
= TELNET_STATE_DATA
;
558 } else if (t_con
->last_escape
== '\x00') {
560 t_con
->last_escape
= *buf_p
;
562 t_con
->state
= TELNET_STATE_DATA
;
564 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
565 t_con
->state
= TELNET_STATE_DATA
;
570 LOG_ERROR("unknown telnet state");
581 static int telnet_connection_closed(struct connection
*connection
)
583 struct telnet_connection
*t_con
= connection
->priv
;
586 log_remove_callback(telnet_log_callback
, connection
);
590 t_con
->prompt
= NULL
;
593 /* save telnet history */
594 telnet_save_history(t_con
);
596 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++) {
597 if (t_con
->history
[i
]) {
598 free(t_con
->history
[i
]);
599 t_con
->history
[i
] = NULL
;
603 /* if this connection registered a debug-message receiver delete it */
604 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
606 if (connection
->priv
) {
607 free(connection
->priv
);
608 connection
->priv
= NULL
;
610 LOG_ERROR("BUG: connection->priv == NULL");
615 int telnet_init(char *banner
)
617 if (strcmp(telnet_port
, "disabled") == 0) {
618 LOG_INFO("telnet server disabled");
622 struct telnet_service
*telnet_service
= malloc(sizeof(struct telnet_service
));
624 telnet_service
->banner
= banner
;
626 return add_service("telnet",
629 telnet_new_connection
,
631 telnet_connection_closed
,
635 /* daemon configuration command telnet_port */
636 COMMAND_HANDLER(handle_telnet_port_command
)
638 return CALL_COMMAND_HANDLER(server_pipe_command
, &telnet_port
);
641 COMMAND_HANDLER(handle_exit_command
)
643 return ERROR_COMMAND_CLOSE_CONNECTION
;
646 static const struct command_registration telnet_command_handlers
[] = {
649 .handler
= handle_exit_command
,
650 .mode
= COMMAND_EXEC
,
652 .help
= "exit telnet session",
655 .name
= "telnet_port",
656 .handler
= handle_telnet_port_command
,
658 .help
= "Specify port on which to listen "
659 "for incoming telnet connections. "
660 "Read help on 'gdb_port'.",
661 .usage
= "[port_num]",
663 COMMAND_REGISTRATION_DONE
666 int telnet_register_commands(struct command_context
*cmd_ctx
)
668 telnet_port
= strdup("4444");
669 return register_commands(cmd_ctx
, NULL
, telnet_command_handlers
);
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)