1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
24 #include "replacements.h"
26 #include "telnet_server.h"
32 #include "target_request.h"
40 static unsigned short telnet_port
= 0;
42 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
43 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
45 static char *negotiate
=
46 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
47 "\xFF\xFB\x01" /* IAC WILL Echo */
48 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
49 "\xFF\xFE\x01"; /* IAC DON'T Echo */
51 #define CTRL(c) (c - '@')
53 /* The only way we can detect that the socket is closed is the first time
54 * we write to it, we will fail. Subsequent write operations will
57 int telnet_write(connection_t
*connection
, const void *data
, int len
)
59 telnet_connection_t
*t_con
= connection
->priv
;
61 return ERROR_SERVER_REMOTE_CLOSED
;
63 if (write_socket(connection
->fd
, data
, len
) == len
)
68 return ERROR_SERVER_REMOTE_CLOSED
;
71 int telnet_prompt(connection_t
*connection
)
73 telnet_connection_t
*t_con
= connection
->priv
;
75 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
78 int telnet_outputline(connection_t
*connection
, const char *line
)
82 /* process lines in buffer */
84 char *line_end
= strchr(line
, '\n');
91 telnet_write(connection
, line
, len
);
94 telnet_write(connection
, "\r\n\0", 3);
106 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
108 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
110 return telnet_outputline(connection
, line
);
113 void telnet_log_callback(void *priv
, const char *file
, int line
,
114 const char *function
, const char *string
)
116 connection_t
*connection
= priv
;
117 telnet_outputline(connection
, string
);
120 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
122 struct command_context_s
*cmd_ctx
= priv
;
123 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
124 telnet_connection_t
*t_con
= connection
->priv
;
128 case TARGET_EVENT_HALTED
:
129 target_arch_state(target
);
130 if (!t_con
->suppress_prompt
)
131 telnet_prompt(connection
);
133 case TARGET_EVENT_RESUMED
:
134 if (!t_con
->suppress_prompt
)
135 telnet_prompt(connection
);
144 int telnet_new_connection(connection_t
*connection
)
146 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
147 telnet_service_t
*telnet_service
= connection
->service
->priv
;
150 connection
->priv
= telnet_connection
;
152 /* initialize telnet connection information */
153 telnet_connection
->closed
= 0;
154 telnet_connection
->line_size
= 0;
155 telnet_connection
->line_cursor
= 0;
156 telnet_connection
->option_size
= 0;
157 telnet_connection
->prompt
= strdup("> ");
158 telnet_connection
->suppress_prompt
= 0;
159 telnet_connection
->state
= TELNET_STATE_DATA
;
161 /* output goes through telnet connection */
162 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
164 /* negotiate telnet options */
165 telnet_write(connection
, negotiate
, strlen(negotiate
));
167 /* print connection banner */
168 if (telnet_service
->banner
)
170 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
171 telnet_write(connection
, "\r\n\0", 3);
174 telnet_prompt(connection
);
176 /* initialize history */
177 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
179 telnet_connection
->history
[i
] = NULL
;
181 telnet_connection
->next_history
= 0;
182 telnet_connection
->current_history
= 0;
184 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
189 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
191 /* move to end of line */
192 if (t_con
->line_cursor
< t_con
->line_size
)
194 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
197 /* backspace, overwrite with space, backspace */
198 while (t_con
->line_size
> 0)
200 telnet_write(connection
, "\b \b", 3);
203 t_con
->line_cursor
= 0;
206 int telnet_input(connection_t
*connection
)
209 char buffer
[TELNET_BUFFER_SIZE
];
211 telnet_connection_t
*t_con
= connection
->priv
;
212 command_context_t
*command_context
= connection
->cmd_ctx
;
214 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
217 return ERROR_SERVER_REMOTE_CLOSED
;
218 else if (bytes_read
== -1)
220 ERROR("error during read: %s", strerror(errno
));
221 return ERROR_SERVER_REMOTE_CLOSED
;
227 switch (t_con
->state
)
229 case TELNET_STATE_DATA
:
230 if (*buf_p
== '\xff')
232 t_con
->state
= TELNET_STATE_IAC
;
236 if (isprint(*buf_p
)) /* printable character */
238 telnet_write(connection
, buf_p
, 1);
239 if (t_con
->line_cursor
== t_con
->line_size
)
241 t_con
->line
[t_con
->line_size
++] = *buf_p
;
242 t_con
->line_cursor
++;
247 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
248 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
250 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
251 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
253 telnet_write(connection
, "\b", 1);
257 else /* non-printable */
259 if (*buf_p
== 0x1b) /* escape */
261 t_con
->state
= TELNET_STATE_ESCAPE
;
262 t_con
->last_escape
= '\x00';
264 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
268 /* skip over combinations with CR/LF + NUL */
269 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
274 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
279 t_con
->line
[t_con
->line_size
] = 0;
281 telnet_write(connection
, "\r\n\x00", 3);
283 if (strcmp(t_con
->line
, "history") == 0)
286 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
288 if (t_con
->history
[i
])
290 telnet_write(connection
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
291 telnet_write(connection
, "\r\n\x00", 3);
294 telnet_prompt(connection
);
295 t_con
->line_size
= 0;
296 t_con
->line_cursor
= 0;
300 log_add_callback(telnet_log_callback
, connection
);
301 t_con
->suppress_prompt
= 1;
303 retval
= command_run_line(command_context
, t_con
->line
);
305 log_remove_callback(telnet_log_callback
, connection
);
306 t_con
->suppress_prompt
= 0;
308 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
310 return ERROR_SERVER_REMOTE_CLOSED
;
313 /* Save only non-blank lines in the history */
314 if (t_con
->line_size
> 0)
316 /* if the history slot is already taken, free it */
317 if (t_con
->history
[t_con
->next_history
])
319 free(t_con
->history
[t_con
->next_history
]);
322 /* add line to history */
323 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
325 /* wrap history at TELNET_LINE_HISTORY_SIZE */
326 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
328 /* current history line starts at the new entry */
329 t_con
->current_history
= t_con
->next_history
;
331 if (t_con
->history
[t_con
->current_history
])
333 free(t_con
->history
[t_con
->current_history
]);
335 t_con
->history
[t_con
->current_history
] = strdup("");
338 int t
= telnet_prompt(connection
);
339 if (t
== ERROR_SERVER_REMOTE_CLOSED
)
342 t_con
->line_size
= 0;
343 t_con
->line_cursor
= 0;
345 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
347 if (t_con
->line_cursor
> 0)
349 if (t_con
->line_cursor
!= t_con
->line_size
)
352 telnet_write(connection
, "\b", 1);
353 t_con
->line_cursor
--;
355 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
357 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
358 telnet_write(connection
, " \b", 2);
359 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
361 telnet_write(connection
, "\b", 1);
367 t_con
->line_cursor
--;
368 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
369 telnet_write(connection
, "\b \b", 3);
373 else if (*buf_p
== 0x15) /* clear line */
375 telnet_clear_line(connection
, t_con
);
377 else if (*buf_p
== CTRL('B')) /* cursor left */
379 if (t_con
->line_cursor
> 0)
381 telnet_write(connection
, "\b", 1);
382 t_con
->line_cursor
--;
384 t_con
->state
= TELNET_STATE_DATA
;
386 else if (*buf_p
== CTRL('F')) /* cursor right */
388 if (t_con
->line_cursor
< t_con
->line_size
)
390 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
392 t_con
->state
= TELNET_STATE_DATA
;
396 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
401 case TELNET_STATE_IAC
:
405 t_con
->state
= TELNET_STATE_DONT
;
408 t_con
->state
= TELNET_STATE_DO
;
411 t_con
->state
= TELNET_STATE_WONT
;
414 t_con
->state
= TELNET_STATE_WILL
;
418 case TELNET_STATE_SB
:
420 case TELNET_STATE_SE
:
422 case TELNET_STATE_WILL
:
423 case TELNET_STATE_WONT
:
424 case TELNET_STATE_DO
:
425 case TELNET_STATE_DONT
:
426 t_con
->state
= TELNET_STATE_DATA
;
428 case TELNET_STATE_ESCAPE
:
429 if (t_con
->last_escape
== '[')
431 if (*buf_p
== 'D') /* cursor left */
433 if (t_con
->line_cursor
> 0)
435 telnet_write(connection
, "\b", 1);
436 t_con
->line_cursor
--;
438 t_con
->state
= TELNET_STATE_DATA
;
440 else if (*buf_p
== 'C') /* cursor right */
442 if (t_con
->line_cursor
< t_con
->line_size
)
444 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
446 t_con
->state
= TELNET_STATE_DATA
;
448 else if (*buf_p
== 'A') /* cursor up */
450 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
451 if (t_con
->history
[last_history
])
453 telnet_clear_line(connection
, t_con
);
454 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
455 t_con
->line_cursor
= t_con
->line_size
;
456 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
457 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
458 t_con
->current_history
= last_history
;
460 t_con
->state
= TELNET_STATE_DATA
;
462 else if (*buf_p
== 'B') /* cursor down */
464 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
465 if (t_con
->history
[next_history
])
467 telnet_clear_line(connection
, t_con
);
468 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
469 t_con
->line_cursor
= t_con
->line_size
;
470 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
471 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
472 t_con
->current_history
= next_history
;
474 t_con
->state
= TELNET_STATE_DATA
;
476 else if (*buf_p
== '3')
478 t_con
->last_escape
= *buf_p
;
482 t_con
->state
= TELNET_STATE_DATA
;
485 else if (t_con
->last_escape
== '3')
487 /* Remove character */
490 if (t_con
->line_cursor
< t_con
->line_size
)
494 /* remove char from line buffer */
495 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
497 /* print remainder of buffer */
498 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
499 /* overwrite last char with whitespace */
500 telnet_write(connection
, " \b", 2);
502 /* move back to cursor position*/
503 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
505 telnet_write(connection
, "\b", 1);
509 t_con
->state
= TELNET_STATE_DATA
;
513 t_con
->state
= TELNET_STATE_DATA
;
516 else if (t_con
->last_escape
== '\x00')
520 t_con
->last_escape
= *buf_p
;
524 t_con
->state
= TELNET_STATE_DATA
;
529 ERROR("BUG: unexpected value in t_con->last_escape");
530 t_con
->state
= TELNET_STATE_DATA
;
535 ERROR("unknown telnet state");
546 int telnet_connection_closed(connection_t
*connection
)
548 telnet_connection_t
*t_con
= connection
->priv
;
554 t_con
->prompt
= NULL
;
557 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
559 if (t_con
->history
[i
])
561 free(t_con
->history
[i
]);
562 t_con
->history
[i
] = NULL
;
566 /* if this connection registered a debug-message receiver delete it */
567 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
569 if (connection
->priv
)
571 free(connection
->priv
);
572 connection
->priv
= NULL
;
576 ERROR("BUG: connection->priv == NULL");
579 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
584 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
586 telnet_connection_t
*t_con
= connection
->priv
;
588 t_con
->prompt
= strdup(prompt
);
593 int telnet_init(char *banner
)
595 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
597 if (telnet_port
== 0)
599 WARNING("no telnet port specified, using default port 4444");
603 telnet_service
->banner
= banner
;
605 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
610 int telnet_register_commands(command_context_t
*command_context
)
612 register_command(command_context
, NULL
, "exit", handle_exit_command
,
613 COMMAND_EXEC
, "exit telnet session");
615 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
621 /* daemon configuration command telnet_port */
622 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
627 telnet_port
= strtoul(args
[0], NULL
, 0);
632 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
634 return ERROR_COMMAND_CLOSE_CONNECTION
;
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)