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

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)