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

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)