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

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)