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

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)