1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2007,2008 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
27 #include "replacements.h"
29 #include "telnet_server.h"
35 #include "target_request.h"
43 static unsigned short telnet_port
= 0;
45 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
46 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
48 static char *negotiate
=
49 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
50 "\xFF\xFB\x01" /* IAC WILL Echo */
51 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
52 "\xFF\xFE\x01"; /* IAC DON'T Echo */
54 #define CTRL(c) (c - '@')
56 /* The only way we can detect that the socket is closed is the first time
57 * we write to it, we will fail. Subsequent write operations will
60 int telnet_write(connection_t
*connection
, const void *data
, int len
)
62 telnet_connection_t
*t_con
= connection
->priv
;
64 return ERROR_SERVER_REMOTE_CLOSED
;
66 if (write_socket(connection
->fd
, data
, len
) == len
)
71 return ERROR_SERVER_REMOTE_CLOSED
;
74 int telnet_prompt(connection_t
*connection
)
76 telnet_connection_t
*t_con
= connection
->priv
;
78 telnet_write(connection
, "\r", 1); /* the prompt is always placed at the line beginning */
79 return telnet_write(connection
, t_con
->prompt
, strlen(t_con
->prompt
));
82 int telnet_outputline(connection_t
*connection
, const char *line
)
86 /* process lines in buffer */
88 char *line_end
= strchr(line
, '\n');
95 telnet_write(connection
, line
, len
);
98 telnet_write(connection
, "\r\n", 2);
110 int telnet_output(struct command_context_s
*cmd_ctx
, const char* line
)
112 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
114 return telnet_outputline(connection
, line
);
117 void telnet_log_callback(void *priv
, const char *file
, int line
,
118 const char *function
, const char *string
)
120 connection_t
*connection
= priv
;
121 telnet_connection_t
*t_con
= connection
->priv
;
124 /* if there is no prompt, simply output the message */
125 if (t_con
->line_cursor
< 0)
127 telnet_outputline(connection
, string
);
131 /* clear the command line */
132 telnet_write(connection
, "\r", 1);
133 for (i
= strlen(t_con
->prompt
) + t_con
->line_size
; i
>0; i
-=16)
134 telnet_write(connection
, " ", i
>16 ? 16 : i
);
135 telnet_write(connection
, "\r", 1);
137 /* output the message */
138 telnet_outputline(connection
, string
);
140 /* put the command line to its previous state */
141 telnet_prompt(connection
);
142 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
143 for (i
=t_con
->line_size
; i
>t_con
->line_cursor
; i
--)
144 telnet_write(connection
, "\b", 1);
147 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
151 case TARGET_EVENT_HALTED
:
152 target_arch_state(target
);
161 int telnet_new_connection(connection_t
*connection
)
163 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
164 telnet_service_t
*telnet_service
= connection
->service
->priv
;
167 connection
->priv
= telnet_connection
;
169 /* initialize telnet connection information */
170 telnet_connection
->closed
= 0;
171 telnet_connection
->line_size
= 0;
172 telnet_connection
->line_cursor
= 0;
173 telnet_connection
->option_size
= 0;
174 telnet_connection
->prompt
= strdup("> ");
175 telnet_connection
->state
= TELNET_STATE_DATA
;
177 /* output goes through telnet connection */
178 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
180 /* negotiate telnet options */
181 telnet_write(connection
, negotiate
, strlen(negotiate
));
183 /* print connection banner */
184 if (telnet_service
->banner
)
186 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
187 telnet_write(connection
, "\r\n", 2);
190 telnet_prompt(connection
);
192 /* initialize history */
193 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
195 telnet_connection
->history
[i
] = NULL
;
197 telnet_connection
->next_history
= 0;
198 telnet_connection
->current_history
= 0;
200 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
202 log_add_callback(telnet_log_callback
, connection
);
209 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
211 /* move to end of line */
212 if (t_con
->line_cursor
< t_con
->line_size
)
214 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
217 /* backspace, overwrite with space, backspace */
218 while (t_con
->line_size
> 0)
220 telnet_write(connection
, "\b \b", 3);
223 t_con
->line_cursor
= 0;
226 int telnet_input(connection_t
*connection
)
229 char buffer
[TELNET_BUFFER_SIZE
];
231 telnet_connection_t
*t_con
= connection
->priv
;
232 command_context_t
*command_context
= connection
->cmd_ctx
;
234 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
237 return ERROR_SERVER_REMOTE_CLOSED
;
238 else if (bytes_read
== -1)
240 LOG_ERROR("error during read: %s", strerror(errno
));
241 return ERROR_SERVER_REMOTE_CLOSED
;
247 switch (t_con
->state
)
249 case TELNET_STATE_DATA
:
250 if (*buf_p
== '\xff')
252 t_con
->state
= TELNET_STATE_IAC
;
256 if (isprint(*buf_p
)) /* printable character */
258 /* watch buffer size leaving one spare character for string null termination */
259 if (t_con
->line_size
== TELNET_LINE_MAX_SIZE
-1)
261 /* output audible bell if buffer is full */
262 telnet_write(connection
, "\x07", 1); /* "\a" does not work, at least on windows */
264 else if (t_con
->line_cursor
== t_con
->line_size
)
266 telnet_write(connection
, buf_p
, 1);
267 t_con
->line
[t_con
->line_size
++] = *buf_p
;
268 t_con
->line_cursor
++;
273 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
274 t_con
->line
[t_con
->line_cursor
] = *buf_p
;
276 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
277 t_con
->line_cursor
++;
278 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
280 telnet_write(connection
, "\b", 1);
284 else /* non-printable */
286 if (*buf_p
== 0x1b) /* escape */
288 t_con
->state
= TELNET_STATE_ESCAPE
;
289 t_con
->last_escape
= '\x00';
291 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
295 /* skip over combinations with CR/LF and NUL characters */
296 if ((bytes_read
> 1) && ((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)))
301 if ((bytes_read
> 1) && (*(buf_p
+ 1) == 0))
306 t_con
->line
[t_con
->line_size
] = 0;
308 telnet_write(connection
, "\r\n\x00", 3);
310 if (strcmp(t_con
->line
, "history") == 0)
313 for (i
= 1; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
315 /* the t_con->next_history line contains empty string (unless NULL), thus it is not printed */
316 char *history_line
= t_con
->history
[(t_con
->next_history
+ i
) % TELNET_LINE_HISTORY_SIZE
];
319 telnet_write(connection
, history_line
, strlen(history_line
));
320 telnet_write(connection
, "\r\n\x00", 3);
323 t_con
->line_size
= 0;
324 t_con
->line_cursor
= 0;
328 /* save only non-blank not repeating lines in the history */
329 char *prev_line
= t_con
->history
[(t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1];
330 if (*t_con
->line
&& (prev_line
== NULL
|| strcmp(t_con
->line
, prev_line
)))
332 /* if the history slot is already taken, free it */
333 if (t_con
->history
[t_con
->next_history
])
335 free(t_con
->history
[t_con
->next_history
]);
338 /* add line to history */
339 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
341 /* wrap history at TELNET_LINE_HISTORY_SIZE */
342 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
344 /* current history line starts at the new entry */
345 t_con
->current_history
= t_con
->next_history
;
347 if (t_con
->history
[t_con
->current_history
])
349 free(t_con
->history
[t_con
->current_history
]);
351 t_con
->history
[t_con
->current_history
] = strdup("");
354 t_con
->line_size
= 0;
356 t_con
->line_cursor
= -1; /* to supress prompt in log callback during command execution */
357 retval
= command_run_line(command_context
, t_con
->line
);
358 t_con
->line_cursor
= 0;
360 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
361 return ERROR_SERVER_REMOTE_CLOSED
;
363 retval
= telnet_prompt(connection
);
364 if (retval
== ERROR_SERVER_REMOTE_CLOSED
)
365 return ERROR_SERVER_REMOTE_CLOSED
;
368 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
370 if (t_con
->line_cursor
> 0)
372 if (t_con
->line_cursor
!= t_con
->line_size
)
375 telnet_write(connection
, "\b", 1);
376 t_con
->line_cursor
--;
378 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
380 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
381 telnet_write(connection
, " \b", 2);
382 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
384 telnet_write(connection
, "\b", 1);
390 t_con
->line_cursor
--;
391 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
392 telnet_write(connection
, "\b \b", 3);
396 else if (*buf_p
== 0x15) /* clear line */
398 telnet_clear_line(connection
, t_con
);
400 else if (*buf_p
== CTRL('B')) /* cursor left */
402 if (t_con
->line_cursor
> 0)
404 telnet_write(connection
, "\b", 1);
405 t_con
->line_cursor
--;
407 t_con
->state
= TELNET_STATE_DATA
;
409 else if (*buf_p
== CTRL('F')) /* cursor right */
411 if (t_con
->line_cursor
< t_con
->line_size
)
413 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
415 t_con
->state
= TELNET_STATE_DATA
;
419 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
424 case TELNET_STATE_IAC
:
428 t_con
->state
= TELNET_STATE_DONT
;
431 t_con
->state
= TELNET_STATE_DO
;
434 t_con
->state
= TELNET_STATE_WONT
;
437 t_con
->state
= TELNET_STATE_WILL
;
441 case TELNET_STATE_SB
:
443 case TELNET_STATE_SE
:
445 case TELNET_STATE_WILL
:
446 case TELNET_STATE_WONT
:
447 case TELNET_STATE_DO
:
448 case TELNET_STATE_DONT
:
449 t_con
->state
= TELNET_STATE_DATA
;
451 case TELNET_STATE_ESCAPE
:
452 if (t_con
->last_escape
== '[')
454 if (*buf_p
== 'D') /* cursor left */
456 if (t_con
->line_cursor
> 0)
458 telnet_write(connection
, "\b", 1);
459 t_con
->line_cursor
--;
461 t_con
->state
= TELNET_STATE_DATA
;
463 else if (*buf_p
== 'C') /* cursor right */
465 if (t_con
->line_cursor
< t_con
->line_size
)
467 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
469 t_con
->state
= TELNET_STATE_DATA
;
471 else if (*buf_p
== 'A') /* cursor up */
473 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
474 if (t_con
->history
[last_history
])
476 telnet_clear_line(connection
, t_con
);
477 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
478 t_con
->line_cursor
= t_con
->line_size
;
479 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
);
480 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
481 t_con
->current_history
= last_history
;
483 t_con
->state
= TELNET_STATE_DATA
;
485 else if (*buf_p
== 'B') /* cursor down */
487 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
488 if (t_con
->history
[next_history
])
490 telnet_clear_line(connection
, t_con
);
491 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
492 t_con
->line_cursor
= t_con
->line_size
;
493 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
);
494 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
495 t_con
->current_history
= next_history
;
497 t_con
->state
= TELNET_STATE_DATA
;
499 else if (*buf_p
== '3')
501 t_con
->last_escape
= *buf_p
;
505 t_con
->state
= TELNET_STATE_DATA
;
508 else if (t_con
->last_escape
== '3')
510 /* Remove character */
513 if (t_con
->line_cursor
< t_con
->line_size
)
517 /* remove char from line buffer */
518 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
520 /* print remainder of buffer */
521 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
522 /* overwrite last char with whitespace */
523 telnet_write(connection
, " \b", 2);
525 /* move back to cursor position*/
526 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
528 telnet_write(connection
, "\b", 1);
532 t_con
->state
= TELNET_STATE_DATA
;
536 t_con
->state
= TELNET_STATE_DATA
;
539 else if (t_con
->last_escape
== '\x00')
543 t_con
->last_escape
= *buf_p
;
547 t_con
->state
= TELNET_STATE_DATA
;
552 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
553 t_con
->state
= TELNET_STATE_DATA
;
558 LOG_ERROR("unknown telnet state");
569 int telnet_connection_closed(connection_t
*connection
)
571 telnet_connection_t
*t_con
= connection
->priv
;
574 log_remove_callback(telnet_log_callback
, connection
);
579 t_con
->prompt
= NULL
;
582 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
584 if (t_con
->history
[i
])
586 free(t_con
->history
[i
]);
587 t_con
->history
[i
] = NULL
;
591 /* if this connection registered a debug-message receiver delete it */
592 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
594 if (connection
->priv
)
596 free(connection
->priv
);
597 connection
->priv
= NULL
;
601 LOG_ERROR("BUG: connection->priv == NULL");
604 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
609 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
611 telnet_connection_t
*t_con
= connection
->priv
;
613 if (t_con
->prompt
!= NULL
)
616 t_con
->prompt
= strdup(prompt
);
621 int telnet_init(char *banner
)
623 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
625 if (telnet_port
== 0)
627 LOG_WARNING("no telnet port specified, using default port 4444");
631 telnet_service
->banner
= banner
;
633 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
638 int telnet_register_commands(command_context_t
*command_context
)
640 register_command(command_context
, NULL
, "exit", handle_exit_command
,
641 COMMAND_EXEC
, "exit telnet session");
643 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
649 /* daemon configuration command telnet_port */
650 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
655 telnet_port
= strtoul(args
[0], NULL
, 0);
660 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
662 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)