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 telnet_write(connection
, "\r", 1); /* the prompt is always placed at the line beginning */
76 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
79 int telnet_outputline(connection_t
*connection
, const char *line
)
83 /* process lines in buffer */
85 char *line_end
= strchr(line
, '\n');
92 telnet_write(connection
, line
, len
);
95 telnet_write(connection
, "\r\n\0", 3);
107 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
109 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
111 return telnet_outputline(connection
, line
);
114 void telnet_log_callback(void *priv
, const char *file
, int line
,
115 const char *function
, const char *string
)
117 connection_t
*connection
= priv
;
118 telnet_connection_t
*t_con
= connection
->priv
;
121 /* clear the command line */
122 telnet_write(connection
, "\r", 1);
123 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
>0; i
-=16)
124 telnet_write(connection
, " ", i
>16 ? 16 : i
);
126 telnet_write(connection
, "\r", 1);
127 telnet_outputline(connection
, string
);
129 /* put the command line to its previous state */
130 telnet_prompt(connection
);
131 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
132 for (i
=t_con
->line_size
; i
>t_con
->line_cursor
; i
--)
133 telnet_write(connection
, "\b", 1);
136 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
140 case TARGET_EVENT_HALTED
:
141 target_arch_state(target
);
150 int telnet_new_connection(connection_t
*connection
)
152 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
153 telnet_service_t
*telnet_service
= connection
->service
->priv
;
156 connection
->priv
= telnet_connection
;
158 /* initialize telnet connection information */
159 telnet_connection
->closed
= 0;
160 telnet_connection
->line_size
= 0;
161 telnet_connection
->line_cursor
= 0;
162 telnet_connection
->option_size
= 0;
163 telnet_connection
->prompt
= strdup("> ");
164 telnet_connection
->state
= TELNET_STATE_DATA
;
166 /* output goes through telnet connection */
167 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
169 /* negotiate telnet options */
170 telnet_write(connection
, negotiate
, strlen(negotiate
));
172 /* print connection banner */
173 if (telnet_service
->banner
)
175 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
176 telnet_write(connection
, "\r\n\0", 3);
179 telnet_prompt(connection
);
181 /* initialize history */
182 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
184 telnet_connection
->history
[i
] = NULL
;
186 telnet_connection
->next_history
= 0;
187 telnet_connection
->current_history
= 0;
189 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
191 log_add_callback(telnet_log_callback
, connection
);
198 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
200 /* move to end of line */
201 if (t_con
->line_cursor
< t_con
->line_size
)
203 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
206 /* backspace, overwrite with space, backspace */
207 while (t_con
->line_size
> 0)
209 telnet_write(connection
, "\b \b", 3);
212 t_con
->line_cursor
= 0;
215 int telnet_input(connection_t
*connection
)
218 char buffer
[TELNET_BUFFER_SIZE
];
220 telnet_connection_t
*t_con
= connection
->priv
;
221 command_context_t
*command_context
= connection
->cmd_ctx
;
223 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
226 return ERROR_SERVER_REMOTE_CLOSED
;
227 else if (bytes_read
== -1)
229 ERROR("error during read: %s", strerror(errno
));
230 return ERROR_SERVER_REMOTE_CLOSED
;
236 switch (t_con
->state
)
238 case TELNET_STATE_DATA
:
239 if (*buf_p
== '\xff')
241 t_con
->state
= TELNET_STATE_IAC
;
245 if (isprint(*buf_p
)) /* printable character */
247 telnet_write(connection
, buf_p
, 1);
248 if (t_con
->line_cursor
== t_con
->line_size
)
250 t_con
->line
[t_con
->line_size
++] = *buf_p
;
251 t_con
->line_cursor
++;
256 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
257 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
259 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
260 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
262 telnet_write(connection
, "\b", 1);
266 else /* non-printable */
268 if (*buf_p
== 0x1b) /* escape */
270 t_con
->state
= TELNET_STATE_ESCAPE
;
271 t_con
->last_escape
= '\x00';
273 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
277 /* skip over combinations with CR/LF + NUL */
278 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
283 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
288 t_con
->line
[t_con
->line_size
] = 0;
290 telnet_write(connection
, "\r\n\x00", 3);
292 if (strcmp(t_con
->line
, "history") == 0)
295 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
297 if (t_con
->history
[i
])
299 telnet_write(connection
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
300 telnet_write(connection
, "\r\n\x00", 3);
303 t_con
->line_size
= 0;
304 t_con
->line_cursor
= 0;
308 /* Save only non-blank lines in the history */
309 if (t_con
->line_size
> 0)
311 /* if the history slot is already taken, free it */
312 if (t_con
->history
[t_con
->next_history
])
314 free(t_con
->history
[t_con
->next_history
]);
317 /* add line to history */
318 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
320 /* wrap history at TELNET_LINE_HISTORY_SIZE */
321 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
323 /* current history line starts at the new entry */
324 t_con
->current_history
= t_con
->next_history
;
326 if (t_con
->history
[t_con
->current_history
])
328 free(t_con
->history
[t_con
->current_history
]);
330 t_con
->history
[t_con
->current_history
] = strdup("");
333 t_con
->line_size
= 0;
334 t_con
->line_cursor
= 0;
336 retval
= command_run_line(command_context
, t_con
->line
);
337 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
338 return ERROR_SERVER_REMOTE_CLOSED
;
340 retval
= telnet_prompt(connection
);
341 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
342 return ERROR_SERVER_REMOTE_CLOSED
;
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
;
551 log_remove_callback(telnet_log_callback
, connection
);
556 t_con
->prompt
= NULL
;
559 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
561 if (t_con
->history
[i
])
563 free(t_con
->history
[i
]);
564 t_con
->history
[i
] = NULL
;
568 /* if this connection registered a debug-message receiver delete it */
569 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
571 if (connection
->priv
)
573 free(connection
->priv
);
574 connection
->priv
= NULL
;
578 ERROR("BUG: connection->priv == NULL");
581 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
586 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
588 telnet_connection_t
*t_con
= connection
->priv
;
590 if (t_con
->prompt
!= NULL
)
593 t_con
->prompt
= strdup(prompt
);
598 int telnet_init(char *banner
)
600 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
602 if (telnet_port
== 0)
604 WARNING("no telnet port specified, using default port 4444");
608 telnet_service
->banner
= banner
;
610 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
615 int telnet_register_commands(command_context_t
*command_context
)
617 register_command(command_context
, NULL
, "exit", handle_exit_command
,
618 COMMAND_EXEC
, "exit telnet session");
620 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
626 /* daemon configuration command telnet_port */
627 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
632 telnet_port
= strtoul(args
[0], NULL
, 0);
637 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
639 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)