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 ***************************************************************************/
20 #include "telnet_server.h"
33 static unsigned short telnet_port
= 0;
35 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
36 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
38 static char *negotiate
=
39 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
40 "\xFF\xFB\x01" /* IAC WILL Echo */
41 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
42 "\xFF\xFE\x01"; /* IAC DON'T Echo */
44 #define CTRL(c) (c - '@')
46 void telnet_prompt(connection_t
*connection
)
48 telnet_connection_t
*t_con
= connection
->priv
;
50 write(connection
->fd
, t_con
->prompt
, strlen(t_con
->prompt
));
53 int telnet_output(struct command_context_s
*cmd_ctx
, char* line
)
55 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
57 write(connection
->fd
, line
, strlen(line
));
58 write(connection
->fd
, "\r\n\0", 3);
63 int telnet_target_callback_event_handler(struct target_s
*target
, enum target_event event
, void *priv
)
65 struct command_context_s
*cmd_ctx
= priv
;
66 connection_t
*connection
= cmd_ctx
->output_handler_priv
;
67 telnet_connection_t
*t_con
= connection
->priv
;
72 case TARGET_EVENT_HALTED
:
73 command_print(cmd_ctx
, "Target %i halted", get_num_by_target(target
));
74 target
->type
->arch_state(target
, buffer
, 512);
76 command_print(cmd_ctx
, "%s", buffer
);
77 telnet_prompt(connection
);
78 t_con
->surpress_prompt
= 1;
80 case TARGET_EVENT_RESUMED
:
81 command_print(cmd_ctx
, "Target %i resumed", get_num_by_target(target
));
82 telnet_prompt(connection
);
83 t_con
->surpress_prompt
= 1;
92 int telnet_new_connection(connection_t
*connection
)
94 telnet_connection_t
*telnet_connection
= malloc(sizeof(telnet_connection_t
));
95 telnet_service_t
*telnet_service
= connection
->service
->priv
;
98 connection
->priv
= telnet_connection
;
100 /* initialize telnet connection information */
101 telnet_connection
->line_size
= 0;
102 telnet_connection
->line_cursor
= 0;
103 telnet_connection
->option_size
= 0;
104 telnet_connection
->prompt
= strdup("> ");
105 telnet_connection
->surpress_prompt
= 0;
106 telnet_connection
->state
= TELNET_STATE_DATA
;
108 /* output goes through telnet connection */
109 command_set_output_handler(connection
->cmd_ctx
, telnet_output
, connection
);
111 /* negotiate telnet options */
112 write(connection
->fd
, negotiate
, strlen(negotiate
));
114 /* print connection banner */
115 if (telnet_service
->banner
)
117 write(connection
->fd
, telnet_service
->banner
, strlen(telnet_service
->banner
));
118 write(connection
->fd
, "\r\n\0", 3);
121 telnet_prompt(connection
);
123 /* initialize history */
124 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
126 telnet_connection
->history
[i
] = NULL
;
128 telnet_connection
->next_history
= 0;
129 telnet_connection
->current_history
= 0;
131 target_register_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
136 void telnet_clear_line(connection_t
*connection
, telnet_connection_t
*t_con
)
138 /* move to end of line */
139 if (t_con
->line_cursor
< t_con
->line_size
)
141 write(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
144 /* backspace, overwrite with space, backspace */
145 while (t_con
->line_size
> 0)
147 write(connection
->fd
, "\b \b", 3);
150 t_con
->line_cursor
= 0;
153 int telnet_input(connection_t
*connection
)
156 char buffer
[TELNET_BUFFER_SIZE
];
158 telnet_connection_t
*t_con
= connection
->priv
;
159 command_context_t
*command_context
= connection
->cmd_ctx
;
161 bytes_read
= read(connection
->fd
, buffer
, TELNET_BUFFER_SIZE
);
164 return ERROR_SERVER_REMOTE_CLOSED
;
165 else if (bytes_read
== -1)
167 ERROR("error during read: %s", strerror(errno
));
168 return ERROR_SERVER_REMOTE_CLOSED
;
174 switch (t_con
->state
)
176 case TELNET_STATE_DATA
:
177 if (*buf_p
== '\xff')
179 t_con
->state
= TELNET_STATE_IAC
;
183 if (isprint(*buf_p
)) /* printable character */
185 write(connection
->fd
, buf_p
, 1);
186 if (t_con
->line_cursor
== t_con
->line_size
)
188 t_con
->line
[t_con
->line_size
++] = *buf_p
;
189 t_con
->line_cursor
++;
194 memmove(t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
195 t_con
->line
[t_con
->line_cursor
++] = *buf_p
;
197 write(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
198 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
200 write(connection
->fd
, "\b", 1);
204 else /* non-printable */
206 if (*buf_p
== 0x1b) /* escape */
208 t_con
->state
= TELNET_STATE_ESCAPE
;
209 t_con
->last_escape
= '\x00';
211 else if ((*buf_p
== 0xd) || (*buf_p
== 0xa)) /* CR/LF */
215 /* skip over combinations with CR/LF + NUL */
216 if (((*(buf_p
+ 1) == 0xa) || (*(buf_p
+ 1) == 0xd)) && (bytes_read
> 1))
221 if ((*(buf_p
+ 1) == 0) && (bytes_read
> 1))
226 t_con
->line
[t_con
->line_size
] = 0;
228 write(connection
->fd
, "\r\n\x00", 3);
230 if (strcmp(t_con
->line
, "history") == 0)
233 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
235 if (t_con
->history
[i
])
237 write(connection
->fd
, t_con
->history
[i
], strlen(t_con
->history
[i
]));
238 write(connection
->fd
, "\r\n\x00", 3);
241 telnet_prompt(connection
);
242 t_con
->line_size
= 0;
243 t_con
->line_cursor
= 0;
247 /* we're running a command, so we need a prompt
248 * if the output handler is called, this gets set again */
249 t_con
->surpress_prompt
= 0;
250 if ((retval
= command_run_line(command_context
, t_con
->line
)) != ERROR_OK
)
252 if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
254 return ERROR_SERVER_REMOTE_CLOSED
;
258 /* if the history slot is already taken, free it */
259 if (t_con
->history
[t_con
->next_history
])
261 free(t_con
->history
[t_con
->next_history
]);
264 /* add line to history */
265 t_con
->history
[t_con
->next_history
++] = strdup(t_con
->line
);
267 /* current history line starts at the new entry */
268 t_con
->current_history
= t_con
->next_history
;
270 if (t_con
->history
[t_con
->current_history
])
272 free(t_con
->history
[t_con
->current_history
]);
274 t_con
->history
[t_con
->current_history
] = strdup("");
276 /* wrap history at TELNET_LINE_HISTORY_SIZE */
277 if (t_con
->next_history
> TELNET_LINE_HISTORY_SIZE
- 1)
278 t_con
->next_history
= 0;
280 if (!t_con
->surpress_prompt
)
282 telnet_prompt(connection
);
286 t_con
->surpress_prompt
= 0;
289 t_con
->line_size
= 0;
290 t_con
->line_cursor
= 0;
292 else if ((*buf_p
== 0x7f) || (*buf_p
== 0x8)) /* delete character */
294 if (t_con
->line_cursor
> 0)
296 if (t_con
->line_cursor
!= t_con
->line_size
)
299 write(connection
->fd
, "\b", 1);
300 t_con
->line_cursor
--;
302 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
304 write(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
305 write(connection
->fd
, " \b", 2);
306 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
308 write(connection
->fd
, "\b", 1);
314 t_con
->line_cursor
--;
315 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
316 write(connection
->fd
, "\b \b", 3);
320 else if (*buf_p
== 0x15) /* clear line */
322 telnet_clear_line(connection
, t_con
);
324 else if (*buf_p
== CTRL('B')) /* cursor left */
326 if (t_con
->line_cursor
> 0)
328 write(connection
->fd
, "\b", 1);
329 t_con
->line_cursor
--;
331 t_con
->state
= TELNET_STATE_DATA
;
333 else if (*buf_p
== CTRL('F')) /* cursor right */
335 if (t_con
->line_cursor
< t_con
->line_size
)
337 write(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
339 t_con
->state
= TELNET_STATE_DATA
;
343 DEBUG("unhandled nonprintable: %2.2x", *buf_p
);
348 case TELNET_STATE_IAC
:
352 t_con
->state
= TELNET_STATE_DONT
;
355 t_con
->state
= TELNET_STATE_DO
;
358 t_con
->state
= TELNET_STATE_WONT
;
361 t_con
->state
= TELNET_STATE_WILL
;
365 case TELNET_STATE_SB
:
367 case TELNET_STATE_SE
:
369 case TELNET_STATE_WILL
:
370 case TELNET_STATE_WONT
:
371 case TELNET_STATE_DO
:
372 case TELNET_STATE_DONT
:
373 t_con
->state
= TELNET_STATE_DATA
;
375 case TELNET_STATE_ESCAPE
:
376 if (t_con
->last_escape
== '[')
378 if (*buf_p
== 'D') /* cursor left */
380 if (t_con
->line_cursor
> 0)
382 write(connection
->fd
, "\b", 1);
383 t_con
->line_cursor
--;
385 t_con
->state
= TELNET_STATE_DATA
;
387 else if (*buf_p
== 'C') /* cursor right */
389 if (t_con
->line_cursor
< t_con
->line_size
)
391 write(connection
->fd
, t_con
->line
+ t_con
->line_cursor
++, 1);
393 t_con
->state
= TELNET_STATE_DATA
;
395 else if (*buf_p
== 'A') /* cursor up */
397 int last_history
= (t_con
->current_history
- 1 >= 0) ? t_con
->current_history
- 1 : 127;
398 if (t_con
->history
[last_history
])
400 telnet_clear_line(connection
, t_con
);
401 t_con
->line_size
= strlen(t_con
->history
[last_history
]);
402 t_con
->line_cursor
= t_con
->line_size
;
403 memcpy(t_con
->line
, t_con
->history
[last_history
], t_con
->line_size
+ 1);
404 write(connection
->fd
, t_con
->line
, t_con
->line_size
);
405 t_con
->current_history
= last_history
;
407 t_con
->state
= TELNET_STATE_DATA
;
409 else if (*buf_p
== 'B') /* cursor down */
411 int next_history
= (t_con
->current_history
+ 1 < 128) ? t_con
->current_history
+ 1 : 0;
412 if (t_con
->history
[next_history
])
414 telnet_clear_line(connection
, t_con
);
415 t_con
->line_size
= strlen(t_con
->history
[next_history
]);
416 t_con
->line_cursor
= t_con
->line_size
;
417 memcpy(t_con
->line
, t_con
->history
[next_history
], t_con
->line_size
+ 1);
418 write(connection
->fd
, t_con
->line
, t_con
->line_size
);
419 t_con
->current_history
= next_history
;
421 t_con
->state
= TELNET_STATE_DATA
;
423 else if (*buf_p
== '3')
425 t_con
->last_escape
= *buf_p
;
429 t_con
->state
= TELNET_STATE_DATA
;
432 else if (t_con
->last_escape
== '3')
434 /* Remove character */
437 if (t_con
->line_cursor
< t_con
->line_size
)
441 /* remove char from line buffer */
442 memmove(t_con
->line
+ t_con
->line_cursor
, t_con
->line
+ t_con
->line_cursor
+ 1, t_con
->line_size
- t_con
->line_cursor
);
444 /* print remainder of buffer */
445 write(connection
->fd
, t_con
->line
+ t_con
->line_cursor
, t_con
->line_size
- t_con
->line_cursor
);
446 /* overwrite last char with whitespace */
447 write(connection
->fd
, " \b", 2);
449 /* move back to cursor position*/
450 for (i
= t_con
->line_cursor
; i
< t_con
->line_size
; i
++)
452 write(connection
->fd
, "\b", 1);
456 t_con
->state
= TELNET_STATE_DATA
;
460 t_con
->state
= TELNET_STATE_DATA
;
463 else if (t_con
->last_escape
== '\x00')
467 t_con
->last_escape
= *buf_p
;
471 t_con
->state
= TELNET_STATE_DATA
;
476 ERROR("BUG: unexpected value in t_con->last_escape");
477 t_con
->state
= TELNET_STATE_DATA
;
482 ERROR("unknown telnet state");
493 int telnet_connection_closed(connection_t
*connection
)
495 telnet_connection_t
*t_con
= connection
->priv
;
501 for (i
= 0; i
< TELNET_LINE_HISTORY_SIZE
; i
++)
503 if (t_con
->history
[i
])
504 free(t_con
->history
[i
]);
507 if (connection
->priv
)
508 free(connection
->priv
);
510 ERROR("BUG: connection->priv == NULL");
512 target_unregister_event_callback(telnet_target_callback_event_handler
, connection
->cmd_ctx
);
517 int telnet_set_prompt(connection_t
*connection
, char *prompt
)
519 telnet_connection_t
*t_con
= connection
->priv
;
521 t_con
->prompt
= strdup(prompt
);
526 int telnet_init(char *banner
)
528 telnet_service_t
*telnet_service
= malloc(sizeof(telnet_service_t
));
530 if (telnet_port
== 0)
532 WARNING("no telnet port specified, using default port 4444");
536 telnet_service
->banner
= banner
;
538 add_service("telnet", CONNECTION_TELNET
, telnet_port
, 1, telnet_new_connection
, telnet_input
, telnet_connection_closed
, telnet_service
);
543 int telnet_register_commands(command_context_t
*command_context
)
545 register_command(command_context
, NULL
, "exit", handle_exit_command
,
546 COMMAND_EXEC
, "exit telnet session");
548 register_command(command_context
, NULL
, "telnet_port", handle_telnet_port_command
,
554 /* daemon configuration command telnet_port */
555 int handle_telnet_port_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
560 /* only if the port wasn't overwritten by cmdline */
561 if (telnet_port
== 0)
562 telnet_port
= strtoul(args
[0], NULL
, 0);
567 int handle_exit_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
569 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)