b40daca8a1b266ab17f4bc094ecddf9d81e158e7
[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, 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 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
76 }
77
78 int telnet_outputline(connection_t *connection, char* line)
79 {
80 telnet_write(connection, line, strlen(line));
81 return telnet_write(connection, "\r\n\0", 3);
82 }
83
84 int telnet_output(struct command_context_s *cmd_ctx, char* line)
85 {
86 connection_t *connection = cmd_ctx->output_handler_priv;
87
88 return telnet_outputline(connection, line);
89 }
90
91 void telnet_log_callback(void *priv, const char *file, int line,
92 const char *function, const char *format, va_list args)
93 {
94 connection_t *connection = priv;
95 char *t = allocPrintf(format, args);
96 char *t2;
97 if (t == NULL)
98 return;
99 t2=t;
100 char *endline;
101 do
102 {
103 if ((endline=strchr(t2, '\n'))!=NULL)
104 {
105 *endline=0;
106 }
107 telnet_outputline(connection, t2);
108 t2=endline+1;
109 } while (endline);
110
111
112 free(t);
113 }
114
115 int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
116 {
117 struct command_context_s *cmd_ctx = priv;
118 connection_t *connection = cmd_ctx->output_handler_priv;
119 telnet_connection_t *t_con = connection->priv;
120
121 switch (event)
122 {
123 case TARGET_EVENT_HALTED:
124 command_print(cmd_ctx, "Target %i halted", get_num_by_target(target));
125 target_arch_state(target);
126 if (!t_con->suppress_prompt)
127 telnet_prompt(connection);
128 break;
129 case TARGET_EVENT_RESUMED:
130 command_print(cmd_ctx, "Target %i resumed", get_num_by_target(target));
131 if (!t_con->suppress_prompt)
132 telnet_prompt(connection);
133 break;
134 default:
135 break;
136 }
137
138 return ERROR_OK;
139 }
140
141 int telnet_new_connection(connection_t *connection)
142 {
143 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
144 telnet_service_t *telnet_service = connection->service->priv;
145 int i;
146
147 connection->priv = telnet_connection;
148
149 /* initialize telnet connection information */
150 telnet_connection->closed = 0;
151 telnet_connection->line_size = 0;
152 telnet_connection->line_cursor = 0;
153 telnet_connection->option_size = 0;
154 telnet_connection->prompt = strdup("> ");
155 telnet_connection->suppress_prompt = 0;
156 telnet_connection->state = TELNET_STATE_DATA;
157
158 /* output goes through telnet connection */
159 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
160
161 /* negotiate telnet options */
162 telnet_write(connection, negotiate, strlen(negotiate));
163
164 /* print connection banner */
165 if (telnet_service->banner)
166 {
167 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
168 telnet_write(connection, "\r\n\0", 3);
169 }
170
171 telnet_prompt(connection);
172
173 /* initialize history */
174 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
175 {
176 telnet_connection->history[i] = NULL;
177 }
178 telnet_connection->next_history = 0;
179 telnet_connection->current_history = 0;
180
181 target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
182
183 return ERROR_OK;
184 }
185
186 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
187 {
188 /* move to end of line */
189 if (t_con->line_cursor < t_con->line_size)
190 {
191 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
192 }
193
194 /* backspace, overwrite with space, backspace */
195 while (t_con->line_size > 0)
196 {
197 telnet_write(connection, "\b \b", 3);
198 t_con->line_size--;
199 }
200 t_con->line_cursor = 0;
201 }
202
203 int telnet_input(connection_t *connection)
204 {
205 int bytes_read;
206 char buffer[TELNET_BUFFER_SIZE];
207 char *buf_p;
208 telnet_connection_t *t_con = connection->priv;
209 command_context_t *command_context = connection->cmd_ctx;
210
211 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
212
213 if (bytes_read == 0)
214 return ERROR_SERVER_REMOTE_CLOSED;
215 else if (bytes_read == -1)
216 {
217 ERROR("error during read: %s", strerror(errno));
218 return ERROR_SERVER_REMOTE_CLOSED;
219 }
220
221 buf_p = buffer;
222 while (bytes_read)
223 {
224 switch (t_con->state)
225 {
226 case TELNET_STATE_DATA:
227 if (*buf_p == '\xff')
228 {
229 t_con->state = TELNET_STATE_IAC;
230 }
231 else
232 {
233 if (isprint(*buf_p)) /* printable character */
234 {
235 telnet_write(connection, buf_p, 1);
236 if (t_con->line_cursor == t_con->line_size)
237 {
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 for (i = t_con->line_cursor; i < t_con->line_size; i++)
249 {
250 telnet_write(connection, "\b", 1);
251 }
252 }
253 }
254 else /* non-printable */
255 {
256 if (*buf_p == 0x1b) /* escape */
257 {
258 t_con->state = TELNET_STATE_ESCAPE;
259 t_con->last_escape = '\x00';
260 }
261 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
262 {
263 int retval;
264
265 /* skip over combinations with CR/LF + NUL */
266 if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
267 {
268 buf_p++;
269 bytes_read--;
270 }
271 if ((*(buf_p + 1) == 0) && (bytes_read > 1))
272 {
273 buf_p++;
274 bytes_read--;
275 }
276 t_con->line[t_con->line_size] = 0;
277
278 telnet_write(connection, "\r\n\x00", 3);
279
280 if (strcmp(t_con->line, "history") == 0)
281 {
282 int i;
283 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
284 {
285 if (t_con->history[i])
286 {
287 telnet_write(connection, t_con->history[i], strlen(t_con->history[i]));
288 telnet_write(connection, "\r\n\x00", 3);
289 }
290 }
291 telnet_prompt(connection);
292 t_con->line_size = 0;
293 t_con->line_cursor = 0;
294 continue;
295 }
296
297 log_setCallback(telnet_log_callback, connection);
298 t_con->suppress_prompt = 1;
299
300 if ((retval = command_run_line(command_context, t_con->line)) != ERROR_OK)
301 {
302 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
303 {
304 return ERROR_SERVER_REMOTE_CLOSED;
305 }
306 }
307
308 t_con->suppress_prompt = 0;
309
310 /* Save only non-blank lines in the history */
311 if (t_con->line_size > 0)
312 {
313 /* if the history slot is already taken, free it */
314 if (t_con->history[t_con->next_history])
315 {
316 free(t_con->history[t_con->next_history]);
317 }
318
319 /* add line to history */
320 t_con->history[t_con->next_history] = strdup(t_con->line);
321
322 /* wrap history at TELNET_LINE_HISTORY_SIZE */
323 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
324
325 /* current history line starts at the new entry */
326 t_con->current_history = t_con->next_history;
327
328 if (t_con->history[t_con->current_history])
329 {
330 free(t_con->history[t_con->current_history]);
331 }
332 t_con->history[t_con->current_history] = strdup("");
333 }
334
335 int t = telnet_prompt(connection);
336 if (t == ERROR_SERVER_REMOTE_CLOSED)
337 return t;
338
339 t_con->line_size = 0;
340 t_con->line_cursor = 0;
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 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
394 }
395 }
396 }
397 break;
398 case TELNET_STATE_IAC:
399 switch (*buf_p)
400 {
401 case '\xfe':
402 t_con->state = TELNET_STATE_DONT;
403 break;
404 case '\xfd':
405 t_con->state = TELNET_STATE_DO;
406 break;
407 case '\xfc':
408 t_con->state = TELNET_STATE_WONT;
409 break;
410 case '\xfb':
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 + 1);
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 + 1);
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 ERROR("BUG: unexpected value in t_con->last_escape");
527 t_con->state = TELNET_STATE_DATA;
528 }
529
530 break;
531 default:
532 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(connection_t *connection)
544 {
545 telnet_connection_t *t_con = connection->priv;
546 int i;
547
548 if (t_con->prompt)
549 {
550 free(t_con->prompt);
551 t_con->prompt = NULL;
552 }
553
554 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
555 {
556 if (t_con->history[i])
557 {
558 free(t_con->history[i]);
559 t_con->history[i] = NULL;
560 }
561 }
562
563 /* if this connection registered a debug-message receiver delete it */
564 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
565
566 if (connection->priv)
567 {
568 free(connection->priv);
569 connection->priv = NULL;
570 }
571 else
572 {
573 ERROR("BUG: connection->priv == NULL");
574 }
575
576 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
577
578 return ERROR_OK;
579 }
580
581 int telnet_set_prompt(connection_t *connection, char *prompt)
582 {
583 telnet_connection_t *t_con = connection->priv;
584
585 t_con->prompt = strdup(prompt);
586
587 return ERROR_OK;
588 }
589
590 int telnet_init(char *banner)
591 {
592 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
593
594 if (telnet_port == 0)
595 {
596 WARNING("no telnet port specified, using default port 4444");
597 telnet_port = 4444;
598 }
599
600 telnet_service->banner = banner;
601
602 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
603
604 return ERROR_OK;
605 }
606
607 int telnet_register_commands(command_context_t *command_context)
608 {
609 register_command(command_context, NULL, "exit", handle_exit_command,
610 COMMAND_EXEC, "exit telnet session");
611
612 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
613 COMMAND_CONFIG, "");
614
615 return ERROR_OK;
616 }
617
618 /* daemon configuration command telnet_port */
619 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
620 {
621 if (argc == 0)
622 return ERROR_OK;
623
624 /* only if the port wasn't overwritten by cmdline */
625 if (telnet_port == 0)
626 telnet_port = strtoul(args[0], NULL, 0);
627
628 return ERROR_OK;
629 }
630
631 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
632 {
633 return ERROR_COMMAND_CLOSE_CONNECTION;
634 }

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)