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
)
80 telnet_write(connection
, line
, strlen(line
));
81 return telnet_write(connection
, "\r\n\0", 3);
84 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
86 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
88 return telnet_outputline(connection
, line
);
91 void telnet_log_callback(void *priv
, const char *file
, int line
,
92 const char *function
, const char *format
, va_list args
)
94 connection_t
*connection
= priv
;
95 char *t
= allocPrintf(format
, args
);
103 if ((endline
=strchr(t2
, '\n'))!=NULL
)
107 telnet_outputline(connection
, t2
);
115 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
117 struct command_context_s
*cmd_ctx
= priv
;
118 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
119 telnet_connection_t
*t_con
= connection
->priv
;
123 case TARGET_EVENT_HALTED
:
124 target_arch_state(target
);
125 if (!t_con
->suppress_prompt
)
126 telnet_prompt(connection
);
128 case TARGET_EVENT_RESUMED
:
129 if (!t_con
->suppress_prompt
)
130 telnet_prompt(connection
);
139 int telnet_new_connection(connection_t
*connection
)
141 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
142 telnet_service_t
*telnet_service
= connection
->service
->priv
;
145 connection
->priv
= telnet_connection
;
147 /* initialize telnet connection information */
148 telnet_connection
->closed
= 0;
149 telnet_connection
->line_size
= 0;
150 telnet_connection
->line_cursor
= 0;
151 telnet_connection
->option_size
= 0;
152 telnet_connection
->prompt
= strdup("> ");
153 telnet_connection
->suppress_prompt
= 0;
154 telnet_connection
->state
= TELNET_STATE_DATA
;
156 /* output goes through telnet connection */
157 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
159 /* negotiate telnet options */
160 telnet_write(connection
, negotiate
, strlen(negotiate
));
162 /* print connection banner */
163 if (telnet_service
->banner
)
165 telnet_write(connection
, telnet_service
->banner
, strlen(telnet_service
->banner
));
166 telnet_write(connection
, "\r\n\0", 3);
169 telnet_prompt(connection
);
171 /* initialize history */
172 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
174 telnet_connection
->history
[i
] = NULL
;
176 telnet_connection
->next_history
= 0;
177 telnet_connection
->current_history
= 0;
179 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
184 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
186 /* move to end of line */
187 if (t_con
->line_cursor
< t_con
->line_size
)
189 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
192 /* backspace, overwrite with space, backspace */
193 while (t_con
->line_size
> 0)
195 telnet_write(connection
, "\b \b", 3);
198 t_con
->line_cursor
= 0;
201 int telnet_input(connection_t
*connection
)
204 char buffer
[TELNET_BUFFER_SIZE
];
206 telnet_connection_t
*t_con
= connection
->priv
;
207 command_context_t
*command_context
= connection
->cmd_ctx
;
209 bytes_read
= read_socket(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
212 return ERROR_SERVER_REMOTE_CLOSED
;
213 else if (bytes_read
== -1)
215 ERROR("error during read: %s", strerror(errno
));
216 return ERROR_SERVER_REMOTE_CLOSED
;
222 switch (t_con
->state
)
224 case TELNET_STATE_DATA
:
225 if (*buf_p
== '\xff')
227 t_con
->state
= TELNET_STATE_IAC
;
231 if (isprint(*buf_p
)) /* printable character */
233 telnet_write(connection
, buf_p
, 1);
234 if (t_con
->line_cursor
== t_con
->line_size
)
236 t_con
->line
[t_con
->line_size
++] = *buf_p
;
237 t_con
->line_cursor
++;
242 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
243 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
245 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
246 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
248 telnet_write(connection
, "\b", 1);
252 else /* non-printable */
254 if (*buf_p
== 0x1b) /* escape */
256 t_con
->state
= TELNET_STATE_ESCAPE
;
257 t_con
->last_escape
= '\x00';
259 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
263 /* skip over combinations with CR/LF + NUL */
264 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
269 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
274 t_con
->line
[t_con
->line_size
] = 0;
276 telnet_write(connection
, "\r\n\x00", 3);
278 if (strcmp(t_con
->line
, "history") == 0)
281 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
283 if (t_con
->history
[i
])
285 telnet_write(connection
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
286 telnet_write(connection
, "\r\n\x00", 3);
289 telnet_prompt(connection
);
290 t_con
->line_size
= 0;
291 t_con
->line_cursor
= 0;
295 log_setCallback(telnet_log_callback
, connection
);
296 t_con
->suppress_prompt
= 1;
298 if ((retval
= command_run_line(command_context
, t_con
->line
)) != ERROR_OK
)
300 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
302 return ERROR_SERVER_REMOTE_CLOSED
;
306 t_con
->suppress_prompt
= 0;
308 /* Save only non-blank lines in the history */
309 if (t_con
->line_size
> 0)
311 /* if the history slot is already taken, free it */
312 if (t_con
->history
[t_con
->next_history
])
314 free(t_con
->history
[t_con
->next_history
]);
317 /* add line to history */
318 t_con
->history
[t_con
->next_history
] = strdup(t_con
->line
);
320 /* wrap history at TELNET_LINE_HISTORY_SIZE */
321 t_con
->next_history
= (t_con
->next_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
323 /* current history line starts at the new entry */
324 t_con
->current_history
= t_con
->next_history
;
326 if (t_con
->history
[t_con
->current_history
])
328 free(t_con
->history
[t_con
->current_history
]);
330 t_con
->history
[t_con
->current_history
] = strdup("");
333 int t
= telnet_prompt(connection
);
334 if (t
== ERROR_SERVER_REMOTE_CLOSED
)
337 t_con
->line_size
= 0;
338 t_con
->line_cursor
= 0;
340 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
342 if (t_con
->line_cursor
> 0)
344 if (t_con
->line_cursor
!= t_con
->line_size
)
347 telnet_write(connection
, "\b", 1);
348 t_con
->line_cursor
--;
350 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
352 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
353 telnet_write(connection
, " \b", 2);
354 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
356 telnet_write(connection
, "\b", 1);
362 t_con
->line_cursor
--;
363 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
364 telnet_write(connection
, "\b \b", 3);
368 else if (*buf_p
== 0x15) /* clear line */
370 telnet_clear_line(connection
, t_con
);
372 else if (*buf_p
== CTRL('B')) /* cursor left */
374 if (t_con
->line_cursor
> 0)
376 telnet_write(connection
, "\b", 1);
377 t_con
->line_cursor
--;
379 t_con
->state
= TELNET_STATE_DATA
;
381 else if (*buf_p
== CTRL('F')) /* cursor right */
383 if (t_con
->line_cursor
< t_con
->line_size
)
385 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
387 t_con
->state
= TELNET_STATE_DATA
;
391 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
396 case TELNET_STATE_IAC
:
400 t_con
->state
= TELNET_STATE_DONT
;
403 t_con
->state
= TELNET_STATE_DO
;
406 t_con
->state
= TELNET_STATE_WONT
;
409 t_con
->state
= TELNET_STATE_WILL
;
413 case TELNET_STATE_SB
:
415 case TELNET_STATE_SE
:
417 case TELNET_STATE_WILL
:
418 case TELNET_STATE_WONT
:
419 case TELNET_STATE_DO
:
420 case TELNET_STATE_DONT
:
421 t_con
->state
= TELNET_STATE_DATA
;
423 case TELNET_STATE_ESCAPE
:
424 if (t_con
->last_escape
== '[')
426 if (*buf_p
== 'D') /* cursor left */
428 if (t_con
->line_cursor
> 0)
430 telnet_write(connection
, "\b", 1);
431 t_con
->line_cursor
--;
433 t_con
->state
= TELNET_STATE_DATA
;
435 else if (*buf_p
== 'C') /* cursor right */
437 if (t_con
->line_cursor
< t_con
->line_size
)
439 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
++, 1);
441 t_con
->state
= TELNET_STATE_DATA
;
443 else if (*buf_p
== 'A') /* cursor up */
445 int last_history
= (t_con
->current_history
> 0) ? t_con
->current_history
- 1 : TELNET_LINE_HISTORY_SIZE
-1;
446 if (t_con
->history
[last_history
])
448 telnet_clear_line(connection
, t_con
);
449 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
450 t_con
->line_cursor
= t_con
->line_size
;
451 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
452 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
453 t_con
->current_history
= last_history
;
455 t_con
->state
= TELNET_STATE_DATA
;
457 else if (*buf_p
== 'B') /* cursor down */
459 int next_history
= (t_con
->current_history
+ 1) % TELNET_LINE_HISTORY_SIZE
;
460 if (t_con
->history
[next_history
])
462 telnet_clear_line(connection
, t_con
);
463 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
464 t_con
->line_cursor
= t_con
->line_size
;
465 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
466 telnet_write(connection
, t_con
->line
, t_con
->line_size
);
467 t_con
->current_history
= next_history
;
469 t_con
->state
= TELNET_STATE_DATA
;
471 else if (*buf_p
== '3')
473 t_con
->last_escape
= *buf_p
;
477 t_con
->state
= TELNET_STATE_DATA
;
480 else if (t_con
->last_escape
== '3')
482 /* Remove character */
485 if (t_con
->line_cursor
< t_con
->line_size
)
489 /* remove char from line buffer */
490 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
492 /* print remainder of buffer */
493 telnet_write(connection
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
494 /* overwrite last char with whitespace */
495 telnet_write(connection
, " \b", 2);
497 /* move back to cursor position*/
498 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
500 telnet_write(connection
, "\b", 1);
504 t_con
->state
= TELNET_STATE_DATA
;
508 t_con
->state
= TELNET_STATE_DATA
;
511 else if (t_con
->last_escape
== '\x00')
515 t_con
->last_escape
= *buf_p
;
519 t_con
->state
= TELNET_STATE_DATA
;
524 ERROR("BUG: unexpected value in t_con->last_escape");
525 t_con
->state
= TELNET_STATE_DATA
;
530 ERROR("unknown telnet state");
541 int telnet_connection_closed(connection_t
*connection
)
543 telnet_connection_t
*t_con
= connection
->priv
;
549 t_con
->prompt
= NULL
;
552 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
554 if (t_con
->history
[i
])
556 free(t_con
->history
[i
]);
557 t_con
->history
[i
] = NULL
;
561 /* if this connection registered a debug-message receiver delete it */
562 delete_debug_msg_receiver(connection
->cmd_ctx
, NULL
);
564 if (connection
->priv
)
566 free(connection
->priv
);
567 connection
->priv
= NULL
;
571 ERROR("BUG: connection->priv == NULL");
574 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
579 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
581 telnet_connection_t
*t_con
= connection
->priv
;
583 t_con
->prompt
= strdup(prompt
);
588 int telnet_init(char *banner
)
590 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
592 if (telnet_port
== 0)
594 WARNING("no telnet port specified, using default port 4444");
598 telnet_service
->banner
= banner
;
600 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
605 int telnet_register_commands(command_context_t
*command_context
)
607 register_command(command_context
, NULL
, "exit", handle_exit_command
,
608 COMMAND_EXEC
, "exit telnet session");
610 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
616 /* daemon configuration command telnet_port */
617 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
622 /* only if the port wasn't overwritten by cmdline */
623 if (telnet_port
== 0)
624 telnet_port
= strtoul(args
[0], NULL
, 0);
629 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
631 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)