1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2007,2008 Ø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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
30 #include "telnet_server.h"
31 #include <target/target_request.h>
33 static unsigned short telnet_port
= 4444;
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 - '@')
43 /* The only way we can detect that the socket is closed is the first time
44 * we write to it, we will fail. Subsequent write operations will
47 int telnet_write(struct connection
*connection
, const void *data
, int len
)
49 struct telnet_connection
*t_con
= connection
->priv
;
51 return ERROR_SERVER_REMOTE_CLOSED
;
53 if (write_socket(connection
->fd
, data
, len
) == len
)
58 return ERROR_SERVER_REMOTE_CLOSED
;
61 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 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
);
84 telnet_write(connection
, "\r\n", 2);
96 int telnet_output(struct command_context
*cmd_ctx
, const char* line
)
98 struct connection
*connection
= cmd_ctx
->output_handler_priv
;
100 return telnet_outputline(connection
, line
);
103 void telnet_log_callback(void *priv
, const char *file
, unsigned line
,
104 const char *function
, const char *string
)
106 struct connection
*connection
= priv
;
107 struct telnet_connection
*t_con
= connection
->priv
;
110 /* if there is no prompt, simply output the message */
111 if (t_con
->line_cursor
< 0)
113 telnet_outputline(connection
, string
);
117 /* clear the command line */
118 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
119 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i
> 16 ? 16 : i
);
120 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
121 telnet_write(connection
, " ", i
> 16 ? 16 : i
);
122 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
> 0; i
-= 16)
123 telnet_write(connection
, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i
> 16 ? 16 : i
);
125 /* output the message */
126 telnet_outputline(connection
, string
);
128 /* put the command line to its previous state */
129 telnet_prompt(connection
);
130 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
131 for (i
= t_con
->line_size
; i
> t_con
->line_cursor
; i
--)
132 telnet_write(connection
, "\b", 1);
135 int telnet_new_connection(struct connection
*connection
)
137 struct telnet_connection
*telnet_connection
= malloc(sizeof(struct telnet_connection
));
138 struct telnet_service
*telnet_service
= connection
->service
->priv
;
141 connection
->priv
= telnet_connection
;
143 /* initialize telnet connection information */
144 telnet_connection
->closed
= 0;
145 telnet_connection
->line_size
= 0;
146 telnet_connection
->line_cursor
= 0;
147 telnet_connection
->option_size
= 0;
148 telnet_connection
->prompt
= strdup("> ");
149 telnet_connection
->state
= TELNET_STATE_DATA
;
151 /* output goes through telnet connection */
152 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
154 /* negotiate telnet options */
155 telnet_write(connection
, negotiate
, strlen(negotiate
));
157 /* print connection banner */
158 if (telnet_service
->banner
)
160 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
161 telnet_write(connection
, "\r\n", 2);
164 telnet_write(connection
, "\r", 1); /* the prompt is always placed at the line beginning */
165 telnet_prompt(connection
);
167 /* initialize history */
168 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
170 telnet_connection
->history
[i
] = NULL
;
172 telnet_connection
->next_history
= 0;
173 telnet_connection
->current_history
= 0;
175 log_add_callback(telnet_log_callback
, connection
);
180 void telnet_clear_line(struct connection
*connection
, struct telnet_connection
*t_con
)
182 /* move to end of line */
183 if (t_con
->line_cursor
< t_con
->line_size
)
185 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
188 /* backspace, overwrite with space, backspace */
189 while (t_con
->line_size
> 0)
191 telnet_write(connection
, "\b \b", 3);
194 t_con
->line_cursor
= 0;
197 int telnet_input(struct connection
*connection
)
200 unsigned char buffer
[TELNET_BUFFER_SIZE
];
201 unsigned char *buf_p
;
202 struct telnet_connection
*t_con
= connection
->priv
;
203 struct command_context
*command_context
= connection
->cmd_ctx
;
205 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
208 return ERROR_SERVER_REMOTE_CLOSED
;
209 else if (bytes_read
== -1)
211 LOG_ERROR("error during read: %s", strerror(errno
));
212 return ERROR_SERVER_REMOTE_CLOSED
;
218 switch (t_con
->state
)
220 case TELNET_STATE_DATA
:
223 t_con
->state
= TELNET_STATE_IAC
;
227 if (isprint(*buf_p
)) /* printable character */
229 /* watch buffer size leaving one spare character for string null termination */
230 if (t_con
->line_size
== TELNET_LINE_MAX_SIZE
-1)
232 /* output audible bell if buffer is full */
233 telnet_write(connection
, "\x07", 1); /* "\a" does not work, at least on windows */
235 else if (t_con
->line_cursor
== t_con
->line_size
)
237 telnet_write(connection
, buf_p
, 1);
238 t_con
->line
[t_con
->line_size
++] = *buf_p
;
239 t_con
->line_cursor
++;
244 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
245 t_con
->line
[t_con
->line_cursor
] = *buf_p
;
247 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
248 t_con
->line_cursor
++;
249 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
251 telnet_write(connection
, "\b", 1);
255 else /* non-printable */
257 if (*buf_p
== 0x1b) /* escape */
259 t_con
->state
= TELNET_STATE_ESCAPE
;
260 t_con
->last_escape
= '\x00';
262 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
266 /* skip over combinations with CR/LF and NUL characters */
267 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)))
272 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0))
277 t_con
->line
[t_con
->line_size
] = 0;
279 telnet_write(connection
, "\r\n\x00", 3);
281 if (strcmp(t_con
->line
, "history") == 0)
284 for (i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
286 /* the t_con->next_history line contains empty string (unless NULL), thus it is not printed */
287 char *history_line
= t_con
->history
[(t_con
->next_history
+ i
) % TELNET_LINE_HISTORY_SIZE
];
290 telnet_write(connection
, history_line
, strlen(history_line
));
291 telnet_write(connection
, "\r\n\x00", 3);
294 t_con
->line_size
= 0;
295 t_con
->line_cursor
= 0;
299 /* save only non-blank not repeating lines in the history */
300 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
301 if (*t_con
->line
&& (prev_line
== NULL
|| strcmp(t_con
->line
, prev_line
)))
303 /* if the history slot is already taken, free it */
304 if (t_con
->history
[t_con
->next_history
])
306 free(t_con
->history
[t_con
->next_history
]);
309 /* add line to history */
310 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
312 /* wrap history at TELNET_LINE_HISTORY_SIZE */
313 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
315 /* current history line starts at the new entry */
316 t_con
->current_history
= t_con
->next_history
;
318 if (t_con
->history
[t_con
->current_history
])
320 free(t_con
->history
[t_con
->current_history
]);
322 t_con
->history
[t_con
->current_history
] = strdup("");
325 t_con
->line_size
= 0;
327 t_con
->line_cursor
= -1; /* to supress prompt in log callback during command execution */
329 retval
= command_run_line(command_context
, t_con
->line
);
331 t_con
->line_cursor
= 0;
333 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
334 return ERROR_SERVER_REMOTE_CLOSED
;
336 telnet_write(connection
, "\r", 1); /* the prompt is always placed at the line beginning */
337 retval
= telnet_prompt(connection
);
338 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
339 return ERROR_SERVER_REMOTE_CLOSED
;
342 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
344 if (t_con
->line_cursor
> 0)
346 if (t_con
->line_cursor
!= t_con
->line_size
)
349 telnet_write(connection
, "\b", 1);
350 t_con
->line_cursor
--;
352 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
354 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
355 telnet_write(connection
, " \b", 2);
356 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
358 telnet_write(connection
, "\b", 1);
364 t_con
->line_cursor
--;
365 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
366 telnet_write(connection
, "\b \b", 3);
370 else if (*buf_p
== 0x15) /* clear line */
372 telnet_clear_line(connection
, t_con
);
374 else if (*buf_p
== CTRL('B')) /* cursor left */
376 if (t_con
->line_cursor
> 0)
378 telnet_write(connection
, "\b", 1);
379 t_con
->line_cursor
--;
381 t_con
->state
= TELNET_STATE_DATA
;
383 else if (*buf_p
== CTRL('F')) /* cursor right */
385 if (t_con
->line_cursor
< t_con
->line_size
)
387 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
389 t_con
->state
= TELNET_STATE_DATA
;
393 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
398 case TELNET_STATE_IAC
:
402 t_con
->state
= TELNET_STATE_DONT
;
405 t_con
->state
= TELNET_STATE_DO
;
408 t_con
->state
= TELNET_STATE_WONT
;
411 t_con
->state
= TELNET_STATE_WILL
;
415 case TELNET_STATE_SB
:
417 case TELNET_STATE_SE
:
419 case TELNET_STATE_WILL
:
420 case TELNET_STATE_WONT
:
421 case TELNET_STATE_DO
:
422 case TELNET_STATE_DONT
:
423 t_con
->state
= TELNET_STATE_DATA
;
425 case TELNET_STATE_ESCAPE
:
426 if (t_con
->last_escape
== '[')
428 if (*buf_p
== 'D') /* cursor left */
430 if (t_con
->line_cursor
> 0)
432 telnet_write(connection
, "\b", 1);
433 t_con
->line_cursor
--;
435 t_con
->state
= TELNET_STATE_DATA
;
437 else if (*buf_p
== 'C') /* cursor right */
439 if (t_con
->line_cursor
< t_con
->line_size
)
441 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
443 t_con
->state
= TELNET_STATE_DATA
;
445 else if (*buf_p
== 'A') /* cursor up */
447 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
448 if (t_con
->history
[last_history
])
450 telnet_clear_line(connection
, t_con
);
451 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
452 t_con
->line_cursor
= t_con
->line_size
;
453 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
);
454 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
455 t_con
->current_history
= last_history
;
457 t_con
->state
= TELNET_STATE_DATA
;
459 else if (*buf_p
== 'B') /* cursor down */
461 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
462 if (t_con
->history
[next_history
])
464 telnet_clear_line(connection
, t_con
);
465 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
466 t_con
->line_cursor
= t_con
->line_size
;
467 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
);
468 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
469 t_con
->current_history
= next_history
;
471 t_con
->state
= TELNET_STATE_DATA
;
473 else if (*buf_p
== '3')
475 t_con
->last_escape
= *buf_p
;
479 t_con
->state
= TELNET_STATE_DATA
;
482 else if (t_con
->last_escape
== '3')
484 /* Remove character */
487 if (t_con
->line_cursor
< t_con
->line_size
)
491 /* remove char from line buffer */
492 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
494 /* print remainder of buffer */
495 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
496 /* overwrite last char with whitespace */
497 telnet_write(connection
, " \b", 2);
499 /* move back to cursor position*/
500 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
502 telnet_write(connection
, "\b", 1);
506 t_con
->state
= TELNET_STATE_DATA
;
510 t_con
->state
= TELNET_STATE_DATA
;
513 else if (t_con
->last_escape
== '\x00')
517 t_con
->last_escape
= *buf_p
;
521 t_con
->state
= TELNET_STATE_DATA
;
526 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
527 t_con
->state
= TELNET_STATE_DATA
;
532 LOG_ERROR("unknown telnet state");
543 int telnet_connection_closed(struct connection
*connection
)
545 struct telnet_connection
*t_con
= connection
->priv
;
548 log_remove_callback(telnet_log_callback
, connection
);
553 t_con
->prompt
= NULL
;
556 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
558 if (t_con
->history
[i
])
560 free(t_con
->history
[i
]);
561 t_con
->history
[i
] = NULL
;
565 /* if this connection registered a debug-message receiver delete it */
566 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
568 if (connection
->priv
)
570 free(connection
->priv
);
571 connection
->priv
= NULL
;
575 LOG_ERROR("BUG: connection->priv == NULL");
581 int telnet_set_prompt(struct connection
*connection
, char *prompt
)
583 struct telnet_connection
*t_con
= connection
->priv
;
585 if (t_con
->prompt
!= NULL
)
588 t_con
->prompt
= strdup(prompt
);
593 int telnet_init(char *banner
)
595 struct telnet_service
*telnet_service
= malloc(sizeof(struct telnet_service
));
597 if (telnet_port
== 0)
599 LOG_INFO("telnet port disabled");
600 free(telnet_service
);
604 telnet_service
->banner
= banner
;
606 add_service("telnet", CONNECTION_TCP
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
611 /* daemon configuration command telnet_port */
612 COMMAND_HANDLER(handle_telnet_port_command
)
614 return CALL_COMMAND_HANDLER(server_port_command
, &telnet_port
);
617 COMMAND_HANDLER(handle_exit_command
)
619 return ERROR_COMMAND_CLOSE_CONNECTION
;
622 static const struct command_registration telnet_command_handlers
[] = {
625 .handler
= handle_exit_command
,
626 .mode
= COMMAND_EXEC
,
627 .help
= "exit telnet session",
630 .name
= "telnet_port",
631 .handler
= handle_telnet_port_command
,
633 .help
= "Specify port on which to listen "
634 "for incoming telnet connections. "
635 "No arguments reports telnet port; zero disables.",
636 .usage
= "[port_num]",
638 COMMAND_REGISTRATION_DONE
641 int telnet_register_commands(struct command_context
*cmd_ctx
)
643 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)