94c89431b977faf5e466e82955688d61b8f554cd
[openocd.git] / src / server / telnet_server.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2007,2008 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
7 * *
8 * Copyright (C) 2008 by Spencer Oliver *
9 * spen@spen-soft.co.uk *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "telnet_server.h"
31 #include <target/target_request.h>
32
33 static unsigned short telnet_port = 4444;
34
35 static char *negotiate =
36 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
37 "\xFF\xFB\x01" /* IAC WILL Echo */
38 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
39 "\xFF\xFE\x01"; /* IAC DON'T Echo */
40
41 #define CTRL(c) (c - '@')
42
43 /* The only way we can detect that the socket is closed is the first time
44 * we write to it, we will fail. Subsequent write operations will
45 * succeed. Shudder!
46 */
47 int telnet_write(struct connection *connection, const void *data, int len)
48 {
49 struct telnet_connection *t_con = connection->priv;
50 if (t_con->closed)
51 return ERROR_SERVER_REMOTE_CLOSED;
52
53 if (write_socket(connection->fd, data, len) == len)
54 {
55 return ERROR_OK;
56 }
57 t_con->closed = 1;
58 return ERROR_SERVER_REMOTE_CLOSED;
59 }
60
61 int telnet_prompt(struct connection *connection)
62 {
63 struct telnet_connection *t_con = connection->priv;
64
65 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
66 }
67
68 int telnet_outputline(struct connection *connection, const char *line)
69 {
70 int len;
71
72 /* process lines in buffer */
73 while (*line) {
74 char *line_end = strchr(line, '\n');
75
76 if (line_end)
77 len = line_end-line;
78 else
79 len = strlen(line);
80
81 telnet_write(connection, line, len);
82 if (line_end)
83 {
84 telnet_write(connection, "\r\n", 2);
85 line += len + 1;
86 }
87 else
88 {
89 line += len;
90 }
91 }
92
93 return ERROR_OK;
94 }
95
96 int telnet_output(struct command_context *cmd_ctx, const char* line)
97 {
98 struct connection *connection = cmd_ctx->output_handler_priv;
99
100 return telnet_outputline(connection, line);
101 }
102
103 void telnet_log_callback(void *priv, const char *file, unsigned line,
104 const char *function, const char *string)
105 {
106 struct connection *connection = priv;
107 struct telnet_connection *t_con = connection->priv;
108 int i;
109
110 /* if there is no prompt, simply output the message */
111 if (t_con->line_cursor < 0)
112 {
113 telnet_outputline(connection, string);
114 return;
115 }
116
117 /* clear the command line */
118 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
119 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
120 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
121 telnet_write(connection, " ", i > 16 ? 16 : i);
122 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
123 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
124
125 /* output the message */
126 telnet_outputline(connection, string);
127
128 /* put the command line to its previous state */
129 telnet_prompt(connection);
130 telnet_write(connection, t_con->line, t_con->line_size);
131 for (i = t_con->line_size; i > t_con->line_cursor; i--)
132 telnet_write(connection, "\b", 1);
133 }
134
135 int telnet_new_connection(struct connection *connection)
136 {
137 struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection));
138 struct telnet_service *telnet_service = connection->service->priv;
139 int i;
140
141 connection->priv = telnet_connection;
142
143 /* initialize telnet connection information */
144 telnet_connection->closed = 0;
145 telnet_connection->line_size = 0;
146 telnet_connection->line_cursor = 0;
147 telnet_connection->option_size = 0;
148 telnet_connection->prompt = strdup("> ");
149 telnet_connection->state = TELNET_STATE_DATA;
150
151 /* output goes through telnet connection */
152 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
153
154 /* negotiate telnet options */
155 telnet_write(connection, negotiate, strlen(negotiate));
156
157 /* print connection banner */
158 if (telnet_service->banner)
159 {
160 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
161 telnet_write(connection, "\r\n", 2);
162 }
163
164 telnet_write(connection, "\r", 1); /* the prompt is always placed at the line beginning */
165 telnet_prompt(connection);
166
167 /* initialize history */
168 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
169 {
170 telnet_connection->history[i] = NULL;
171 }
172 telnet_connection->next_history = 0;
173 telnet_connection->current_history = 0;
174
175 log_add_callback(telnet_log_callback, connection);
176
177 return ERROR_OK;
178 }
179
180 void telnet_clear_line(struct connection *connection, struct telnet_connection *t_con)
181 {
182 /* move to end of line */
183 if (t_con->line_cursor < t_con->line_size)
184 {
185 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
186 }
187
188 /* backspace, overwrite with space, backspace */
189 while (t_con->line_size > 0)
190 {
191 telnet_write(connection, "\b \b", 3);
192 t_con->line_size--;
193 }
194 t_con->line_cursor = 0;
195 }
196
197 int telnet_input(struct connection *connection)
198 {
199 int bytes_read;
200 unsigned char buffer[TELNET_BUFFER_SIZE];
201 unsigned char *buf_p;
202 struct telnet_connection *t_con = connection->priv;
203 struct command_context *command_context = connection->cmd_ctx;
204
205 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
206
207 if (bytes_read == 0)
208 return ERROR_SERVER_REMOTE_CLOSED;
209 else if (bytes_read == -1)
210 {
211 LOG_ERROR("error during read: %s", strerror(errno));
212 return ERROR_SERVER_REMOTE_CLOSED;
213 }
214
215 buf_p = buffer;
216 while (bytes_read)
217 {
218 switch (t_con->state)
219 {
220 case TELNET_STATE_DATA:
221 if (*buf_p == 0xff)
222 {
223 t_con->state = TELNET_STATE_IAC;
224 }
225 else
226 {
227 if (isprint(*buf_p)) /* printable character */
228 {
229 /* watch buffer size leaving one spare character for string null termination */
230 if (t_con->line_size == TELNET_LINE_MAX_SIZE-1)
231 {
232 /* output audible bell if buffer is full */
233 telnet_write(connection, "\x07", 1); /* "\a" does not work, at least on windows */
234 }
235 else if (t_con->line_cursor == t_con->line_size)
236 {
237 telnet_write(connection, buf_p, 1);
238 t_con->line[t_con->line_size++] = *buf_p;
239 t_con->line_cursor++;
240 }
241 else
242 {
243 int i;
244 memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
245 t_con->line[t_con->line_cursor] = *buf_p;
246 t_con->line_size++;
247 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
248 t_con->line_cursor++;
249 for (i = t_con->line_cursor; i < t_con->line_size; i++)
250 {
251 telnet_write(connection, "\b", 1);
252 }
253 }
254 }
255 else /* non-printable */
256 {
257 if (*buf_p == 0x1b) /* escape */
258 {
259 t_con->state = TELNET_STATE_ESCAPE;
260 t_con->last_escape = '\x00';
261 }
262 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
263 {
264 int retval;
265
266 /* skip over combinations with CR/LF and NUL characters */
267 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)))
268 {
269 buf_p++;
270 bytes_read--;
271 }
272 if ((bytes_read > 1) && (*(buf_p + 1) == 0))
273 {
274 buf_p++;
275 bytes_read--;
276 }
277 t_con->line[t_con->line_size] = 0;
278
279 telnet_write(connection, "\r\n\x00", 3);
280
281 if (strcmp(t_con->line, "history") == 0)
282 {
283 int i;
284 for (i = 1; i < TELNET_LINE_HISTORY_SIZE; i++)
285 {
286 /* the t_con->next_history line contains empty string (unless NULL), thus it is not printed */
287 char *history_line = t_con->history[(t_con->next_history + i) % TELNET_LINE_HISTORY_SIZE];
288 if (history_line)
289 {
290 telnet_write(connection, history_line, strlen(history_line));
291 telnet_write(connection, "\r\n\x00", 3);
292 }
293 }
294 t_con->line_size = 0;
295 t_con->line_cursor = 0;
296 continue;
297 }
298
299 /* save only non-blank not repeating lines in the history */
300 char *prev_line = t_con->history[(t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
301 if (*t_con->line && (prev_line == NULL || strcmp(t_con->line, prev_line)))
302 {
303 /* if the history slot is already taken, free it */
304 if (t_con->history[t_con->next_history])
305 {
306 free(t_con->history[t_con->next_history]);
307 }
308
309 /* add line to history */
310 t_con->history[t_con->next_history] = strdup(t_con->line);
311
312 /* wrap history at TELNET_LINE_HISTORY_SIZE */
313 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
314
315 /* current history line starts at the new entry */
316 t_con->current_history = t_con->next_history;
317
318 if (t_con->history[t_con->current_history])
319 {
320 free(t_con->history[t_con->current_history]);
321 }
322 t_con->history[t_con->current_history] = strdup("");
323 }
324
325 t_con->line_size = 0;
326
327 t_con->line_cursor = -1; /* to supress prompt in log callback during command execution */
328
329 retval = command_run_line(command_context, t_con->line);
330
331 t_con->line_cursor = 0;
332
333 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
334 return ERROR_SERVER_REMOTE_CLOSED;
335
336 telnet_write(connection, "\r", 1); /* the prompt is always placed at the line beginning */
337 retval = telnet_prompt(connection);
338 if (retval == ERROR_SERVER_REMOTE_CLOSED)
339 return ERROR_SERVER_REMOTE_CLOSED;
340
341 }
342 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
343 {
344 if (t_con->line_cursor > 0)
345 {
346 if (t_con->line_cursor != t_con->line_size)
347 {
348 int i;
349 telnet_write(connection, "\b", 1);
350 t_con->line_cursor--;
351 t_con->line_size--;
352 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
353
354 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
355 telnet_write(connection, " \b", 2);
356 for (i = t_con->line_cursor; i < t_con->line_size; i++)
357 {
358 telnet_write(connection, "\b", 1);
359 }
360 }
361 else
362 {
363 t_con->line_size--;
364 t_con->line_cursor--;
365 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
366 telnet_write(connection, "\b \b", 3);
367 }
368 }
369 }
370 else if (*buf_p == 0x15) /* clear line */
371 {
372 telnet_clear_line(connection, t_con);
373 }
374 else if (*buf_p == CTRL('B')) /* cursor left */
375 {
376 if (t_con->line_cursor > 0)
377 {
378 telnet_write(connection, "\b", 1);
379 t_con->line_cursor--;
380 }
381 t_con->state = TELNET_STATE_DATA;
382 }
383 else if (*buf_p == CTRL('F')) /* cursor right */
384 {
385 if (t_con->line_cursor < t_con->line_size)
386 {
387 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
388 }
389 t_con->state = TELNET_STATE_DATA;
390 }
391 else
392 {
393 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
394 }
395 }
396 }
397 break;
398 case TELNET_STATE_IAC:
399 switch (*buf_p)
400 {
401 case 0xfe:
402 t_con->state = TELNET_STATE_DONT;
403 break;
404 case 0xfd:
405 t_con->state = TELNET_STATE_DO;
406 break;
407 case 0xfc:
408 t_con->state = TELNET_STATE_WONT;
409 break;
410 case 0xfb:
411 t_con->state = TELNET_STATE_WILL;
412 break;
413 }
414 break;
415 case TELNET_STATE_SB:
416 break;
417 case TELNET_STATE_SE:
418 break;
419 case TELNET_STATE_WILL:
420 case TELNET_STATE_WONT:
421 case TELNET_STATE_DO:
422 case TELNET_STATE_DONT:
423 t_con->state = TELNET_STATE_DATA;
424 break;
425 case TELNET_STATE_ESCAPE:
426 if (t_con->last_escape == '[')
427 {
428 if (*buf_p == 'D') /* cursor left */
429 {
430 if (t_con->line_cursor > 0)
431 {
432 telnet_write(connection, "\b", 1);
433 t_con->line_cursor--;
434 }
435 t_con->state = TELNET_STATE_DATA;
436 }
437 else if (*buf_p == 'C') /* cursor right */
438 {
439 if (t_con->line_cursor < t_con->line_size)
440 {
441 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
442 }
443 t_con->state = TELNET_STATE_DATA;
444 }
445 else if (*buf_p == 'A') /* cursor up */
446 {
447 int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
448 if (t_con->history[last_history])
449 {
450 telnet_clear_line(connection, t_con);
451 t_con->line_size = strlen(t_con->history[last_history]);
452 t_con->line_cursor = t_con->line_size;
453 memcpy(t_con->line, t_con->history[last_history], t_con->line_size);
454 telnet_write(connection, t_con->line, t_con->line_size);
455 t_con->current_history = last_history;
456 }
457 t_con->state = TELNET_STATE_DATA;
458 }
459 else if (*buf_p == 'B') /* cursor down */
460 {
461 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
462 if (t_con->history[next_history])
463 {
464 telnet_clear_line(connection, t_con);
465 t_con->line_size = strlen(t_con->history[next_history]);
466 t_con->line_cursor = t_con->line_size;
467 memcpy(t_con->line, t_con->history[next_history], t_con->line_size);
468 telnet_write(connection, t_con->line, t_con->line_size);
469 t_con->current_history = next_history;
470 }
471 t_con->state = TELNET_STATE_DATA;
472 }
473 else if (*buf_p == '3')
474 {
475 t_con->last_escape = *buf_p;
476 }
477 else
478 {
479 t_con->state = TELNET_STATE_DATA;
480 }
481 }
482 else if (t_con->last_escape == '3')
483 {
484 /* Remove character */
485 if (*buf_p == '~')
486 {
487 if (t_con->line_cursor < t_con->line_size)
488 {
489 int i;
490 t_con->line_size--;
491 /* remove char from line buffer */
492 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
493
494 /* print remainder of buffer */
495 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
496 /* overwrite last char with whitespace */
497 telnet_write(connection, " \b", 2);
498
499 /* move back to cursor position*/
500 for (i = t_con->line_cursor; i < t_con->line_size; i++)
501 {
502 telnet_write(connection, "\b", 1);
503 }
504 }
505
506 t_con->state = TELNET_STATE_DATA;
507 }
508 else
509 {
510 t_con->state = TELNET_STATE_DATA;
511 }
512 }
513 else if (t_con->last_escape == '\x00')
514 {
515 if (*buf_p == '[')
516 {
517 t_con->last_escape = *buf_p;
518 }
519 else
520 {
521 t_con->state = TELNET_STATE_DATA;
522 }
523 }
524 else
525 {
526 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
527 t_con->state = TELNET_STATE_DATA;
528 }
529
530 break;
531 default:
532 LOG_ERROR("unknown telnet state");
533 exit(-1);
534 }
535
536 bytes_read--;
537 buf_p++;
538 }
539
540 return ERROR_OK;
541 }
542
543 int telnet_connection_closed(struct connection *connection)
544 {
545 struct telnet_connection *t_con = connection->priv;
546 int i;
547
548 log_remove_callback(telnet_log_callback, connection);
549
550 if (t_con->prompt)
551 {
552 free(t_con->prompt);
553 t_con->prompt = NULL;
554 }
555
556 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
557 {
558 if (t_con->history[i])
559 {
560 free(t_con->history[i]);
561 t_con->history[i] = NULL;
562 }
563 }
564
565 /* if this connection registered a debug-message receiver delete it */
566 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
567
568 if (connection->priv)
569 {
570 free(connection->priv);
571 connection->priv = NULL;
572 }
573 else
574 {
575 LOG_ERROR("BUG: connection->priv == NULL");
576 }
577
578 return ERROR_OK;
579 }
580
581 int telnet_set_prompt(struct connection *connection, char *prompt)
582 {
583 struct telnet_connection *t_con = connection->priv;
584
585 if (t_con->prompt != NULL)
586 free(t_con->prompt);
587
588 t_con->prompt = strdup(prompt);
589
590 return ERROR_OK;
591 }
592
593 int telnet_init(char *banner)
594 {
595 struct telnet_service *telnet_service = malloc(sizeof(struct telnet_service));
596
597 if (telnet_port == 0)
598 {
599 LOG_INFO("telnet port disabled");
600 free(telnet_service);
601 return ERROR_OK;
602 }
603
604 telnet_service->banner = banner;
605
606 add_service("telnet", CONNECTION_TCP, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
607
608 return ERROR_OK;
609 }
610
611 /* daemon configuration command telnet_port */
612 COMMAND_HANDLER(handle_telnet_port_command)
613 {
614 return CALL_COMMAND_HANDLER(server_port_command, &telnet_port);
615 }
616
617 COMMAND_HANDLER(handle_exit_command)
618 {
619 return ERROR_COMMAND_CLOSE_CONNECTION;
620 }
621
622 static const struct command_registration telnet_command_handlers[] = {
623 {
624 .name = "exit",
625 .handler = handle_exit_command,
626 .mode = COMMAND_EXEC,
627 .help = "exit telnet session",
628 },
629 {
630 .name = "telnet_port",
631 .handler = handle_telnet_port_command,
632 .mode = COMMAND_ANY,
633 .help = "Specify port on which to listen "
634 "for incoming telnet connections. "
635 "No arguments reports telnet port; zero disables.",
636 .usage = "[port_num]",
637 },
638 COMMAND_REGISTRATION_DONE
639 };
640
641 int telnet_register_commands(struct command_context *cmd_ctx)
642 {
643 return register_commands(cmd_ctx, NULL, telnet_command_handlers);
644 }

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)