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