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
, 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
, char* line
)
81 /* process lines in buffer */
84 char *next
= strchr(p
, '\n');
90 telnet_write(connection
, p
, strlen(p
));
93 telnet_write(connection
, "\r\n\0", 3);
102 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
104 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
106 return telnet_outputline(connection
, line
);
109 void telnet_log_callback(void *priv
, const char *file
, int line
,
110 const char *function
, const char *format
, va_list args
)
112 connection_t
*connection
= priv
;
113 char *t
= alloc_printf(format
, args
);
116 telnet_outputline(connection
, t
);
121 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
123 struct command_context_s
*cmd_ctx
= priv
;
124 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
125 telnet_connection_t
*t_con
= connection
->priv
;
129 case TARGET_EVENT_HALTED
:
130 target_arch_state(target
);
131 if (!t_con
->suppress_prompt
)
132 telnet_prompt(connection
);
134 case TARGET_EVENT_RESUMED
:
135 if (!t_con
->suppress_prompt
)
136 telnet_prompt(connection
);
145 int telnet_new_connection(connection_t
*connection
)
147 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
148 telnet_service_t
*telnet_service
= connection
->service
->priv
;
151 connection
->priv
= telnet_connection
;
153 /* initialize telnet connection information */
154 telnet_connection
->closed
= 0;
155 telnet_connection
->line_size
= 0;
156 telnet_connection
->line_cursor
= 0;
157 telnet_connection
->option_size
= 0;
158 telnet_connection
->prompt
= strdup("> ");
159 telnet_connection
->suppress_prompt
= 0;
160 telnet_connection
->state
= TELNET_STATE_DATA
;
162 /* output goes through telnet connection */
163 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
165 /* negotiate telnet options */
166 telnet_write(connection
, negotiate
, strlen(negotiate
));
168 /* print connection banner */
169 if (telnet_service
->banner
)
171 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
172 telnet_write(connection
, "\r\n\0", 3);
175 telnet_prompt(connection
);
177 /* initialize history */
178 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
180 telnet_connection
->history
[i
] = NULL
;
182 telnet_connection
->next_history
= 0;
183 telnet_connection
->current_history
= 0;
185 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
190 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
192 /* move to end of line */
193 if (t_con
->line_cursor
< t_con
->line_size
)
195 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
198 /* backspace, overwrite with space, backspace */
199 while (t_con
->line_size
> 0)
201 telnet_write(connection
, "\b \b", 3);
204 t_con
->line_cursor
= 0;
207 int telnet_input(connection_t
*connection
)
210 char buffer
[TELNET_BUFFER_SIZE
];
212 telnet_connection_t
*t_con
= connection
->priv
;
213 command_context_t
*command_context
= connection
->cmd_ctx
;
215 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
218 return ERROR_SERVER_REMOTE_CLOSED
;
219 else if (bytes_read
== -1)
221 ERROR("error during read: %s", strerror(errno
));
222 return ERROR_SERVER_REMOTE_CLOSED
;
228 switch (t_con
->state
)
230 case TELNET_STATE_DATA
:
231 if (*buf_p
== '\xff')
233 t_con
->state
= TELNET_STATE_IAC
;
237 if (isprint(*buf_p
)) /* printable character */
239 telnet_write(connection
, buf_p
, 1);
240 if (t_con
->line_cursor
== t_con
->line_size
)
242 t_con
->line
[t_con
->line_size
++] = *buf_p
;
243 t_con
->line_cursor
++;
248 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
249 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
251 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
252 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
254 telnet_write(connection
, "\b", 1);
258 else /* non-printable */
260 if (*buf_p
== 0x1b) /* escape */
262 t_con
->state
= TELNET_STATE_ESCAPE
;
263 t_con
->last_escape
= '\x00';
265 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
269 /* skip over combinations with CR/LF + NUL */
270 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
275 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
280 t_con
->line
[t_con
->line_size
] = 0;
282 telnet_write(connection
, "\r\n\x00", 3);
284 if (strcmp(t_con
->line
, "history") == 0)
287 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
289 if (t_con
->history
[i
])
291 telnet_write(connection
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
292 telnet_write(connection
, "\r\n\x00", 3);
295 telnet_prompt(connection
);
296 t_con
->line_size
= 0;
297 t_con
->line_cursor
= 0;
301 log_add_callback(telnet_log_callback
, connection
);
302 t_con
->suppress_prompt
= 1;
304 retval
= command_run_line(command_context
, t_con
->line
);
306 log_remove_callback(telnet_log_callback
, connection
);
307 t_con
->suppress_prompt
= 0;
309 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
311 return ERROR_SERVER_REMOTE_CLOSED
;
314 /* Save only non-blank lines in the history */
315 if (t_con
->line_size
> 0)
317 /* if the history slot is already taken, free it */
318 if (t_con
->history
[t_con
->next_history
])
320 free(t_con
->history
[t_con
->next_history
]);
323 /* add line to history */
324 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
326 /* wrap history at TELNET_LINE_HISTORY_SIZE */
327 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
329 /* current history line starts at the new entry */
330 t_con
->current_history
= t_con
->next_history
;
332 if (t_con
->history
[t_con
->current_history
])
334 free(t_con
->history
[t_con
->current_history
]);
336 t_con
->history
[t_con
->current_history
] = strdup("");
339 int t
= telnet_prompt(connection
);
340 if (t
== ERROR_SERVER_REMOTE_CLOSED
)
343 t_con
->line_size
= 0;
344 t_con
->line_cursor
= 0;
346 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
348 if (t_con
->line_cursor
> 0)
350 if (t_con
->line_cursor
!= t_con
->line_size
)
353 telnet_write(connection
, "\b", 1);
354 t_con
->line_cursor
--;
356 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
358 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
359 telnet_write(connection
, " \b", 2);
360 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
362 telnet_write(connection
, "\b", 1);
368 t_con
->line_cursor
--;
369 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
370 telnet_write(connection
, "\b \b", 3);
374 else if (*buf_p
== 0x15) /* clear line */
376 telnet_clear_line(connection
, t_con
);
378 else if (*buf_p
== CTRL('B')) /* cursor left */
380 if (t_con
->line_cursor
> 0)
382 telnet_write(connection
, "\b", 1);
383 t_con
->line_cursor
--;
385 t_con
->state
= TELNET_STATE_DATA
;
387 else if (*buf_p
== CTRL('F')) /* cursor right */
389 if (t_con
->line_cursor
< t_con
->line_size
)
391 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
393 t_con
->state
= TELNET_STATE_DATA
;
397 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
402 case TELNET_STATE_IAC
:
406 t_con
->state
= TELNET_STATE_DONT
;
409 t_con
->state
= TELNET_STATE_DO
;
412 t_con
->state
= TELNET_STATE_WONT
;
415 t_con
->state
= TELNET_STATE_WILL
;
419 case TELNET_STATE_SB
:
421 case TELNET_STATE_SE
:
423 case TELNET_STATE_WILL
:
424 case TELNET_STATE_WONT
:
425 case TELNET_STATE_DO
:
426 case TELNET_STATE_DONT
:
427 t_con
->state
= TELNET_STATE_DATA
;
429 case TELNET_STATE_ESCAPE
:
430 if (t_con
->last_escape
== '[')
432 if (*buf_p
== 'D') /* cursor left */
434 if (t_con
->line_cursor
> 0)
436 telnet_write(connection
, "\b", 1);
437 t_con
->line_cursor
--;
439 t_con
->state
= TELNET_STATE_DATA
;
441 else if (*buf_p
== 'C') /* cursor right */
443 if (t_con
->line_cursor
< t_con
->line_size
)
445 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
447 t_con
->state
= TELNET_STATE_DATA
;
449 else if (*buf_p
== 'A') /* cursor up */
451 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
452 if (t_con
->history
[last_history
])
454 telnet_clear_line(connection
, t_con
);
455 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
456 t_con
->line_cursor
= t_con
->line_size
;
457 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
458 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
459 t_con
->current_history
= last_history
;
461 t_con
->state
= TELNET_STATE_DATA
;
463 else if (*buf_p
== 'B') /* cursor down */
465 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
466 if (t_con
->history
[next_history
])
468 telnet_clear_line(connection
, t_con
);
469 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
470 t_con
->line_cursor
= t_con
->line_size
;
471 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
472 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
473 t_con
->current_history
= next_history
;
475 t_con
->state
= TELNET_STATE_DATA
;
477 else if (*buf_p
== '3')
479 t_con
->last_escape
= *buf_p
;
483 t_con
->state
= TELNET_STATE_DATA
;
486 else if (t_con
->last_escape
== '3')
488 /* Remove character */
491 if (t_con
->line_cursor
< t_con
->line_size
)
495 /* remove char from line buffer */
496 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
498 /* print remainder of buffer */
499 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
500 /* overwrite last char with whitespace */
501 telnet_write(connection
, " \b", 2);
503 /* move back to cursor position*/
504 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
506 telnet_write(connection
, "\b", 1);
510 t_con
->state
= TELNET_STATE_DATA
;
514 t_con
->state
= TELNET_STATE_DATA
;
517 else if (t_con
->last_escape
== '\x00')
521 t_con
->last_escape
= *buf_p
;
525 t_con
->state
= TELNET_STATE_DATA
;
530 ERROR("BUG: unexpected value in t_con->last_escape");
531 t_con
->state
= TELNET_STATE_DATA
;
536 ERROR("unknown telnet state");
547 int telnet_connection_closed(connection_t
*connection
)
549 telnet_connection_t
*t_con
= connection
->priv
;
555 t_con
->prompt
= NULL
;
558 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
560 if (t_con
->history
[i
])
562 free(t_con
->history
[i
]);
563 t_con
->history
[i
] = NULL
;
567 /* if this connection registered a debug-message receiver delete it */
568 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
570 if (connection
->priv
)
572 free(connection
->priv
);
573 connection
->priv
= NULL
;
577 ERROR("BUG: connection->priv == NULL");
580 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
585 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
587 telnet_connection_t
*t_con
= connection
->priv
;
589 t_con
->prompt
= strdup(prompt
);
594 int telnet_init(char *banner
)
596 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
598 if (telnet_port
== 0)
600 WARNING("no telnet port specified, using default port 4444");
604 telnet_service
->banner
= banner
;
606 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
611 int telnet_register_commands(command_context_t
*command_context
)
613 register_command(command_context
, NULL
, "exit", handle_exit_command
,
614 COMMAND_EXEC
, "exit telnet session");
616 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
622 /* daemon configuration command telnet_port */
623 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
628 /* only if the port wasn't overwritten by cmdline */
629 if (telnet_port
== 0)
630 telnet_port
= strtoul(args
[0], NULL
, 0);
635 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
637 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)