- Fixes '=' whitespace
[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 * Copyright (C) 2008 by Spencer Oliver *
9 * spen@spen-soft.co.uk *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "telnet_server.h"
31 #include "target_request.h"
32
33 static unsigned short telnet_port = 4444;
34
35 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
36 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
37
38 static char *negotiate =
39 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
40 "\xFF\xFB\x01" /* IAC WILL Echo */
41 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
42 "\xFF\xFE\x01"; /* IAC DON'T Echo */
43
44 #define CTRL(c) (c - '@')
45
46 /* The only way we can detect that the socket is closed is the first time
47 * we write to it, we will fail. Subsequent write operations will
48 * succeed. Shudder!
49 */
50 int telnet_write(connection_t *connection, const void *data, int len)
51 {
52 telnet_connection_t *t_con = connection->priv;
53 if (t_con->closed)
54 return ERROR_SERVER_REMOTE_CLOSED;
55
56 if (write_socket(connection->fd, data, len) == len)
57 {
58 return ERROR_OK;
59 }
60 t_con->closed = 1;
61 return ERROR_SERVER_REMOTE_CLOSED;
62 }
63
64 int telnet_prompt(connection_t *connection)
65 {
66 telnet_connection_t *t_con = connection->priv;
67
68 telnet_write(connection, "\r", 1); /* the prompt is always placed at the line beginning */
69 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
70 }
71
72 int telnet_outputline(connection_t *connection, const char *line)
73 {
74 int len;
75
76 /* process lines in buffer */
77 while (*line) {
78 char *line_end = strchr(line, '\n');
79
80 if (line_end)
81 len = line_end-line;
82 else
83 len = strlen(line);
84
85 telnet_write(connection, line, len);
86 if (line_end)
87 {
88 telnet_write(connection, "\r\n", 2);
89 line += len+1;
90 }
91 else
92 {
93 line += len;
94 }
95 }
96
97 return ERROR_OK;
98 }
99
100 int telnet_output(struct command_context_s *cmd_ctx, const char* line)
101 {
102 connection_t *connection = cmd_ctx->output_handler_priv;
103
104 return telnet_outputline(connection, line);
105 }
106
107 void telnet_log_callback(void *priv, const char *file, int line,
108 const char *function, const char *string)
109 {
110 connection_t *connection = priv;
111 telnet_connection_t *t_con = connection->priv;
112 int i;
113
114 /* if there is no prompt, simply output the message */
115 if (t_con->line_cursor < 0)
116 {
117 telnet_outputline(connection, string);
118 return;
119 }
120
121 /* clear the command line */
122 telnet_write(connection, "\r", 1);
123 for (i = strlen(t_con->prompt) + t_con->line_size; i>0; i -= 16)
124 telnet_write(connection, " ", i>16 ? 16 : i);
125 telnet_write(connection, "\r", 1);
126
127 /* output the message */
128 telnet_outputline(connection, string);
129
130 /* put the command line to its previous state */
131 telnet_prompt(connection);
132 telnet_write(connection, t_con->line, t_con->line_size);
133 for (i = t_con->line_size; i>t_con->line_cursor; i--)
134 telnet_write(connection, "\b", 1);
135 }
136
137 int telnet_new_connection(connection_t *connection)
138 {
139 telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
140 telnet_service_t *telnet_service = connection->service->priv;
141 int i;
142
143 connection->priv = telnet_connection;
144
145 /* initialize telnet connection information */
146 telnet_connection->closed = 0;
147 telnet_connection->line_size = 0;
148 telnet_connection->line_cursor = 0;
149 telnet_connection->option_size = 0;
150 telnet_connection->prompt = strdup("> ");
151 telnet_connection->state = TELNET_STATE_DATA;
152
153 /* output goes through telnet connection */
154 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
155
156 /* negotiate telnet options */
157 telnet_write(connection, negotiate, strlen(negotiate));
158
159 /* print connection banner */
160 if (telnet_service->banner)
161 {
162 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
163 telnet_write(connection, "\r\n", 2);
164 }
165
166 telnet_prompt(connection);
167
168 /* initialize history */
169 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
170 {
171 telnet_connection->history[i] = NULL;
172 }
173 telnet_connection->next_history = 0;
174 telnet_connection->current_history = 0;
175
176 log_add_callback(telnet_log_callback, connection);
177
178 return ERROR_OK;
179 }
180
181 void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
182 {
183 /* move to end of line */
184 if (t_con->line_cursor < t_con->line_size)
185 {
186 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
187 }
188
189 /* backspace, overwrite with space, backspace */
190 while (t_con->line_size > 0)
191 {
192 telnet_write(connection, "\b \b", 3);
193 t_con->line_size--;
194 }
195 t_con->line_cursor = 0;
196 }
197
198 int telnet_input(connection_t *connection)
199 {
200 int bytes_read;
201 char buffer[TELNET_BUFFER_SIZE];
202 char *buf_p;
203 telnet_connection_t *t_con = connection->priv;
204 command_context_t *command_context = connection->cmd_ctx;
205
206 bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
207
208 if (bytes_read == 0)
209 return ERROR_SERVER_REMOTE_CLOSED;
210 else if (bytes_read == -1)
211 {
212 LOG_ERROR("error during read: %s", strerror(errno));
213 return ERROR_SERVER_REMOTE_CLOSED;
214 }
215
216 buf_p = buffer;
217 while (bytes_read)
218 {
219 switch (t_con->state)
220 {
221 case TELNET_STATE_DATA:
222 if (*buf_p == '\xff')
223 {
224 t_con->state = TELNET_STATE_IAC;
225 }
226 else
227 {
228 if (isprint(*buf_p)) /* printable character */
229 {
230 /* watch buffer size leaving one spare character for string null termination */
231 if (t_con->line_size == TELNET_LINE_MAX_SIZE-1)
232 {
233 /* output audible bell if buffer is full */
234 telnet_write(connection, "\x07", 1); /* "\a" does not work, at least on windows */
235 }
236 else if (t_con->line_cursor == t_con->line_size)
237 {
238 telnet_write(connection, buf_p, 1);
239 t_con->line[t_con->line_size++] = *buf_p;
240 t_con->line_cursor++;
241 }
242 else
243 {
244 int i;
245 memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
246 t_con->line[t_con->line_cursor] = *buf_p;
247 t_con->line_size++;
248 telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
249 t_con->line_cursor++;
250 for (i = t_con->line_cursor; i < t_con->line_size; i++)
251 {
252 telnet_write(connection, "\b", 1);
253 }
254 }
255 }
256 else /* non-printable */
257 {
258 if (*buf_p == 0x1b) /* escape */
259 {
260 t_con->state = TELNET_STATE_ESCAPE;
261 t_con->last_escape = '\x00';
262 }
263 else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
264 {
265 int retval;
266
267 /* skip over combinations with CR/LF and NUL characters */
268 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)))
269 {
270 buf_p++;
271 bytes_read--;
272 }
273 if ((bytes_read > 1) && (*(buf_p + 1) == 0))
274 {
275 buf_p++;
276 bytes_read--;
277 }
278 t_con->line[t_con->line_size] = 0;
279
280 telnet_write(connection, "\r\n\x00", 3);
281
282 if (strcmp(t_con->line, "history") == 0)
283 {
284 int i;
285 for (i = 1; i < TELNET_LINE_HISTORY_SIZE; i++)
286 {
287 /* the t_con->next_history line contains empty string (unless NULL), thus it is not printed */
288 char *history_line = t_con->history[(t_con->next_history + i) % TELNET_LINE_HISTORY_SIZE];
289 if (history_line)
290 {
291 telnet_write(connection, history_line, strlen(history_line));
292 telnet_write(connection, "\r\n\x00", 3);
293 }
294 }
295 t_con->line_size = 0;
296 t_con->line_cursor = 0;
297 continue;
298 }
299
300 /* save only non-blank not repeating lines in the history */
301 char *prev_line = t_con->history[(t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
302 if (*t_con->line && (prev_line == NULL || strcmp(t_con->line, prev_line)))
303 {
304 /* if the history slot is already taken, free it */
305 if (t_con->history[t_con->next_history])
306 {
307 free(t_con->history[t_con->next_history]);
308 }
309
310 /* add line to history */
311 t_con->history[t_con->next_history] = strdup(t_con->line);
312
313 /* wrap history at TELNET_LINE_HISTORY_SIZE */
314 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
315
316 /* current history line starts at the new entry */
317 t_con->current_history = t_con->next_history;
318
319 if (t_con->history[t_con->current_history])
320 {
321 free(t_con->history[t_con->current_history]);
322 }
323 t_con->history[t_con->current_history] = strdup("");
324 }
325
326 t_con->line_size = 0;
327
328 t_con->line_cursor = -1; /* to supress prompt in log callback during command execution */
329
330 retval = command_run_line(command_context, t_con->line);
331
332 t_con->line_cursor = 0;
333
334 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
335 return ERROR_SERVER_REMOTE_CLOSED;
336
337 retval = telnet_prompt(connection);
338 if (retval == ERROR_SERVER_REMOTE_CLOSED)
339 return ERROR_SERVER_REMOTE_CLOSED;
340
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 LOG_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);
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);
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 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
527 t_con->state = TELNET_STATE_DATA;
528 }
529
530 break;
531 default:
532 LOG_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 log_remove_callback(telnet_log_callback, connection);
549
550 if (t_con->prompt)
551 {
552 free(t_con->prompt);
553 t_con->prompt = NULL;
554 }
555
556 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
557 {
558 if (t_con->history[i])
559 {
560 free(t_con->history[i]);
561 t_con->history[i] = NULL;
562 }
563 }
564
565 /* if this connection registered a debug-message receiver delete it */
566 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
567
568 if (connection->priv)
569 {
570 free(connection->priv);
571 connection->priv = NULL;
572 }
573 else
574 {
575 LOG_ERROR("BUG: connection->priv == NULL");
576 }
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 if (t_con->prompt != NULL)
586 free(t_con->prompt);
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 LOG_INFO("telnet port disabled");
600 return ERROR_OK;
601 }
602
603 telnet_service->banner = banner;
604
605 add_service("telnet", CONNECTION_TCP, 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_ANY, "port on which to listen for incoming telnet connections");
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 {
626 command_print(cmd_ctx, "%d", telnet_port);
627 return ERROR_OK;
628 }
629
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)