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 void telnet_prompt(connection_t
*connection
)
55 telnet_connection_t
*t_con
= connection
->priv
;
57 write_socket(connection
->fd
, t_con
->prompt
, strlen(t_con
->prompt
));
60 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
62 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
64 write_socket(connection
->fd
, line
, strlen(line
));
65 write_socket(connection
->fd
, "\r\n\0", 3);
70 void telnet_log_callback(void *priv
, const char *file
, int line
,
71 const char *function
, const char *format
, va_list args
)
73 connection_t
*connection
= priv
;
74 char *t
= allocPrintf(format
, args
);
78 telnet_output(connection
->cmd_ctx
, t
);
82 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
84 struct command_context_s
*cmd_ctx
= priv
;
85 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
86 telnet_connection_t
*t_con
= connection
->priv
;
91 case TARGET_EVENT_HALTED
:
92 command_print(cmd_ctx
, "Target %i halted", get_num_by_target(target
));
93 target
->type
->arch_state(target
, buffer
, 512);
95 command_print(cmd_ctx
, "%s", buffer
);
96 if (!t_con
->suppress_prompt
)
97 telnet_prompt(connection
);
99 case TARGET_EVENT_RESUMED
:
100 command_print(cmd_ctx
, "Target %i resumed", get_num_by_target(target
));
101 if (!t_con
->suppress_prompt
)
102 telnet_prompt(connection
);
111 int telnet_new_connection(connection_t
*connection
)
113 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
114 telnet_service_t
*telnet_service
= connection
->service
->priv
;
117 connection
->priv
= telnet_connection
;
119 /* initialize telnet connection information */
120 telnet_connection
->line_size
= 0;
121 telnet_connection
->line_cursor
= 0;
122 telnet_connection
->option_size
= 0;
123 telnet_connection
->prompt
= strdup("> ");
124 telnet_connection
->suppress_prompt
= 0;
125 telnet_connection
->state
= TELNET_STATE_DATA
;
127 /* output goes through telnet connection */
128 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
130 /* negotiate telnet options */
131 write_socket(connection
->fd
, negotiate
, strlen(negotiate
));
133 /* print connection banner */
134 if (telnet_service
->banner
)
136 write_socket(connection
->fd
, telnet_service
->banner
, strlen(telnet_service
->banner
));
137 write_socket(connection
->fd
, "\r\n\0", 3);
140 telnet_prompt(connection
);
142 /* initialize history */
143 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
145 telnet_connection
->history
[i
] = NULL
;
147 telnet_connection
->next_history
= 0;
148 telnet_connection
->current_history
= 0;
150 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
155 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
157 /* move to end of line */
158 if (t_con
->line_cursor
< t_con
->line_size
)
160 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
163 /* backspace, overwrite with space, backspace */
164 while (t_con
->line_size
> 0)
166 write_socket(connection
->fd
, "\b \b", 3);
169 t_con
->line_cursor
= 0;
172 int telnet_input(connection_t
*connection
)
175 char buffer
[TELNET_BUFFER_SIZE
];
177 telnet_connection_t
*t_con
= connection
->priv
;
178 command_context_t
*command_context
= connection
->cmd_ctx
;
180 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
183 return ERROR_SERVER_REMOTE_CLOSED
;
184 else if (bytes_read
== -1)
186 ERROR("error during read: %s", strerror(errno
));
187 return ERROR_SERVER_REMOTE_CLOSED
;
193 switch (t_con
->state
)
195 case TELNET_STATE_DATA
:
196 if (*buf_p
== '\xff')
198 t_con
->state
= TELNET_STATE_IAC
;
202 if (isprint(*buf_p
)) /* printable character */
204 write_socket(connection
->fd
, buf_p
, 1);
205 if (t_con
->line_cursor
== t_con
->line_size
)
207 t_con
->line
[t_con
->line_size
++] = *buf_p
;
208 t_con
->line_cursor
++;
213 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
214 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
216 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
217 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
219 write_socket(connection
->fd
, "\b", 1);
223 else /* non-printable */
225 if (*buf_p
== 0x1b) /* escape */
227 t_con
->state
= TELNET_STATE_ESCAPE
;
228 t_con
->last_escape
= '\x00';
230 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
234 /* skip over combinations with CR/LF + NUL */
235 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
240 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
245 t_con
->line
[t_con
->line_size
] = 0;
247 write_socket(connection
->fd
, "\r\n\x00", 3);
249 if (strcmp(t_con
->line
, "history") == 0)
252 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
254 if (t_con
->history
[i
])
256 write_socket(connection
->fd
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
257 write_socket(connection
->fd
, "\r\n\x00", 3);
260 telnet_prompt(connection
);
261 t_con
->line_size
= 0;
262 t_con
->line_cursor
= 0;
266 log_setCallback(telnet_log_callback
, connection
);
267 t_con
->suppress_prompt
= 1;
269 if ((retval
= command_run_line(command_context
, t_con
->line
)) != ERROR_OK
)
271 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
273 return ERROR_SERVER_REMOTE_CLOSED
;
277 t_con
->suppress_prompt
= 0;
279 /* Save only non-blank lines in the history */
280 if (t_con
->line_size
> 0)
282 /* if the history slot is already taken, free it */
283 if (t_con
->history
[t_con
->next_history
])
285 free(t_con
->history
[t_con
->next_history
]);
288 /* add line to history */
289 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
291 /* wrap history at TELNET_LINE_HISTORY_SIZE */
292 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
294 /* current history line starts at the new entry */
295 t_con
->current_history
= t_con
->next_history
;
297 if (t_con
->history
[t_con
->current_history
])
299 free(t_con
->history
[t_con
->current_history
]);
301 t_con
->history
[t_con
->current_history
] = strdup("");
304 /* output prompt after command */
305 telnet_prompt(connection
);
307 t_con
->line_size
= 0;
308 t_con
->line_cursor
= 0;
310 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
312 if (t_con
->line_cursor
> 0)
314 if (t_con
->line_cursor
!= t_con
->line_size
)
317 write_socket(connection
->fd
, "\b", 1);
318 t_con
->line_cursor
--;
320 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
322 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
323 write_socket(connection
->fd
, " \b", 2);
324 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
326 write_socket(connection
->fd
, "\b", 1);
332 t_con
->line_cursor
--;
333 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
334 write_socket(connection
->fd
, "\b \b", 3);
338 else if (*buf_p
== 0x15) /* clear line */
340 telnet_clear_line(connection
, t_con
);
342 else if (*buf_p
== CTRL('B')) /* cursor left */
344 if (t_con
->line_cursor
> 0)
346 write_socket(connection
->fd
, "\b", 1);
347 t_con
->line_cursor
--;
349 t_con
->state
= TELNET_STATE_DATA
;
351 else if (*buf_p
== CTRL('F')) /* cursor right */
353 if (t_con
->line_cursor
< t_con
->line_size
)
355 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
357 t_con
->state
= TELNET_STATE_DATA
;
361 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
366 case TELNET_STATE_IAC
:
370 t_con
->state
= TELNET_STATE_DONT
;
373 t_con
->state
= TELNET_STATE_DO
;
376 t_con
->state
= TELNET_STATE_WONT
;
379 t_con
->state
= TELNET_STATE_WILL
;
383 case TELNET_STATE_SB
:
385 case TELNET_STATE_SE
:
387 case TELNET_STATE_WILL
:
388 case TELNET_STATE_WONT
:
389 case TELNET_STATE_DO
:
390 case TELNET_STATE_DONT
:
391 t_con
->state
= TELNET_STATE_DATA
;
393 case TELNET_STATE_ESCAPE
:
394 if (t_con
->last_escape
== '[')
396 if (*buf_p
== 'D') /* cursor left */
398 if (t_con
->line_cursor
> 0)
400 write_socket(connection
->fd
, "\b", 1);
401 t_con
->line_cursor
--;
403 t_con
->state
= TELNET_STATE_DATA
;
405 else if (*buf_p
== 'C') /* cursor right */
407 if (t_con
->line_cursor
< t_con
->line_size
)
409 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
411 t_con
->state
= TELNET_STATE_DATA
;
413 else if (*buf_p
== 'A') /* cursor up */
415 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
416 if (t_con
->history
[last_history
])
418 telnet_clear_line(connection
, t_con
);
419 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
420 t_con
->line_cursor
= t_con
->line_size
;
421 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
422 write_socket(connection
->fd
, t_con
->line
, t_con
->line_size
);
423 t_con
->current_history
= last_history
;
425 t_con
->state
= TELNET_STATE_DATA
;
427 else if (*buf_p
== 'B') /* cursor down */
429 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
430 if (t_con
->history
[next_history
])
432 telnet_clear_line(connection
, t_con
);
433 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
434 t_con
->line_cursor
= t_con
->line_size
;
435 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
436 write_socket(connection
->fd
, t_con
->line
, t_con
->line_size
);
437 t_con
->current_history
= next_history
;
439 t_con
->state
= TELNET_STATE_DATA
;
441 else if (*buf_p
== '3')
443 t_con
->last_escape
= *buf_p
;
447 t_con
->state
= TELNET_STATE_DATA
;
450 else if (t_con
->last_escape
== '3')
452 /* Remove character */
455 if (t_con
->line_cursor
< t_con
->line_size
)
459 /* remove char from line buffer */
460 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
462 /* print remainder of buffer */
463 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
464 /* overwrite last char with whitespace */
465 write_socket(connection
->fd
, " \b", 2);
467 /* move back to cursor position*/
468 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
470 write_socket(connection
->fd
, "\b", 1);
474 t_con
->state
= TELNET_STATE_DATA
;
478 t_con
->state
= TELNET_STATE_DATA
;
481 else if (t_con
->last_escape
== '\x00')
485 t_con
->last_escape
= *buf_p
;
489 t_con
->state
= TELNET_STATE_DATA
;
494 ERROR("BUG: unexpected value in t_con->last_escape");
495 t_con
->state
= TELNET_STATE_DATA
;
500 ERROR("unknown telnet state");
511 int telnet_connection_closed(connection_t
*connection
)
513 telnet_connection_t
*t_con
= connection
->priv
;
519 t_con
->prompt
= NULL
;
522 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
524 if (t_con
->history
[i
])
526 free(t_con
->history
[i
]);
527 t_con
->history
[i
] = NULL
;
531 /* if this connection registered a debug-message receiver delete it */
532 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
534 if (connection
->priv
)
536 free(connection
->priv
);
537 connection
->priv
= NULL
;
541 ERROR("BUG: connection->priv == NULL");
544 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
549 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
551 telnet_connection_t
*t_con
= connection
->priv
;
553 t_con
->prompt
= strdup(prompt
);
558 int telnet_init(char *banner
)
560 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
562 if (telnet_port
== 0)
564 WARNING("no telnet port specified, using default port 4444");
568 telnet_service
->banner
= banner
;
570 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
575 int telnet_register_commands(command_context_t
*command_context
)
577 register_command(command_context
, NULL
, "exit", handle_exit_command
,
578 COMMAND_EXEC
, "exit telnet session");
580 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
586 /* daemon configuration command telnet_port */
587 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
592 /* only if the port wasn't overwritten by cmdline */
593 if (telnet_port
== 0)
594 telnet_port
= strtoul(args
[0], NULL
, 0);
599 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
601 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)