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

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)