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 - '@')
54 /* The only way we can detect that the socket is closed is the first time
55 * we write to it, we will fail. Subsequent write operations will
58 int telnet_write(connection_t
*connection
, void *data
, int len
)
60 telnet_connection_t
*t_con
= connection
->priv
;
62 return ERROR_SERVER_REMOTE_CLOSED
;
64 if (write_socket(connection
->fd
, data
, len
)==len
)
69 return ERROR_SERVER_REMOTE_CLOSED
;
73 int telnet_prompt(connection_t
*connection
)
75 telnet_connection_t
*t_con
= connection
->priv
;
77 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
80 int telnet_outputline(connection_t
*connection
, char* line
)
82 telnet_write(connection
, line
, strlen(line
));
83 return telnet_write(connection
, "\r\n\0", 3);
87 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
89 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
91 return telnet_outputline(connection
, line
);
94 void telnet_log_callback(void *priv
, const char *file
, int line
,
95 const char *function
, const char *format
, va_list args
)
97 connection_t
*connection
= priv
;
98 char *t
=allocPrintf(format
, args
);
102 telnet_outputline(connection
, t
);
108 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
110 struct command_context_s
*cmd_ctx
= priv
;
115 case TARGET_EVENT_HALTED
:
116 command_print(cmd_ctx
, "Target %i halted", get_num_by_target(target
));
117 target
->type
->arch_state(target
, buffer
, 512);
119 command_print(cmd_ctx
, "%s", buffer
);
121 case TARGET_EVENT_RESUMED
:
122 command_print(cmd_ctx
, "Target %i resumed", get_num_by_target(target
));
131 int telnet_new_connection(connection_t
*connection
)
133 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
134 telnet_service_t
*telnet_service
= connection
->service
->priv
;
137 connection
->priv
= telnet_connection
;
139 /* initialize telnet connection information */
140 telnet_connection
->closed
= 0;
141 telnet_connection
->line_size
= 0;
142 telnet_connection
->line_cursor
= 0;
143 telnet_connection
->option_size
= 0;
144 telnet_connection
->prompt
= strdup("> ");
145 telnet_connection
->state
= TELNET_STATE_DATA
;
147 /* output goes through telnet connection */
148 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
150 /* negotiate telnet options */
151 telnet_write(connection
, negotiate
, strlen(negotiate
));
153 /* print connection banner */
154 if (telnet_service
->banner
)
156 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
157 telnet_write(connection
, "\r\n\0", 3);
160 telnet_prompt(connection
);
162 /* initialize history */
163 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
165 telnet_connection
->history
[i
] = NULL
;
167 telnet_connection
->next_history
= 0;
168 telnet_connection
->current_history
= 0;
170 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
175 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
177 /* move to end of line */
178 if (t_con
->line_cursor
< t_con
->line_size
)
180 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
183 /* backspace, overwrite with space, backspace */
184 while (t_con
->line_size
> 0)
186 telnet_write(connection
, "\b \b", 3);
189 t_con
->line_cursor
= 0;
192 int telnet_input(connection_t
*connection
)
195 char buffer
[TELNET_BUFFER_SIZE
];
197 telnet_connection_t
*t_con
= connection
->priv
;
198 command_context_t
*command_context
= connection
->cmd_ctx
;
200 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
203 return ERROR_SERVER_REMOTE_CLOSED
;
204 else if (bytes_read
== -1)
206 ERROR("error during read: %s", strerror(errno
));
207 return ERROR_SERVER_REMOTE_CLOSED
;
213 switch (t_con
->state
)
215 case TELNET_STATE_DATA
:
216 if (*buf_p
== '\xff')
218 t_con
->state
= TELNET_STATE_IAC
;
222 if (isprint(*buf_p
)) /* printable character */
224 telnet_write(connection
, buf_p
, 1);
225 if (t_con
->line_cursor
== t_con
->line_size
)
227 t_con
->line
[t_con
->line_size
++] = *buf_p
;
228 t_con
->line_cursor
++;
233 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
234 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
236 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
237 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
239 telnet_write(connection
, "\b", 1);
243 else /* non-printable */
245 if (*buf_p
== 0x1b) /* escape */
247 t_con
->state
= TELNET_STATE_ESCAPE
;
248 t_con
->last_escape
= '\x00';
250 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
254 /* skip over combinations with CR/LF + NUL */
255 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
260 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
265 t_con
->line
[t_con
->line_size
] = 0;
267 telnet_write(connection
, "\r\n\x00", 3);
269 if (strcmp(t_con
->line
, "history") == 0)
272 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
274 if (t_con
->history
[i
])
276 telnet_write(connection
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
277 telnet_write(connection
, "\r\n\x00", 3);
280 telnet_prompt(connection
);
281 t_con
->line_size
= 0;
282 t_con
->line_cursor
= 0;
286 log_setCallback(telnet_log_callback
, connection
);
288 if ((retval
= command_run_line(command_context
, t_con
->line
)) != ERROR_OK
)
290 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
292 return ERROR_SERVER_REMOTE_CLOSED
;
296 /* Save only non-blank lines in the history */
297 if (t_con
->line_size
> 0)
299 /* if the history slot is already taken, free it */
300 if (t_con
->history
[t_con
->next_history
])
302 free(t_con
->history
[t_con
->next_history
]);
305 /* add line to history */
306 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
308 /* wrap history at TELNET_LINE_HISTORY_SIZE */
309 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
311 /* current history line starts at the new entry */
312 t_con
->current_history
= t_con
->next_history
;
314 if (t_con
->history
[t_con
->current_history
])
316 free(t_con
->history
[t_con
->current_history
]);
318 t_con
->history
[t_con
->current_history
] = strdup("");
321 int t
=telnet_prompt(connection
);
322 if (t
==ERROR_SERVER_REMOTE_CLOSED
)
325 t_con
->line_size
= 0;
326 t_con
->line_cursor
= 0;
328 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
330 if (t_con
->line_cursor
> 0)
332 if (t_con
->line_cursor
!= t_con
->line_size
)
335 telnet_write(connection
, "\b", 1);
336 t_con
->line_cursor
--;
338 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
340 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
341 telnet_write(connection
, " \b", 2);
342 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
344 telnet_write(connection
, "\b", 1);
350 t_con
->line_cursor
--;
351 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
352 telnet_write(connection
, "\b \b", 3);
356 else if (*buf_p
== 0x15) /* clear line */
358 telnet_clear_line(connection
, t_con
);
360 else if (*buf_p
== CTRL('B')) /* cursor left */
362 if (t_con
->line_cursor
> 0)
364 telnet_write(connection
, "\b", 1);
365 t_con
->line_cursor
--;
367 t_con
->state
= TELNET_STATE_DATA
;
369 else if (*buf_p
== CTRL('F')) /* cursor right */
371 if (t_con
->line_cursor
< t_con
->line_size
)
373 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
375 t_con
->state
= TELNET_STATE_DATA
;
379 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
384 case TELNET_STATE_IAC
:
388 t_con
->state
= TELNET_STATE_DONT
;
391 t_con
->state
= TELNET_STATE_DO
;
394 t_con
->state
= TELNET_STATE_WONT
;
397 t_con
->state
= TELNET_STATE_WILL
;
401 case TELNET_STATE_SB
:
403 case TELNET_STATE_SE
:
405 case TELNET_STATE_WILL
:
406 case TELNET_STATE_WONT
:
407 case TELNET_STATE_DO
:
408 case TELNET_STATE_DONT
:
409 t_con
->state
= TELNET_STATE_DATA
;
411 case TELNET_STATE_ESCAPE
:
412 if (t_con
->last_escape
== '[')
414 if (*buf_p
== 'D') /* cursor left */
416 if (t_con
->line_cursor
> 0)
418 telnet_write(connection
, "\b", 1);
419 t_con
->line_cursor
--;
421 t_con
->state
= TELNET_STATE_DATA
;
423 else if (*buf_p
== 'C') /* cursor right */
425 if (t_con
->line_cursor
< t_con
->line_size
)
427 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
429 t_con
->state
= TELNET_STATE_DATA
;
431 else if (*buf_p
== 'A') /* cursor up */
433 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
434 if (t_con
->history
[last_history
])
436 telnet_clear_line(connection
, t_con
);
437 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
438 t_con
->line_cursor
= t_con
->line_size
;
439 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
440 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
441 t_con
->current_history
= last_history
;
443 t_con
->state
= TELNET_STATE_DATA
;
445 else if (*buf_p
== 'B') /* cursor down */
447 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
448 if (t_con
->history
[next_history
])
450 telnet_clear_line(connection
, t_con
);
451 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
452 t_con
->line_cursor
= t_con
->line_size
;
453 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
454 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
455 t_con
->current_history
= next_history
;
457 t_con
->state
= TELNET_STATE_DATA
;
459 else if (*buf_p
== '3')
461 t_con
->last_escape
= *buf_p
;
465 t_con
->state
= TELNET_STATE_DATA
;
468 else if (t_con
->last_escape
== '3')
470 /* Remove character */
473 if (t_con
->line_cursor
< t_con
->line_size
)
477 /* remove char from line buffer */
478 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
480 /* print remainder of buffer */
481 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
482 /* overwrite last char with whitespace */
483 telnet_write(connection
, " \b", 2);
485 /* move back to cursor position*/
486 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
488 telnet_write(connection
, "\b", 1);
492 t_con
->state
= TELNET_STATE_DATA
;
496 t_con
->state
= TELNET_STATE_DATA
;
499 else if (t_con
->last_escape
== '\x00')
503 t_con
->last_escape
= *buf_p
;
507 t_con
->state
= TELNET_STATE_DATA
;
512 ERROR("BUG: unexpected value in t_con->last_escape");
513 t_con
->state
= TELNET_STATE_DATA
;
518 ERROR("unknown telnet state");
529 int telnet_connection_closed(connection_t
*connection
)
531 telnet_connection_t
*t_con
= connection
->priv
;
537 t_con
->prompt
= NULL
;
540 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
542 if (t_con
->history
[i
])
544 free(t_con
->history
[i
]);
545 t_con
->history
[i
] = NULL
;
549 /* if this connection registered a debug-message receiver delete it */
550 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
552 if (connection
->priv
)
554 free(connection
->priv
);
555 connection
->priv
= NULL
;
559 ERROR("BUG: connection->priv == NULL");
562 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
567 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
569 telnet_connection_t
*t_con
= connection
->priv
;
571 t_con
->prompt
= strdup(prompt
);
576 int telnet_init(char *banner
)
578 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
580 if (telnet_port
== 0)
582 WARNING("no telnet port specified, using default port 4444");
586 telnet_service
->banner
= banner
;
588 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
593 int telnet_register_commands(command_context_t
*command_context
)
595 register_command(command_context
, NULL
, "exit", handle_exit_command
,
596 COMMAND_EXEC
, "exit telnet session");
598 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
604 /* daemon configuration command telnet_port */
605 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
610 /* only if the port wasn't overwritten by cmdline */
611 if (telnet_port
== 0)
612 telnet_port
= strtoul(args
[0], NULL
, 0);
617 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
619 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)