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

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)