1 /*
2     ettercap -- text only GUI
3 
4     Copyright (C) ALoR & NaGA
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 
20 */
21 
22 #include <ec.h>
23 #include <ec_poll.h>
24 #include <ec_ui.h>
25 #include <ec_threads.h>
26 #include <ec_hook.h>
27 #include <ec_interfaces.h>
28 #include <ec_format.h>
29 #include <ec_plugins.h>
30 #include <ec_text.h>
31 #include <ec_scan.h>
32 #include <ec_mitm.h>
33 
34 #ifdef OS_WINDOWS
35    #include <missing/termios_mingw.h>
36 #else
37    #include <termios.h>
38 #endif
39 
40 /* globals */
41 
42 struct termios old_tc;
43 struct termios new_tc;
44 
45 
46 /* proto */
47 
48 void text_interface(void);
49 static void text_init(void);
50 static void text_cleanup(void);
51 static void text_msg(const char *msg);
52 static void text_error(const char *msg);
53 static void text_fatal_error(const char *msg);
54 static void text_input(const char *title, char *input, size_t n, void (*callback)(void));
55 static void text_help(void);
56 static int text_progress(char *title, int value, int max);
57 static void text_run_plugin(void);
58 static void text_run_filter(void);
59 static void text_stats(void);
60 static void text_stop_cont(void);
61 static void text_hosts_list(void);
62 static void text_profile_list(void);
63 static void text_visualization(void);
64 static void text_redirects(void);
65 
66 /*******************************************/
67 
68 
set_text_interface(void)69 void set_text_interface(void)
70 {
71    struct ui_ops ops;
72 
73    /* wipe the struct */
74    memset(&ops, 0, sizeof(ops));
75 
76    /* register the functions */
77    ops.init = &text_init;
78    ops.start = &text_interface;
79    ops.cleanup = &text_cleanup;
80    ops.msg = &text_msg;
81    ops.error = &text_error;
82    ops.fatal_error = &text_fatal_error;
83    ops.input = &text_input;
84    ops.progress = &text_progress;
85    ops.type = UI_TEXT;
86 
87    ui_register(&ops);
88 
89    /*
90     * add the hook to dispatcher to print the
91     * packets in the right format
92     */
93    hook_add(HOOK_DISPATCHER, text_print_packet);
94 }
95 
96 /*
97  * set the terminal as non blocking
98  */
99 
text_init(void)100 static void text_init(void)
101 {
102    /* taken from readchar.c, by M. Andreoli (2000) */
103 
104    tcgetattr(0, &old_tc);
105    new_tc = old_tc;
106    new_tc.c_lflag &= ~(ECHO | ICANON);   /* raw output */
107    new_tc.c_cc[VTIME] = 1;
108 
109    tcsetattr(0, TCSANOW, &new_tc);
110 }
111 
112 /*
113  * reset to the previous state
114  */
115 
text_cleanup(void)116 static void text_cleanup(void)
117 {
118    /* flush the last user messages */
119    ui_msg_flush(MSG_ALL);
120 
121    fprintf(stdout, "\n");
122 
123    tcsetattr(0, TCSANOW, &old_tc);
124 }
125 
126 /*
127  * print a USER_MSG()
128  */
129 
text_msg(const char * msg)130 static void text_msg(const char *msg)
131 {
132    /* avoid implicit format bugs */
133    fprintf(stdout, "%s", msg);
134    /* allow non buffered messages */
135    fflush(stdout);
136 }
137 
138 
139 /*
140  * print an error
141  */
text_error(const char * msg)142 static void text_error(const char *msg)
143 {
144    /* avoid implicit format bugs */
145    fprintf(stdout, "\nFATAL: %s\n\n", msg);
146    /* allow non buffered messages */
147    fflush(stdout);
148 }
149 
150 
151 /*
152  * handle a fatal error and exit
153  */
text_fatal_error(const char * msg)154 static void text_fatal_error(const char *msg)
155 {
156    /* avoid implicit format bugs */
157    fprintf(stdout, "\n"EC_COLOR_RED"FATAL: "EC_COLOR_END"%s\n\n", msg);
158    /* allow non buffered messages */
159    fflush(stdout);
160 
161    /* restore console settings */
162    tcsetattr(0, TCSANOW, &old_tc);
163 
164    /* exit without calling atexit() */
165    _exit(-1);
166 }
167 
168 
169 /*
170  * display the 'title' and get the 'input' from the user
171  */
text_input(const char * title,char * input,size_t n,void (* callback)(void))172 static void text_input(const char *title, char *input, size_t n, void (*callback)(void))
173 {
174    char *p;
175 
176    /* display the title */
177    fprintf(stdout, "%s", title);
178    fflush(stdout);
179 
180    /* repristinate the buffer input */
181    tcsetattr(0, TCSANOW, &old_tc);
182 
183    /* wipe the buffer */
184    memset(input, 0, n);
185 
186    /* get the user input */
187    fgets(input, n, stdin);
188 
189    /* trim the \n */
190    if ((p = strrchr(input, '\n')) != NULL)
191       *p = '\0';
192    else {
193       /*
194        * eat the input until \n
195        * this will happen if the user has entered
196        * more chars than n
197        */
198       while(getc(stdin) != '\n');
199    }
200 
201    /* disable buffered input */
202    tcsetattr(0, TCSANOW, &new_tc);
203 
204    /*
205     * call the supplied function
206     * the callee is aware of the buffer to be used
207     */
208    if (callback != NULL)
209       callback();
210 }
211 
212 /*
213  * implement the progress bar
214  */
text_progress(char * title,int value,int max)215 static int text_progress(char *title, int value, int max)
216 {
217    float percent;
218    int i;
219 
220    /* variable not used */
221    (void) title;
222 
223    /* calculate the percent */
224    percent = (float)(value)*100/(max);
225 
226    /*
227     * we use stderr to avoid scrambling of
228     * logfile generated by: ./ettercap -T > logfile
229     */
230 
231    switch(value % 4) {
232       case 0:
233          fprintf(stderr, "\r| |");
234       break;
235       case 1:
236          fprintf(stderr, "\r/ |");
237       break;
238       case 2:
239          fprintf(stderr, "\r- |");
240       break;
241       case 3:
242          fprintf(stderr, "\r\\ |");
243       break;
244    }
245 
246    /* fill the bar */
247    for (i=0; i < percent/2; i++)
248       fprintf(stderr, "=");
249 
250    fprintf(stderr, ">");
251 
252    /* fill the empty part of the bar */
253    for(; i < 50; i++)
254       fprintf(stderr, " ");
255 
256    fprintf(stderr, "| %6.2f %%", percent );
257 
258    fflush(stderr);
259 
260    if (value == max) {
261       fprintf(stderr, "\r* |==================================================>| 100.00 %%\n\n");
262       return UI_PROGRESS_FINISHED;
263    }
264 
265    return UI_PROGRESS_UPDATED;
266 }
267 
268 
269 /* the interface */
270 
text_interface(void)271 void text_interface(void)
272 {
273    struct plugin_list *plugin, *tmp;
274 
275    DEBUG_MSG("text_interface");
276 
277    LIST_FOREACH_SAFE(plugin, &EC_GBL_OPTIONS->plugins, next, tmp) {
278       /* check if the specified plugin exists */
279       if (search_plugin(plugin->name) != E_SUCCESS) {
280          plugin->exists = false;
281          USER_MSG("Sorry, plugin '%s' can not be found - skipping!\n\n",
282                plugin->name);
283       }
284 
285    }
286 
287    /* build the list of active hosts */
288    build_hosts_list();
289 
290    /* start the mitm attack */
291    mitm_start();
292 
293    /* start the sniffing method */
294    EXECUTE(EC_GBL_SNIFF->start);
295 
296    /* it is difficult to be interactive while reading from file... */
297    if (!EC_GBL_OPTIONS->read) {
298       USER_MSG("\nText only Interface activated...\n");
299       USER_MSG("Hit 'h' for inline help\n\n");
300    }
301 
302    /* flush all the messages */
303    ui_msg_flush(MSG_ALL);
304 
305    /* if we have to activate a plugin */
306    if (!LIST_EMPTY(&EC_GBL_OPTIONS->plugins)) {
307       /*
308        * execute the plugin and close the interface if
309        * the plugin was not found or it has completed
310        * its execution
311        */
312       LIST_FOREACH_SAFE(plugin, &EC_GBL_OPTIONS->plugins, next, tmp) {
313           if (plugin->exists && text_plugin(plugin->name) != PLUGIN_RUNNING)
314              /* skip plugin */
315              USER_MSG("Plugin '%s' can not be started - skipping!\n\n",
316                    plugin->name);
317       }
318    }
319 
320    /* neverending loop for user input */
321    LOOP {
322 
323       CANCELLATION_POINT();
324 
325       /* if there is a pending char to be read */
326       if (ec_poll_in(fileno(stdin), 10) || ec_poll_buffer(EC_GBL_OPTIONS->script)) {
327 
328          char ch = 0;
329 
330          /* get the input from the stdin or the buffer */
331          if (ec_poll_buffer(EC_GBL_OPTIONS->script))
332             ch = getchar_buffer(&EC_GBL_OPTIONS->script);
333          else
334             ch = getchar();
335 
336          switch(ch) {
337             case 'H':
338             case 'h':
339                text_help();
340                break;
341             case 'P':
342             case 'p':
343                text_run_plugin();
344                break;
345             case 'F':
346             case 'f':
347                text_run_filter();
348                break;
349             case 'S':
350             case 's':
351                text_stats();
352                break;
353             case 'L':
354             case 'l':
355                text_hosts_list();
356                break;
357             case 'V':
358             case 'v':
359                text_visualization();
360                break;
361             case 'O':
362             case 'o':
363                text_profile_list();
364                break;
365             case 'C':
366             case 'c':
367                text_connections();
368                break;
369             case ' ':
370                text_stop_cont();
371                break;
372             case 'R':
373             case 'r':
374                text_redirects();
375                break;
376             case 'Q':
377             case 'q':
378                USER_MSG("Closing text interface...\n\n");
379                return;
380                break;
381          }
382 
383       }
384 
385       /* print pending USER_MSG messages */
386       ui_msg_flush(INT_MAX);
387 
388    }
389 
390    /* NOT REACHED */
391 
392 }
393 
394 /* print the help screen */
395 
text_help(void)396 static void text_help(void)
397 {
398    fprintf(stderr, "\nInline help:\n\n");
399    fprintf(stderr, " [vV]      - change the visualization mode\n");
400    fprintf(stderr, " [pP]      - activate a plugin\n");
401    fprintf(stderr, " [fF]      - (de)activate a filter\n");
402    fprintf(stderr, " [lL]      - print the hosts list\n");
403    fprintf(stderr, " [oO]      - print the profiles list\n");
404    fprintf(stderr, " [cC]      - print the connections list\n");
405    fprintf(stderr, " [rR]      - adjust SSL intercept rules\n");
406    fprintf(stderr, " [sS]      - print interfaces statistics\n");
407    fprintf(stderr, " [<space>] - stop/cont printing packets\n");
408    fprintf(stderr, " [qQ]      - quit\n\n");
409 }
410 
411 /*
412  * stops or continues to print packets
413  * it is another way to control the -q option
414  */
415 
text_stop_cont(void)416 static void text_stop_cont(void)
417 {
418    /* revert the quiet option */
419    EC_GBL_OPTIONS->quiet = (EC_GBL_OPTIONS->quiet) ? 0 : 1;
420 
421    if (EC_GBL_OPTIONS->quiet)
422       fprintf(stderr, "\nPacket visualization stopped...\n");
423    else
424       fprintf(stderr, "\nPacket visualization restarted...\n");
425 }
426 
427 
428 /*
429  * display a list of plugin, and prompt
430  * the user for a plugin to run.
431  */
text_run_plugin(void)432 static void text_run_plugin(void)
433 {
434    char name[20];
435    int restore = 0;
436    char *p;
437 
438 #ifndef HAVE_PLUGINS
439    fprintf(stderr, "Plugin support was not compiled in...");
440    return;
441 #endif
442 
443    /* there are no plugins */
444    if (text_plugin("list") == -E_NOTFOUND)
445       return;
446 
447    /* stop the visualization while the plugin interface is running */
448    if (!EC_GBL_OPTIONS->quiet) {
449       text_stop_cont();
450       restore = 1;
451    }
452 
453    /* print the messages created by text_plugin */
454    ui_msg_flush(MSG_ALL);
455 
456    /* repristinate the buffer input */
457    tcsetattr(0, TCSANOW, &old_tc);
458 
459    fprintf(stdout, "Plugin name (0 to quit): ");
460    fflush(stdout);
461 
462    /* get the user input */
463    fgets(name, 20, stdin);
464 
465    /* trim the \n */
466    if ((p = strrchr(name, '\n')) != NULL)
467       *p = '\0';
468 
469    /* disable buffered input */
470    tcsetattr(0, TCSANOW, &new_tc);
471 
472    if (!strcmp(name, "0")) {
473       if (restore)
474          text_stop_cont();
475       return;
476    }
477 
478    /* run the plugin */
479    text_plugin(name);
480 
481    /* continue the visualization */
482    if (restore)
483       text_stop_cont();
484 
485 }
486 
487 
text_print_filter_cb(struct filter_list * l,void * arg)488 static int text_print_filter_cb(struct filter_list *l, void *arg) {
489    int *i = (int *)arg;
490    fprintf(stdout, "[%d (%d)]: %s\n", (*i)++, l->enabled, l->name);
491    return 1;
492 }
493 
text_toggle_filter_cb(struct filter_list * l,void * arg)494 static int text_toggle_filter_cb(struct filter_list *l, void *arg) {
495    int *number = (int *)arg;
496    if (!--(*number)) {
497       /* we reached the item */
498       l->enabled = ! l->enabled;
499       return 0; /* no need to traverse the list any further */
500    }
501    return 1;
502 }
503 
504 /*
505  * display the list of loaded filters and
506  * allow the user to enable or disable them
507  */
text_run_filter(void)508 static void text_run_filter(void) {
509    int restore = 0;
510    /* stop the visualization while the plugin interface is running */
511    if (!EC_GBL_OPTIONS->quiet) {
512       text_stop_cont();
513       restore = 1;
514    }
515    ui_msg_flush(MSG_ALL);
516 
517    fprintf(stderr, "\nLoaded etterfilter scripts:\n\n");
518    while(1) {
519       char input[20];
520       int i = 1;
521       int number = -1;
522 
523       /* repristinate the buffer input */
524       tcsetattr(0, TCSANOW, &old_tc);
525 
526       filter_walk_list( text_print_filter_cb, &i );
527       int c;
528       do {
529          fprintf(stdout, "\nEnter a number to enable/disable filter (0 to quit): ");
530          /* get the user input */
531          fgets(input, 19, stdin);
532          number = -1;
533          c=sscanf(input, "%d", &number);
534          if(c!=1)
535             fprintf(stdout, "\nYou need to enter a number, please try again.");
536       } while(c!=1);
537       if (number == 0) {
538          break;
539       } else if (number > 0) {
540          filter_walk_list( text_toggle_filter_cb, &number );
541       }
542    };
543 
544    /* disable buffered input */
545    tcsetattr(0, TCSANOW, &new_tc);
546 
547    /* continue the visualization */
548    if (restore)
549       text_stop_cont();
550 }
551 
552 /*
553  * print the interface statistics
554  */
text_stats(void)555 static void text_stats(void)
556 {
557    DEBUG_MSG("text_stats (pcap) : %" PRIu64 " %" PRIu64 " %" PRIu64,
558                                                 EC_GBL_STATS->ps_recv,
559                                                 EC_GBL_STATS->ps_drop,
560                                                 EC_GBL_STATS->ps_ifdrop);
561    DEBUG_MSG("text_stats (BH) : [%lu][%lu] p/s -- [%lu][%lu] b/s",
562          EC_GBL_STATS->bh.rate_adv, EC_GBL_STATS->bh.rate_worst,
563          EC_GBL_STATS->bh.thru_adv, EC_GBL_STATS->bh.thru_worst);
564 
565    DEBUG_MSG("text_stats (TH) : [%lu][%lu] p/s -- [%lu][%lu] b/s",
566          EC_GBL_STATS->th.rate_adv, EC_GBL_STATS->th.rate_worst,
567          EC_GBL_STATS->th.thru_adv, EC_GBL_STATS->th.thru_worst);
568 
569    DEBUG_MSG("text_stats (queue) : %lu %lu", EC_GBL_STATS->queue_curr, EC_GBL_STATS->queue_max);
570 
571 
572    fprintf(stdout, "\n Received packets    : %8" PRIu64 "\n", EC_GBL_STATS->ps_recv);
573    fprintf(stdout,   " Dropped packets     : %8" PRIu64 "  %.2f %%\n", EC_GBL_STATS->ps_drop,
574          (EC_GBL_STATS->ps_recv) ? (float)EC_GBL_STATS->ps_drop * 100 / EC_GBL_STATS->ps_recv : 0 );
575    fprintf(stdout,   " Forwarded           : %8" PRIu64 "  bytes: %8" PRIu64 "\n\n",
576            EC_GBL_STATS->ps_sent, EC_GBL_STATS->bs_sent);
577 
578    fprintf(stdout,   " Current queue len   : %lu/%lu\n", EC_GBL_STATS->queue_curr, EC_GBL_STATS->queue_max);
579    fprintf(stdout,   " Sampling rate       : %d\n\n", EC_GBL_CONF->sampling_rate);
580 
581    fprintf(stdout,   " Bottom Half received packet : pck: %8" PRIu64 "  byte: %8" PRIu64 "\n",
582          EC_GBL_STATS->bh.pck_recv, EC_GBL_STATS->bh.pck_size);
583    fprintf(stdout,   " Top Half received packet    : pck: %8" PRIu64 "  byte: %8" PRIu64 "\n",
584          EC_GBL_STATS->th.pck_recv, EC_GBL_STATS->th.pck_size);
585    fprintf(stdout,   " Interesting packets         : %.2f %%\n\n",
586          (EC_GBL_STATS->bh.pck_recv) ? (float)EC_GBL_STATS->th.pck_recv * 100 / EC_GBL_STATS->bh.pck_recv : 0 );
587 
588    fprintf(stdout,   " Bottom Half packet rate : worst: %8lu  adv: %8lu p/s\n",
589          EC_GBL_STATS->bh.rate_worst, EC_GBL_STATS->bh.rate_adv);
590    fprintf(stdout,   " Top Half packet rate    : worst: %8lu  adv: %8lu p/s\n\n",
591          EC_GBL_STATS->th.rate_worst, EC_GBL_STATS->th.rate_adv);
592 
593    fprintf(stdout,   " Bottom Half throughput  : worst: %8lu  adv: %8lu b/s\n",
594          EC_GBL_STATS->bh.thru_worst, EC_GBL_STATS->bh.thru_adv);
595    fprintf(stdout,   " Top Half throughput     : worst: %8lu  adv: %8lu b/s\n\n",
596          EC_GBL_STATS->th.thru_worst, EC_GBL_STATS->th.thru_adv);
597 }
598 
599 /*
600  * prints the hosts list
601  */
602 
text_hosts_list(void)603 static void text_hosts_list(void)
604 {
605    struct hosts_list *hl;
606    char ip[MAX_ASCII_ADDR_LEN];
607    char mac[MAX_ASCII_ADDR_LEN];
608    int i = 1;
609 
610    fprintf(stdout, "\n\nHosts list:\n\n");
611 
612    /* print the list */
613    LIST_FOREACH(hl, &EC_GBL_HOSTLIST, next) {
614 
615       ip_addr_ntoa(&hl->ip, ip);
616       mac_addr_ntoa(hl->mac, mac);
617 
618       if (hl->hostname)
619          fprintf(stdout, "%d)\t%s\t%s\t%s\n", i++, ip, mac, hl->hostname);
620       else
621          fprintf(stdout, "%d)\t%s\t%s\n", i++, ip, mac);
622 
623    }
624 
625    fprintf(stdout, "\n\n");
626 
627 }
628 
629 /*
630  * prompt the user for the visualization mode
631  */
632 
text_visualization(void)633 static void text_visualization(void)
634 {
635    char format[16];
636    int restore = 0;
637 
638    /* stop the visualization while the plugin interface is running */
639    if (!EC_GBL_OPTIONS->quiet) {
640       text_stop_cont();
641       restore = 1;
642    }
643 
644    /* repristinate the buffere input */
645    tcsetattr(0, TCSANOW, &old_tc);
646 
647    fprintf(stdout, "\n\nVisualization format: ");
648    fflush(stdout);
649 
650    scanf("%15s", format);
651 
652    /* disable buffered input */
653    tcsetattr(0, TCSANOW, &new_tc);
654 
655    /* set the format */
656    set_format(format);
657 
658    /* continue the packet printing */
659    if (restore)
660       text_stop_cont();
661 }
662 
663 
664 /*
665  * enter the profile interface
666  */
667 
text_profile_list(void)668 static void text_profile_list(void)
669 {
670    int restore = 0;
671 
672    /* stop the visualization while the profiles interface is running */
673    if (!EC_GBL_OPTIONS->quiet) {
674       text_stop_cont();
675       restore = 1;
676    }
677 
678    /* execute the profiles interface */
679    text_profiles();
680 
681    /* continue the visualization */
682    if (restore)
683       text_stop_cont();
684 }
685 
686 /*
687  * print all redirect rules and ask user to add or delete
688  */
text_redirects(void)689 static void text_redirects(void)
690 {
691    char input[20];
692    int restore = 0, num, ret;
693    char *p, cmd;
694 
695 
696    /* print registered entries */
697    text_redirect_print();
698 
699    /* stop the virtualization while the redirect interface is running */
700    if (!EC_GBL_OPTIONS->quiet) {
701       text_stop_cont();
702       restore = 1;
703    }
704    /* print all pending user messages */
705    ui_msg_flush(MSG_ALL);
706 
707    tcsetattr(0, TCSANOW, &old_tc);
708 
709    /* print instructions */
710    fprintf(stdout, "'d <number>' to delete or 'i' to insert new redirect "
711          "(0 to quit): ");
712    fflush(stdout);
713 
714    /* get user input */
715    fgets(input, 20, stdin);
716 
717    do {
718       /* remote trailing line feed */
719       if ((p = strrchr(input, '\n')) != NULL)
720          *p = 0;
721 
722       ret = sscanf(input, "%c %d", &cmd, &num);
723 
724       if (ret == 1 && tolower(cmd) == 'i') {
725          text_redirect_add();
726 
727          /* print registered entries */
728          text_redirect_print();
729 
730 
731       }
732       else if (ret == 2 && tolower(cmd) == 'd') {
733          text_redirect_del(num);
734 
735          /* print registered entries */
736          text_redirect_print();
737       }
738       else if (!strcmp(input, "0") || !strcmp(input, "exit"))
739          break;
740 
741       else
742          INSTANT_USER_MSG("Invalid input\n");
743 
744       /* print instructions */
745       fprintf(stdout, "'d <number>' to delete or 'i' to insert new redirect "
746             "(0 to quit): ");
747       fflush(stdout);
748 
749    } while (fgets(input, 20, stdin) != NULL);
750 
751 
752    /* disable buffered input */
753    tcsetattr(0, TCSANOW, &new_tc);
754 
755    if (restore)
756       text_stop_cont();
757 }
758 /* EOF */
759 
760 // vim:ts=3:expandtab
761 
762