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