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

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)