5d2e3bc9c15f30b7efa899580a2ba2cd80336573
[openocd.git] / src / server / telnet_server.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2007-2010 √ėyvind Harboe *
6 * oyvind.harboe@zylin.com *
7 * *
8 * Copyright (C) 2008 by Spencer Oliver *
9 * spen@spen-soft.co.uk *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "telnet_server.h"
32 #include <target/target_request.h>
33
34 static const char *telnet_port;
35
36 static char *negotiate =
37 "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
38 "\xFF\xFB\x01" /* IAC WILL Echo */
39 "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
40 "\xFF\xFE\x01"; /* IAC DON'T Echo */
41
42 #define CTRL(c) (c - '@')
43
44 /* The only way we can detect that the socket is closed is the first time
45 * we write to it, we will fail. Subsequent write operations will
46 * succeed. Shudder!
47 */
48 static int telnet_write(struct connection *connection, const void *data,
49 int len)
50 {
51 struct telnet_connection *t_con = connection->priv;
52 if (t_con->closed)
53 return ERROR_SERVER_REMOTE_CLOSED;
54
55 if (connection_write(connection, data, len) == len)
56 return ERROR_OK;
57 t_con->closed = 1;
58 return ERROR_SERVER_REMOTE_CLOSED;
59 }
60
61 static int telnet_prompt(struct connection *connection)
62 {
63 struct telnet_connection *t_con = connection->priv;
64
65 return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
66 }
67
68 static int telnet_outputline(struct connection *connection, const char *line)
69 {
70 int len;
71
72 /* process lines in buffer */
73 while (*line) {
74 char *line_end = strchr(line, '\n');
75
76 if (line_end)
77 len = line_end-line;
78 else
79 len = strlen(line);
80
81 telnet_write(connection, line, len);
82 if (line_end) {
83 telnet_write(connection, "\r\n", 2);
84 line += len + 1;
85 } else
86 line += len;
87 }
88
89 return ERROR_OK;
90 }
91
92 static int telnet_output(struct command_context *cmd_ctx, const char *line)
93 {
94 struct connection *connection = cmd_ctx->output_handler_priv;
95
96 return telnet_outputline(connection, line);
97 }
98
99 static void telnet_log_callback(void *priv, const char *file, unsigned line,
100 const char *function, const char *string)
101 {
102 struct connection *connection = priv;
103 struct telnet_connection *t_con = connection->priv;
104 int i;
105
106 /* if there is no prompt, simply output the message */
107 if (t_con->line_cursor < 0) {
108 telnet_outputline(connection, string);
109 return;
110 }
111
112 /* clear the command line */
113 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
114 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
115 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
116 telnet_write(connection, " ", i > 16 ? 16 : i);
117 for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16)
118 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i);
119
120 /* output the message */
121 telnet_outputline(connection, string);
122
123 /* put the command line to its previous state */
124 telnet_prompt(connection);
125 telnet_write(connection, t_con->line, t_con->line_size);
126 for (i = t_con->line_size; i > t_con->line_cursor; i--)
127 telnet_write(connection, "\b", 1);
128 }
129
130 static int telnet_new_connection(struct connection *connection)
131 {
132 struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection));
133 struct telnet_service *telnet_service = connection->service->priv;
134 int i;
135
136 connection->priv = telnet_connection;
137
138 /* initialize telnet connection information */
139 telnet_connection->closed = 0;
140 telnet_connection->line_size = 0;
141 telnet_connection->line_cursor = 0;
142 telnet_connection->option_size = 0;
143 telnet_connection->prompt = strdup("> ");
144 telnet_connection->state = TELNET_STATE_DATA;
145
146 /* output goes through telnet connection */
147 command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
148
149 /* negotiate telnet options */
150 telnet_write(connection, negotiate, strlen(negotiate));
151
152 /* print connection banner */
153 if (telnet_service->banner) {
154 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
155 telnet_write(connection, "\r\n", 2);
156 }
157
158 telnet_write(connection, "\r", 1); /* the prompt is always placed at the line beginning
159 **/
160 telnet_prompt(connection);
161
162 /* initialize history */
163 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
164 telnet_connection->history[i] = NULL;
165 telnet_connection->next_history = 0;
166 telnet_connection->current_history = 0;
167
168 log_add_callback(telnet_log_callback, connection);
169
170 return ERROR_OK;
171 }
172
173 static void telnet_clear_line(struct connection *connection,
174 struct telnet_connection *t_con)
175 {
176 /* move to end of line */
177 if (t_con->line_cursor < t_con->line_size)
178 telnet_write(connection,
179 t_con->line + t_con->line_cursor,
180 t_con->line_size - t_con->line_cursor);
181
182 /* backspace, overwrite with space, backspace */
183 while (t_con->line_size > 0) {
184 telnet_write(connection, "\b \b", 3);
185 t_con->line_size--;
186 }
187 t_con->line_cursor = 0;
188 }
189
190 static int telnet_input(struct connection *connection)
191 {
192 int bytes_read;
193 unsigned char buffer[TELNET_BUFFER_SIZE];
194 unsigned char *buf_p;
195 struct telnet_connection *t_con = connection->priv;
196 struct command_context *command_context = connection->cmd_ctx;
197
198 bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
199
200 if (bytes_read == 0)
201 return ERROR_SERVER_REMOTE_CLOSED;
202 else if (bytes_read == -1) {
203 LOG_ERROR("error during read: %s", strerror(errno));
204 return ERROR_SERVER_REMOTE_CLOSED;
205 }
206
207 buf_p = buffer;
208 while (bytes_read) {
209 switch (t_con->state) {
210 case TELNET_STATE_DATA:
211 if (*buf_p == 0xff)
212 t_con->state = TELNET_STATE_IAC;
213 else {
214 if (isprint(*buf_p)) { /* printable character */
215 /* watch buffer size leaving one spare character for
216 *string null termination */
217 if (t_con->line_size == TELNET_LINE_MAX_SIZE-1) {
218 /* output audible bell if buffer is full */
219 telnet_write(connection, "\x07", 1); /*
220 *"\a"
221 *does
222 *not
223 *work,
224 *at
225 *least
226 *on
227 *windows
228 **/
229 } else if (t_con->line_cursor == t_con->line_size) {
230 telnet_write(connection, buf_p, 1);
231 t_con->line[t_con->line_size++] = *buf_p;
232 t_con->line_cursor++;
233 } else {
234 int i;
235 memmove(t_con->line + t_con->line_cursor + 1,
236 t_con->line + t_con->line_cursor,
237 t_con->line_size - t_con->line_cursor);
238 t_con->line[t_con->line_cursor] = *buf_p;
239 t_con->line_size++;
240 telnet_write(connection,
241 t_con->line + t_con->line_cursor,
242 t_con->line_size - t_con->line_cursor);
243 t_con->line_cursor++;
244 for (i = t_con->line_cursor; i < t_con->line_size; i++)
245 telnet_write(connection, "\b", 1);
246 }
247 } else { /* non-printable */
248 if (*buf_p == 0x1b) { /* escape */
249 t_con->state = TELNET_STATE_ESCAPE;
250 t_con->last_escape = '\x00';
251 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /*
252 *CR/LF
253 **/
254 int retval;
255
256 /* skip over combinations with CR/LF and NUL
257 *characters */
258 if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
259 (*(buf_p + 1) == 0xd))) {
260 buf_p++;
261 bytes_read--;
262 }
263 if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
264 buf_p++;
265 bytes_read--;
266 }
267 t_con->line[t_con->line_size] = 0;
268
269 telnet_write(connection, "\r\n\x00", 3);
270
271 if (strcmp(t_con->line, "history") == 0) {
272 int i;
273 for (i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
274 /* the t_con->next_history
275 *line contains empty string
276 *(unless NULL), thus it is
277 *not printed */
278 char *history_line = t_con->history[(t_con->
279 next_history + i) %
280 TELNET_LINE_HISTORY_SIZE];
281 if (history_line) {
282 telnet_write(connection, history_line,
283 strlen(history_line));
284 telnet_write(connection, "\r\n\x00", 3);
285 }
286 }
287 t_con->line_size = 0;
288 t_con->line_cursor = 0;
289 continue;
290 }
291
292 /* save only non-blank not repeating lines
293 *in the history */
294 char *prev_line = t_con->history[(t_con->current_history > 0) ?
295 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
296 if (*t_con->line && (prev_line == NULL ||
297 strcmp(t_con->line, prev_line))) {
298 /* if the history slot is already
299 *taken, free it */
300 if (t_con->history[t_con->next_history])
301 free(t_con->history[t_con->next_history]);
302
303 /* add line to history */
304 t_con->history[t_con->next_history] = strdup(t_con->line);
305
306 /* wrap history at
307 *TELNET_LINE_HISTORY_SIZE */
308 t_con->next_history = (t_con->next_history + 1) %
309 TELNET_LINE_HISTORY_SIZE;
310
311 /* current history line starts at
312 *the new entry */
313 t_con->current_history =
314 t_con->next_history;
315
316 if (t_con->history[t_con->current_history])
317 free(t_con->history[t_con->current_history]);
318 t_con->history[t_con->current_history] = strdup("");
319 }
320
321 t_con->line_size = 0;
322
323 t_con->line_cursor = -1; /* to supress prompt
324 *in log callback
325 *during command
326 *execution */
327
328 retval = command_run_line(command_context, t_con->line);
329
330 t_con->line_cursor = 0;
331
332 if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
333 return ERROR_SERVER_REMOTE_CLOSED;
334
335 telnet_write(connection, "\r", 1); /*
336 *the
337 *prompt
338 *is
339 *always
340 *placed
341 *at
342 *the
343 *line
344 *beginning
345 **/
346 retval = telnet_prompt(connection);
347 if (retval == ERROR_SERVER_REMOTE_CLOSED)
348 return ERROR_SERVER_REMOTE_CLOSED;
349
350 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /*
351 *delete
352 *character
353 **/
354 if (t_con->line_cursor > 0) {
355 if (t_con->line_cursor != t_con->line_size) {
356 int i;
357 telnet_write(connection, "\b", 1);
358 t_con->line_cursor--;
359 t_con->line_size--;
360 memmove(t_con->line + t_con->line_cursor,
361 t_con->line + t_con->line_cursor + 1,
362 t_con->line_size -
363 t_con->line_cursor);
364
365 telnet_write(connection,
366 t_con->line + t_con->line_cursor,
367 t_con->line_size -
368 t_con->line_cursor);
369 telnet_write(connection, " \b", 2);
370 for (i = t_con->line_cursor; i < t_con->line_size; i++)
371 telnet_write(connection, "\b", 1);
372 } else {
373 t_con->line_size--;
374 t_con->line_cursor--;
375 /* back space: move the
376 *'printer' head one char
377 *back, overwrite with
378 *space, move back again */
379 telnet_write(connection, "\b \b", 3);
380 }
381 }
382 } else if (*buf_p == 0x15) /* clear line */
383 telnet_clear_line(connection, t_con);
384 else if (*buf_p == CTRL('B')) { /* cursor left */
385 if (t_con->line_cursor > 0) {
386 telnet_write(connection, "\b", 1);
387 t_con->line_cursor--;
388 }
389 t_con->state = TELNET_STATE_DATA;
390 } else if (*buf_p == CTRL('F')) { /* cursor right */
391 if (t_con->line_cursor < t_con->line_size)
392 telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
393 t_con->state = TELNET_STATE_DATA;
394 } else
395 LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
396 }
397 }
398 break;
399 case TELNET_STATE_IAC:
400 switch (*buf_p) {
401 case 0xfe:
402 t_con->state = TELNET_STATE_DONT;
403 break;
404 case 0xfd:
405 t_con->state = TELNET_STATE_DO;
406 break;
407 case 0xfc:
408 t_con->state = TELNET_STATE_WONT;
409 break;
410 case 0xfb:
411 t_con->state = TELNET_STATE_WILL;
412 break;
413 }
414 break;
415 case TELNET_STATE_SB:
416 break;
417 case TELNET_STATE_SE:
418 break;
419 case TELNET_STATE_WILL:
420 case TELNET_STATE_WONT:
421 case TELNET_STATE_DO:
422 case TELNET_STATE_DONT:
423 t_con->state = TELNET_STATE_DATA;
424 break;
425 case TELNET_STATE_ESCAPE:
426 if (t_con->last_escape == '[') {
427 if (*buf_p == 'D') { /* cursor left */
428 if (t_con->line_cursor > 0) {
429 telnet_write(connection, "\b", 1);
430 t_con->line_cursor--;
431 }
432 t_con->state = TELNET_STATE_DATA;
433 } else if (*buf_p == 'C') { /* cursor right */
434 if (t_con->line_cursor < t_con->line_size)
435 telnet_write(connection,
436 t_con->line + t_con->line_cursor++, 1);
437 t_con->state = TELNET_STATE_DATA;
438 } else if (*buf_p == 'A') { /* cursor up */
439 int last_history = (t_con->current_history > 0) ?
440 t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
441 if (t_con->history[last_history]) {
442 telnet_clear_line(connection, t_con);
443 t_con->line_size = strlen(t_con->history[last_history]);
444 t_con->line_cursor = t_con->line_size;
445 memcpy(t_con->line, t_con->history[last_history], t_con->line_size);
446 telnet_write(connection, t_con->line, t_con->line_size);
447 t_con->current_history = last_history;
448 }
449 t_con->state = TELNET_STATE_DATA;
450 } else if (*buf_p == 'B') { /* cursor down */
451 int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
452 if (t_con->history[next_history]) {
453 telnet_clear_line(connection, t_con);
454 t_con->line_size = strlen(t_con->history[next_history]);
455 t_con->line_cursor = t_con->line_size;
456 memcpy(t_con->line, t_con->history[next_history], t_con->line_size);
457 telnet_write(connection, t_con->line, t_con->line_size);
458 t_con->current_history = next_history;
459 }
460 t_con->state = TELNET_STATE_DATA;
461 } else if (*buf_p == '3')
462 t_con->last_escape = *buf_p;
463 else
464 t_con->state = TELNET_STATE_DATA;
465 } else if (t_con->last_escape == '3') {
466 /* Remove character */
467 if (*buf_p == '~') {
468 if (t_con->line_cursor < t_con->line_size) {
469 int i;
470 t_con->line_size--;
471 /* remove char from line buffer */
472 memmove(t_con->line + t_con->line_cursor,
473 t_con->line + t_con->line_cursor + 1,
474 t_con->line_size - t_con->line_cursor);
475
476 /* print remainder of buffer */
477 telnet_write(connection, t_con->line + t_con->line_cursor,
478 t_con->line_size - t_con->line_cursor);
479 /* overwrite last char with whitespace */
480 telnet_write(connection, " \b", 2);
481
482 /* move back to cursor position*/
483 for (i = t_con->line_cursor; i < t_con->line_size; i++)
484 telnet_write(connection, "\b", 1);
485 }
486
487 t_con->state = TELNET_STATE_DATA;
488 } else
489 t_con->state = TELNET_STATE_DATA;
490 } else if (t_con->last_escape == '\x00') {
491 if (*buf_p == '[')
492 t_con->last_escape = *buf_p;
493 else
494 t_con->state = TELNET_STATE_DATA;
495 } else {
496 LOG_ERROR("BUG: unexpected value in t_con->last_escape");
497 t_con->state = TELNET_STATE_DATA;
498 }
499
500 break;
501 default:
502 LOG_ERROR("unknown telnet state");
503 exit(-1);
504 }
505
506 bytes_read--;
507 buf_p++;
508 }
509
510 return ERROR_OK;
511 }
512
513 static int telnet_connection_closed(struct connection *connection)
514 {
515 struct telnet_connection *t_con = connection->priv;
516 int i;
517
518 log_remove_callback(telnet_log_callback, connection);
519
520 if (t_con->prompt) {
521 free(t_con->prompt);
522 t_con->prompt = NULL;
523 }
524
525 for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
526 if (t_con->history[i]) {
527 free(t_con->history[i]);
528 t_con->history[i] = NULL;
529 }
530 }
531
532 /* if this connection registered a debug-message receiver delete it */
533 delete_debug_msg_receiver(connection->cmd_ctx, NULL);
534
535 if (connection->priv) {
536 free(connection->priv);
537 connection->priv = NULL;
538 } else
539 LOG_ERROR("BUG: connection->priv == NULL");
540
541 return ERROR_OK;
542 }
543
544 int telnet_init(char *banner)
545 {
546 if (strcmp(telnet_port, "disabled") == 0) {
547 LOG_INFO("telnet server disabled");
548 return ERROR_OK;
549 }
550
551 struct telnet_service *telnet_service = malloc(sizeof(struct telnet_service));
552
553 telnet_service->banner = banner;
554
555 return add_service("telnet",
556 telnet_port,
557 1,
558 telnet_new_connection,
559 telnet_input,
560 telnet_connection_closed,
561 telnet_service);
562 }
563
564 /* daemon configuration command telnet_port */
565 COMMAND_HANDLER(handle_telnet_port_command)
566 {
567 return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
568 }
569
570 COMMAND_HANDLER(handle_exit_command)
571 {
572 return ERROR_COMMAND_CLOSE_CONNECTION;
573 }
574
575 static const struct command_registration telnet_command_handlers[] = {
576 {
577 .name = "exit",
578 .handler = handle_exit_command,
579 .mode = COMMAND_EXEC,
580 .usage = "",
581 .help = "exit telnet session",
582 },
583 {
584 .name = "telnet_port",
585 .handler = handle_telnet_port_command,
586 .mode = COMMAND_ANY,
587 .help = "Specify port on which to listen "
588 "for incoming telnet connections. "
589 "Read help on 'gdb_port'.",
590 .usage = "[port_num]",
591 },
592 COMMAND_REGISTRATION_DONE
593 };
594
595 int telnet_register_commands(struct command_context *cmd_ctx)
596 {
597 telnet_port = strdup("4444");
598 return register_commands(cmd_ctx, NULL, telnet_command_handlers);
599 }