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 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
72 struct command_context_s
*cmd_ctx
= priv
;
73 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
74 telnet_connection_t
*t_con
= connection
->priv
;
79 case TARGET_EVENT_HALTED
:
80 command_print(cmd_ctx
, "Target %i halted", get_num_by_target(target
));
81 target
->type
->arch_state(target
, buffer
, 512);
83 command_print(cmd_ctx
, "%s", buffer
);
84 telnet_prompt(connection
);
85 t_con
->suppress_prompt
= 1;
87 case TARGET_EVENT_RESUMED
:
88 command_print(cmd_ctx
, "Target %i resumed", get_num_by_target(target
));
89 telnet_prompt(connection
);
90 t_con
->suppress_prompt
= 1;
99 int telnet_new_connection(connection_t
*connection
)
101 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
102 telnet_service_t
*telnet_service
= connection
->service
->priv
;
105 connection
->priv
= telnet_connection
;
107 /* initialize telnet connection information */
108 telnet_connection
->line_size
= 0;
109 telnet_connection
->line_cursor
= 0;
110 telnet_connection
->option_size
= 0;
111 telnet_connection
->prompt
= strdup("> ");
112 telnet_connection
->suppress_prompt
= 0;
113 telnet_connection
->state
= TELNET_STATE_DATA
;
115 /* output goes through telnet connection */
116 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
118 /* negotiate telnet options */
119 write_socket(connection
->fd
, negotiate
, strlen(negotiate
));
121 /* print connection banner */
122 if (telnet_service
->banner
)
124 write_socket(connection
->fd
, telnet_service
->banner
, strlen(telnet_service
->banner
));
125 write_socket(connection
->fd
, "\r\n\0", 3);
128 telnet_prompt(connection
);
130 /* initialize history */
131 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
133 telnet_connection
->history
[i
] = NULL
;
135 telnet_connection
->next_history
= 0;
136 telnet_connection
->current_history
= 0;
138 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
143 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
145 /* move to end of line */
146 if (t_con
->line_cursor
< t_con
->line_size
)
148 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
151 /* backspace, overwrite with space, backspace */
152 while (t_con
->line_size
> 0)
154 write_socket(connection
->fd
, "\b \b", 3);
157 t_con
->line_cursor
= 0;
160 int telnet_input(connection_t
*connection
)
163 char buffer
[TELNET_BUFFER_SIZE
];
165 telnet_connection_t
*t_con
= connection
->priv
;
166 command_context_t
*command_context
= connection
->cmd_ctx
;
168 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
171 return ERROR_SERVER_REMOTE_CLOSED
;
172 else if (bytes_read
== -1)
174 ERROR("error during read: %s", strerror(errno
));
175 return ERROR_SERVER_REMOTE_CLOSED
;
181 switch (t_con
->state
)
183 case TELNET_STATE_DATA
:
184 if (*buf_p
== '\xff')
186 t_con
->state
= TELNET_STATE_IAC
;
190 if (isprint(*buf_p
)) /* printable character */
192 write_socket(connection
->fd
, buf_p
, 1);
193 if (t_con
->line_cursor
== t_con
->line_size
)
195 t_con
->line
[t_con
->line_size
++] = *buf_p
;
196 t_con
->line_cursor
++;
201 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
202 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
204 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
205 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
207 write_socket(connection
->fd
, "\b", 1);
211 else /* non-printable */
213 if (*buf_p
== 0x1b) /* escape */
215 t_con
->state
= TELNET_STATE_ESCAPE
;
216 t_con
->last_escape
= '\x00';
218 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
222 /* skip over combinations with CR/LF + NUL */
223 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
228 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
233 t_con
->line
[t_con
->line_size
] = 0;
235 write_socket(connection
->fd
, "\r\n\x00", 3);
237 if (strcmp(t_con
->line
, "history") == 0)
240 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
242 if (t_con
->history
[i
])
244 write_socket(connection
->fd
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
245 write_socket(connection
->fd
, "\r\n\x00", 3);
248 telnet_prompt(connection
);
249 t_con
->line_size
= 0;
250 t_con
->line_cursor
= 0;
254 /* we're running a command, so we need a prompt
255 * if the output handler is called, this gets set again */
256 t_con
->suppress_prompt
= 0;
257 if ((retval
= command_run_line(command_context
, t_con
->line
)) != ERROR_OK
)
259 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
261 return ERROR_SERVER_REMOTE_CLOSED
;
265 /* Save only non-blank lines in the history */
266 if (t_con
->line_size
> 0)
268 /* if the history slot is already taken, free it */
269 if (t_con
->history
[t_con
->next_history
])
271 free(t_con
->history
[t_con
->next_history
]);
274 /* add line to history */
275 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
277 /* wrap history at TELNET_LINE_HISTORY_SIZE */
278 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
280 /* current history line starts at the new entry */
281 t_con
->current_history
= t_con
->next_history
;
283 if (t_con
->history
[t_con
->current_history
])
285 free(t_con
->history
[t_con
->current_history
]);
287 t_con
->history
[t_con
->current_history
] = strdup("");
290 if (!t_con
->suppress_prompt
)
292 telnet_prompt(connection
);
296 t_con
->suppress_prompt
= 0;
299 t_con
->line_size
= 0;
300 t_con
->line_cursor
= 0;
302 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
304 if (t_con
->line_cursor
> 0)
306 if (t_con
->line_cursor
!= t_con
->line_size
)
309 write_socket(connection
->fd
, "\b", 1);
310 t_con
->line_cursor
--;
312 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
314 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
315 write_socket(connection
->fd
, " \b", 2);
316 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
318 write_socket(connection
->fd
, "\b", 1);
324 t_con
->line_cursor
--;
325 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
326 write_socket(connection
->fd
, "\b \b", 3);
330 else if (*buf_p
== 0x15) /* clear line */
332 telnet_clear_line(connection
, t_con
);
334 else if (*buf_p
== CTRL('B')) /* cursor left */
336 if (t_con
->line_cursor
> 0)
338 write_socket(connection
->fd
, "\b", 1);
339 t_con
->line_cursor
--;
341 t_con
->state
= TELNET_STATE_DATA
;
343 else if (*buf_p
== CTRL('F')) /* cursor right */
345 if (t_con
->line_cursor
< t_con
->line_size
)
347 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
349 t_con
->state
= TELNET_STATE_DATA
;
353 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
358 case TELNET_STATE_IAC
:
362 t_con
->state
= TELNET_STATE_DONT
;
365 t_con
->state
= TELNET_STATE_DO
;
368 t_con
->state
= TELNET_STATE_WONT
;
371 t_con
->state
= TELNET_STATE_WILL
;
375 case TELNET_STATE_SB
:
377 case TELNET_STATE_SE
:
379 case TELNET_STATE_WILL
:
380 case TELNET_STATE_WONT
:
381 case TELNET_STATE_DO
:
382 case TELNET_STATE_DONT
:
383 t_con
->state
= TELNET_STATE_DATA
;
385 case TELNET_STATE_ESCAPE
:
386 if (t_con
->last_escape
== '[')
388 if (*buf_p
== 'D') /* cursor left */
390 if (t_con
->line_cursor
> 0)
392 write_socket(connection
->fd
, "\b", 1);
393 t_con
->line_cursor
--;
395 t_con
->state
= TELNET_STATE_DATA
;
397 else if (*buf_p
== 'C') /* cursor right */
399 if (t_con
->line_cursor
< t_con
->line_size
)
401 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
403 t_con
->state
= TELNET_STATE_DATA
;
405 else if (*buf_p
== 'A') /* cursor up */
407 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
408 if (t_con
->history
[last_history
])
410 telnet_clear_line(connection
, t_con
);
411 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
412 t_con
->line_cursor
= t_con
->line_size
;
413 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
414 write_socket(connection
->fd
, t_con
->line
, t_con
->line_size
);
415 t_con
->current_history
= last_history
;
417 t_con
->state
= TELNET_STATE_DATA
;
419 else if (*buf_p
== 'B') /* cursor down */
421 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
422 if (t_con
->history
[next_history
])
424 telnet_clear_line(connection
, t_con
);
425 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
426 t_con
->line_cursor
= t_con
->line_size
;
427 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
428 write_socket(connection
->fd
, t_con
->line
, t_con
->line_size
);
429 t_con
->current_history
= next_history
;
431 t_con
->state
= TELNET_STATE_DATA
;
433 else if (*buf_p
== '3')
435 t_con
->last_escape
= *buf_p
;
439 t_con
->state
= TELNET_STATE_DATA
;
442 else if (t_con
->last_escape
== '3')
444 /* Remove character */
447 if (t_con
->line_cursor
< t_con
->line_size
)
451 /* remove char from line buffer */
452 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
454 /* print remainder of buffer */
455 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
456 /* overwrite last char with whitespace */
457 write_socket(connection
->fd
, " \b", 2);
459 /* move back to cursor position*/
460 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
462 write_socket(connection
->fd
, "\b", 1);
466 t_con
->state
= TELNET_STATE_DATA
;
470 t_con
->state
= TELNET_STATE_DATA
;
473 else if (t_con
->last_escape
== '\x00')
477 t_con
->last_escape
= *buf_p
;
481 t_con
->state
= TELNET_STATE_DATA
;
486 ERROR("BUG: unexpected value in t_con->last_escape");
487 t_con
->state
= TELNET_STATE_DATA
;
492 ERROR("unknown telnet state");
503 int telnet_connection_closed(connection_t
*connection
)
505 telnet_connection_t
*t_con
= connection
->priv
;
511 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
513 if (t_con
->history
[i
])
514 free(t_con
->history
[i
]);
517 /* if this connection registered a debug-message receiver delete it */
518 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
520 if (connection
->priv
)
521 free(connection
->priv
);
523 ERROR("BUG: connection->priv == NULL");
525 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
530 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
532 telnet_connection_t
*t_con
= connection
->priv
;
534 t_con
->prompt
= strdup(prompt
);
539 int telnet_init(char *banner
)
541 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
543 if (telnet_port
== 0)
545 WARNING("no telnet port specified, using default port 4444");
549 telnet_service
->banner
= banner
;
551 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
556 int telnet_register_commands(command_context_t
*command_context
)
558 register_command(command_context
, NULL
, "exit", handle_exit_command
,
559 COMMAND_EXEC
, "exit telnet session");
561 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
567 /* daemon configuration command telnet_port */
568 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
573 /* only if the port wasn't overwritten by cmdline */
574 if (telnet_port
== 0)
575 telnet_port
= strtoul(args
[0], NULL
, 0);
580 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
582 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)