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

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)