3a74f5ac4c8905dd1c98e7b4b12220f4a9611742
[openocd.git] / src / server / telnet_server.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
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. *
9 * *
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. *
14 * *
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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "replacements.h"
25
26 #include "telnet_server.h"
27
28 #include "server.h"
29 #include "log.h"
30 #include "command.h"
31 #include "target.h"
32 #include "target_request.h"
33
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 static unsigned short telnet_port = 0;
41
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);
44
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 */
50
51 #define CTRL(c) (c - '@')
52
53 void telnet_prompt(connection_t *connection)
54 {
55 telnet_connection_t *t_con = connection->priv;
56
57 write_socket(connection->fd, t_con->prompt, strlen(t_con->prompt));
58 }
59
60 int telnet_output(struct command_context_s *cmd_ctx, char* line)
61 {
62 connection_t *connection = cmd_ctx->output_handler_priv;
63
64 write_socket(connection->fd, line, strlen(line));
65 write_socket(connection->fd, "\r\n\0", 3);
66
67 return ERROR_OK;
68 }
69
70 int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
71 {
72 struct command_context_s *cmd_ctx = priv;
73 char buffer[512];
74
75 switch (event)
76 {
77 case TARGET_EVENT_HALTED:
78 command_print(cmd_ctx, "Target %i halted", get_num_by_target(target));
79 target->type->arch_state(target, buffer, 512);
80 buffer[511] = 0;
81 command_print(cmd_ctx, "%s", buffer);
82 break;
83 case TARGET_EVENT_RESUMED:
84 command_print(cmd_ctx, "Target %i resumed", get_num_by_target(target));
85 break;
86 default:
87 break;
88 }
89
90 return ERROR_OK;
91 }
92
93 int telnet_new_connection(connection_t *connection)
94 {
95 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
96 telnet_service_t *telnet_service = connection->service->priv;
97 int i;
98
99 connection->priv = telnet_connection;
100
101 /* initialize telnet connection information */
102 telnet_connection->line_size = 0;
103 telnet_connection->line_cursor = 0;
104 telnet_connection->option_size = 0;
105 telnet_connection->prompt = strdup("> ");
106 telnet_connection->state = TELNET_STATE_DATA;
107
108 /* output goes through telnet connection */
109 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
110
111 /* negotiate telnet options */
112 write_socket(connection->fd, negotiate, strlen(negotiate));
113
114 /* print connection banner */
115 if (telnet_service->banner)
116 {
117 write_socket(connection->fd, telnet_service->banner, strlen(telnet_service->banner));
118 write_socket(connection->fd, "\r\n\0", 3);
119 }
120
121 telnet_prompt(connection);
122
123 /* initialize history */
124 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
125 {
126 telnet_connection->history[i] = NULL;
127 }
128 telnet_connection->next_history = 0;
129 telnet_connection->current_history = 0;
130
131 target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
132
133 return ERROR_OK;
134 }
135
136 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
137 {
138 /* move to end of line */
139 if (t_con->line_cursor < t_con->line_size)
140 {
141 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
142 }
143
144 /* backspace, overwrite with space, backspace */
145 while (t_con->line_size > 0)
146 {
147 write_socket(connection->fd, "\b \b", 3);
148 t_con->line_size--;
149 }
150 t_con->line_cursor = 0;
151 }
152
153 int telnet_input(connection_t *connection)
154 {
155 int bytes_read;
156 char buffer[TELNET_BUFFER_SIZE];
157 char *buf_p;
158 telnet_connection_t *t_con = connection->priv;
159 command_context_t *command_context = connection->cmd_ctx;
160
161 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
162
163 if (bytes_read == 0)
164 return ERROR_SERVER_REMOTE_CLOSED;
165 else if (bytes_read == -1)
166 {
167 ERROR("error during read: %s", strerror(errno));
168 return ERROR_SERVER_REMOTE_CLOSED;
169 }
170
171 buf_p = buffer;
172 while (bytes_read)
173 {
174 switch (t_con->state)
175 {
176 case TELNET_STATE_DATA:
177 if (*buf_p == '\xff')
178 {
179 t_con->state = TELNET_STATE_IAC;
180 }
181 else
182 {
183 if (isprint(*buf_p)) /* printable character */
184 {
185 write_socket(connection->fd, buf_p, 1);
186 if (t_con->line_cursor == t_con->line_size)
187 {
188 t_con->line[t_con->line_size++] = *buf_p;
189 t_con->line_cursor++;
190 }
191 else
192 {
193 int i;
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;
196 t_con->line_size++;
197 write_socket(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++)
199 {
200 write_socket(connection->fd, "\b", 1);
201 }
202 }
203 }
204 else /* non-printable */
205 {
206 if (*buf_p == 0x1b) /* escape */
207 {
208 t_con->state = TELNET_STATE_ESCAPE;
209 t_con->last_escape = '\x00';
210 }
211 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
212 {
213 int retval;
214
215 /* skip over combinations with CR/LF + NUL */
216 if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
217 {
218 buf_p++;
219 bytes_read--;
220 }
221 if ((*(buf_p + 1) == 0) && (bytes_read > 1))
222 {
223 buf_p++;
224 bytes_read--;
225 }
226 t_con->line[t_con->line_size] = 0;
227
228 write_socket(connection->fd, "\r\n\x00", 3);
229
230 if (strcmp(t_con->line, "history") == 0)
231 {
232 int i;
233 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
234 {
235 if (t_con->history[i])
236 {
237 write_socket(connection->fd, t_con->history[i], strlen(t_con->history[i]));
238 write_socket(connection->fd, "\r\n\x00", 3);
239 }
240 }
241 telnet_prompt(connection);
242 t_con->line_size = 0;
243 t_con->line_cursor = 0;
244 continue;
245 }
246
247 if ((retval = command_run_line(command_context, t_con->line)) != ERROR_OK)
248 {
249 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
250 {
251 return ERROR_SERVER_REMOTE_CLOSED;
252 }
253 }
254
255 /* Save only non-blank lines in the history */
256 if (t_con->line_size > 0)
257 {
258 /* if the history slot is already taken, free it */
259 if (t_con->history[t_con->next_history])
260 {
261 free(t_con->history[t_con->next_history]);
262 }
263
264 /* add line to history */
265 t_con->history[t_con->next_history] = strdup(t_con->line);
266
267 /* wrap history at TELNET_LINE_HISTORY_SIZE */
268 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
269
270 /* current history line starts at the new entry */
271 t_con->current_history = t_con->next_history;
272
273 if (t_con->history[t_con->current_history])
274 {
275 free(t_con->history[t_con->current_history]);
276 }
277 t_con->history[t_con->current_history] = strdup("");
278 }
279
280 /* output prompt after command */
281 telnet_prompt(connection);
282
283 t_con->line_size = 0;
284 t_con->line_cursor = 0;
285 }
286 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
287 {
288 if (t_con->line_cursor > 0)
289 {
290 if (t_con->line_cursor != t_con->line_size)
291 {
292 int i;
293 write_socket(connection->fd, "\b", 1);
294 t_con->line_cursor--;
295 t_con->line_size--;
296 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
297
298 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
299 write_socket(connection->fd, " \b", 2);
300 for (i = t_con->line_cursor; i < t_con->line_size; i++)
301 {
302 write_socket(connection->fd, "\b", 1);
303 }
304 }
305 else
306 {
307 t_con->line_size--;
308 t_con->line_cursor--;
309 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
310 write_socket(connection->fd, "\b \b", 3);
311 }
312 }
313 }
314 else if (*buf_p == 0x15) /* clear line */
315 {
316 telnet_clear_line(connection, t_con);
317 }
318 else if (*buf_p == CTRL('B')) /* cursor left */
319 {
320 if (t_con->line_cursor > 0)
321 {
322 write_socket(connection->fd, "\b", 1);
323 t_con->line_cursor--;
324 }
325 t_con->state = TELNET_STATE_DATA;
326 }
327 else if (*buf_p == CTRL('F')) /* cursor right */
328 {
329 if (t_con->line_cursor < t_con->line_size)
330 {
331 write_socket(connection->fd, t_con->line + t_con->line_cursor++, 1);
332 }
333 t_con->state = TELNET_STATE_DATA;
334 }
335 else
336 {
337 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
338 }
339 }
340 }
341 break;
342 case TELNET_STATE_IAC:
343 switch (*buf_p)
344 {
345 case '\xfe':
346 t_con->state = TELNET_STATE_DONT;
347 break;
348 case '\xfd':
349 t_con->state = TELNET_STATE_DO;
350 break;
351 case '\xfc':
352 t_con->state = TELNET_STATE_WONT;
353 break;
354 case '\xfb':
355 t_con->state = TELNET_STATE_WILL;
356 break;
357 }
358 break;
359 case TELNET_STATE_SB:
360 break;
361 case TELNET_STATE_SE:
362 break;
363 case TELNET_STATE_WILL:
364 case TELNET_STATE_WONT:
365 case TELNET_STATE_DO:
366 case TELNET_STATE_DONT:
367 t_con->state = TELNET_STATE_DATA;
368 break;
369 case TELNET_STATE_ESCAPE:
370 if (t_con->last_escape == '[')
371 {
372 if (*buf_p == 'D') /* cursor left */
373 {
374 if (t_con->line_cursor > 0)
375 {
376 write_socket(connection->fd, "\b", 1);
377 t_con->line_cursor--;
378 }
379 t_con->state = TELNET_STATE_DATA;
380 }
381 else if (*buf_p == 'C') /* cursor right */
382 {
383 if (t_con->line_cursor < t_con->line_size)
384 {
385 write_socket(connection->fd, t_con->line + t_con->line_cursor++, 1);
386 }
387 t_con->state = TELNET_STATE_DATA;
388 }
389 else if (*buf_p == 'A') /* cursor up */
390 {
391 int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
392 if (t_con->history[last_history])
393 {
394 telnet_clear_line(connection, t_con);
395 t_con->line_size = strlen(t_con->history[last_history]);
396 t_con->line_cursor = t_con->line_size;
397 memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
398 write_socket(connection->fd, t_con->line, t_con->line_size);
399 t_con->current_history = last_history;
400 }
401 t_con->state = TELNET_STATE_DATA;
402 }
403 else if (*buf_p == 'B') /* cursor down */
404 {
405 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
406 if (t_con->history[next_history])
407 {
408 telnet_clear_line(connection, t_con);
409 t_con->line_size = strlen(t_con->history[next_history]);
410 t_con->line_cursor = t_con->line_size;
411 memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
412 write_socket(connection->fd, t_con->line, t_con->line_size);
413 t_con->current_history = next_history;
414 }
415 t_con->state = TELNET_STATE_DATA;
416 }
417 else if (*buf_p == '3')
418 {
419 t_con->last_escape = *buf_p;
420 }
421 else
422 {
423 t_con->state = TELNET_STATE_DATA;
424 }
425 }
426 else if (t_con->last_escape == '3')
427 {
428 /* Remove character */
429 if (*buf_p == '~')
430 {
431 if (t_con->line_cursor < t_con->line_size)
432 {
433 int i;
434 t_con->line_size--;
435 /* remove char from line buffer */
436 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
437
438 /* print remainder of buffer */
439 write_socket(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
440 /* overwrite last char with whitespace */
441 write_socket(connection->fd, " \b", 2);
442
443 /* move back to cursor position*/
444 for (i = t_con->line_cursor; i < t_con->line_size; i++)
445 {
446 write_socket(connection->fd, "\b", 1);
447 }
448 }
449
450 t_con->state = TELNET_STATE_DATA;
451 }
452 else
453 {
454 t_con->state = TELNET_STATE_DATA;
455 }
456 }
457 else if (t_con->last_escape == '\x00')
458 {
459 if (*buf_p == '[')
460 {
461 t_con->last_escape = *buf_p;
462 }
463 else
464 {
465 t_con->state = TELNET_STATE_DATA;
466 }
467 }
468 else
469 {
470 ERROR("BUG: unexpected value in t_con->last_escape");
471 t_con->state = TELNET_STATE_DATA;
472 }
473
474 break;
475 default:
476 ERROR("unknown telnet state");
477 exit(-1);
478 }
479
480 bytes_read--;
481 buf_p++;
482 }
483
484 return ERROR_OK;
485 }
486
487 int telnet_connection_closed(connection_t *connection)
488 {
489 telnet_connection_t *t_con = connection->priv;
490 int i;
491
492 if (t_con->prompt)
493 {
494 free(t_con->prompt);
495 t_con->prompt = NULL;
496 }
497
498 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
499 {
500 if (t_con->history[i])
501 {
502 free(t_con->history[i]);
503 t_con->history[i] = NULL;
504 }
505 }
506
507 /* if this connection registered a debug-message receiver delete it */
508 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
509
510 if (connection->priv)
511 {
512 free(connection->priv);
513 connection->priv = NULL;
514 }
515 else
516 {
517 ERROR("BUG: connection->priv == NULL");
518 }
519
520 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
521
522 return ERROR_OK;
523 }
524
525 int telnet_set_prompt(connection_t *connection, char *prompt)
526 {
527 telnet_connection_t *t_con = connection->priv;
528
529 t_con->prompt = strdup(prompt);
530
531 return ERROR_OK;
532 }
533
534 int telnet_init(char *banner)
535 {
536 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
537
538 if (telnet_port == 0)
539 {
540 WARNING("no telnet port specified, using default port 4444");
541 telnet_port = 4444;
542 }
543
544 telnet_service->banner = banner;
545
546 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
547
548 return ERROR_OK;
549 }
550
551 int telnet_register_commands(command_context_t *command_context)
552 {
553 register_command(command_context, NULL, "exit", handle_exit_command,
554 COMMAND_EXEC, "exit telnet session");
555
556 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
557 COMMAND_CONFIG, "");
558
559 return ERROR_OK;
560 }
561
562 /* daemon configuration command telnet_port */
563 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
564 {
565 if (argc == 0)
566 return ERROR_OK;
567
568 /* only if the port wasn't overwritten by cmdline */
569 if (telnet_port == 0)
570 telnet_port = strtoul(args[0], NULL, 0);
571
572 return ERROR_OK;
573 }
574
575 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
576 {
577 return ERROR_COMMAND_CLOSE_CONNECTION;
578 }

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)