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

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)