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_outputline(connection_t
*connection
, char* line
)
62 write_socket(connection
->fd
, line
, strlen(line
));
63 return write_socket(connection
->fd
, "\r\n\0", 3);
66 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
68 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
70 write_socket(connection
->fd
, line
, strlen(line
));
71 write_socket(connection
->fd
, "\r\n\0", 3);
76 void telnet_log_callback(void *privData
, const char *file
, int line
,
77 const char *function
, const char *format
, va_list args
)
79 connection_t
*connection
=(connection_t
*)privData
;
80 char *t
=allocPrintf(format
, args
);
84 telnet_outputline(connection
, t
);
89 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
91 struct command_context_s
*cmd_ctx
= priv
;
96 case TARGET_EVENT_HALTED
:
97 command_print(cmd_ctx
, "Target %i halted", get_num_by_target(target
));
98 target
->type
->arch_state(target
, buffer
, 512);
100 command_print(cmd_ctx
, "%s", buffer
);
102 case TARGET_EVENT_RESUMED
:
103 command_print(cmd_ctx
, "Target %i resumed", get_num_by_target(target
));
112 int telnet_new_connection(connection_t
*connection
)
114 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
115 telnet_service_t
*telnet_service
= connection
->service
->priv
;
118 connection
->priv
= telnet_connection
;
120 /* initialize telnet connection information */
121 telnet_connection
->line_size
= 0;
122 telnet_connection
->line_cursor
= 0;
123 telnet_connection
->option_size
= 0;
124 telnet_connection
->prompt
= strdup("> ");
125 telnet_connection
->state
= TELNET_STATE_DATA
;
127 /* output goes through telnet connection */
128 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
130 /* negotiate telnet options */
131 write_socket(connection
->fd
, negotiate
, strlen(negotiate
));
133 /* print connection banner */
134 if (telnet_service
->banner
)
136 write_socket(connection
->fd
, telnet_service
->banner
, strlen(telnet_service
->banner
));
137 write_socket(connection
->fd
, "\r\n\0", 3);
140 telnet_prompt(connection
);
142 /* initialize history */
143 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
145 telnet_connection
->history
[i
] = NULL
;
147 telnet_connection
->next_history
= 0;
148 telnet_connection
->current_history
= 0;
150 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
155 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
157 /* move to end of line */
158 if (t_con
->line_cursor
< t_con
->line_size
)
160 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
163 /* backspace, overwrite with space, backspace */
164 while (t_con
->line_size
> 0)
166 write_socket(connection
->fd
, "\b \b", 3);
169 t_con
->line_cursor
= 0;
172 int telnet_input(connection_t
*connection
)
175 char buffer
[TELNET_BUFFER_SIZE
];
177 telnet_connection_t
*t_con
= connection
->priv
;
178 command_context_t
*command_context
= connection
->cmd_ctx
;
180 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
183 return ERROR_SERVER_REMOTE_CLOSED
;
184 else if (bytes_read
== -1)
186 ERROR("error during read: %s", strerror(errno
));
187 return ERROR_SERVER_REMOTE_CLOSED
;
193 switch (t_con
->state
)
195 case TELNET_STATE_DATA
:
196 if (*buf_p
== '\xff')
198 t_con
->state
= TELNET_STATE_IAC
;
202 if (isprint(*buf_p
)) /* printable character */
204 write_socket(connection
->fd
, buf_p
, 1);
205 if (t_con
->line_cursor
== t_con
->line_size
)
207 t_con
->line
[t_con
->line_size
++] = *buf_p
;
208 t_con
->line_cursor
++;
213 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
214 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
216 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
217 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
219 write_socket(connection
->fd
, "\b", 1);
223 else /* non-printable */
225 if (*buf_p
== 0x1b) /* escape */
227 t_con
->state
= TELNET_STATE_ESCAPE
;
228 t_con
->last_escape
= '\x00';
230 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
234 /* skip over combinations with CR/LF + NUL */
235 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
240 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
245 t_con
->line
[t_con
->line_size
] = 0;
247 write_socket(connection
->fd
, "\r\n\x00", 3);
249 if (strcmp(t_con
->line
, "history") == 0)
252 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
254 if (t_con
->history
[i
])
256 write_socket(connection
->fd
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
257 write_socket(connection
->fd
, "\r\n\x00", 3);
260 telnet_prompt(connection
);
261 t_con
->line_size
= 0;
262 t_con
->line_cursor
= 0;
268 log_setCallback(telnet_log_callback
, connection
);
270 if ((retval
= command_run_line(command_context
, t_con
->line
)) != ERROR_OK
)
272 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
274 return ERROR_SERVER_REMOTE_CLOSED
;
278 /* Save only non-blank lines in the history */
279 if (t_con
->line_size
> 0)
281 /* if the history slot is already taken, free it */
282 if (t_con
->history
[t_con
->next_history
])
284 free(t_con
->history
[t_con
->next_history
]);
287 /* add line to history */
288 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
290 /* wrap history at TELNET_LINE_HISTORY_SIZE */
291 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
293 /* current history line starts at the new entry */
294 t_con
->current_history
= t_con
->next_history
;
296 if (t_con
->history
[t_con
->current_history
])
298 free(t_con
->history
[t_con
->current_history
]);
300 t_con
->history
[t_con
->current_history
] = strdup("");
303 /* output prompt after command */
304 telnet_prompt(connection
);
306 t_con
->line_size
= 0;
307 t_con
->line_cursor
= 0;
309 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
311 if (t_con
->line_cursor
> 0)
313 if (t_con
->line_cursor
!= t_con
->line_size
)
316 write_socket(connection
->fd
, "\b", 1);
317 t_con
->line_cursor
--;
319 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
321 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
322 write_socket(connection
->fd
, " \b", 2);
323 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
325 write_socket(connection
->fd
, "\b", 1);
331 t_con
->line_cursor
--;
332 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
333 write_socket(connection
->fd
, "\b \b", 3);
337 else if (*buf_p
== 0x15) /* clear line */
339 telnet_clear_line(connection
, t_con
);
341 else if (*buf_p
== CTRL('B')) /* cursor left */
343 if (t_con
->line_cursor
> 0)
345 write_socket(connection
->fd
, "\b", 1);
346 t_con
->line_cursor
--;
348 t_con
->state
= TELNET_STATE_DATA
;
350 else if (*buf_p
== CTRL('F')) /* cursor right */
352 if (t_con
->line_cursor
< t_con
->line_size
)
354 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
356 t_con
->state
= TELNET_STATE_DATA
;
360 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
365 case TELNET_STATE_IAC
:
369 t_con
->state
= TELNET_STATE_DONT
;
372 t_con
->state
= TELNET_STATE_DO
;
375 t_con
->state
= TELNET_STATE_WONT
;
378 t_con
->state
= TELNET_STATE_WILL
;
382 case TELNET_STATE_SB
:
384 case TELNET_STATE_SE
:
386 case TELNET_STATE_WILL
:
387 case TELNET_STATE_WONT
:
388 case TELNET_STATE_DO
:
389 case TELNET_STATE_DONT
:
390 t_con
->state
= TELNET_STATE_DATA
;
392 case TELNET_STATE_ESCAPE
:
393 if (t_con
->last_escape
== '[')
395 if (*buf_p
== 'D') /* cursor left */
397 if (t_con
->line_cursor
> 0)
399 write_socket(connection
->fd
, "\b", 1);
400 t_con
->line_cursor
--;
402 t_con
->state
= TELNET_STATE_DATA
;
404 else if (*buf_p
== 'C') /* cursor right */
406 if (t_con
->line_cursor
< t_con
->line_size
)
408 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
410 t_con
->state
= TELNET_STATE_DATA
;
412 else if (*buf_p
== 'A') /* cursor up */
414 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
415 if (t_con
->history
[last_history
])
417 telnet_clear_line(connection
, t_con
);
418 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
419 t_con
->line_cursor
= t_con
->line_size
;
420 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
421 write_socket(connection
->fd
, t_con
->line
, t_con
->line_size
);
422 t_con
->current_history
= last_history
;
424 t_con
->state
= TELNET_STATE_DATA
;
426 else if (*buf_p
== 'B') /* cursor down */
428 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
429 if (t_con
->history
[next_history
])
431 telnet_clear_line(connection
, t_con
);
432 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
433 t_con
->line_cursor
= t_con
->line_size
;
434 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
435 write_socket(connection
->fd
, t_con
->line
, t_con
->line_size
);
436 t_con
->current_history
= next_history
;
438 t_con
->state
= TELNET_STATE_DATA
;
440 else if (*buf_p
== '3')
442 t_con
->last_escape
= *buf_p
;
446 t_con
->state
= TELNET_STATE_DATA
;
449 else if (t_con
->last_escape
== '3')
451 /* Remove character */
454 if (t_con
->line_cursor
< t_con
->line_size
)
458 /* remove char from line buffer */
459 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
461 /* print remainder of buffer */
462 write_socket(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
463 /* overwrite last char with whitespace */
464 write_socket(connection
->fd
, " \b", 2);
466 /* move back to cursor position*/
467 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
469 write_socket(connection
->fd
, "\b", 1);
473 t_con
->state
= TELNET_STATE_DATA
;
477 t_con
->state
= TELNET_STATE_DATA
;
480 else if (t_con
->last_escape
== '\x00')
484 t_con
->last_escape
= *buf_p
;
488 t_con
->state
= TELNET_STATE_DATA
;
493 ERROR("BUG: unexpected value in t_con->last_escape");
494 t_con
->state
= TELNET_STATE_DATA
;
499 ERROR("unknown telnet state");
510 int telnet_connection_closed(connection_t
*connection
)
512 telnet_connection_t
*t_con
= connection
->priv
;
518 t_con
->prompt
= NULL
;
521 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
523 if (t_con
->history
[i
])
525 free(t_con
->history
[i
]);
526 t_con
->history
[i
] = NULL
;
530 /* if this connection registered a debug-message receiver delete it */
531 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
533 if (connection
->priv
)
535 free(connection
->priv
);
536 connection
->priv
= NULL
;
540 ERROR("BUG: connection->priv == NULL");
543 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
548 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
550 telnet_connection_t
*t_con
= connection
->priv
;
552 t_con
->prompt
= strdup(prompt
);
557 int telnet_init(char *banner
)
559 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
561 if (telnet_port
== 0)
563 WARNING("no telnet port specified, using default port 4444");
567 telnet_service
->banner
= banner
;
569 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
574 int telnet_register_commands(command_context_t
*command_context
)
576 register_command(command_context
, NULL
, "exit", handle_exit_command
,
577 COMMAND_EXEC
, "exit telnet session");
579 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
585 /* daemon configuration command telnet_port */
586 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
591 /* only if the port wasn't overwritten by cmdline */
592 if (telnet_port
== 0)
593 telnet_port
= strtoul(args
[0], NULL
, 0);
598 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
600 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)