- prepare OpenOCD for branching, created ./trunk/
[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->surpress_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->surpress_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->surpress_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->surpress_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 /* if the history slot is already taken, free it */
259 if (t_con->history[t_con->next_history])
260 {
261 free(t_con->history[t_con->next_history]);
262 }
263
264 /* add line to history */
265 t_con->history[t_con->next_history++] = strdup(t_con->line);
266
267 /* current history line starts at the new entry */
268 t_con->current_history = t_con->next_history;
269
270 if (t_con->history[t_con->current_history])
271 {
272 free(t_con->history[t_con->current_history]);
273 }
274 t_con->history[t_con->current_history] = strdup("");
275
276 /* wrap history at TELNET_LINE_HISTORY_SIZE */
277 if (t_con->next_history > TELNET_LINE_HISTORY_SIZE - 1)
278 t_con->next_history = 0;
279
280 if (!t_con->surpress_prompt)
281 {
282 telnet_prompt(connection);
283 }
284 else
285 {
286 t_con->surpress_prompt = 0;
287 }
288
289 t_con->line_size = 0;
290 t_con->line_cursor = 0;
291 }
292 else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
293 {
294 if (t_con->line_cursor > 0)
295 {
296 if (t_con->line_cursor != t_con->line_size)
297 {
298 int i;
299 write(connection->fd, "\b", 1);
300 t_con->line_cursor--;
301 t_con->line_size--;
302 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
303
304 write(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
305 write(connection->fd, " \b", 2);
306 for (i = t_con->line_cursor; i < t_con->line_size; i++)
307 {
308 write(connection->fd, "\b", 1);
309 }
310 }
311 else
312 {
313 t_con->line_size--;
314 t_con->line_cursor--;
315 /* back space: move the 'printer' head one char back, overwrite with space, move back again */
316 write(connection->fd, "\b \b", 3);
317 }
318 }
319 }
320 else if (*buf_p == 0x15) /* clear line */
321 {
322 telnet_clear_line(connection, t_con);
323 }
324 else if (*buf_p == CTRL('B')) /* cursor left */
325 {
326 if (t_con->line_cursor > 0)
327 {
328 write(connection->fd, "\b", 1);
329 t_con->line_cursor--;
330 }
331 t_con->state = TELNET_STATE_DATA;
332 }
333 else if (*buf_p == CTRL('F')) /* cursor right */
334 {
335 if (t_con->line_cursor < t_con->line_size)
336 {
337 write(connection->fd, t_con->line + t_con->line_cursor++, 1);
338 }
339 t_con->state = TELNET_STATE_DATA;
340 }
341 else
342 {
343 DEBUG("unhandled nonprintable: %2.2x", *buf_p);
344 }
345 }
346 }
347 break;
348 case TELNET_STATE_IAC:
349 switch (*buf_p)
350 {
351 case '\xfe':
352 t_con->state = TELNET_STATE_DONT;
353 break;
354 case '\xfd':
355 t_con->state = TELNET_STATE_DO;
356 break;
357 case '\xfc':
358 t_con->state = TELNET_STATE_WONT;
359 break;
360 case '\xfb':
361 t_con->state = TELNET_STATE_WILL;
362 break;
363 }
364 break;
365 case TELNET_STATE_SB:
366 break;
367 case TELNET_STATE_SE:
368 break;
369 case TELNET_STATE_WILL:
370 case TELNET_STATE_WONT:
371 case TELNET_STATE_DO:
372 case TELNET_STATE_DONT:
373 t_con->state = TELNET_STATE_DATA;
374 break;
375 case TELNET_STATE_ESCAPE:
376 if (t_con->last_escape == '[')
377 {
378 if (*buf_p == 'D') /* cursor left */
379 {
380 if (t_con->line_cursor > 0)
381 {
382 write(connection->fd, "\b", 1);
383 t_con->line_cursor--;
384 }
385 t_con->state = TELNET_STATE_DATA;
386 }
387 else if (*buf_p == 'C') /* cursor right */
388 {
389 if (t_con->line_cursor < t_con->line_size)
390 {
391 write(connection->fd, t_con->line + t_con->line_cursor++, 1);
392 }
393 t_con->state = TELNET_STATE_DATA;
394 }
395 else if (*buf_p == 'A') /* cursor up */
396 {
397 int last_history = (t_con->current_history - 1 >= 0) ? t_con->current_history - 1 : 127;
398 if (t_con->history[last_history])
399 {
400 telnet_clear_line(connection, t_con);
401 t_con->line_size = strlen(t_con->history[last_history]);
402 t_con->line_cursor = t_con->line_size;
403 memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
404 write(connection->fd, t_con->line, t_con->line_size);
405 t_con->current_history = last_history;
406 }
407 t_con->state = TELNET_STATE_DATA;
408 }
409 else if (*buf_p == 'B') /* cursor down */
410 {
411 int next_history = (t_con->current_history + 1 < 128) ? t_con->current_history + 1 : 0;
412 if (t_con->history[next_history])
413 {
414 telnet_clear_line(connection, t_con);
415 t_con->line_size = strlen(t_con->history[next_history]);
416 t_con->line_cursor = t_con->line_size;
417 memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
418 write(connection->fd, t_con->line, t_con->line_size);
419 t_con->current_history = next_history;
420 }
421 t_con->state = TELNET_STATE_DATA;
422 }
423 else if (*buf_p == '3')
424 {
425 t_con->last_escape = *buf_p;
426 }
427 else
428 {
429 t_con->state = TELNET_STATE_DATA;
430 }
431 }
432 else if (t_con->last_escape == '3')
433 {
434 /* Remove character */
435 if (*buf_p == '~')
436 {
437 if (t_con->line_cursor < t_con->line_size)
438 {
439 int i;
440 t_con->line_size--;
441 /* remove char from line buffer */
442 memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
443
444 /* print remainder of buffer */
445 write(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
446 /* overwrite last char with whitespace */
447 write(connection->fd, " \b", 2);
448
449 /* move back to cursor position*/
450 for (i = t_con->line_cursor; i < t_con->line_size; i++)
451 {
452 write(connection->fd, "\b", 1);
453 }
454 }
455
456 t_con->state = TELNET_STATE_DATA;
457 }
458 else
459 {
460 t_con->state = TELNET_STATE_DATA;
461 }
462 }
463 else if (t_con->last_escape == '\x00')
464 {
465 if (*buf_p == '[')
466 {
467 t_con->last_escape = *buf_p;
468 }
469 else
470 {
471 t_con->state = TELNET_STATE_DATA;
472 }
473 }
474 else
475 {
476 ERROR("BUG: unexpected value in t_con->last_escape");
477 t_con->state = TELNET_STATE_DATA;
478 }
479
480 break;
481 default:
482 ERROR("unknown telnet state");
483 exit(-1);
484 }
485
486 bytes_read--;
487 buf_p++;
488 }
489
490 return ERROR_OK;
491 }
492
493 int telnet_connection_closed(connection_t *connection)
494 {
495 telnet_connection_t *t_con = connection->priv;
496 int i;
497
498 if (t_con->prompt)
499 free(t_con->prompt);
500
501 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
502 {
503 if (t_con->history[i])
504 free(t_con->history[i]);
505 }
506
507 if (connection->priv)
508 free(connection->priv);
509 else
510 ERROR("BUG: connection->priv == NULL");
511
512 target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
513
514 return ERROR_OK;
515 }
516
517 int telnet_set_prompt(connection_t *connection, char *prompt)
518 {
519 telnet_connection_t *t_con = connection->priv;
520
521 t_con->prompt = strdup(prompt);
522
523 return ERROR_OK;
524 }
525
526 int telnet_init(char *banner)
527 {
528 telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
529
530 if (telnet_port == 0)
531 {
532 WARNING("no telnet port specified, using default port 4444");
533 telnet_port = 4444;
534 }
535
536 telnet_service->banner = banner;
537
538 add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
539
540 return ERROR_OK;
541 }
542
543 int telnet_register_commands(command_context_t *command_context)
544 {
545 register_command(command_context, NULL, "exit", handle_exit_command,
546 COMMAND_EXEC, "exit telnet session");
547
548 register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
549 COMMAND_CONFIG, "");
550
551 return ERROR_OK;
552 }
553
554 /* daemon configuration command telnet_port */
555 int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
556 {
557 if (argc == 0)
558 return ERROR_OK;
559
560 /* only if the port wasn't overwritten by cmdline */
561 if (telnet_port == 0)
562 telnet_port = strtoul(args[0], NULL, 0);
563
564 return ERROR_OK;
565 }
566
567 int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
568 {
569 return ERROR_COMMAND_CLOSE_CONNECTION;
570 }

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)