build: cleanup src/server directory
[fw/openocd] / 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 }