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 /* if there is no prompt, simply output the message */
122 if (t_con
->line_cursor
< 0)
124 telnet_outputline(connection
, string
);
128 /* clear the command line */
129 telnet_write(connection
, "\r", 1);
130 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
>0; i
-=16)
131 telnet_write(connection
, " ", i
>16 ? 16 : i
);
132 telnet_write(connection
, "\r", 1);
134 /* output the message */
135 telnet_outputline(connection
, string
);
137 /* put the command line to its previous state */
138 telnet_prompt(connection
);
139 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
140 for (i
=t_con
->line_size
; i
>t_con
->line_cursor
; i
--)
141 telnet_write(connection
, "\b", 1);
144 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
148 case TARGET_EVENT_HALTED
:
149 target_arch_state(target
);
158 int telnet_new_connection(connection_t
*connection
)
160 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
161 telnet_service_t
*telnet_service
= connection
->service
->priv
;
164 connection
->priv
= telnet_connection
;
166 /* initialize telnet connection information */
167 telnet_connection
->closed
= 0;
168 telnet_connection
->line_size
= 0;
169 telnet_connection
->line_cursor
= 0;
170 telnet_connection
->option_size
= 0;
171 telnet_connection
->prompt
= strdup("> ");
172 telnet_connection
->state
= TELNET_STATE_DATA
;
174 /* output goes through telnet connection */
175 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
177 /* negotiate telnet options */
178 telnet_write(connection
, negotiate
, strlen(negotiate
));
180 /* print connection banner */
181 if (telnet_service
->banner
)
183 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
184 telnet_write(connection
, "\r\n\0", 3);
187 telnet_prompt(connection
);
189 /* initialize history */
190 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
192 telnet_connection
->history
[i
] = NULL
;
194 telnet_connection
->next_history
= 0;
195 telnet_connection
->current_history
= 0;
197 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
199 log_add_callback(telnet_log_callback
, connection
);
206 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
208 /* move to end of line */
209 if (t_con
->line_cursor
< t_con
->line_size
)
211 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
214 /* backspace, overwrite with space, backspace */
215 while (t_con
->line_size
> 0)
217 telnet_write(connection
, "\b \b", 3);
220 t_con
->line_cursor
= 0;
223 int telnet_input(connection_t
*connection
)
226 char buffer
[TELNET_BUFFER_SIZE
];
228 telnet_connection_t
*t_con
= connection
->priv
;
229 command_context_t
*command_context
= connection
->cmd_ctx
;
231 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
234 return ERROR_SERVER_REMOTE_CLOSED
;
235 else if (bytes_read
== -1)
237 ERROR("error during read: %s", strerror(errno
));
238 return ERROR_SERVER_REMOTE_CLOSED
;
244 switch (t_con
->state
)
246 case TELNET_STATE_DATA
:
247 if (*buf_p
== '\xff')
249 t_con
->state
= TELNET_STATE_IAC
;
253 if (isprint(*buf_p
)) /* printable character */
255 /* watch buffer size leaving one spare character for string null termination */
256 if (t_con
->line_size
== TELNET_LINE_MAX_SIZE
-1)
258 /* output audible bell if buffer is full */
259 telnet_write(connection
, "\x07", 1); /* "\a" does not work, at least on windows */
261 else if (t_con
->line_cursor
== t_con
->line_size
)
263 telnet_write(connection
, buf_p
, 1);
264 t_con
->line
[t_con
->line_size
++] = *buf_p
;
265 t_con
->line_cursor
++;
270 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
271 t_con
->line
[t_con
->line_cursor
] = *buf_p
;
273 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
274 t_con
->line_cursor
++;
275 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
277 telnet_write(connection
, "\b", 1);
281 else /* non-printable */
283 if (*buf_p
== 0x1b) /* escape */
285 t_con
->state
= TELNET_STATE_ESCAPE
;
286 t_con
->last_escape
= '\x00';
288 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
292 /* skip over combinations with CR/LF and NUL characters */
293 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)))
298 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0))
303 t_con
->line
[t_con
->line_size
] = 0;
305 telnet_write(connection
, "\r\n\x00", 3);
307 if (strcmp(t_con
->line
, "history") == 0)
310 for (i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
312 /* the t_con->next_history line contains empty string (unless NULL), thus it is not printed */
313 char *history_line
= t_con
->history
[(t_con
->next_history
+ i
) % TELNET_LINE_HISTORY_SIZE
];
316 telnet_write(connection
, history_line
, strlen(history_line
));
317 telnet_write(connection
, "\r\n\x00", 3);
320 t_con
->line_size
= 0;
321 t_con
->line_cursor
= 0;
325 /* save only non-blank not repeating lines in the history */
326 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
327 if (*t_con
->line
&& (prev_line
== NULL
|| strcmp(t_con
->line
, prev_line
)))
329 /* if the history slot is already taken, free it */
330 if (t_con
->history
[t_con
->next_history
])
332 free(t_con
->history
[t_con
->next_history
]);
335 /* add line to history */
336 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
338 /* wrap history at TELNET_LINE_HISTORY_SIZE */
339 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
341 /* current history line starts at the new entry */
342 t_con
->current_history
= t_con
->next_history
;
344 if (t_con
->history
[t_con
->current_history
])
346 free(t_con
->history
[t_con
->current_history
]);
348 t_con
->history
[t_con
->current_history
] = strdup("");
351 t_con
->line_size
= 0;
353 t_con
->line_cursor
= -1; /* to supress prompt in log callback during command execution */
354 retval
= command_run_line(command_context
, t_con
->line
);
355 t_con
->line_cursor
= 0;
357 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
358 return ERROR_SERVER_REMOTE_CLOSED
;
360 retval
= telnet_prompt(connection
);
361 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
362 return ERROR_SERVER_REMOTE_CLOSED
;
365 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
367 if (t_con
->line_cursor
> 0)
369 if (t_con
->line_cursor
!= t_con
->line_size
)
372 telnet_write(connection
, "\b", 1);
373 t_con
->line_cursor
--;
375 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
377 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
378 telnet_write(connection
, " \b", 2);
379 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
381 telnet_write(connection
, "\b", 1);
387 t_con
->line_cursor
--;
388 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
389 telnet_write(connection
, "\b \b", 3);
393 else if (*buf_p
== 0x15) /* clear line */
395 telnet_clear_line(connection
, t_con
);
397 else if (*buf_p
== CTRL('B')) /* cursor left */
399 if (t_con
->line_cursor
> 0)
401 telnet_write(connection
, "\b", 1);
402 t_con
->line_cursor
--;
404 t_con
->state
= TELNET_STATE_DATA
;
406 else if (*buf_p
== CTRL('F')) /* cursor right */
408 if (t_con
->line_cursor
< t_con
->line_size
)
410 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
412 t_con
->state
= TELNET_STATE_DATA
;
416 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
421 case TELNET_STATE_IAC
:
425 t_con
->state
= TELNET_STATE_DONT
;
428 t_con
->state
= TELNET_STATE_DO
;
431 t_con
->state
= TELNET_STATE_WONT
;
434 t_con
->state
= TELNET_STATE_WILL
;
438 case TELNET_STATE_SB
:
440 case TELNET_STATE_SE
:
442 case TELNET_STATE_WILL
:
443 case TELNET_STATE_WONT
:
444 case TELNET_STATE_DO
:
445 case TELNET_STATE_DONT
:
446 t_con
->state
= TELNET_STATE_DATA
;
448 case TELNET_STATE_ESCAPE
:
449 if (t_con
->last_escape
== '[')
451 if (*buf_p
== 'D') /* cursor left */
453 if (t_con
->line_cursor
> 0)
455 telnet_write(connection
, "\b", 1);
456 t_con
->line_cursor
--;
458 t_con
->state
= TELNET_STATE_DATA
;
460 else if (*buf_p
== 'C') /* cursor right */
462 if (t_con
->line_cursor
< t_con
->line_size
)
464 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
466 t_con
->state
= TELNET_STATE_DATA
;
468 else if (*buf_p
== 'A') /* cursor up */
470 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
471 if (t_con
->history
[last_history
])
473 telnet_clear_line(connection
, t_con
);
474 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
475 t_con
->line_cursor
= t_con
->line_size
;
476 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
);
477 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
478 t_con
->current_history
= last_history
;
480 t_con
->state
= TELNET_STATE_DATA
;
482 else if (*buf_p
== 'B') /* cursor down */
484 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
485 if (t_con
->history
[next_history
])
487 telnet_clear_line(connection
, t_con
);
488 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
489 t_con
->line_cursor
= t_con
->line_size
;
490 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
);
491 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
492 t_con
->current_history
= next_history
;
494 t_con
->state
= TELNET_STATE_DATA
;
496 else if (*buf_p
== '3')
498 t_con
->last_escape
= *buf_p
;
502 t_con
->state
= TELNET_STATE_DATA
;
505 else if (t_con
->last_escape
== '3')
507 /* Remove character */
510 if (t_con
->line_cursor
< t_con
->line_size
)
514 /* remove char from line buffer */
515 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
517 /* print remainder of buffer */
518 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
519 /* overwrite last char with whitespace */
520 telnet_write(connection
, " \b", 2);
522 /* move back to cursor position*/
523 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
525 telnet_write(connection
, "\b", 1);
529 t_con
->state
= TELNET_STATE_DATA
;
533 t_con
->state
= TELNET_STATE_DATA
;
536 else if (t_con
->last_escape
== '\x00')
540 t_con
->last_escape
= *buf_p
;
544 t_con
->state
= TELNET_STATE_DATA
;
549 ERROR("BUG: unexpected value in t_con->last_escape");
550 t_con
->state
= TELNET_STATE_DATA
;
555 ERROR("unknown telnet state");
566 int telnet_connection_closed(connection_t
*connection
)
568 telnet_connection_t
*t_con
= connection
->priv
;
571 log_remove_callback(telnet_log_callback
, connection
);
576 t_con
->prompt
= NULL
;
579 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
581 if (t_con
->history
[i
])
583 free(t_con
->history
[i
]);
584 t_con
->history
[i
] = NULL
;
588 /* if this connection registered a debug-message receiver delete it */
589 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
591 if (connection
->priv
)
593 free(connection
->priv
);
594 connection
->priv
= NULL
;
598 ERROR("BUG: connection->priv == NULL");
601 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
606 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
608 telnet_connection_t
*t_con
= connection
->priv
;
610 if (t_con
->prompt
!= NULL
)
613 t_con
->prompt
= strdup(prompt
);
618 int telnet_init(char *banner
)
620 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
622 if (telnet_port
== 0)
624 WARNING("no telnet port specified, using default port 4444");
628 telnet_service
->banner
= banner
;
630 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
635 int telnet_register_commands(command_context_t
*command_context
)
637 register_command(command_context
, NULL
, "exit", handle_exit_command
,
638 COMMAND_EXEC
, "exit telnet session");
640 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
646 /* daemon configuration command telnet_port */
647 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
652 telnet_port
= strtoul(args
[0], NULL
, 0);
657 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
659 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)