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

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)