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
)
124 case TARGET_EVENT_HALTED
:
125 target_arch_state(target
);
134 int telnet_new_connection(connection_t
*connection
)
136 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
137 telnet_service_t
*telnet_service
= connection
->service
->priv
;
140 connection
->priv
= telnet_connection
;
142 /* initialize telnet connection information */
143 telnet_connection
->closed
= 0;
144 telnet_connection
->line_size
= 0;
145 telnet_connection
->line_cursor
= 0;
146 telnet_connection
->option_size
= 0;
147 telnet_connection
->prompt
= strdup("> ");
148 telnet_connection
->state
= TELNET_STATE_DATA
;
150 /* output goes through telnet connection */
151 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
153 /* negotiate telnet options */
154 telnet_write(connection
, negotiate
, strlen(negotiate
));
156 /* print connection banner */
157 if (telnet_service
->banner
)
159 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
160 telnet_write(connection
, "\r\n\0", 3);
163 telnet_prompt(connection
);
165 /* initialize history */
166 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
168 telnet_connection
->history
[i
] = NULL
;
170 telnet_connection
->next_history
= 0;
171 telnet_connection
->current_history
= 0;
173 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
178 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
180 /* move to end of line */
181 if (t_con
->line_cursor
< t_con
->line_size
)
183 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
186 /* backspace, overwrite with space, backspace */
187 while (t_con
->line_size
> 0)
189 telnet_write(connection
, "\b \b", 3);
192 t_con
->line_cursor
= 0;
195 int telnet_input(connection_t
*connection
)
198 char buffer
[TELNET_BUFFER_SIZE
];
200 telnet_connection_t
*t_con
= connection
->priv
;
201 command_context_t
*command_context
= connection
->cmd_ctx
;
203 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
206 return ERROR_SERVER_REMOTE_CLOSED
;
207 else if (bytes_read
== -1)
209 ERROR("error during read: %s", strerror(errno
));
210 return ERROR_SERVER_REMOTE_CLOSED
;
216 switch (t_con
->state
)
218 case TELNET_STATE_DATA
:
219 if (*buf_p
== '\xff')
221 t_con
->state
= TELNET_STATE_IAC
;
225 if (isprint(*buf_p
)) /* printable character */
227 telnet_write(connection
, buf_p
, 1);
228 if (t_con
->line_cursor
== t_con
->line_size
)
230 t_con
->line
[t_con
->line_size
++] = *buf_p
;
231 t_con
->line_cursor
++;
236 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
237 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
239 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
240 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
242 telnet_write(connection
, "\b", 1);
246 else /* non-printable */
248 if (*buf_p
== 0x1b) /* escape */
250 t_con
->state
= TELNET_STATE_ESCAPE
;
251 t_con
->last_escape
= '\x00';
253 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
257 /* skip over combinations with CR/LF + NUL */
258 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
263 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
268 t_con
->line
[t_con
->line_size
] = 0;
270 telnet_write(connection
, "\r\n\x00", 3);
272 if (strcmp(t_con
->line
, "history") == 0)
275 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
277 if (t_con
->history
[i
])
279 telnet_write(connection
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
280 telnet_write(connection
, "\r\n\x00", 3);
283 telnet_prompt(connection
);
284 t_con
->line_size
= 0;
285 t_con
->line_cursor
= 0;
289 log_add_callback(telnet_log_callback
, connection
);
291 retval
= command_run_line(command_context
, t_con
->line
);
293 log_remove_callback(telnet_log_callback
, connection
);
295 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
297 return ERROR_SERVER_REMOTE_CLOSED
;
300 /* Save only non-blank lines in the history */
301 if (t_con
->line_size
> 0)
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 int t
= telnet_prompt(connection
);
326 if (t
== ERROR_SERVER_REMOTE_CLOSED
)
329 t_con
->line_size
= 0;
330 t_con
->line_cursor
= 0;
332 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
334 if (t_con
->line_cursor
> 0)
336 if (t_con
->line_cursor
!= t_con
->line_size
)
339 telnet_write(connection
, "\b", 1);
340 t_con
->line_cursor
--;
342 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
344 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
345 telnet_write(connection
, " \b", 2);
346 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
348 telnet_write(connection
, "\b", 1);
354 t_con
->line_cursor
--;
355 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
356 telnet_write(connection
, "\b \b", 3);
360 else if (*buf_p
== 0x15) /* clear line */
362 telnet_clear_line(connection
, t_con
);
364 else if (*buf_p
== CTRL('B')) /* cursor left */
366 if (t_con
->line_cursor
> 0)
368 telnet_write(connection
, "\b", 1);
369 t_con
->line_cursor
--;
371 t_con
->state
= TELNET_STATE_DATA
;
373 else if (*buf_p
== CTRL('F')) /* cursor right */
375 if (t_con
->line_cursor
< t_con
->line_size
)
377 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
379 t_con
->state
= TELNET_STATE_DATA
;
383 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
388 case TELNET_STATE_IAC
:
392 t_con
->state
= TELNET_STATE_DONT
;
395 t_con
->state
= TELNET_STATE_DO
;
398 t_con
->state
= TELNET_STATE_WONT
;
401 t_con
->state
= TELNET_STATE_WILL
;
405 case TELNET_STATE_SB
:
407 case TELNET_STATE_SE
:
409 case TELNET_STATE_WILL
:
410 case TELNET_STATE_WONT
:
411 case TELNET_STATE_DO
:
412 case TELNET_STATE_DONT
:
413 t_con
->state
= TELNET_STATE_DATA
;
415 case TELNET_STATE_ESCAPE
:
416 if (t_con
->last_escape
== '[')
418 if (*buf_p
== 'D') /* cursor left */
420 if (t_con
->line_cursor
> 0)
422 telnet_write(connection
, "\b", 1);
423 t_con
->line_cursor
--;
425 t_con
->state
= TELNET_STATE_DATA
;
427 else if (*buf_p
== 'C') /* cursor right */
429 if (t_con
->line_cursor
< t_con
->line_size
)
431 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
433 t_con
->state
= TELNET_STATE_DATA
;
435 else if (*buf_p
== 'A') /* cursor up */
437 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
438 if (t_con
->history
[last_history
])
440 telnet_clear_line(connection
, t_con
);
441 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
442 t_con
->line_cursor
= t_con
->line_size
;
443 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
444 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
445 t_con
->current_history
= last_history
;
447 t_con
->state
= TELNET_STATE_DATA
;
449 else if (*buf_p
== 'B') /* cursor down */
451 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
452 if (t_con
->history
[next_history
])
454 telnet_clear_line(connection
, t_con
);
455 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
456 t_con
->line_cursor
= t_con
->line_size
;
457 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
458 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
459 t_con
->current_history
= next_history
;
461 t_con
->state
= TELNET_STATE_DATA
;
463 else if (*buf_p
== '3')
465 t_con
->last_escape
= *buf_p
;
469 t_con
->state
= TELNET_STATE_DATA
;
472 else if (t_con
->last_escape
== '3')
474 /* Remove character */
477 if (t_con
->line_cursor
< t_con
->line_size
)
481 /* remove char from line buffer */
482 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
484 /* print remainder of buffer */
485 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
486 /* overwrite last char with whitespace */
487 telnet_write(connection
, " \b", 2);
489 /* move back to cursor position*/
490 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
492 telnet_write(connection
, "\b", 1);
496 t_con
->state
= TELNET_STATE_DATA
;
500 t_con
->state
= TELNET_STATE_DATA
;
503 else if (t_con
->last_escape
== '\x00')
507 t_con
->last_escape
= *buf_p
;
511 t_con
->state
= TELNET_STATE_DATA
;
516 ERROR("BUG: unexpected value in t_con->last_escape");
517 t_con
->state
= TELNET_STATE_DATA
;
522 ERROR("unknown telnet state");
533 int telnet_connection_closed(connection_t
*connection
)
535 telnet_connection_t
*t_con
= connection
->priv
;
541 t_con
->prompt
= NULL
;
544 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
546 if (t_con
->history
[i
])
548 free(t_con
->history
[i
]);
549 t_con
->history
[i
] = NULL
;
553 /* if this connection registered a debug-message receiver delete it */
554 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
556 if (connection
->priv
)
558 free(connection
->priv
);
559 connection
->priv
= NULL
;
563 ERROR("BUG: connection->priv == NULL");
566 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
571 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
573 telnet_connection_t
*t_con
= connection
->priv
;
575 t_con
->prompt
= strdup(prompt
);
580 int telnet_init(char *banner
)
582 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
584 if (telnet_port
== 0)
586 WARNING("no telnet port specified, using default port 4444");
590 telnet_service
->banner
= banner
;
592 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
597 int telnet_register_commands(command_context_t
*command_context
)
599 register_command(command_context
, NULL
, "exit", handle_exit_command
,
600 COMMAND_EXEC
, "exit telnet session");
602 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
608 /* daemon configuration command telnet_port */
609 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
614 telnet_port
= strtoul(args
[0], NULL
, 0);
619 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
621 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)