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