telnet_server: fix scan-build warning
[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, see <http://www.gnu.org/licenses/>. *
23  ***************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "telnet_server.h"
30 #include <target/target_request.h>
31 #include <helper/configuration.h>
32 #include <helper/list.h>
33
34 static 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 #define TELNET_HISTORY  ".openocd_history"
44
45 /* The only way we can detect that the socket is closed is the first time
46  * we write to it, we will fail. Subsequent write operations will
47  * succeed. Shudder!
48  */
49 static int telnet_write(struct connection *connection, const void *data,
50         int len)
51 {
52         struct telnet_connection *t_con = connection->priv;
53         if (t_con->closed)
54                 return ERROR_SERVER_REMOTE_CLOSED;
55
56         if (connection_write(connection, data, len) == len)
57                 return ERROR_OK;
58         t_con->closed = true;
59         return ERROR_SERVER_REMOTE_CLOSED;
60 }
61
62 /* output an audible bell */
63 static int telnet_bell(struct connection *connection)
64 {
65         /* ("\a" does not work, at least on windows) */
66         return telnet_write(connection, "\x07", 1);
67 }
68
69 static int telnet_prompt(struct connection *connection)
70 {
71         struct telnet_connection *t_con = connection->priv;
72
73         return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
74 }
75
76 static int telnet_outputline(struct connection *connection, const char *line)
77 {
78         int len;
79
80         /* process lines in buffer */
81         while (*line) {
82                 char *line_end = strchr(line, '\n');
83
84                 if (line_end)
85                         len = line_end-line;
86                 else
87                         len = strlen(line);
88
89                 telnet_write(connection, line, len);
90                 if (line_end) {
91                         telnet_write(connection, "\r\n", 2);
92                         line += len + 1;
93                 } else
94                         line += len;
95         }
96
97         return ERROR_OK;
98 }
99
100 static int telnet_output(struct command_context *cmd_ctx, const char *line)
101 {
102         struct connection *connection = cmd_ctx->output_handler_priv;
103
104         return telnet_outputline(connection, line);
105 }
106
107 static void telnet_log_callback(void *priv, const char *file, unsigned line,
108         const char *function, const char *string)
109 {
110         struct connection *connection = priv;
111         struct telnet_connection *t_con = connection->priv;
112         size_t i;
113         size_t tmp;
114
115         /* If the prompt is not visible, simply output the message. */
116         if (!t_con->prompt_visible) {
117                 telnet_outputline(connection, string);
118                 return;
119         }
120
121         /* Clear the command line. */
122         tmp = strlen(t_con->prompt) + t_con->line_size;
123
124         for (i = 0; i < tmp; i += 16)
125                 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
126                         MIN(tmp - i, 16));
127
128         for (i = 0; i < tmp; i += 16)
129                 telnet_write(connection, "                ", MIN(tmp - i, 16));
130
131         for (i = 0; i < tmp; i += 16)
132                 telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
133                         MIN(tmp - i, 16));
134
135         telnet_outputline(connection, string);
136
137         /* Put the command line to its previous state. */
138         telnet_prompt(connection);
139         telnet_write(connection, t_con->line, t_con->line_size);
140
141         for (i = t_con->line_cursor; i < t_con->line_size; i++)
142                 telnet_write(connection, "\b", 1);
143 }
144
145 static void telnet_load_history(struct telnet_connection *t_con)
146 {
147         FILE *histfp;
148         char buffer[TELNET_BUFFER_SIZE];
149         int i = 0;
150
151         char *history = get_home_dir(TELNET_HISTORY);
152
153         if (!history) {
154                 LOG_INFO("unable to get user home directory, telnet history will be disabled");
155                 return;
156         }
157
158         histfp = fopen(history, "rb");
159
160         if (histfp) {
161
162                 while (fgets(buffer, sizeof(buffer), histfp)) {
163
164                         char *p = strchr(buffer, '\n');
165                         if (p)
166                                 *p = '\0';
167                         if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
168                                 t_con->history[i++] = strdup(buffer);
169                 }
170
171                 t_con->next_history = i;
172                 t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
173                 /* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
174                 t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
175                 fclose(histfp);
176         }
177
178         free(history);
179 }
180
181 static void telnet_save_history(struct telnet_connection *t_con)
182 {
183         FILE *histfp;
184         int i;
185         int num;
186
187         char *history = get_home_dir(TELNET_HISTORY);
188
189         if (!history) {
190                 LOG_INFO("unable to get user home directory, telnet history will be disabled");
191                 return;
192         }
193
194         histfp = fopen(history, "wb");
195
196         if (histfp) {
197
198                 num = TELNET_LINE_HISTORY_SIZE;
199                 i = t_con->current_history + 1;
200                 i %= TELNET_LINE_HISTORY_SIZE;
201
202                 while (!t_con->history[i] && num > 0) {
203                         i++;
204                         i %= TELNET_LINE_HISTORY_SIZE;
205                         num--;
206                 }
207
208                 if (num > 0) {
209                         for (; num > 0; num--) {
210                                 fprintf(histfp, "%s\n", t_con->history[i]);
211                                 i++;
212                                 i %= TELNET_LINE_HISTORY_SIZE;
213                         }
214                 }
215                 fclose(histfp);
216         }
217
218         free(history);
219 }
220
221 static int telnet_new_connection(struct connection *connection)
222 {
223         struct telnet_connection *telnet_connection;
224         struct telnet_service *telnet_service = connection->service->priv;
225
226         telnet_connection = calloc(1, sizeof(struct telnet_connection));
227
228         if (!telnet_connection) {
229                 LOG_ERROR("Failed to allocate telnet connection.");
230                 return ERROR_FAIL;
231         }
232
233         connection->priv = telnet_connection;
234
235         /* initialize telnet connection information */
236         telnet_connection->prompt = strdup("> ");
237         telnet_connection->prompt_visible = true;
238         telnet_connection->state = TELNET_STATE_DATA;
239
240         /* output goes through telnet connection */
241         command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
242
243         /* negotiate telnet options */
244         telnet_write(connection, negotiate, strlen(negotiate));
245
246         /* print connection banner */
247         if (telnet_service->banner) {
248                 telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
249                 telnet_write(connection, "\r\n", 2);
250         }
251
252         /* the prompt is always placed at the line beginning */
253         telnet_write(connection, "\r", 1);
254         telnet_prompt(connection);
255
256         telnet_load_history(telnet_connection);
257
258         log_add_callback(telnet_log_callback, connection);
259
260         return ERROR_OK;
261 }
262
263 static void telnet_clear_line(struct connection *connection,
264         struct telnet_connection *t_con)
265 {
266         /* move to end of line */
267         if (t_con->line_cursor < t_con->line_size)
268                 telnet_write(connection,
269                         t_con->line + t_con->line_cursor,
270                         t_con->line_size - t_con->line_cursor);
271
272         /* backspace, overwrite with space, backspace */
273         while (t_con->line_size > 0) {
274                 telnet_write(connection, "\b \b", 3);
275                 t_con->line_size--;
276         }
277         t_con->line_cursor = 0;
278 }
279
280 static void telnet_history_go(struct connection *connection, int idx)
281 {
282         struct telnet_connection *t_con = connection->priv;
283
284         if (t_con->history[idx]) {
285                 telnet_clear_line(connection, t_con);
286                 t_con->line_size = strlen(t_con->history[idx]);
287                 t_con->line_cursor = t_con->line_size;
288                 memcpy(t_con->line, t_con->history[idx], t_con->line_size);
289                 telnet_write(connection, t_con->line, t_con->line_size);
290                 t_con->current_history = idx;
291         }
292         t_con->state = TELNET_STATE_DATA;
293 }
294
295 static void telnet_history_up(struct connection *connection)
296 {
297         struct telnet_connection *t_con = connection->priv;
298
299         size_t last_history = (t_con->current_history > 0) ?
300                                 t_con->current_history - 1 :
301                                 TELNET_LINE_HISTORY_SIZE-1;
302         telnet_history_go(connection, last_history);
303 }
304
305 static void telnet_history_down(struct connection *connection)
306 {
307         struct telnet_connection *t_con = connection->priv;
308         size_t next_history;
309
310         next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
311         telnet_history_go(connection, next_history);
312 }
313
314 static void telnet_history_add(struct connection *connection)
315 {
316         struct telnet_connection *t_con = connection->priv;
317
318         /* save only non-blank not repeating lines in the history */
319         char *prev_line = t_con->history[(t_con->current_history > 0) ?
320                         t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1];
321
322         if (*t_con->line && (!prev_line || strcmp(t_con->line, prev_line))) {
323                 /* if the history slot is already taken, free it */
324                 free(t_con->history[t_con->next_history]);
325
326                 /* add line to history */
327                 t_con->history[t_con->next_history] = strdup(t_con->line);
328
329                 /* wrap history at TELNET_LINE_HISTORY_SIZE */
330                 t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
331
332                 /* current history line starts at the new entry */
333                 t_con->current_history = t_con->next_history;
334
335                 free(t_con->history[t_con->current_history]);
336                 t_con->history[t_con->current_history] = strdup("");
337         }
338 }
339
340 static int telnet_history_print(struct connection *connection)
341 {
342         struct telnet_connection *tc;
343
344         tc = connection->priv;
345
346         for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) {
347                 char *line;
348
349                 /*
350                  * The tc->next_history line contains empty string (unless NULL), thus
351                  * it is not printed.
352                  */
353                 line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE];
354
355                 if (line) {
356                         telnet_write(connection, line, strlen(line));
357                         telnet_write(connection, "\r\n\x00", 3);
358                 }
359         }
360
361         tc->line_size = 0;
362         tc->line_cursor = 0;
363
364         /* The prompt is always placed at the line beginning. */
365         telnet_write(connection, "\r", 1);
366
367         return telnet_prompt(connection);
368 }
369
370 static void telnet_move_cursor(struct connection *connection, size_t pos)
371 {
372         struct telnet_connection *tc = connection->priv;
373         size_t tmp;
374
375         if (pos == tc->line_cursor) /* nothing to do */
376                 return;
377
378         if (pos > tc->line_size) /* out of bounds */
379                 return;
380
381         if (pos < tc->line_cursor) {
382                 tmp = tc->line_cursor - pos;
383
384                 for (size_t i = 0; i < tmp; i += 16)
385                         telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
386                                 MIN(tmp - i, 16));
387         } else {
388                 tmp = pos - tc->line_cursor;
389
390                 for (size_t i = 0; i < tmp; i += 16)
391                         telnet_write(connection, tc->line + tc->line_cursor + i,
392                                 MIN(tmp - i, 16));
393         }
394
395         tc->line_cursor = pos;
396 }
397
398 /* check buffer size leaving one spare character for string null termination */
399 static inline bool telnet_can_insert(struct connection *connection, size_t len)
400 {
401         struct telnet_connection *t_con = connection->priv;
402
403         return t_con->line_size + len < TELNET_LINE_MAX_SIZE;
404 }
405
406 /* write to telnet console, and update the telnet_connection members
407  * this function is capable of inserting in the middle of a line
408  * please ensure that data does not contain special characters (\n, \r, \t, \b ...)
409  *
410  * returns false when it fails to insert the requested data
411  */
412 static bool telnet_insert(struct connection *connection, const void *data, size_t len)
413 {
414         struct telnet_connection *t_con = connection->priv;
415
416         if (!telnet_can_insert(connection, len)) {
417                 telnet_bell(connection);
418                 return false;
419         }
420
421         if (t_con->line_cursor < t_con->line_size) {
422                 /* we have some content after the cursor */
423                 memmove(t_con->line + t_con->line_cursor + len,
424                                 t_con->line + t_con->line_cursor,
425                                 t_con->line_size - t_con->line_cursor);
426         }
427
428         strncpy(t_con->line + t_con->line_cursor, data, len);
429
430         telnet_write(connection,
431                         t_con->line + t_con->line_cursor,
432                         t_con->line_size + len - t_con->line_cursor);
433
434         t_con->line_size += len;
435         t_con->line_cursor += len;
436
437         for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
438                 telnet_write(connection, "\b", 1);
439
440         return true;
441 }
442
443 static void telnet_delete_character(struct connection *connection)
444 {
445         struct telnet_connection *t_con = connection->priv;
446
447         if (t_con->line_cursor == 0)
448                 return;
449
450         if (t_con->line_cursor != t_con->line_size) {
451                 size_t i;
452                 telnet_write(connection, "\b", 1);
453                 t_con->line_cursor--;
454                 t_con->line_size--;
455                 memmove(t_con->line + t_con->line_cursor,
456                                 t_con->line + t_con->line_cursor + 1,
457                                 t_con->line_size -
458                                 t_con->line_cursor);
459
460                 telnet_write(connection,
461                                 t_con->line + t_con->line_cursor,
462                                 t_con->line_size -
463                                 t_con->line_cursor);
464                 telnet_write(connection, " \b", 2);
465                 for (i = t_con->line_cursor; i < t_con->line_size; i++)
466                         telnet_write(connection, "\b", 1);
467         } else {
468                 t_con->line_size--;
469                 t_con->line_cursor--;
470                 /* back space: move the 'printer' head one char
471                  * back, overwrite with space, move back again */
472                 telnet_write(connection, "\b \b", 3);
473         }
474 }
475
476 static void telnet_remove_character(struct connection *connection)
477 {
478         struct telnet_connection *t_con = connection->priv;
479
480         if (t_con->line_cursor < t_con->line_size) {
481                 size_t i;
482                 t_con->line_size--;
483                 /* remove char from line buffer */
484                 memmove(t_con->line + t_con->line_cursor,
485                                 t_con->line + t_con->line_cursor + 1,
486                                 t_con->line_size - t_con->line_cursor);
487
488                 /* print remainder of buffer */
489                 telnet_write(connection, t_con->line + t_con->line_cursor,
490                                 t_con->line_size - t_con->line_cursor);
491                 /* overwrite last char with whitespace */
492                 telnet_write(connection, " \b", 2);
493
494                 /* move back to cursor position*/
495                 for (i = t_con->line_cursor; i < t_con->line_size; i++)
496                         telnet_write(connection, "\b", 1);
497         }
498 }
499
500 static int telnet_exec_line(struct connection *connection)
501 {
502         struct telnet_connection *t_con = connection->priv;
503         struct command_context *command_context = connection->cmd_ctx;
504         int retval;
505
506         telnet_write(connection, "\r\n\x00", 3);
507
508         if (strcmp(t_con->line, "history") == 0) {
509                 retval = telnet_history_print(connection);
510
511                 if (retval != ERROR_OK)
512                         return retval;
513
514                 return ERROR_OK;
515         }
516
517         telnet_history_add(connection);
518
519         t_con->line_size = 0;
520
521         /* to suppress prompt in log callback during command execution */
522         t_con->prompt_visible = false;
523
524         if (strcmp(t_con->line, "shutdown") == 0)
525                 telnet_save_history(t_con);
526
527         retval = command_run_line(command_context, t_con->line);
528
529         t_con->line_cursor = 0;
530         t_con->prompt_visible = true;
531
532         if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
533                 return ERROR_SERVER_REMOTE_CLOSED;
534
535         /* the prompt is always placed at the line beginning */
536         telnet_write(connection, "\r", 1);
537
538         retval = telnet_prompt(connection);
539         if (retval == ERROR_SERVER_REMOTE_CLOSED)
540                 return ERROR_SERVER_REMOTE_CLOSED;
541
542         return ERROR_OK;
543 }
544
545 static void telnet_cut_line_to_end(struct connection *connection)
546 {
547         struct telnet_connection *t_con = connection->priv;
548
549         /* FIXME: currently this function does not save to clipboard */
550
551         if (t_con->line_cursor < t_con->line_size) {
552                 /* overwrite with space, until end of line, move back */
553                 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
554                         telnet_write(connection, " ", 1);
555                 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
556                         telnet_write(connection, "\b", 1);
557                 t_con->line[t_con->line_cursor] = '\0';
558                 t_con->line_size = t_con->line_cursor;
559         }
560 }
561
562 static void telnet_interrupt(struct connection *connection)
563 {
564         struct telnet_connection *t_con = connection->priv;
565
566         /* print '^C' at line end, and display a new command prompt */
567         telnet_move_cursor(connection, t_con->line_size);
568         telnet_write(connection, "^C\n\r", 4);
569         t_con->line_cursor = 0;
570         t_con->line_size = 0;
571         telnet_prompt(connection);
572 }
573
574 static void telnet_auto_complete(struct connection *connection)
575 {
576         struct telnet_connection *t_con = connection->priv;
577         struct command_context *command_context = connection->cmd_ctx;
578
579         struct cmd_match {
580                 char *cmd;
581                 struct list_head lh;
582         };
583
584         LIST_HEAD(matches);
585
586         /* - user command sequence, either at line beginning
587          *   or we start over after these characters ';', '[', '{'
588          * - user variable sequence, start after the character '$'
589          *   and do not contain white spaces */
590         bool is_variable_auto_completion = false;
591         bool have_spaces = false;
592         size_t seq_start = (t_con->line_cursor == 0) ? 0 : (t_con->line_cursor - 1);
593         while (1) {
594                 char c = t_con->line[seq_start];
595
596                 if (c == ';' || c == '[' || c == '{') {
597                         seq_start++;
598                         break;
599                 } else if (c == ' ') {
600                         have_spaces = true;
601                 } else if (c == '$' && !have_spaces) {
602                         is_variable_auto_completion = true;
603                         seq_start++;
604                         break;
605                 }
606
607                 if (seq_start == 0)
608                         break;
609
610                 seq_start--;
611         }
612
613         /* user command position in the line, ignore leading spaces */
614         size_t usr_cmd_pos = seq_start;
615         while ((usr_cmd_pos < t_con->line_cursor) && isspace(t_con->line[usr_cmd_pos]))
616                 usr_cmd_pos++;
617
618         /* check user command length */
619         if (t_con->line_cursor < usr_cmd_pos) {
620                 telnet_bell(connection);
621                 return;
622         }
623         size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
624
625         /* optimize multiple spaces in the user command,
626          * because info commands does not tolerate multiple spaces */
627         size_t optimized_spaces = 0;
628         char query[usr_cmd_len + 1];
629         for (size_t i = 0; i < usr_cmd_len; i++) {
630                 if ((i < usr_cmd_len - 1) && isspace(t_con->line[usr_cmd_pos + i])
631                                 && isspace(t_con->line[usr_cmd_pos + i + 1])) {
632                         optimized_spaces++;
633                         continue;
634                 }
635
636                 query[i - optimized_spaces] = t_con->line[usr_cmd_pos + i];
637         }
638
639         usr_cmd_len -= optimized_spaces;
640         query[usr_cmd_len] = '\0';
641
642         /* filter commands */
643         char *query_cmd;
644
645         if (is_variable_auto_completion)
646                 query_cmd = alloc_printf("lsort [info vars {%s*}]", query);
647         else
648                 query_cmd = alloc_printf("_telnet_autocomplete_helper {%s*}", query);
649
650         if (!query_cmd) {
651                 LOG_ERROR("Out of memory");
652                 return;
653         }
654
655         int retval = Jim_EvalSource(command_context->interp, __FILE__, __LINE__, query_cmd);
656         free(query_cmd);
657         if (retval != JIM_OK)
658                 return;
659
660         Jim_Obj *list = Jim_GetResult(command_context->interp);
661         Jim_IncrRefCount(list);
662
663         /* common prefix length of the matched commands */
664         size_t common_len = 0;
665         char *first_match = NULL; /* used to compute the common prefix length */
666
667         int len = Jim_ListLength(command_context->interp, list);
668         for (int i = 0; i < len; i++) {
669                 Jim_Obj *elem = Jim_ListGetIndex(command_context->interp, list, i);
670                 Jim_IncrRefCount(elem);
671
672                 char *name = (char *)Jim_GetString(elem, NULL);
673
674                 /* validate the command */
675                 bool ignore_cmd = false;
676                 if (!is_variable_auto_completion) {
677                         Jim_Cmd *jim_cmd = Jim_GetCommand(command_context->interp, elem, JIM_NONE);
678
679                         if (!jim_cmd) {
680                                 /* Why we are here? Let's ignore it! */
681                                 ignore_cmd = true;
682                         } else if (jimcmd_is_oocd_command(jim_cmd)) {
683                                 struct command *cmd = jimcmd_privdata(jim_cmd);
684
685                                 if (cmd && !cmd->handler && !cmd->jim_handler) {
686                                         /* Initial part of a multi-word command. Ignore it! */
687                                         ignore_cmd = true;
688                                 } else if (cmd && cmd->mode == COMMAND_CONFIG) {
689                                         /* Not executable after config phase. Ignore it! */
690                                         ignore_cmd = true;
691                                 }
692                         }
693                 }
694
695                 /* save the command in the prediction list */
696                 if (!ignore_cmd) {
697                         struct cmd_match *match = calloc(1, sizeof(struct cmd_match));
698                         if (!match) {
699                                 LOG_ERROR("Out of memory");
700                                 Jim_DecrRefCount(command_context->interp, elem);
701                                 break; /* break the for loop */
702                         }
703
704                         if (list_empty(&matches)) {
705                                 common_len = strlen(name);
706                                 first_match = name;
707                         } else {
708                                 size_t new_common_len = usr_cmd_len; /* save some loops */
709
710                                 while (new_common_len < common_len && first_match[new_common_len] == name[new_common_len])
711                                         new_common_len++;
712
713                                 common_len = new_common_len;
714                         }
715
716                         match->cmd = name;
717                         list_add_tail(&match->lh, &matches);
718                 }
719
720                 Jim_DecrRefCount(command_context->interp, elem);
721         }
722         /* end of command filtering */
723
724         /* proceed with auto-completion */
725         if (list_empty(&matches))
726                 telnet_bell(connection);
727         else if (common_len == usr_cmd_len && list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
728                 telnet_insert(connection, " ", 1);
729         else if (common_len > usr_cmd_len) {
730                 int completion_size = common_len - usr_cmd_len;
731                 if (telnet_insert(connection, first_match + usr_cmd_len, completion_size)) {
732                         /* in bash this extra space is only added when the cursor in at the end of line */
733                         if (list_is_singular(&matches) && t_con->line_cursor == t_con->line_size)
734                                 telnet_insert(connection, " ", 1);
735                 }
736         } else if (!list_is_singular(&matches)) {
737                 telnet_write(connection, "\n\r", 2);
738
739                 struct cmd_match *match;
740                 list_for_each_entry(match, &matches, lh) {
741                         telnet_write(connection, match->cmd, strlen(match->cmd));
742                         telnet_write(connection, "\n\r", 2);
743                 }
744
745                 telnet_prompt(connection);
746                 telnet_write(connection, t_con->line, t_con->line_size);
747
748                 /* restore the terminal visible cursor location */
749                 for (size_t i = t_con->line_cursor; i < t_con->line_size; i++)
750                         telnet_write(connection, "\b", 1);
751         }
752
753         /* destroy the command_list */
754         struct cmd_match *tmp, *match;
755         list_for_each_entry_safe(match, tmp, &matches, lh)
756                 free(match);
757
758         Jim_DecrRefCount(command_context->interp, list);
759 }
760
761 static int telnet_input(struct connection *connection)
762 {
763         int bytes_read;
764         unsigned char buffer[TELNET_BUFFER_SIZE];
765         unsigned char *buf_p;
766         struct telnet_connection *t_con = connection->priv;
767
768         bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE);
769
770         if (bytes_read == 0)
771                 return ERROR_SERVER_REMOTE_CLOSED;
772         else if (bytes_read == -1) {
773                 LOG_ERROR("error during read: %s", strerror(errno));
774                 return ERROR_SERVER_REMOTE_CLOSED;
775         }
776
777         buf_p = buffer;
778         while (bytes_read) {
779                 switch (t_con->state) {
780                         case TELNET_STATE_DATA:
781                                 if (*buf_p == 0xff) {
782                                         t_con->state = TELNET_STATE_IAC;
783                                 } else {
784                                         if (isprint(*buf_p)) {  /* printable character */
785                                                 telnet_insert(connection, buf_p, 1);
786                                         } else { /* non-printable */
787                                                 if (*buf_p == 0x1b) {   /* escape */
788                                                         t_con->state = TELNET_STATE_ESCAPE;
789                                                         t_con->last_escape = '\x00';
790                                                 } else if ((*buf_p == 0xd) || (*buf_p == 0xa)) { /* CR/LF */
791                                                         int retval;
792
793                                                         /* skip over combinations with CR/LF and NUL characters */
794                                                         if ((bytes_read > 1) && ((*(buf_p + 1) == 0xa) ||
795                                                                         (*(buf_p + 1) == 0xd))) {
796                                                                 buf_p++;
797                                                                 bytes_read--;
798                                                         }
799                                                         if ((bytes_read > 1) && (*(buf_p + 1) == 0)) {
800                                                                 buf_p++;
801                                                                 bytes_read--;
802                                                         }
803                                                         t_con->line[t_con->line_size] = 0;
804
805                                                         retval = telnet_exec_line(connection);
806                                                         if (retval != ERROR_OK)
807                                                                 return retval;
808                                                 } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) {       /* delete character */
809                                                         telnet_delete_character(connection);
810                                                 } else if (*buf_p == 0x15) {    /* clear line */
811                                                         telnet_clear_line(connection, t_con);
812                                                 } else if (*buf_p == CTRL('B')) {       /* cursor left */
813                                                         telnet_move_cursor(connection, t_con->line_cursor - 1);
814                                                         t_con->state = TELNET_STATE_DATA;
815                                                 } else if (*buf_p == CTRL('C')) {       /* interrupt */
816                                                         telnet_interrupt(connection);
817                                                 } else if (*buf_p == CTRL('F')) {       /* cursor right */
818                                                         telnet_move_cursor(connection, t_con->line_cursor + 1);
819                                                         t_con->state = TELNET_STATE_DATA;
820                                                 } else if (*buf_p == CTRL('P')) {       /* cursor up */
821                                                         telnet_history_up(connection);
822                                                 } else if (*buf_p == CTRL('N')) {       /* cursor down */
823                                                         telnet_history_down(connection);
824                                                 } else if (*buf_p == CTRL('A')) {       /* move the cursor to the beginning of the line */
825                                                         telnet_move_cursor(connection, 0);
826                                                 } else if (*buf_p == CTRL('E')) {       /* move the cursor to the end of the line */
827                                                         telnet_move_cursor(connection, t_con->line_size);
828                                                 } else if (*buf_p == CTRL('K')) {       /* kill line to end */
829                                                         telnet_cut_line_to_end(connection);
830                                                 } else if (*buf_p == '\t') {
831                                                         telnet_auto_complete(connection);
832                                                 } else {
833                                                         LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p);
834                                                 }
835                                         }
836                                 }
837                                 break;
838                         case TELNET_STATE_IAC:
839                                 switch (*buf_p) {
840                                 case 0xfe:
841                                         t_con->state = TELNET_STATE_DONT;
842                                         break;
843                                 case 0xfd:
844                                         t_con->state = TELNET_STATE_DO;
845                                         break;
846                                 case 0xfc:
847                                         t_con->state = TELNET_STATE_WONT;
848                                         break;
849                                 case 0xfb:
850                                         t_con->state = TELNET_STATE_WILL;
851                                         break;
852                                 }
853                                 break;
854                         case TELNET_STATE_SB:
855                                 break;
856                         case TELNET_STATE_SE:
857                                 break;
858                         case TELNET_STATE_WILL:
859                         case TELNET_STATE_WONT:
860                         case TELNET_STATE_DO:
861                         case TELNET_STATE_DONT:
862                                 t_con->state = TELNET_STATE_DATA;
863                                 break;
864                         case TELNET_STATE_ESCAPE:
865                                 if (t_con->last_escape == '[') {
866                                         if (*buf_p == 'D') {    /* cursor left */
867                                                 telnet_move_cursor(connection, t_con->line_cursor - 1);
868                                                 t_con->state = TELNET_STATE_DATA;
869                                         } else if (*buf_p == 'C') {     /* cursor right */
870                                                 telnet_move_cursor(connection, t_con->line_cursor + 1);
871                                                 t_con->state = TELNET_STATE_DATA;
872                                         } else if (*buf_p == 'A') {     /* cursor up */
873                                                 telnet_history_up(connection);
874                                         } else if (*buf_p == 'B') {     /* cursor down */
875                                                 telnet_history_down(connection);
876                                         } else if (*buf_p == 'F') { /* end key */
877                                                 telnet_move_cursor(connection, t_con->line_size);
878                                                 t_con->state = TELNET_STATE_DATA;
879                                         } else if (*buf_p == 'H') { /* home key */
880                                                 telnet_move_cursor(connection, 0);
881                                                 t_con->state = TELNET_STATE_DATA;
882                                         } else if (*buf_p == '3') {
883                                                 t_con->last_escape = *buf_p;
884                                         } else {
885                                                 t_con->state = TELNET_STATE_DATA;
886                                         }
887                                 } else if (t_con->last_escape == '3') {
888                                         /* Remove character */
889                                         if (*buf_p == '~') {
890                                                 telnet_remove_character(connection);
891                                                 t_con->state = TELNET_STATE_DATA;
892                                         } else
893                                                 t_con->state = TELNET_STATE_DATA;
894                                 } else if (t_con->last_escape == '\x00') {
895                                         if (*buf_p == '[')
896                                                 t_con->last_escape = *buf_p;
897                                         else
898                                                 t_con->state = TELNET_STATE_DATA;
899                                 } else {
900                                         LOG_ERROR("BUG: unexpected value in t_con->last_escape");
901                                         t_con->state = TELNET_STATE_DATA;
902                                 }
903
904                                 break;
905                         default:
906                                 LOG_ERROR("unknown telnet state");
907                                 return ERROR_FAIL;
908                 }
909
910                 bytes_read--;
911                 buf_p++;
912         }
913
914         return ERROR_OK;
915 }
916
917 static int telnet_connection_closed(struct connection *connection)
918 {
919         struct telnet_connection *t_con = connection->priv;
920         int i;
921
922         log_remove_callback(telnet_log_callback, connection);
923
924         free(t_con->prompt);
925         t_con->prompt = NULL;
926
927         /* save telnet history */
928         telnet_save_history(t_con);
929
930         for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) {
931                 free(t_con->history[i]);
932                 t_con->history[i] = NULL;
933         }
934
935         /* if this connection registered a debug-message receiver delete it */
936         delete_debug_msg_receiver(connection->cmd_ctx, NULL);
937
938         free(connection->priv);
939         connection->priv = NULL;
940
941         return ERROR_OK;
942 }
943
944 static const struct service_driver telnet_service_driver = {
945         .name = "telnet",
946         .new_connection_during_keep_alive_handler = NULL,
947         .new_connection_handler = telnet_new_connection,
948         .input_handler = telnet_input,
949         .connection_closed_handler = telnet_connection_closed,
950         .keep_client_alive_handler = NULL,
951 };
952
953 int telnet_init(char *banner)
954 {
955         if (strcmp(telnet_port, "disabled") == 0) {
956                 LOG_INFO("telnet server disabled");
957                 return ERROR_OK;
958         }
959
960         struct telnet_service *telnet_service =
961                 malloc(sizeof(struct telnet_service));
962
963         if (!telnet_service) {
964                 LOG_ERROR("Failed to allocate telnet service.");
965                 return ERROR_FAIL;
966         }
967
968         telnet_service->banner = banner;
969
970         int ret = add_service(&telnet_service_driver, telnet_port, CONNECTION_LIMIT_UNLIMITED,
971                 telnet_service);
972
973         if (ret != ERROR_OK) {
974                 free(telnet_service);
975                 return ret;
976         }
977
978         return ERROR_OK;
979 }
980
981 /* daemon configuration command telnet_port */
982 COMMAND_HANDLER(handle_telnet_port_command)
983 {
984         return CALL_COMMAND_HANDLER(server_pipe_command, &telnet_port);
985 }
986
987 COMMAND_HANDLER(handle_exit_command)
988 {
989         return ERROR_COMMAND_CLOSE_CONNECTION;
990 }
991
992 static const struct command_registration telnet_command_handlers[] = {
993         {
994                 .name = "exit",
995                 .handler = handle_exit_command,
996                 .mode = COMMAND_EXEC,
997                 .usage = "",
998                 .help = "exit telnet session",
999         },
1000         {
1001                 .name = "telnet_port",
1002                 .handler = handle_telnet_port_command,
1003                 .mode = COMMAND_CONFIG,
1004                 .help = "Specify port on which to listen "
1005                         "for incoming telnet connections.  "
1006                         "Read help on 'gdb_port'.",
1007                 .usage = "[port_num]",
1008         },
1009         COMMAND_REGISTRATION_DONE
1010 };
1011
1012 int telnet_register_commands(struct command_context *cmd_ctx)
1013 {
1014         telnet_port = strdup("4444");
1015         return register_commands(cmd_ctx, NULL, telnet_command_handlers);
1016 }
1017
1018 void telnet_service_free(void)
1019 {
1020         free(telnet_port);
1021 }