From 3c6aa47f331c10a112fb11497d34625d9d38845b Mon Sep 17 00:00:00 2001 From: Packit Service Date: Dec 09 2020 17:27:48 +0000 Subject: Prepare for a new update Reverting patches so we can apply the latest update and changes can be seen in the spec file and sources. --- diff --git a/Makefile b/Makefile index f6eb8ab..68db0fc 100644 --- a/Makefile +++ b/Makefile @@ -70,5 +70,6 @@ clean: uninstall: /bin/rm -rf $(SBIN)/iptstate + /bin/rm -rf $(MAN)/man1/iptstate.1 /bin/rm -rf $(MAN)/man8/iptstate.8 diff --git a/Makefile.man8 b/Makefile.man8 deleted file mode 100644 index 68db0fc..0000000 --- a/Makefile.man8 +++ /dev/null @@ -1,75 +0,0 @@ -# -# Copyright (C) 2002 - present Phil Dibowitz. -# -# See iptstate.cc for copyright info -# -# Makefile for IPTState -# - -### USERS CAN CHANGE STUFF HERE - -PREFIX?=/usr -SBIN?=$(PREFIX)/sbin -INSTALL?=/usr/bin/install -STRIP?=/usr/bin/strip -MAN?=$(PREFIX)/share/man - -### ADVANCED USERS AND PACKAGERS MIGHT WANT TO CHANGE THIS - -CXX?= g++ -CXXFLAGS?= -g -Wall -O2 -CXXFILES?= iptstate.cc - -# THIS IS FOR NORMAL COMPILATION -LIBS?= -lncurses -lnetfilter_conntrack -CPPFLAGS= - -### YOU SHOULDN'T NEED TO CHANGE ANYTHING BELOW THIS - -all: iptstate - - -iptstate: iptstate.cc Makefile - @\ - echo "+------------------------------------------------------------+" ;\ - echo "| Welcome to IP Tables State by Phil Dibowitz |" ;\ - echo "| |" ;\ - echo "| PLEASE read the LICENSE and the README for important info. |" ;\ - echo "| |" ;\ - echo "| You may also wish to read the README for install info, |" ;\ - echo "| the WISHLIST for upcoming features, BUGS for known bugs |" ;\ - echo "| and info on bug reports, and the Changelog to find out |" ;\ - echo "| what's new. |" ;\ - echo "| |" ;\ - echo "| Let's compile... |" ;\ - echo "+------------------------------------------------------------+" ;\ - echo ""; - - $(CXX) $(CXXFLAGS) $(CXXFILES) -o iptstate $(LIBS) $(CPPFLAGS) - @touch iptstate - - @\ - echo "" ;\ - echo "All done. Do 'make install' as root and you should be set to go!" ;\ - echo "" - -strip: iptstate - $(STRIP) iptstate - @touch strip - - -install: - $(INSTALL) -D --mode=755 iptstate $(SBIN)/iptstate - $(INSTALL) -D --mode=444 iptstate.8 $(MAN)/man8/iptstate.8 - - -clean: - /bin/rm -rf iptstate - /bin/rm -rf strip - - -uninstall: - /bin/rm -rf $(SBIN)/iptstate - /bin/rm -rf $(MAN)/man1/iptstate.1 - /bin/rm -rf $(MAN)/man8/iptstate.8 - diff --git a/iptstate.cc b/iptstate.cc index e366da2..78e7e16 100644 --- a/iptstate.cc +++ b/iptstate.cc @@ -2179,6 +2179,7 @@ int main(int argc, char *argv[]) // Command Line Arguments while ((tmpint = getopt_long(argc, argv, "Cd:D:hlmcoLfpR:r1b:s:S:tv", long_options, &option_index)) != EOF) { + printf("loop: %d\n", tmpint); switch (tmpint) { case 0: /* Apparently this test is needed?! Seems lame! */ @@ -2407,10 +2408,7 @@ int main(int argc, char *argv[]) prompt = "Counters requested, but not enabled in the"; prompt += " kernel!"; flags.counters = 0; - if (flags.single) - cerr << prompt << endl; - else - c_warn(mainwin, prompt, flags); + c_warn(mainwin, prompt, flags); } // Sort our table diff --git a/iptstate.cc.no_debug b/iptstate.cc.no_debug deleted file mode 100644 index 78e7e16..0000000 --- a/iptstate.cc.no_debug +++ /dev/null @@ -1,2816 +0,0 @@ -/* - * vim:textwidth=80:tabstop=2:shiftwidth=2:expandtab:ai - * - * iptstate.cc - * IPTables State - * - * ----------------------------------- - * - * Copyright (C) 2002 - present Phil Dibowitz - * - * This software is provided 'as-is', without any express or - * implied warranty. In no event will the authors be held - * liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it - * and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * ----------------------------------- - * - * The idea of statetop comes from IP Filter by Darren Reed. - * - * This package's main purpose is to provide a state-top type - * interface for IP Tables. I've added in the "single run" - * option since there's no nice way to do that either. - * - * NOTE: If you are planning on packaging and/or submitting my software for/to - * a Linux distribution, I would appreciate a heads up. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// There are no C++-ified versions of these. -#include -#include -extern "C" { - #include -}; -#include -#include -#include -using namespace std; - -#define VERSION "2.2.6" -/* - * MAXCONS is set to 16k, the default number of states in iptables. Generally - * speaking the ncurses pad is this many lines long, but since ncurses - * uses a short for their dimensions, a pad can never be longer than 32767. - * Thus we define both of these values and NLINES as the lesser of the two. - */ -#define MAXCONS 16384 -#define MAXLINES 32767 -#if MAXCONS < MAXLINES - #define NLINES MAXCONS -#else - #define NLINES MAXLINES -#endif -#define MAXFIELDS 20 -// This is the default format string if we don't dynamically determine it -#define DEFAULT_FORMAT "%-21s %-21s %-7s %-12s %-9s\n" -// The following MUST be the same as the above -#define DEFAULT_SRC 21 -#define DEFAULT_DST 21 -#define DEFAULT_PROTO 7 -#define DEFAULT_STATE 12 -#define DEFAULT_TTL 9 -// This is the format string for the "totals" line, always. -#define TOTALS_FORMAT \ - "Total States: %i -- TCP: %i UDP: %i ICMP: %i Other: %i (Filtered: %i)\n" -// Options for truncating from the front or the back -#define TRUNC_FRONT 0 -#define TRUNC_END 1 -// maxlength for string we pass to inet_ntop() -#define NAMELEN 100 -// Sorting options -#define SORT_SRC 0 -#define SORT_SRC_PT 1 -#define SORT_DST 2 -#define SORT_DST_PT 3 -#define SORT_PROTO 4 -#define SORT_STATE 5 -#define SORT_TTL 6 -#define SORT_BYTES 7 -#define SORT_PACKETS 8 -#define SORT_MAX 8 - -/* - * GLOBAL CONSTANTS - */ - -/* - * GLOBAL VARS - */ -int sort_factor = 1; -bool need_resize = false; - -/* shameless stolen from libnetfilter_conntrack_tcp.c */ -static const char *states[] = { - "NONE", - "SYN_SENT", - "SYN_RECV", - "ESTABLISHED", - "FIN_WAIT", - "CLOSE_WAIT", - "LAST_ACK", - "TIME_WAIT", - "CLOSE", - "LISTEN" -}; - - -/* - * STRUCTS - */ -// One state-table entry -struct tentry_t { - string proto, state, ttl, sname, dname, spname, dpname; - in6_addr src, dst; - uint8_t family; - unsigned long srcpt, dstpt, bytes, packets, s; -}; -// x/y of the terminal window -struct screensize_t { - unsigned int x, y; -}; -// Struct 'o flags -struct flags_t { - bool single, totals, lookup, skiplb, staticsize, skipdns, tag_truncate, - filter_src, filter_dst, filter_srcpt, filter_dstpt, noscroll, nocolor, - counters; -}; -// Struct 'o counters -struct counters_t { - unsigned int total, tcp, udp, icmp, other, skipped; -}; -// Various filters to be applied pending the right flags in flags_t -struct filters_t { - in6_addr src, dst; - uint8_t srcfam, dstfam; - unsigned long srcpt, dstpt; -}; -// The max-length of fields in the stable table -struct max_t { - unsigned int src, dst, proto, state, ttl; - unsigned long bytes, packets; -}; -struct hook_data { - vector *stable; - flags_t *flags; - max_t *max; - counters_t *counts; - const filters_t *filters; -}; - - -/* - * GENERAL HELPER FUNCTIONS - */ - -/* - * split a string into two strings based on the first occurance - * of any character - */ -void split(char s, string line, string &p1, string &p2) -{ - int pos = line.find(s); - p1 = line.substr(0, pos); - p2 = line.substr(pos+1, line.size()-pos); -} - -/* - * split a string into an array of strings based on - * any character - */ -void splita(char s, string line, vector &result) -{ - int pos, size; - int i=0; - string temp, temp1; - temp = line; - while ((temp.find(s) != string::npos) && (i < MAXFIELDS-1)) { - pos = temp.find(s); - result[i] = temp.substr(0, pos); - size = temp.size(); - temp = temp.substr(pos+1, size-pos-1); - if (result[i] != "") { - i++; - } - } - result[i] = temp; -} - -/* - * This determines the length of an integer (i.e. number of digits) - */ -unsigned int digits(unsigned long x) -{ - return (unsigned int) floor(log10((double)x))+1; -} - -/* - * Check to ensure an IP is valid - */ -bool check_ip(const char *arg, in6_addr *addr, uint8_t *family) -{ - int ret; - ret = inet_pton(AF_INET6, arg, addr); - if (ret) { - *family = AF_INET6; - return true; - } - ret = inet_pton(AF_INET, arg, addr); - if (ret) { - *family = AF_INET; - return true; - } - return false; -} - -/* - * The help - */ -void version() -{ - cout << "IPTables State Top Version " << VERSION << endl << endl; -} - -void help() -{ - cout << "IPTables State Top Version " << VERSION << endl; - cout << "Usage: iptstate []\n\n"; - cout << " -c, --no-color\n"; - cout << "\tToggle color-code by protocol\n\n"; - cout << " -C, --counters\n"; - cout << "\tToggle display of bytes/packets counters\n\n"; - cout << " -d, --dst-filter \n"; - cout << "\tOnly show states with a destination of \n"; - cout << "\tNote, that this must be an IP, hostname matching is" - << " not yet supported.\n\n"; - cout << " -D --dstpt-filter \n"; - cout << "\tOnly show states with a destination port of \n\n"; - cout << " -h, --help\n"; - cout << "\tThis help message\n\n"; - cout << " -l, --lookup\n"; - cout << "\tShow hostnames instead of IP addresses. Enabling this will also" - << " enable\n\t-L to prevent an ever-growing number of DNS requests.\n\n"; - cout << " -m, --mark-truncated\n"; - cout << "\tMark truncated hostnames with a '+'\n\n"; - cout << " -o, --no-dynamic\n"; - cout << "\tToggle dynamic formatting\n\n"; - cout << " -L, --no-dns\n"; - cout << "\tSkip outgoing DNS lookup states\n\n"; - cout << " -f, --no-loopback\n"; - cout << "\tFilter states on loopback\n\n"; - cout << " -p, --no-scroll\n"; - cout << "\tNo scrolling (don't use a \"pad\")\n\n"; - cout << " -r, --reverse\n"; - cout << "\tReverse sort order\n\n"; - cout << " -R, --rate \n"; - cout << "\tRefresh rate, followed by rate in seconds\n"; - cout << "\tNote: For statetop, not applicable for -s\n\n"; - cout << " -1, --single\n"; - cout << "\tSingle run (no curses)\n\n"; - cout << " -b, --sort \n"; - cout << "\tThis determines what column to sort by. Options:\n"; - cout << "\t d: Destination IP (or Name)\n"; - cout << "\t p: Protocol\n"; - cout << "\t s: State\n"; - cout << "\t t: TTL\n"; - cout << "\t b: Bytes\n"; - cout << "\t P: Packets\n"; - cout << "\tTo sort by Source IP (or Name), don't use -b.\n"; - cout << "\tNote that bytes/packets are only available when" - << " supported in the kernel,\n"; - cout << "\tand enabled with -C\n\n"; - cout << " -s, --src-filter \n"; - cout << "\tOnly show states with a source of \n"; - cout << "\tNote, that this must be an IP, hostname matching is" - << " not yet supported.\n\n"; - cout << " -S, --srcpt-filter \n"; - cout << "\tOnly show states with a source port of \n\n"; - cout << " -t, --totals\n"; - cout << "\tToggle display of totals\n\n"; - cout << "See man iptstate(8) or the interactive help for more" - << " information.\n"; - exit(0); -} - -/* - * Resolve hostnames - */ -void resolve_host(const uint8_t &family, const in6_addr &ip, string &name) -{ - struct hostent *hostinfo = NULL; - - if ((hostinfo = gethostbyaddr((char *)&ip, sizeof(ip), family)) != NULL) { - name = hostinfo->h_name; - } else { - char str[NAMELEN]; - name = inet_ntop(family, (void *)&ip, str, NAMELEN-1) ; - } -} - -void resolve_port(const unsigned int &port, string &name, const string &proto) -{ - struct servent *portinfo = NULL; - - if ((portinfo = getservbyport(htons(port), proto.c_str())) != NULL) { - name = portinfo->s_name; - } else { - ostringstream buf; - buf.str(""); - buf << port; - name = buf.str(); - } -} - -/* - * If lookup mode is on, we lookup the names and put them in the structure. - * - * If lookup mode is not on, we generate strings of the addresses and put - * those in the structure. - * - * Finally, we update the max_t structure. - * - * NOTE: We stringify addresses largely because in the IPv6 case we need - * to treat them like truncate-able strings. - */ -void stringify_entry(tentry_t *entry, max_t &max, const flags_t &flags) -{ - unsigned int size = 0; - ostringstream buffer; - char tmp[NAMELEN]; - - bool have_port = entry->proto == "tcp" || entry->proto == "udp"; - if (!have_port) { - entry->spname = entry->dpname = ""; - } - - if (flags.lookup) { - resolve_host(entry->family, entry->src, entry->sname); - resolve_host(entry->family, entry->dst, entry->dname); - if (have_port) { - resolve_port(entry->srcpt, entry->spname, entry->proto); - resolve_port(entry->dstpt, entry->dpname, entry->proto); - } - } else { - buffer << inet_ntop(entry->family, (void*)&(entry->src), tmp, NAMELEN-1); - entry->sname = buffer.str(); - buffer.str(""); - buffer << inet_ntop(entry->family, (void*)&(entry->dst), tmp, NAMELEN-1); - entry->dname = buffer.str(); - buffer.str(""); - if (have_port) { - buffer << entry->srcpt; - entry->spname = buffer.str(); - buffer.str(""); - buffer << entry->dstpt; - entry->dpname = buffer.str(); - buffer.str(""); - } - } - - size = entry->sname.size() + entry->spname.size() + 1; - if (size > max.src) - max.src = size; - - size = entry->dname.size() + entry->dpname.size() + 1; - if (size > max.dst) - max.dst = size; -} - - -/* - * SORT FUNCTIONS - */ -bool src_sort(tentry_t *one, tentry_t *two) -{ - /* - * memcmp() will properly sort v4 or v6 addresses, but not cross-family - * (presumably because of garbage in the top 96 bytes when you store - * a v4 address in a in6_addr), so we sort by family and then memcmp() - * within the same family. - */ - if (one->family == two->family) { - return memcmp(one->src.s6_addr, two->src.s6_addr, 16) * sort_factor < 0; - } else if (one->family == AF_INET) { - return sort_factor > 0; - } else { - return sort_factor < 0; - } -} - -bool srcname_sort(tentry_t *one, tentry_t *two) -{ - return one->sname.compare(two->sname) * sort_factor < 0; -} - -bool dst_sort(tentry_t *one, tentry_t *two) -{ - // See src_sort() for details - if (one->family == two->family) { - return memcmp(one->dst.s6_addr, two->dst.s6_addr, 16) * sort_factor < 0; - } else if (one->family == AF_INET) { - return sort_factor > 0; - } else { - return sort_factor < 0; - } -} - -bool dstname_sort(tentry_t *one, tentry_t *two) -{ - return one->dname.compare(two->dname) * sort_factor < 0; -} - -/* - * int comparison that takes care of sort_factor - * used for ports, bytes, etc... - */ -bool cmpint(int one, int two) -{ - return (sort_factor > 0) ? one < two : one > two; -} - -bool srcpt_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->srcpt, two->srcpt); -} - -bool dstpt_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->dstpt, two->dstpt); -} - -bool proto_sort(tentry_t *one, tentry_t *two) -{ - return one->proto.compare(two->proto) * sort_factor < 0; -} - -bool state_sort(tentry_t *one, tentry_t *two) -{ - return one->state.compare(two->state) * sort_factor < 0; -} - -bool ttl_sort(tentry_t *one, tentry_t *two) -{ - return one->ttl.compare(two->ttl) * sort_factor < 0; -} - -bool bytes_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->bytes, two->bytes); -} - -bool packets_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->packets, two->packets); -} - -/* - * CURSES HELPER FUNCTIONS - */ - -/* - * Finish-up for curses environment - */ -void end_curses() -{ - curs_set(1); - nocbreak(); - endwin(); - cout << endl; -} - -/* - * SIGWINCH signal handler. - */ -void winch_handler(int sig) -{ - sigset_t mask_set; - sigset_t old_set; - // Reset signal handler - signal(28, winch_handler); - // ignore this signal for a bit - sigfillset(&mask_set); - sigprocmask(SIG_SETMASK, &mask_set, &old_set); - - need_resize = true; -} - -/* - * SIGKILL signal handler - */ -void kill_handler(int sig) -{ - end_curses(); - printf("Caught signal %d, cleaning up.\n", sig); - exit(0); -} - -/* - * Start-up for curses environment - * - * NOTE: That by default we create a pad. A pad is a special type of window that - * can be bigger than the screen. See the comments in interactive_help() - * below for how to use it and how it works. - * - * However, pad's lack the double-buffering and other features of standard - * ncurses windows and thus can appear slower. Thus we allow the user to - * downgrade to standard windows if they choose. See the comments - * switch_scroll() for more details. - * - */ -static WINDOW* start_curses(flags_t &flags) -{ - int y, x; - initscr(); - cbreak(); - noecho(); - halfdelay(1); - - /* - * If we're starting curses, we care about SIGWNCH, SIGINT, and SIGTERM - * so this seems like as good a place as any to setup our signal - * handler. - */ - // Resize - signal(28, winch_handler); - // Shutdown - signal(2, kill_handler); - signal(15, kill_handler); - - if (has_colors()) { - start_color(); - // for tcp - init_pair(1, COLOR_GREEN, COLOR_BLACK); - // for udp - init_pair(2, COLOR_YELLOW, COLOR_BLACK); - // for icmp - init_pair(3, COLOR_RED, COLOR_BLACK); - // for prompts - init_pair(4, COLOR_BLACK, COLOR_RED); - // for the currently selected row - init_pair(5, COLOR_BLACK, COLOR_GREEN); - init_pair(6, COLOR_BLACK, COLOR_YELLOW); - init_pair(7, COLOR_BLACK, COLOR_RED); - } else { - flags.nocolor = true; - } - - if (!flags.noscroll) { - getmaxyx(stdscr, y, x); - return newpad(NLINES, x); - } - return stdscr; -} - -/* - * Figure out the best way to get the screensize_t, and then do it - */ -screensize_t get_size(const bool &single) -{ - int maxx = 0, maxy = 0; - if (!single) { - getmaxyx(stdscr, maxy, maxx); - } else { - maxx = 72; - if (getenv("COLS")) - maxx=atoi(getenv("COLS")); - } - - screensize_t a; - a.x = maxx; - a.y = maxy; - - return a; -} - -/* - * Error function for screen being too small. - */ -void term_too_small() -{ - end_curses(); - cout << "I'm sorry, your terminal must be atleast 72 columns" - << "wide to run iptstate\n"; - exit(3); -} - -/* - * This is one of those "well, I should impliment it to be complete, but - * I doubt it'll get used very often features." It was a nice-thing-to-do - * to impliment the ability for iptstate to use stdscr instead of a pad - * as this provides the doulbe-buffering and other features that pads - * do not. This is probably useful to a small subset of users. It's pretty - * unlikely people will want to interactively want to change this during - * runtime, but since I implimented noscroll, it's only proper to impliment - * interactive toggling. - * - * TECH NOTE: - * This is just a note for myself so I remember why this is the way it is. - * - * The syntax WINDOW *&mainwin is right, thought it's doing what you'd - * expect WINDOW &*mainwin to do... except that's invalid. So it's just a - * &foo pass on a WINDOW*. - */ -void switch_scroll(flags_t &flags, WINDOW *&mainwin) -{ - int x, y; - if (flags.noscroll) { - getmaxyx(stdscr, y, x); - // remove stuff from the bottom window - erase(); - // build pad - wmove(mainwin, 0, 0); - mainwin = newpad(NLINES, x); - wmove(mainwin, 0, 0); - keypad(mainwin,1); - halfdelay(1); - } else { - // delete pad - delwin(mainwin); - mainwin = stdscr; - keypad(mainwin,1); - halfdelay(1); - } - - flags.noscroll = !flags.noscroll; -} - -/* - * Prompt the user for something, and get an answer. - */ -void get_input(WINDOW *win, string &input, const string &prompt, - const flags_t &flags) -{ - - /* - * This function is here so that we can prompt and get an answer - * and the user can get an echo of what they're inputting. This is - * already a non-straight-forward thing to do in cbreak() mode, but - * it turns out that using pads makes it even more difficult. - * - * It's worth noting that I tried doin a simple waddch() and then - * prefresh as one would expect, but it didn't echo the chars. - * Because we're using pads I have to do a waddchar() and then - * a prefresh(). - * - * Note, that the documentation says that if we're using waddchar() - * we shouldn't need any refresh, but it doesn't echo without it. - * This is probably because waddch() calls wrefresh() instead of - * prefresh(). - */ - - input = ""; - int x, y; - getmaxyx(stdscr, y, x); - WINDOW *cmd = subpad(win, 1, x, 0, 0); - if (!flags.nocolor) - wattron(cmd, COLOR_PAIR(4)); - keypad(cmd, true); - wprintw(cmd, prompt.c_str()); - wclrtoeol(cmd); - prefresh(cmd, 0, 0, 0, 0, 0, x); - - - int ch; - int charcount = 0; - echo(); - nodelay(cmd,0); - - while (1) { - ch = wgetch(cmd); - switch (ch) { - case '\n': - // 7 is ^G - case 7: - if (ch == 7) - input = ""; - if (!flags.nocolor) - wattroff(cmd, COLOR_PAIR(4)); - delwin(cmd); - noecho(); - wmove(win, 0, 0); - return; - break; - // 8 is shift-backspace - just incase - case KEY_BACKSPACE: - case 8: - if (charcount > 0) { - input = input.substr(0, input.size()-1); - wechochar(cmd, '\b'); - wechochar(cmd, ' '); - wechochar(cmd, '\b'); - charcount--; - } - break; - case ERR: - continue; - break; - default: - input += ch; - charcount++; - wechochar(cmd, ch); - } - prefresh(cmd, 0, 0, 0, 0, 0, x); - } -} - -/* - * Create a window with noticable colors (if colors are enabled) - * and print a warning. Means curses_warning. - */ -void c_warn(WINDOW *win, const string &warning, const flags_t &flags) -{ - - /* - * This function is here so that we can warn a user in curses, - * usually about bad input. - */ - - int x, y; - getmaxyx(stdscr, y, x); - WINDOW *warn = subpad(win, 1, x, 0, 0); - if (!flags.nocolor) - wattron(warn, COLOR_PAIR(4)); - wprintw(warn, warning.c_str()); - wprintw(warn, " Press any key to continue..."); - wclrtoeol(warn); - prefresh(warn, 0, 0, 0, 0, 0, x); - while ((y = getch())) { - if (y != ERR) { - break; - } - prefresh(warn, 0, 0, 0, 0, 0, x); - } - if (!flags.nocolor) - wattroff(warn, COLOR_PAIR(4)); - delwin(warn); - noecho(); - wmove(win, 0, 0); - return; -} - -/* - * Initialize the max_t structure with some sane defaults. We'll grow - * them later as needed. - */ -void initialize_maxes(max_t &max, flags_t &flags) -{ - /* - * For NO lookup: - * src/dst IP can be no bigger than 21 chars: - * IP (max of 15) + colon (1) + port (max of 5) = 21 - * - * For lookup: - * if it's a name, we start with the width of the header, and we can - * grow from there as needed. - */ - if (flags.lookup) { - max.src = 6; - max.dst = 11; - } else { - max.src = max.dst = 21; - } - /* - * The proto starts at 3, since tcp/udp are the most common, but will - * grow if we see bigger proto strings such as "ICMP". - */ - max.proto = 3; - /* - * "ESTABLISHED" is generally the longest state, we almost always have - * several, so we'll start with this. It also looks really bad if state - * is changing size a lot, so we start with a common minumum. - */ - max.state = 11; - // TTL we statically make 7: xxx:xx:xx - max.ttl = 9; - - // Start with something sane - max.bytes = 2; - max.packets = 2; -} - -/* - * The actual work of handling a resize. - */ -void handle_resize(WINDOW *&win, const flags_t &flags, screensize_t &ssize) -{ - if (flags.noscroll) { - endwin(); - refresh(); - return; - } - - /* - * OK, the above case without pads is easy. But pads is tricker. - * In order to properly handle SIGWINCH we need to: - * - * - Tear down the pad (delwin) - * - Reset the terminal settings to non-visual mode (endwin) - * - Return to visual mode (refresh) - * - Get the new size (getmaxyx) - * - Rebuild the pad - * - * Note that we don't get the new size without the endwin/refresh - * and thus the new pad doesn't get built right, and everything wraps. - * - * This order must be preserved. - */ - - /* - * Tear down... - */ - delwin(win); - endwin(); - /* - * Start up... - */ - refresh(); - getmaxyx(stdscr, ssize.y, ssize.x); - win = newpad(NLINES, ssize.x); - keypad(win, true); - wmove(win, 0, 0); - - return; -} - -/* - * Take in a 'curr' value, and delete a given conntrack - */ -void delete_state(WINDOW *&win, const tentry_t *entry, const flags_t &flags) -{ - struct nfct_handle *cth; - struct nf_conntrack *ct; - cth = nfct_open(CONNTRACK, 0); - ct = nfct_new(); - int ret; - string response; - char str[NAMELEN]; - string src, dst; - src = inet_ntop(entry->family, (void *)&(entry->src), str, NAMELEN-1); - dst = inet_ntop(entry->family, (void *)&(entry->dst), str, NAMELEN-1); - - ostringstream msg; - msg.str(""); - msg << "Deleting state: "; - if (entry->proto == "tcp" || entry->proto == "udp") { - msg << src << ":" << entry->srcpt << " -> " << dst << ":" << entry->dstpt; - } else { - msg << src << " -> " << dst; - } - msg << " -- Are you sure? (y/n)"; - get_input(win, response, msg.str(), flags); - - if (response != "y" && response != "Y" && response != "yes" && - response != "YES" && response != "Yes") { - c_warn(win, "NOT deleting state.", flags); - return; - } - - nfct_set_attr_u8(ct, ATTR_ORIG_L3PROTO, entry->family); - - if (entry->family == AF_INET) { - nfct_set_attr(ct, ATTR_ORIG_IPV4_SRC, (void *)&(entry->src.s6_addr)); - nfct_set_attr(ct, ATTR_ORIG_IPV4_DST, (void *)&(entry->dst.s6_addr)); - } else if (entry->family == AF_INET6) { - nfct_set_attr(ct, ATTR_ORIG_IPV6_SRC, (void *)&(entry->src.s6_addr)); - nfct_set_attr(ct, ATTR_ORIG_IPV6_DST, (void *)&(entry->dst.s6_addr)); - } - - // undo our space optimization so the kernel can find the state. - protoent *pn; - if (entry->proto == "icmp6") - pn = getprotobyname("ipv6-icmp"); - else - pn = getprotobyname(entry->proto.c_str()); - - nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, pn->p_proto); - - if (entry->proto == "tcp" || entry->proto == "udp") { - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, htons(entry->srcpt)); - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, htons(entry->dstpt)); - } else if (entry->proto == "icmp" || entry->proto == "icmp6") { - string type, code, id, tmp; - split('/', entry->state, type, tmp); - split(' ', tmp, code, tmp); - split('(', tmp, tmp, id); - split(')', id, id, tmp); - - nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, atoi(type.c_str())); - nfct_set_attr_u8(ct, ATTR_ICMP_CODE, atoi(code.c_str())); - nfct_set_attr_u16(ct, ATTR_ICMP_ID, atoi(id.c_str())); - } - - ret = nfct_query(cth, NFCT_Q_DESTROY, ct); - if (ret < 0) { - string msg = "Failed to delete state: "; - msg += strerror(errno); - c_warn(win, msg.c_str(), flags); - } - -} - - -/* - * CORE FUNCTIONS - */ - -/* - * Callback for conntrack - */ -int conntrack_hook(enum nf_conntrack_msg_type nf_type, struct nf_conntrack *ct, - void *tmp) -{ - - /* - * start by getting our struct back - */ - struct hook_data *data = static_cast(tmp); - - /* - * and pull out the pieces - */ - vector *stable = data->stable; - flags_t *flags = data->flags; - max_t *max = data->max; - counters_t *counts = data->counts; - const filters_t *filters = data->filters; - - // our table entry - tentry_t *entry = new tentry_t; - - // some vars - struct protoent* pe = NULL; - int seconds, minutes, hours; - char ttlc[11]; - ostringstream buffer; - - /* - * Clear the entry - */ - entry->sname = ""; - entry->dname = ""; - entry->srcpt = 0; - entry->dstpt = 0; - entry->proto = ""; - entry->ttl = ""; - entry->state = ""; - - /* - * First, we read stuff into the array that's always the - * same regardless of protocol - */ - - short int pr = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); - pe = getprotobynumber(pr); - if (pe == NULL) { - buffer << pr; - entry->proto = buffer.str(); - buffer.str(""); - } else { - entry->proto = pe->p_name; - /* - * if proto is "ipv6-icmp" we can just say "icmp6" to save space... - * it's more common/standard anyway - */ - if (entry->proto == "ipv6-icmp") - entry->proto = "icmp6"; - } - - // ttl - seconds = nfct_get_attr_u32(ct, ATTR_TIMEOUT); - minutes = seconds/60; - hours = minutes/60; - minutes = minutes%60; - seconds = seconds%60; - // Format it with snprintf and store it in the table - snprintf(ttlc,11, "%3i:%02i:%02i", hours, minutes, seconds); - entry->ttl = ttlc; - - entry->family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO); - // Everything has addresses - if (entry->family == AF_INET) { - memcpy(entry->src.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC), - sizeof(uint8_t[16])); - memcpy(entry->dst.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV4_DST), - sizeof(uint8_t[16])); - } else if (entry->family == AF_INET6) { - memcpy(entry->src.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC), - sizeof(uint8_t[16])); - memcpy(entry->dst.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV6_DST), - sizeof(uint8_t[16])); - } else { - fprintf(stderr, "UNKNOWN FAMILY!\n"); - exit(1); - } - - // Counters (summary, in + out) - entry->bytes = nfct_get_attr_u32(ct, ATTR_ORIG_COUNTER_BYTES) + - nfct_get_attr_u32(ct, ATTR_REPL_COUNTER_BYTES); - entry->packets = nfct_get_attr_u32(ct, ATTR_ORIG_COUNTER_PACKETS) + - nfct_get_attr_u32(ct, ATTR_REPL_COUNTER_PACKETS); - - if (digits(entry->bytes) > max->bytes) { - max->bytes = digits(entry->bytes); - } - if (digits(entry->packets) > max->packets) { - max->packets = digits(entry->packets); - } - - if (entry->proto.size() > max->proto) - max->proto = entry->proto.size(); - - // OK, proto dependent stuff - if (entry->proto == "tcp" || entry->proto == "udp") { - entry->srcpt = htons(nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); - entry->dstpt = htons(nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); - } - - if (entry->proto == "tcp") { - entry->state = states[nfct_get_attr_u8(ct, ATTR_TCP_STATE)]; - counts->tcp++; - } else if (entry->proto == "udp") { - entry->state = ""; - counts->udp++; - } else if (entry->proto == "icmp" || entry->proto == "icmp6") { - buffer.str(""); - buffer << (int)nfct_get_attr_u8(ct, ATTR_ICMP_TYPE) << "/" - << (int)nfct_get_attr_u8(ct, ATTR_ICMP_CODE) << " (" - << nfct_get_attr_u16(ct, ATTR_ICMP_ID) << ")"; - entry->state = buffer.str(); - counts->icmp++; - if (entry->state.size() > max->state) - max->state = entry->state.size(); - } else { - counts->other++; - } - - /* - * FILTERING - */ - - /* - * FIXME: Filtering needs to be pulled into it's own function. - */ - struct in_addr lb; - struct in6_addr lb6; - inet_pton(AF_INET, "127.0.0.1", &lb); - inet_pton(AF_INET6, "::1", &lb6); - size_t entrysize = entry->family == AF_INET - ? sizeof(in_addr) - : sizeof(in6_addr); - if (flags->skiplb && (entry->family == AF_INET - ? !memcmp(&(entry->src), &lb, sizeof(in_addr)) - : !memcmp(&(entry->src), &lb6, sizeof(in6_addr)))) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->skipdns && (entry->dstpt == 53)) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_src && (memcmp(&(entry->src), &(filters->src), entrysize))) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_srcpt && (entry->srcpt != filters->srcpt)) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_dst && (memcmp(&(entry->dst), &(filters->dst), entrysize))) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_dstpt && (entry->dstpt != filters->dstpt)) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - /* - * RESOLVE - */ - - // Resolve names - if necessary - or generate strings of address, - // and calculate max sizes - stringify_entry(entry, *max, *flags); - - /* - * Add this to the array - */ - stable->push_back(entry); - - return NFCT_CB_CONTINUE; -} - -/* - * This is the core of this program - build a table of states. - * - * For the new libnetfilter_conntrack code, the bulk of build_table was moved - * to the conntrack callback function. - */ -void build_table(flags_t &flags, const filters_t &filters, vector - &stable, counters_t &counts, max_t &max) -{ - /* - * Variables - */ - int res=0; - vector fields(MAXFIELDS); - static struct nfct_handle *cth; - u_int8_t family = AF_UNSPEC; - - /* - * This is the ugly struct for the nfct hook, that holds pointers to - * all of the things the callback will need to fill our table - */ - struct hook_data hook; - hook.stable = &stable; - hook.flags = &flags; - hook.max = &max; - hook.counts = &counts; - hook.filters = &filters; - - /* - * Initialization - */ - // Nuke the tentry_t's we made before deleting the vector of pointers - for ( - vector::iterator it = stable.begin(); - it != stable.end(); - it++ - ) { - delete *it; - } - stable.clear(); - counts.tcp = counts.udp = counts.icmp = counts.other = counts.skipped = 0; - - cth = nfct_open(CONNTRACK, 0); - if (!cth) { - end_curses(); - printf("ERROR: couldn't establish conntrack connection\n"); - exit(2); - } - nfct_callback_register(cth, NFCT_T_ALL, conntrack_hook, (void *)&hook); - res = nfct_query(cth, NFCT_Q_DUMP, &family); - if (res < 0) { - end_curses(); - printf("ERROR: Couldn't retreive conntrack table: %s\n", strerror(errno)); - exit(2); - } - nfct_close(cth); -} - -/* - * This sorts the table based on the current sorting preference - */ -void sort_table(const int &sortby, const bool &lookup, const int &sort_factor, - vector &stable, string &sorting) -{ - switch (sortby) { - case SORT_SRC: - if (lookup) { - std::sort(stable.begin(), stable.end(), srcname_sort); - sorting = "SrcName"; - } else { - std::sort(stable.begin(), stable.end(), src_sort); - sorting = "SrcIP"; - } - break; - - case SORT_SRC_PT: - std::sort(stable.begin(), stable.end(), srcpt_sort); - sorting = "SrcPort"; - break; - - case SORT_DST: - if (lookup) { - std::sort(stable.begin(), stable.end(), dstname_sort); - sorting = "DstName"; - } else { - std::sort(stable.begin(), stable.end(), dst_sort); - sorting = "DstIP"; - } - break; - - case SORT_DST_PT: - std::sort(stable.begin(), stable.end(), dstpt_sort); - sorting = "DstPort"; - break; - - case SORT_PROTO: - std::sort(stable.begin(), stable.end(), proto_sort); - sorting = "Prt"; - break; - - case SORT_STATE: - std::sort(stable.begin(), stable.end(), state_sort); - sorting = "State"; - break; - - case SORT_TTL: - std::sort(stable.begin(), stable.end(), ttl_sort); - sorting = "TTL"; - break; - - case SORT_BYTES: - std::sort(stable.begin(), stable.end(), bytes_sort); - sorting = "Bytes"; - break; - - case SORT_PACKETS: - std::sort(stable.begin(), stable.end(), packets_sort); - sorting = "Packets"; - break; - - default: - //we should never get here - sorting = "??unknown??"; - break; - - } //switch - - if (sort_factor == -1) - sorting = sorting + " reverse"; - -} - -void print_headers(const flags_t &flags, const string &format, - const string &sorting, const filters_t &filters, - const counters_t &counts, const screensize_t &ssize, - int table_size, WINDOW *mainwin) -{ - if (flags.single) { - cout << "IP Tables State Top -- Sort by: " << sorting << endl; - } else { - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - wmove(mainwin,0, ssize.x/2-15); - wattron(mainwin, A_BOLD); - wprintw(mainwin, "IPTState - IPTables State Top\n"); - - wprintw(mainwin, "Version: "); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-13s", VERSION); - - wattron(mainwin, A_BOLD); - wprintw(mainwin, "Sort: "); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-16s", sorting.c_str()); - - wattron(mainwin, A_BOLD); - wprintw(mainwin, "b"); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-19s", ": change sorting"); - - wattron(mainwin, A_BOLD); - wprintw(mainwin, "h"); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-s\n", ": help"); - } - - /* - * If enabled, print totals - */ - if (flags.totals) { - if (flags.single) - printf(TOTALS_FORMAT, table_size+counts.skipped, counts.tcp, counts.udp, - counts.icmp, counts.other, counts.skipped); - else - wprintw(mainwin, TOTALS_FORMAT, table_size+counts.skipped, counts.tcp, - counts.udp, counts.icmp, counts.other, counts.skipped); - } - - /* - * If any, print filters - */ - char tmp[NAMELEN]; - if (flags.filter_src || flags.filter_dst || flags.filter_srcpt - || flags.filter_dstpt) { - - if (flags.single) { - printf("Filters: "); - } else { - wattron(mainwin, A_BOLD); - wprintw(mainwin, "Filters: "); - wattroff(mainwin, A_BOLD); - } - - bool printed_a_filter = false; - - if (flags.filter_src) { - inet_ntop(filters.srcfam, &filters.src, tmp, NAMELEN-1); - if (flags.single) - printf("src: %s", tmp); - else - wprintw(mainwin, "src: %s", tmp); - printed_a_filter = true; - } - if (flags.filter_srcpt) { - if (printed_a_filter) { - if (flags.single) - printf(", "); - else - waddstr(mainwin, ", "); - } - if (flags.single) - printf("sport: %lu", filters.srcpt); - else - wprintw(mainwin, "sport: %lu", filters.srcpt); - printed_a_filter = true; - } - if (flags.filter_dst) { - if (printed_a_filter) { - if (flags.single) - printf(", "); - else - waddstr(mainwin, ", "); - } - inet_ntop(filters.dstfam, &filters.dst, tmp, NAMELEN-1); - if (flags.single) - printf("dst: %s", tmp); - else - wprintw(mainwin, "dst: %s", tmp); - printed_a_filter = true; - } - if (flags.filter_dstpt) { - if (printed_a_filter) { - if (flags.single) - printf(", "); - else - waddstr(mainwin, ", "); - } - if (flags.single) - printf("dport: %lu", filters.dstpt); - else - wprintw(mainwin, "dport: %lu", filters.dstpt); - printed_a_filter = true; - } - if (flags.single) - printf("\n"); - else - wprintw(mainwin, "\n"); - } - - /* - * Print column headers - */ - if (flags.single) { - if (flags.counters) - printf(format.c_str(), "Source", "Destination", "Prt", "State", "TTL", - "B", "P"); - else - printf(format.c_str(), "Source", "Destination", "Prt", "State", "TTL"); - } else { - wattron(mainwin, A_BOLD); - if (flags.counters) - wprintw(mainwin, format.c_str(), "Source", "Destination", "Prt", - "State", "TTL", "B", "P"); - else - wprintw(mainwin, format.c_str(), "Source", "Destination", "Prt", - "State", "TTL"); - wattroff(mainwin, A_BOLD); - } - -} - - -void truncate(string &string, int length, bool mark, char direction) -{ - int s = (direction == 'f') ? string.size() - length : 0; - - string = string.substr(s, length); - if (mark) { - int m = (direction == 'f') ? 0 : string.size() - 1; - string[m] = '+'; - } -} - -/* - * Based on the format pre-chosen, truncate src/dst as needed, and then - * generate the host:port strings and drop them off in the src/dst string - * objects passed in. - */ -void format_src_dst(tentry_t *table, string &src, string &dst, - const flags_t &flags, const max_t &max) -{ - ostringstream buffer; - bool have_port = table->proto == "tcp" || table->proto == "udp"; - char direction; - unsigned int length; - - // What length would we currently use? - length = table->sname.size(); - if (have_port) - length += table->spname.size() + 1; - - // If it's too long, figure out how room we have and truncate it - if (length > max.src) { - length = max.src; - if (have_port) - length -= 1 + table->spname.size(); - direction = (flags.lookup) ? 'e' : 'f'; - truncate(table->sname, length, flags.tag_truncate, direction); - } - - // ... and repeat - length = table->dname.size(); - if (have_port) - length += table->dpname.size() + 1; - if (length > max.dst) { - length = max.dst; - if (have_port) - length -= 1 + table->dpname.size(); - direction = (flags.lookup) ? 'f' : 'e'; - truncate(table->dname, length, flags.tag_truncate, direction); - } - - buffer << table->sname; - if (have_port) - buffer << ":" << table->spname; - src = buffer.str(); - buffer.str(""); - buffer << table->dname; - if (have_port) - buffer << ":" << table->dpname; - dst = buffer.str(); - buffer.str(""); -} - -/* - * An abstraction of priting a line for both single/curses modes - */ -void printline(tentry_t *table, const flags_t &flags, const string &format, - const max_t &max, WINDOW *mainwin, const bool curr) -{ - ostringstream buffer; - buffer.str(""); - string src, dst, b, p; - - // Generate strings for src/dest, truncating and marking as necessary - format_src_dst(table, src, dst, flags, max); - - if (flags.counters) { - buffer << table->bytes; - b = buffer.str(); - buffer.str(""); - buffer << table->packets; - p = buffer.str(); - buffer.str(""); - } - - if (flags.single) { - if (flags.counters) - printf(format.c_str(), src.c_str(), dst.c_str(), table->proto.c_str(), - table->state.c_str(), table->ttl.c_str(), b.c_str(), p.c_str()); - else - printf(format.c_str(), src.c_str(), dst.c_str(), table->proto.c_str(), - table->state.c_str(), table->ttl.c_str()); - } else { - int color = 0; - if (!flags.nocolor) { - if (table->proto == "tcp") - color = 1; - else if (table->proto == "udp") - color = 2; - else if (table->proto == "icmp" || table->proto == "icmp6") - color = 3; - if (curr) - color += 4; - wattron(mainwin, COLOR_PAIR(color)); - } - if (flags.counters) - wprintw(mainwin, format.c_str(), src.c_str(), dst.c_str(), - table->proto.c_str(), table->state.c_str(), table->ttl.c_str(), - b.c_str(), p.c_str()); - else - wprintw(mainwin, format.c_str(), src.c_str(), dst.c_str(), - table->proto.c_str(), table->state.c_str(), table->ttl.c_str()); - - if (!flags.nocolor && color != 0) - wattroff(mainwin, COLOR_PAIR(color)); - } -} - -/* - * This does all the work of actually printing the table including - * various bits of formatting. It handles both curses and non-curses runs. - */ -void print_table(vector &stable, const flags_t &flags, - const string &format, const string &sorting, - const filters_t &filters, const counters_t &counts, - const screensize_t &ssize, const max_t &max, WINDOW *mainwin, - unsigned int &curr) -{ - /* - * Print headers - */ - print_headers(flags, format, sorting, filters, counts, ssize, stable.size(), - mainwin); - - /* - * Print the state table - */ - unsigned int limit = (stable.size() < NLINES) ? stable.size() : NLINES; - for (unsigned int tmpint=0; tmpint < limit; tmpint++) { - printline(stable[tmpint], flags, format, max, mainwin, (curr == tmpint)); - if (!flags.single && flags.noscroll - && (tmpint >= ssize.y-4 || (flags.totals && tmpint >= ssize.y-5))) - break; - - } - - /* - * We don't want to lave things on the screen we didn't draw - * this time. - */ - if (!flags.single) - wclrtobot(mainwin); - -} - -/* - * Dynamically build a format to fit the most amount of data on the screen - */ -void determine_format(WINDOW *mainwin, max_t &max, screensize_t &ssize, - string &format, flags_t &flags) -{ - - /* - * NOTE: When doing proper dynamic format building, we fill the - * entire screen, so curses puts in a newline for us. However - * with "staticsize" we must add a newline. Also with "single" - * mode we must add it as well since there's no curses there. - * - * Thus DEFAULT_FORMAT (only used for staticsize) has it, and - * at the bottom of this function we add a \n if flags.single - * is set. - */ - if (flags.staticsize) { - format = DEFAULT_FORMAT; - max.src = DEFAULT_SRC; - max.dst = DEFAULT_DST; - max.proto = DEFAULT_PROTO; - max.state = DEFAULT_STATE; - max.ttl = DEFAULT_TTL; - return; - } - - ssize = get_size(flags.single); - - /* The screen must be 85 chars wide to be able to fit in - * counters when we display IP addresses... - * - * in lookup mode, we can truncate names, but in IP mode, - * truncation makes no sense, so we just disable counters if - * we run into this. - */ - if (ssize.x < 85 && flags.counters && !flags.lookup) { - string prompt = "Window too narrow for counters! Disabling."; - c_warn(mainwin, prompt, flags); - flags.counters = false; - } - - /* what's left is the above three, plus 4 spaces - * (one between each of 5 fields) - */ - unsigned int left = ssize.x - max.ttl - max.state - max.proto - 4; - if (flags.counters) - left -= (max.bytes + max.packets + 2); - - /* - * The rest is *prolly* going to be divided between src - * and dst, so we see if that works. If 'left' is odd though - * we give the extra space to src. - */ - unsigned int src, dst; - src = dst = left / 2; - bool left_odd = false; - if ((left % 2) == 1) { - left_odd = true; - src++; - } - if ((max.src + max.dst) < left) { - /* - * This means we can fit without an truncation, but it doesn't - * necessarily mean that we can just give half to src and half - * to dst... so lets figure that out. - */ - - if (max.src < src && max.dst < dst) { - /* - * This case applies if: - * we're even and they both fit in left/2 - * OR - * we're odd and dst fits in left/2 - * and src fits in left/2+1 - * - * Since we've already calculated src/dst that way - * we just combine this check as they both require - * the same outcome. - */ - } else if (left_odd && (src < left / 2) && (dst < left / 2 + 1)) { - /* - * If src can fit in left/2 and dst in left/2+1 - * then we switch them. - */ - src = dst; - dst++; - } else if (max.src > max.dst) { - /* - * If we're here, we can fit them, but we can't fit them - * and still keep the two columns relatively equal. Ah - * well. - * - * Either max gets the bigger chunk and everything else - * to dst... - */ - src = max.src; - dst = left - max.src; - } else { - /* - * ...or the other way around - */ - dst = max.dst; - src = left - max.dst; - } - } else if (max.src < src) { - /* - * If we're here, we do have to truncate, but if one column is - * very small, we should not give it more space than it needs. - */ - src = max.src; - dst = left - max.src; - } else if (max.dst < dst) { - /* - * same as above. - */ - dst = max.dst; - src = left - max.dst; - } - - /* - * If nothing matched, then they're both bigger than left/2, so we'll - * leave the default we set above. - */ - - ostringstream buffer; - buffer << "\%-" << src << "s \%-" << dst << "s \%-" << max.proto << "s \%-" - << max.state << "s \%-" << max.ttl << "s"; - - if (flags.counters) - buffer << " \%-" << max.bytes << "s \%-" << max.packets << "s"; - - if (flags.single) - buffer << "\n"; - - format = buffer.str(); - - max.dst = dst; - max.src = src; -} - -/* - * Interactive help - */ -void interactive_help(const string &sorting, const flags_t &flags, - const filters_t &filters) -{ - - /* - * This is the max we need the pad to be, and thus how - * big we're going to create the pad. - * - * In many cases we'd make the pad very very large and not - * worry about it. However, in this case: - * 1. We know exactly how big we need it to be, and it's - * not going to change interactively. - * 2. We want to draw a "box" around the window and if the - * pad is huge then the box will get drawn around that. - * - * So... we have 32 lines of help, plus a top and bottom border, - * thus maxrows is 34. - * - * Our help text is not wider than 80, so we'll se that standard - * width. - * - * If the screen is bigger than this, we deal with it below. - */ - unsigned int maxrows = 41; - unsigned int maxcols = 80; - - /* - * The actual screen size - */ - screensize_t ssize = get_size(flags.single); - - /* - * If the biggest we think we'll need is smaller than the screen, - * then lets grow the pad to the size of the screen so that the - * main window isn't peeking through. - */ - if (maxrows < ssize.y) - maxrows = ssize.y; - if (maxcols < ssize.x) - maxcols = ssize.x; - - /* - * Where we are withing the pad (for printing). We can't just print - * newlines and expect it to work. Cause, well, it doesn't. You have - * to tell it where on the pad to print, specifically. - */ - unsigned int x, y; - x = y = 0; - - /* - * The current position on the pad we're showing (top left) - */ - unsigned int px, py; - px = py = 0; - - /* - * As noted above, we create the biggest pad we might need - */ - static WINDOW *helpwin; - helpwin = newpad(maxrows, maxcols); - - /* - * Create a box, and then add one to "x" and "y" so we don't write - * on the line, - */ - box(helpwin, ACS_VLINE, ACS_HLINE); - x++; - y++; - - - /* - * we want arrow keys to work - */ - keypad(helpwin, true); - - // Prolly not needed - wmove(helpwin, 0, 0); - - // Print opener - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, "IPTState "); - waddstr(helpwin, VERSION); - wattroff(helpwin, A_BOLD); - // this is \n - y++; - - // We don't want anything except the title up against the - // border - x++; - - string nav = "Up/j, Down/k, Left/h, Right/l, PageUp/^u, PageDown/^d, "; - nav += " Home, or End"; - // Print instructions first - mvwaddstr(helpwin, y++, x, "Navigation:"); - mvwaddstr(helpwin, y++, x, nav.c_str()); - mvwaddstr(helpwin, y++, x, " Press any other key to continue..."); - y++; - - // Print settings - mvwaddstr(helpwin, y++, x, "Current settings:"); - - mvwaddstr(helpwin, y++, x, " Sorting by: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin, sorting.c_str()); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Dynamic formatting: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(!flags.staticsize) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Skip loopback states: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.skiplb) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Resolve hostnames: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.lookup) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Mark truncated hostnames: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.tag_truncate) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Colors: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(!flags.nocolor) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Skip outgoing DNS lookup states: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.skipdns) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Enable scroll: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(!flags.noscroll) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Display totals: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.totals) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Display counters: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.counters) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - char tmp[NAMELEN]; - if (flags.filter_src) { - inet_ntop(filters.srcfam, &filters.src, tmp, - filters.srcfam == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)); - mvwaddstr(helpwin, y++, x, " Source filter: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin, tmp); - wattroff(helpwin, A_BOLD); - } - if (flags.filter_dst) { - inet_ntop(filters.dstfam, &filters.dst, tmp, - filters.dstfam == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)); - mvwaddstr(helpwin, y++, x, " Destination filter: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin, tmp); - wattroff(helpwin, A_BOLD); - } - if (flags.filter_srcpt) { - mvwaddstr(helpwin, y++, x, " Source port filter: "); - wattron(helpwin, A_BOLD); - wprintw(helpwin, "%lu", filters.srcpt); - wattroff(helpwin, A_BOLD); - } - if (flags.filter_dstpt) { - mvwaddstr(helpwin, y++, x, " Destination port filter: "); - wattron(helpwin, A_BOLD); - wprintw(helpwin, "%lu", filters.dstpt); - wattroff(helpwin, A_BOLD); - } - - y++; - - // Print commands - mvwaddstr(helpwin, y++, x, "Interactive commands:"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " c"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tUse colors"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " C"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of bytes/packets counters"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " b"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tSort by next column"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " B"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tSort by previous column"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " d"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange destination filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " D"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange destination port filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " f"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of loopback states"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " h"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tDisplay this help message"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " l"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle DNS lookups"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " L"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of outgoing DNS states"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " m"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle marking truncated hostnames with a '+'"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " o"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle dynamic or old formatting"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " p"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle scrolling"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " q"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tQuit"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " r"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle reverse sorting"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " R"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange the refresh rate"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " s"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange source filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " S"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange source port filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " t"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of totals"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " x"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tDelete the currently highlighted state from netfilter"); - - y++; - - wmove(helpwin, 0, 0); - - /* - * refresh from wherever we are the pad - * and the top of the window to the bottom of the window. - */ - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - // kill line buffering - cbreak(); - // nodelay with a 0 here causes getch() to block until key is pressed. - nodelay( helpwin, 0 ); - int c; - while ((c = wgetch(helpwin))) { - switch (c) { - case ERR: - continue; - break; - case KEY_DOWN: - case 'j': - /* - * py is the top of the window, - * ssize.y is the height of the window, - * so py+ssize.y is the bottom of the window. - * - * Since y is the bottom of the text we've - * written, if - * py+ssize.y == y - * then the bottom of the screen as at the - * bottom of the text, no more scrolling. - */ - if (py + ssize.y < y) - py++; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_UP: - case 'k': - if (py > 0) - py--; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_RIGHT: - case 'l': - /* - * px is the left of the window, - * ssize.x is the width of the window, - * so px+ssize.x os the right side of the window. - * - * So if px+ssize.x == 80 (more than the width - * of our text), no more scrolling. - */ - if (px + ssize.x < 80) - px++; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_LEFT: - case 'h': - if (px > 0) - px--; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_HOME: - case KEY_SHOME: - case KEY_FIND: - px = py = 0; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_END: - py = y-ssize.y; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case 4: - case KEY_NPAGE: - case KEY_SNEXT: - if (flags.noscroll) - break; - /* - * If the screen is bigger than the text, - * ignore - */ - if (y < ssize.y) - break; - /* - * Otherwise, if the bottom of the screen - * (current position + screen size - * == py + ssize.y) - * were to go down one screen (thus: - * py + ssize.y*2), - * and that is bigger than the whole text, just - * go to the bottom. - * - * Otherwise, go down a screen size. - */ - if ((py + (ssize.y * 2)) > y) { - py = y-ssize.y; - } else { - py += ssize.y; - } - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case 21: - case KEY_PPAGE: - case KEY_SPREVIOUS: - if (flags.noscroll) - break; - /* - * If we're at the top, ignore this. - */ - if (py == 0) - break; - /* - * Otherwise if we're less than a page from the - * top, go to the top, else, go up a page. - */ - if (py < ssize.y) - py = 0; - else - py -= ssize.y; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - - case 'q': - default: - goto out; - break; - } - if (need_resize) { - goto out; - } - } -out: - // once a key is pressed, tear down the help window. - delwin(helpwin); - refresh(); - halfdelay(1); -} - - -/* - * MAIN - */ -int main(int argc, char *argv[]) -{ - - // Variables - string line, src, dst, srcpt, dstpt, proto, code, type, state, ttl, mins, - secs, hrs, sorting, tmpstring, format, prompt; - ostringstream ostream; - vector stable; - int tmpint = 0, sortby = 0, rate = 1, hdrs = 0; - unsigned int py = 0, px = 0, curr_state = 0; - timeval selecttimeout; - fd_set readfd; - flags_t flags; - counters_t counts; - screensize_t ssize; - filters_t filters; - max_t max; - - /* - * Initialize - */ - flags.single = flags.totals = flags.lookup = flags.skiplb = flags.staticsize - = flags.skipdns = flags.tag_truncate = flags.filter_src - = flags.filter_dst = flags.filter_srcpt = flags.filter_dstpt - = flags.noscroll = flags.nocolor = flags.counters = false; - ssize.x = ssize.y = 0; - counts.tcp = counts.udp = counts.icmp = counts.other = counts.skipped = 0; - filters.src = filters.dst = in6addr_any; - filters.srcpt = filters.dstpt = 0; - max.src = max.dst = max.proto = max.state = max.ttl = 0; - px = py = 0; - - static struct option long_options[] = { - {"counters", no_argument , 0, 'C'}, - {"dst-filter", required_argument, 0, 'd'}, - {"dstpt-filter", required_argument, 0, 'D'}, - {"help", no_argument, 0, 'h'}, - {"lookup", no_argument, 0, 'l'}, - {"mark-truncated", no_argument, 0, 'm'}, - {"no-color", no_argument, 0, 'c'}, - {"no-dynamic", no_argument, 0, 'o'}, - {"no-dns", no_argument, 0, 'L'}, - {"no-loopback", no_argument, 0, 'f'}, - {"no-scroll", no_argument, 0, 'p'}, - {"rate", required_argument, 0, 'R'}, - {"reverse", no_argument, 0, 'r'}, - {"single", no_argument, 0, '1'}, - {"sort", required_argument, 0, 'b'}, - {"src-filter", required_argument, 0, 's'}, - {"srcpt-filter", required_argument, 0, 'S'}, - {"totals", no_argument, 0, 't'}, - {"version", no_argument, 0, 'v'}, - {0, 0, 0,0} - }; - int option_index = 0; - - // Command Line Arguments - while ((tmpint = getopt_long(argc, argv, "Cd:D:hlmcoLfpR:r1b:s:S:tv", - long_options, &option_index)) != EOF) { - printf("loop: %d\n", tmpint); - switch (tmpint) { - case 0: - /* Apparently this test is needed?! Seems lame! */ - if (long_options[option_index].flag != 0) - break; - - /* - * Long-only options go here, like so: - * - * tmpstring = long_options[option_index].name; - * if (tmpstring == "srcpt-filter") { - * ... - * } else if (...) { - * ... - * } - * - */ - - break; - // --counters - case 'C': - flags.counters = true; - break; - // --dst-filter - case 'd': - if (optarg == NULL) - break; - // See check_ip() note above - if (!check_ip(optarg, &filters.dst, &filters.dstfam)) { - cerr << "Invalid IP address: " << optarg - << endl; - exit(1); - } - flags.filter_dst = true; - break; - // --dstpt-filter - case 'D': - /* - * even though this won't be an IP address - * aton() won't complain about anything that's - * just digits, so it's an easy check. - */ - if (optarg == NULL) - break; - flags.filter_dstpt = true; - filters.dstpt = atoi(optarg); - break; - // --help - case 'h': - help(); - break; - // --lookup - case 'l': - flags.lookup = true; - // and also set skipdns for sanity - flags.skipdns = true; - break; - // --mark-truncated - case 'm': - flags.tag_truncate = true; - break; - // --color - case 'c': - flags.nocolor = false; - break; - // --no-dynamic - case 'o': - flags.staticsize = true; - break; - // --no-dns - case 'L': - flags.skipdns = true; - break; - // --no-loopback - case 'f': - flags.skiplb = true; - break; - // --no-scroll - case 'p': - flags.noscroll = true; - break; - // --reverse - case 'r': - sort_factor = -1; - break; - // --rate - case 'R': - rate = atoi(optarg); - break; - // --sort - case 'b': - if (*optarg == 'd') - sortby = SORT_DST; - else if (*optarg == 'D') { - sortby = SORT_DST_PT; - } else if (*optarg == 'S') - sortby = SORT_SRC_PT; - else if (*optarg == 'p') - sortby = SORT_PROTO; - else if (*optarg == 's') - sortby = SORT_STATE; - else if (*optarg == 't') - sortby = SORT_TTL; - else if (*optarg == 'b' && flags.counters) - sortby = SORT_BYTES; - else if (*optarg == 'P' && flags.counters) - sortby = SORT_PACKETS; - break; - // --single - case '1': - flags.single = true; - break; - // --src-filter - case 's': - if (optarg == NULL) - break; - if (!check_ip(optarg, &filters.src, &filters.srcfam)) { - cerr << "Invalid IP address: " << optarg << endl; - exit(1); - } - flags.filter_src = true; - break; - // --srcpt-filter - case 'S': - if (optarg == NULL) - break; - flags.filter_srcpt = true; - filters.srcpt = atoi(optarg); - break; - // --totals - case 't': - flags.totals = true; - break; - // --version - case 'v': - version(); - exit(0); - break; - // catch-all - default: - // getopts should already have printed a message - exit(1); - break; - } - } - - if (rate < 0 || rate > 60) { - rate = 1; - } - - // Initialize Curses Stuff - static WINDOW *mainwin = NULL; - if (!flags.single) { - mainwin = start_curses(flags); - keypad(mainwin, true); - } - - /* - * We want to keep going until the user stops us - * unless they use single run mode - * in which case, we'll deal with that down below - */ - while (1) { - - /* - * We get the screensize_t up-front so we can die if the - * screen doesn't meet our minimum requirements without making - * the user wait while we gather and process all the data. - * We'll do it again afterwards just in case - */ - - ssize = get_size(flags.single); - - if (ssize.x < 72) { - term_too_small(); - } - - // And our header size - hdrs = 3; - if (flags.totals) { - hdrs++; - } - if (flags.filter_src || flags.filter_dst || flags.filter_srcpt - || flags.filter_dstpt) { - hdrs++; - } - - // clear maxes - initialize_maxes(max, flags); - - // Build our table - build_table(flags, filters, stable, counts, max); - - /* - * Now that we have the new table, make sure our page/cursor - * positions still make sense. - */ - if (curr_state > stable.size() - 1) { - curr_state = stable.size() - 1; - } - - /* - * The bottom of the screen is stable.size()+hdrs+1 - * (the +1 is so we can have a blank line at the end) - * but we want to never have py be more than that - ssize.y - * so we're showing a page full of states. - */ - int bottom = stable.size() + hdrs + 1 - ssize.y; - if (bottom < 0) - bottom = 0; - if (py > (unsigned)bottom) - py = bottom; - - /* - * Originally I strived to do this the "right" way by calling - * nfct_is_set(ct, ATTR_ORIG_COUNGERS) to determine if - * counters were enabled. BUT, if counters are not enabled, - * nfct_get_attr() returns NULL, so this test is just as - * valid. - * - * Conversely checking is_set and then get_attr() inside our - * callback is twice the calls per-state if they are enabled, - * for no additional benefit. - */ - if (flags.counters && stable.size() > 0 && stable[0]->bytes == 0) { - prompt = "Counters requested, but not enabled in the"; - prompt += " kernel!"; - flags.counters = 0; - c_warn(mainwin, prompt, flags); - } - - // Sort our table - sort_table(sortby, flags.lookup, sort_factor, stable, sorting); - - /* - * From here on out 'max' is no longer "the maximum size of - * this field throughout the table", but is instead the actual - * size to print each field. - * - * BTW, we do "get_size" again here incase the window changed - * while we were off parsing and sorting data. - */ - determine_format(mainwin, max, ssize, format, flags); - - /* - * Now we print out the table in whichever format we're - * configured for - */ - print_table(stable, flags, format, sorting, filters, counts, ssize, max, - mainwin, curr_state); - - // Exit if we're only supposed to run once - if (flags.single) - exit(0); - - // Otherwise refresh the curses display - if (flags.noscroll) { - refresh(); - } else { - prefresh(mainwin, py, px, 0, 0, ssize.y-1, ssize.x-1); - } - - //check for key presses for one second - //or whatever the user said - selecttimeout.tv_sec = rate; - selecttimeout.tv_usec = 0; - // I don't care about fractions of seconds. I don't want them. - FD_ZERO(&readfd); - FD_SET(0, &readfd); - select(1,&readfd, NULL, NULL, &selecttimeout); - if (FD_ISSET(0, &readfd)) { - tmpint = wgetch(mainwin); - switch (tmpint) { - // This is ^L - case 12: - handle_resize(mainwin, flags, ssize); - break; - /* - * This is at the top because the rest are in - * order of longopts, and q isn't in longopts - */ - case 'q': - goto out; - break; - /* - * General option toggles - */ - case 'c': - /* - * we only want to pay attention to this - * command if colors are available - */ - if (has_colors()) - flags.nocolor = !flags.nocolor; - break; - case 'C': - flags.counters = !flags.counters; - if (sortby >= SORT_BYTES) - sortby = SORT_BYTES-1; - break; - case 'h': - interactive_help(sorting, flags, filters); - break; - case 'l': - flags.lookup = !flags.lookup; - /* - * If we just turned on lookup, also turn on filtering DNS states. - * They can turn it off if they want, but generally this is the safer - * approach. - */ - if (flags.lookup) { - flags.skipdns = true; - } - break; - case 'm': - flags.tag_truncate = !flags.tag_truncate; - break; - case 'o': - flags.staticsize = !flags.staticsize; - break; - case 'L': - flags.skipdns = !flags.skipdns; - break; - case 'f': - flags.skiplb = !flags.skiplb; - break; - case 'p': - switch_scroll(flags, mainwin); - break; - case 'r': - sort_factor = -sort_factor; - break; - case 'b': - if (sortby < SORT_MAX) { - sortby++; - if (!flags.counters && sortby >= SORT_BYTES) - sortby = 0; - } else { - sortby = 0; - } - break; - case 'B': - if (sortby > 0) { - sortby--; - } else { - if (flags.counters) - sortby=SORT_MAX; - else - sortby=SORT_BYTES-1; - } - break; - case 't': - flags.totals = !flags.totals; - break; - /* - * Update-filters - */ - case 'd': - prompt = "New Destination Filter? (leave blank"; - prompt += " for none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_dst = false; - filters.dst = in6addr_any; - } else { - if (!check_ip(tmpstring.c_str(), &filters.dst, &filters.dstfam)) { - prompt = "Invalid IP,"; - prompt += " ignoring!"; - c_warn(mainwin, prompt, flags); - } else { - flags.filter_dst = true; - } - } - break; - case 'D': - prompt = "New dstpt filter? (leave blank for"; - prompt += " none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_dstpt = false; - filters.dstpt = 0; - } else { - flags.filter_dstpt = true; - filters.dstpt = atoi(tmpstring.c_str()); - } - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - break; - case 'R': - prompt = "Rate: "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring != "") { - int i = atoi(tmpstring.c_str()); - if (i < 1) { - prompt = "Invalid rate,"; - prompt += " ignoring!"; - c_warn(mainwin, prompt, flags); - } else { - rate = i; - } - } - break; - case 's': - prompt = "New src filter? (leave blank for"; - prompt += " none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_src = false; - filters.src = in6addr_any; - } else { - if (!check_ip(tmpstring.c_str(), &filters.src, &filters.srcfam)) { - prompt = "Invalid IP,"; - prompt += " ignoring!"; - c_warn(mainwin, prompt, flags); - } else { - flags.filter_src = true; - } - } - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - break; - case 'S': - prompt = "New srcpt filter? (leave blank for"; - prompt += " none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_srcpt = false; - filters.srcpt = 0; - } else { - flags.filter_srcpt = true; - filters.srcpt = atoi(tmpstring.c_str()); - } - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - break; - case 'x': - delete_state(mainwin, stable[curr_state], flags); - break; - /* - * Window navigation - */ - case KEY_DOWN: - case 'j': - if (flags.noscroll) - break; - /* - * GENERAL NOTE: - * py is the top of the window, - * ssize.y is the height of the window, - * so py+ssize.y is the bottom of the window. - * - * BOTTOM OF SCROLLING: - * Since stable.size()+hdrs+1 is - * the bottom of the text we've written, if - * py+ssize.y == stable.size()+hdrs+1 - * then the bottom of the screen as at the - * bottom of the text, no more scrolling. - * - * However, we only want to scroll the page - * when the cursor is at the bottom, i.e. - * when curr_state+4 == py+ssize.y - */ - - /* - * If we have room to scroll down AND if cur is - * at the bottom of a page scroll down. - */ - if ((py + ssize.y <= stable.size() + hdrs + 1) - && (curr_state + 4 == py + ssize.y)) - py++; - - /* - * As long as the cursor isn't at the end, - * move it down one. - */ - if (curr_state < stable.size() - 1) - curr_state++; - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_UP: - case 'k': - if (flags.noscroll) - break; - - /* - * This one is tricky. - * - * First thing we need to know is when the cursor - * is at the top of the page. This is simply when - * curr_state+hdrs+1 cursor location), is - * exactly one more than the top of the window - * (py), * i.e. when curr_state+hdrs+1 == py+1. - * - * PAGE SCROLLING: - * IF we're not page-scrolled all the way up - * (i.e. py > 0) - * AND the cursor is at the top of the page - * OR the cursor is at the top of the list, - * AND we're not yet at the top (showing - * the headers). - * THEN we scroll up. - * - * CURSOR SCROLLING: - * Unlike KEY_DOWN, we don't break just because - * the cursor can't move anymore - on the way - * ip we may still have page-scrolling to do. So - * test to make sure we're not at state 0, and - * if so, we scroll up. - */ - - /* - * Basically: - * IF the cursor bumps the top of the screen - * OR we need to scroll up for headers - */ - if ((py > 0 && (curr_state + hdrs + 1) == (py + 1)) - || (curr_state == 0 && py > 0)) - py--; - - if (curr_state > 0) - curr_state--; - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - // 4 is ^d - case 4: - case KEY_NPAGE: - case KEY_SNEXT: - if (flags.noscroll) - break; - /* - * If the screen is bigger than the text, - * and the cursor is at the bottom, ignore. - */ - if (stable.size() + hdrs + 1 < ssize.y && curr_state == stable.size()) - break; - - /* - * Otherwise, if the bottom of the screen - * (current position + screen size - * == py + ssize.y) - * were to go down one screen (thus: - * py + ssize.y*2), - * and that is bigger than the whole pad, just - * go to the bottom. - * - * Otherwise, go down a screen size. - */ - if (py + ssize.y * 2 > stable.size() + hdrs + 1) { - py = stable.size() + hdrs + 1 - ssize.y; - } else { - py += ssize.y; - } - - /* - * For the cursor, we try to move it down one - * screen as well, but if that's too far, - * we bring it up to the largest number it can - * be. - */ - curr_state += ssize.y; - if (curr_state > stable.size()) { - curr_state = stable.size(); - } - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - // 21 is ^u - case 21: - case KEY_PPAGE: - case KEY_SPREVIOUS: - if (flags.noscroll) - break; - /* - * If we're at the top, ignore - */ - if (py == 0 && curr_state == 0) - break; - /* - * Otherwise if we're less than a page from the - * top, go to the top, else go up a page. - */ - if (py < ssize.y) { - py = 0; - } else { - py -= ssize.y; - } - - /* - * We bring the cursor up a page too, unless - * that's too far. - */ - if (curr_state < ssize.y) { - curr_state = 0; - } else { - curr_state -= ssize.y; - } - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_HOME: - if (flags.noscroll) - break; - px = py = curr_state = 0; - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_END: - if (flags.noscroll) - break; - py = stable.size() + hdrs + 1 - ssize.y; - if (py < 0) - py = 0; - curr_state = stable.size(); - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - } - } - /* - * If we got a sigwinch, we need to redraw - */ - if (need_resize) { - handle_resize(mainwin, flags, ssize); - need_resize = false; - } - } // end while(1) - - out: - - /* - * The user has broken out of the loop, take down the curses - */ - end_curses(); - - // And we're done - return(0); - -} // end main diff --git a/iptstate.cc.rhbz599181 b/iptstate.cc.rhbz599181 deleted file mode 100644 index c7d3971..0000000 --- a/iptstate.cc.rhbz599181 +++ /dev/null @@ -1,2815 +0,0 @@ -/* - * vim:textwidth=80:tabstop=2:shiftwidth=2:expandtab:ai - * - * iptstate.cc - * IPTables State - * - * ----------------------------------- - * - * Copyright (C) 2002 - present Phil Dibowitz - * - * This software is provided 'as-is', without any express or - * implied warranty. In no event will the authors be held - * liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it - * and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * ----------------------------------- - * - * The idea of statetop comes from IP Filter by Darren Reed. - * - * This package's main purpose is to provide a state-top type - * interface for IP Tables. I've added in the "single run" - * option since there's no nice way to do that either. - * - * NOTE: If you are planning on packaging and/or submitting my software for/to - * a Linux distribution, I would appreciate a heads up. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// There are no C++-ified versions of these. -#include -#include -extern "C" { - #include -}; -#include -#include -#include -using namespace std; - -#define VERSION "2.2.6" -/* - * MAXCONS is set to 16k, the default number of states in iptables. Generally - * speaking the ncurses pad is this many lines long, but since ncurses - * uses a short for their dimensions, a pad can never be longer than 32767. - * Thus we define both of these values and NLINES as the lesser of the two. - */ -#define MAXCONS 16384 -#define MAXLINES 32767 -#if MAXCONS < MAXLINES - #define NLINES MAXCONS -#else - #define NLINES MAXLINES -#endif -#define MAXFIELDS 20 -// This is the default format string if we don't dynamically determine it -#define DEFAULT_FORMAT "%-21s %-21s %-7s %-12s %-9s\n" -// The following MUST be the same as the above -#define DEFAULT_SRC 21 -#define DEFAULT_DST 21 -#define DEFAULT_PROTO 7 -#define DEFAULT_STATE 12 -#define DEFAULT_TTL 9 -// This is the format string for the "totals" line, always. -#define TOTALS_FORMAT \ - "Total States: %i -- TCP: %i UDP: %i ICMP: %i Other: %i (Filtered: %i)\n" -// Options for truncating from the front or the back -#define TRUNC_FRONT 0 -#define TRUNC_END 1 -// maxlength for string we pass to inet_ntop() -#define NAMELEN 100 -// Sorting options -#define SORT_SRC 0 -#define SORT_SRC_PT 1 -#define SORT_DST 2 -#define SORT_DST_PT 3 -#define SORT_PROTO 4 -#define SORT_STATE 5 -#define SORT_TTL 6 -#define SORT_BYTES 7 -#define SORT_PACKETS 8 -#define SORT_MAX 8 - -/* - * GLOBAL CONSTANTS - */ - -/* - * GLOBAL VARS - */ -int sort_factor = 1; -bool need_resize = false; - -/* shameless stolen from libnetfilter_conntrack_tcp.c */ -static const char *states[] = { - "NONE", - "SYN_SENT", - "SYN_RECV", - "ESTABLISHED", - "FIN_WAIT", - "CLOSE_WAIT", - "LAST_ACK", - "TIME_WAIT", - "CLOSE", - "LISTEN" -}; - - -/* - * STRUCTS - */ -// One state-table entry -struct tentry_t { - string proto, state, ttl, sname, dname, spname, dpname; - in6_addr src, dst; - uint8_t family; - unsigned long srcpt, dstpt, bytes, packets, s; -}; -// x/y of the terminal window -struct screensize_t { - unsigned int x, y; -}; -// Struct 'o flags -struct flags_t { - bool single, totals, lookup, skiplb, staticsize, skipdns, tag_truncate, - filter_src, filter_dst, filter_srcpt, filter_dstpt, noscroll, nocolor, - counters; -}; -// Struct 'o counters -struct counters_t { - unsigned int total, tcp, udp, icmp, other, skipped; -}; -// Various filters to be applied pending the right flags in flags_t -struct filters_t { - in6_addr src, dst; - uint8_t srcfam, dstfam; - unsigned long srcpt, dstpt; -}; -// The max-length of fields in the stable table -struct max_t { - unsigned int src, dst, proto, state, ttl; - unsigned long bytes, packets; -}; -struct hook_data { - vector *stable; - flags_t *flags; - max_t *max; - counters_t *counts; - const filters_t *filters; -}; - - -/* - * GENERAL HELPER FUNCTIONS - */ - -/* - * split a string into two strings based on the first occurance - * of any character - */ -void split(char s, string line, string &p1, string &p2) -{ - int pos = line.find(s); - p1 = line.substr(0, pos); - p2 = line.substr(pos+1, line.size()-pos); -} - -/* - * split a string into an array of strings based on - * any character - */ -void splita(char s, string line, vector &result) -{ - int pos, size; - int i=0; - string temp, temp1; - temp = line; - while ((temp.find(s) != string::npos) && (i < MAXFIELDS-1)) { - pos = temp.find(s); - result[i] = temp.substr(0, pos); - size = temp.size(); - temp = temp.substr(pos+1, size-pos-1); - if (result[i] != "") { - i++; - } - } - result[i] = temp; -} - -/* - * This determines the length of an integer (i.e. number of digits) - */ -unsigned int digits(unsigned long x) -{ - return (unsigned int) floor(log10((double)x))+1; -} - -/* - * Check to ensure an IP is valid - */ -bool check_ip(const char *arg, in6_addr *addr, uint8_t *family) -{ - int ret; - ret = inet_pton(AF_INET6, arg, addr); - if (ret) { - *family = AF_INET6; - return true; - } - ret = inet_pton(AF_INET, arg, addr); - if (ret) { - *family = AF_INET; - return true; - } - return false; -} - -/* - * The help - */ -void version() -{ - cout << "IPTables State Top Version " << VERSION << endl << endl; -} - -void help() -{ - cout << "IPTables State Top Version " << VERSION << endl; - cout << "Usage: iptstate []\n\n"; - cout << " -c, --no-color\n"; - cout << "\tToggle color-code by protocol\n\n"; - cout << " -C, --counters\n"; - cout << "\tToggle display of bytes/packets counters\n\n"; - cout << " -d, --dst-filter \n"; - cout << "\tOnly show states with a destination of \n"; - cout << "\tNote, that this must be an IP, hostname matching is" - << " not yet supported.\n\n"; - cout << " -D --dstpt-filter \n"; - cout << "\tOnly show states with a destination port of \n\n"; - cout << " -h, --help\n"; - cout << "\tThis help message\n\n"; - cout << " -l, --lookup\n"; - cout << "\tShow hostnames instead of IP addresses. Enabling this will also" - << " enable\n\t-L to prevent an ever-growing number of DNS requests.\n\n"; - cout << " -m, --mark-truncated\n"; - cout << "\tMark truncated hostnames with a '+'\n\n"; - cout << " -o, --no-dynamic\n"; - cout << "\tToggle dynamic formatting\n\n"; - cout << " -L, --no-dns\n"; - cout << "\tSkip outgoing DNS lookup states\n\n"; - cout << " -f, --no-loopback\n"; - cout << "\tFilter states on loopback\n\n"; - cout << " -p, --no-scroll\n"; - cout << "\tNo scrolling (don't use a \"pad\")\n\n"; - cout << " -r, --reverse\n"; - cout << "\tReverse sort order\n\n"; - cout << " -R, --rate \n"; - cout << "\tRefresh rate, followed by rate in seconds\n"; - cout << "\tNote: For statetop, not applicable for -s\n\n"; - cout << " -1, --single\n"; - cout << "\tSingle run (no curses)\n\n"; - cout << " -b, --sort \n"; - cout << "\tThis determines what column to sort by. Options:\n"; - cout << "\t d: Destination IP (or Name)\n"; - cout << "\t p: Protocol\n"; - cout << "\t s: State\n"; - cout << "\t t: TTL\n"; - cout << "\t b: Bytes\n"; - cout << "\t P: Packets\n"; - cout << "\tTo sort by Source IP (or Name), don't use -b.\n"; - cout << "\tNote that bytes/packets are only available when" - << " supported in the kernel,\n"; - cout << "\tand enabled with -C\n\n"; - cout << " -s, --src-filter \n"; - cout << "\tOnly show states with a source of \n"; - cout << "\tNote, that this must be an IP, hostname matching is" - << " not yet supported.\n\n"; - cout << " -S, --srcpt-filter \n"; - cout << "\tOnly show states with a source port of \n\n"; - cout << " -t, --totals\n"; - cout << "\tToggle display of totals\n\n"; - cout << "See man iptstate(8) or the interactive help for more" - << " information.\n"; - exit(0); -} - -/* - * Resolve hostnames - */ -void resolve_host(const uint8_t &family, const in6_addr &ip, string &name) -{ - struct hostent *hostinfo = NULL; - - if ((hostinfo = gethostbyaddr((char *)&ip, sizeof(ip), family)) != NULL) { - name = hostinfo->h_name; - } else { - char str[NAMELEN]; - name = inet_ntop(family, (void *)&ip, str, NAMELEN-1) ; - } -} - -void resolve_port(const unsigned int &port, string &name, const string &proto) -{ - struct servent *portinfo = NULL; - - if ((portinfo = getservbyport(htons(port), proto.c_str())) != NULL) { - name = portinfo->s_name; - } else { - ostringstream buf; - buf.str(""); - buf << port; - name = buf.str(); - } -} - -/* - * If lookup mode is on, we lookup the names and put them in the structure. - * - * If lookup mode is not on, we generate strings of the addresses and put - * those in the structure. - * - * Finally, we update the max_t structure. - * - * NOTE: We stringify addresses largely because in the IPv6 case we need - * to treat them like truncate-able strings. - */ -void stringify_entry(tentry_t *entry, max_t &max, const flags_t &flags) -{ - unsigned int size = 0; - ostringstream buffer; - char tmp[NAMELEN]; - - bool have_port = entry->proto == "tcp" || entry->proto == "udp"; - if (!have_port) { - entry->spname = entry->dpname = ""; - } - - if (flags.lookup) { - resolve_host(entry->family, entry->src, entry->sname); - resolve_host(entry->family, entry->dst, entry->dname); - if (have_port) { - resolve_port(entry->srcpt, entry->spname, entry->proto); - resolve_port(entry->dstpt, entry->dpname, entry->proto); - } - } else { - buffer << inet_ntop(entry->family, (void*)&(entry->src), tmp, NAMELEN-1); - entry->sname = buffer.str(); - buffer.str(""); - buffer << inet_ntop(entry->family, (void*)&(entry->dst), tmp, NAMELEN-1); - entry->dname = buffer.str(); - buffer.str(""); - if (have_port) { - buffer << entry->srcpt; - entry->spname = buffer.str(); - buffer.str(""); - buffer << entry->dstpt; - entry->dpname = buffer.str(); - buffer.str(""); - } - } - - size = entry->sname.size() + entry->spname.size() + 1; - if (size > max.src) - max.src = size; - - size = entry->dname.size() + entry->dpname.size() + 1; - if (size > max.dst) - max.dst = size; -} - - -/* - * SORT FUNCTIONS - */ -bool src_sort(tentry_t *one, tentry_t *two) -{ - /* - * memcmp() will properly sort v4 or v6 addresses, but not cross-family - * (presumably because of garbage in the top 96 bytes when you store - * a v4 address in a in6_addr), so we sort by family and then memcmp() - * within the same family. - */ - if (one->family == two->family) { - return memcmp(one->src.s6_addr, two->src.s6_addr, 16) * sort_factor < 0; - } else if (one->family == AF_INET) { - return sort_factor > 0; - } else { - return sort_factor < 0; - } -} - -bool srcname_sort(tentry_t *one, tentry_t *two) -{ - return one->sname.compare(two->sname) * sort_factor < 0; -} - -bool dst_sort(tentry_t *one, tentry_t *two) -{ - // See src_sort() for details - if (one->family == two->family) { - return memcmp(one->dst.s6_addr, two->dst.s6_addr, 16) * sort_factor < 0; - } else if (one->family == AF_INET) { - return sort_factor > 0; - } else { - return sort_factor < 0; - } -} - -bool dstname_sort(tentry_t *one, tentry_t *two) -{ - return one->dname.compare(two->dname) * sort_factor < 0; -} - -/* - * int comparison that takes care of sort_factor - * used for ports, bytes, etc... - */ -bool cmpint(int one, int two) -{ - return (sort_factor > 0) ? one < two : one > two; -} - -bool srcpt_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->srcpt, two->srcpt); -} - -bool dstpt_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->dstpt, two->dstpt); -} - -bool proto_sort(tentry_t *one, tentry_t *two) -{ - return one->proto.compare(two->proto) * sort_factor < 0; -} - -bool state_sort(tentry_t *one, tentry_t *two) -{ - return one->state.compare(two->state) * sort_factor < 0; -} - -bool ttl_sort(tentry_t *one, tentry_t *two) -{ - return one->ttl.compare(two->ttl) * sort_factor < 0; -} - -bool bytes_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->bytes, two->bytes); -} - -bool packets_sort(tentry_t *one, tentry_t *two) -{ - return cmpint(one->packets, two->packets); -} - -/* - * CURSES HELPER FUNCTIONS - */ - -/* - * Finish-up for curses environment - */ -void end_curses() -{ - curs_set(1); - nocbreak(); - endwin(); - cout << endl; -} - -/* - * SIGWINCH signal handler. - */ -void winch_handler(int sig) -{ - sigset_t mask_set; - sigset_t old_set; - // Reset signal handler - signal(28, winch_handler); - // ignore this signal for a bit - sigfillset(&mask_set); - sigprocmask(SIG_SETMASK, &mask_set, &old_set); - - need_resize = true; -} - -/* - * SIGKILL signal handler - */ -void kill_handler(int sig) -{ - end_curses(); - printf("Caught signal %d, cleaning up.\n", sig); - exit(0); -} - -/* - * Start-up for curses environment - * - * NOTE: That by default we create a pad. A pad is a special type of window that - * can be bigger than the screen. See the comments in interactive_help() - * below for how to use it and how it works. - * - * However, pad's lack the double-buffering and other features of standard - * ncurses windows and thus can appear slower. Thus we allow the user to - * downgrade to standard windows if they choose. See the comments - * switch_scroll() for more details. - * - */ -static WINDOW* start_curses(flags_t &flags) -{ - int y, x; - initscr(); - cbreak(); - noecho(); - halfdelay(1); - - /* - * If we're starting curses, we care about SIGWNCH, SIGINT, and SIGTERM - * so this seems like as good a place as any to setup our signal - * handler. - */ - // Resize - signal(28, winch_handler); - // Shutdown - signal(2, kill_handler); - signal(15, kill_handler); - - if (has_colors()) { - start_color(); - // for tcp - init_pair(1, COLOR_GREEN, COLOR_BLACK); - // for udp - init_pair(2, COLOR_YELLOW, COLOR_BLACK); - // for icmp - init_pair(3, COLOR_RED, COLOR_BLACK); - // for prompts - init_pair(4, COLOR_BLACK, COLOR_RED); - // for the currently selected row - init_pair(5, COLOR_BLACK, COLOR_GREEN); - init_pair(6, COLOR_BLACK, COLOR_YELLOW); - init_pair(7, COLOR_BLACK, COLOR_RED); - } else { - flags.nocolor = true; - } - - if (!flags.noscroll) { - getmaxyx(stdscr, y, x); - return newpad(NLINES, x); - } - return stdscr; -} - -/* - * Figure out the best way to get the screensize_t, and then do it - */ -screensize_t get_size(const bool &single) -{ - int maxx = 0, maxy = 0; - if (!single) { - getmaxyx(stdscr, maxy, maxx); - } else { - maxx = 72; - if (getenv("COLS")) - maxx=atoi(getenv("COLS")); - } - - screensize_t a; - a.x = maxx; - a.y = maxy; - - return a; -} - -/* - * Error function for screen being too small. - */ -void term_too_small() -{ - end_curses(); - cout << "I'm sorry, your terminal must be atleast 72 columns" - << "wide to run iptstate\n"; - exit(3); -} - -/* - * This is one of those "well, I should impliment it to be complete, but - * I doubt it'll get used very often features." It was a nice-thing-to-do - * to impliment the ability for iptstate to use stdscr instead of a pad - * as this provides the doulbe-buffering and other features that pads - * do not. This is probably useful to a small subset of users. It's pretty - * unlikely people will want to interactively want to change this during - * runtime, but since I implimented noscroll, it's only proper to impliment - * interactive toggling. - * - * TECH NOTE: - * This is just a note for myself so I remember why this is the way it is. - * - * The syntax WINDOW *&mainwin is right, thought it's doing what you'd - * expect WINDOW &*mainwin to do... except that's invalid. So it's just a - * &foo pass on a WINDOW*. - */ -void switch_scroll(flags_t &flags, WINDOW *&mainwin) -{ - int x, y; - if (flags.noscroll) { - getmaxyx(stdscr, y, x); - // remove stuff from the bottom window - erase(); - // build pad - wmove(mainwin, 0, 0); - mainwin = newpad(NLINES, x); - wmove(mainwin, 0, 0); - keypad(mainwin,1); - halfdelay(1); - } else { - // delete pad - delwin(mainwin); - mainwin = stdscr; - keypad(mainwin,1); - halfdelay(1); - } - - flags.noscroll = !flags.noscroll; -} - -/* - * Prompt the user for something, and get an answer. - */ -void get_input(WINDOW *win, string &input, const string &prompt, - const flags_t &flags) -{ - - /* - * This function is here so that we can prompt and get an answer - * and the user can get an echo of what they're inputting. This is - * already a non-straight-forward thing to do in cbreak() mode, but - * it turns out that using pads makes it even more difficult. - * - * It's worth noting that I tried doin a simple waddch() and then - * prefresh as one would expect, but it didn't echo the chars. - * Because we're using pads I have to do a waddchar() and then - * a prefresh(). - * - * Note, that the documentation says that if we're using waddchar() - * we shouldn't need any refresh, but it doesn't echo without it. - * This is probably because waddch() calls wrefresh() instead of - * prefresh(). - */ - - input = ""; - int x, y; - getmaxyx(stdscr, y, x); - WINDOW *cmd = subpad(win, 1, x, 0, 0); - if (!flags.nocolor) - wattron(cmd, COLOR_PAIR(4)); - keypad(cmd, true); - wprintw(cmd, prompt.c_str()); - wclrtoeol(cmd); - prefresh(cmd, 0, 0, 0, 0, 0, x); - - - int ch; - int charcount = 0; - echo(); - nodelay(cmd,0); - - while (1) { - ch = wgetch(cmd); - switch (ch) { - case '\n': - // 7 is ^G - case 7: - if (ch == 7) - input = ""; - if (!flags.nocolor) - wattroff(cmd, COLOR_PAIR(4)); - delwin(cmd); - noecho(); - wmove(win, 0, 0); - return; - break; - // 8 is shift-backspace - just incase - case KEY_BACKSPACE: - case 8: - if (charcount > 0) { - input = input.substr(0, input.size()-1); - wechochar(cmd, '\b'); - wechochar(cmd, ' '); - wechochar(cmd, '\b'); - charcount--; - } - break; - case ERR: - continue; - break; - default: - input += ch; - charcount++; - wechochar(cmd, ch); - } - prefresh(cmd, 0, 0, 0, 0, 0, x); - } -} - -/* - * Create a window with noticable colors (if colors are enabled) - * and print a warning. Means curses_warning. - */ -void c_warn(WINDOW *win, const string &warning, const flags_t &flags) -{ - - /* - * This function is here so that we can warn a user in curses, - * usually about bad input. - */ - - int x, y; - getmaxyx(stdscr, y, x); - WINDOW *warn = subpad(win, 1, x, 0, 0); - if (!flags.nocolor) - wattron(warn, COLOR_PAIR(4)); - wprintw(warn, warning.c_str()); - wprintw(warn, " Press any key to continue..."); - wclrtoeol(warn); - prefresh(warn, 0, 0, 0, 0, 0, x); - while ((y = getch())) { - if (y != ERR) { - break; - } - prefresh(warn, 0, 0, 0, 0, 0, x); - } - if (!flags.nocolor) - wattroff(warn, COLOR_PAIR(4)); - delwin(warn); - noecho(); - wmove(win, 0, 0); - return; -} - -/* - * Initialize the max_t structure with some sane defaults. We'll grow - * them later as needed. - */ -void initialize_maxes(max_t &max, flags_t &flags) -{ - /* - * For NO lookup: - * src/dst IP can be no bigger than 21 chars: - * IP (max of 15) + colon (1) + port (max of 5) = 21 - * - * For lookup: - * if it's a name, we start with the width of the header, and we can - * grow from there as needed. - */ - if (flags.lookup) { - max.src = 6; - max.dst = 11; - } else { - max.src = max.dst = 21; - } - /* - * The proto starts at 3, since tcp/udp are the most common, but will - * grow if we see bigger proto strings such as "ICMP". - */ - max.proto = 3; - /* - * "ESTABLISHED" is generally the longest state, we almost always have - * several, so we'll start with this. It also looks really bad if state - * is changing size a lot, so we start with a common minumum. - */ - max.state = 11; - // TTL we statically make 7: xxx:xx:xx - max.ttl = 9; - - // Start with something sane - max.bytes = 2; - max.packets = 2; -} - -/* - * The actual work of handling a resize. - */ -void handle_resize(WINDOW *&win, const flags_t &flags, screensize_t &ssize) -{ - if (flags.noscroll) { - endwin(); - refresh(); - return; - } - - /* - * OK, the above case without pads is easy. But pads is tricker. - * In order to properly handle SIGWINCH we need to: - * - * - Tear down the pad (delwin) - * - Reset the terminal settings to non-visual mode (endwin) - * - Return to visual mode (refresh) - * - Get the new size (getmaxyx) - * - Rebuild the pad - * - * Note that we don't get the new size without the endwin/refresh - * and thus the new pad doesn't get built right, and everything wraps. - * - * This order must be preserved. - */ - - /* - * Tear down... - */ - delwin(win); - endwin(); - /* - * Start up... - */ - refresh(); - getmaxyx(stdscr, ssize.y, ssize.x); - win = newpad(NLINES, ssize.x); - keypad(win, true); - wmove(win, 0, 0); - - return; -} - -/* - * Take in a 'curr' value, and delete a given conntrack - */ -void delete_state(WINDOW *&win, const tentry_t *entry, const flags_t &flags) -{ - struct nfct_handle *cth; - struct nf_conntrack *ct; - cth = nfct_open(CONNTRACK, 0); - ct = nfct_new(); - int ret; - string response; - char str[NAMELEN]; - string src, dst; - src = inet_ntop(entry->family, (void *)&(entry->src), str, NAMELEN-1); - dst = inet_ntop(entry->family, (void *)&(entry->dst), str, NAMELEN-1); - - ostringstream msg; - msg.str(""); - msg << "Deleting state: "; - if (entry->proto == "tcp" || entry->proto == "udp") { - msg << src << ":" << entry->srcpt << " -> " << dst << ":" << entry->dstpt; - } else { - msg << src << " -> " << dst; - } - msg << " -- Are you sure? (y/n)"; - get_input(win, response, msg.str(), flags); - - if (response != "y" && response != "Y" && response != "yes" && - response != "YES" && response != "Yes") { - c_warn(win, "NOT deleting state.", flags); - return; - } - - nfct_set_attr_u8(ct, ATTR_ORIG_L3PROTO, entry->family); - - if (entry->family == AF_INET) { - nfct_set_attr(ct, ATTR_ORIG_IPV4_SRC, (void *)&(entry->src.s6_addr)); - nfct_set_attr(ct, ATTR_ORIG_IPV4_DST, (void *)&(entry->dst.s6_addr)); - } else if (entry->family == AF_INET6) { - nfct_set_attr(ct, ATTR_ORIG_IPV6_SRC, (void *)&(entry->src.s6_addr)); - nfct_set_attr(ct, ATTR_ORIG_IPV6_DST, (void *)&(entry->dst.s6_addr)); - } - - // undo our space optimization so the kernel can find the state. - protoent *pn; - if (entry->proto == "icmp6") - pn = getprotobyname("ipv6-icmp"); - else - pn = getprotobyname(entry->proto.c_str()); - - nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, pn->p_proto); - - if (entry->proto == "tcp" || entry->proto == "udp") { - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, htons(entry->srcpt)); - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, htons(entry->dstpt)); - } else if (entry->proto == "icmp" || entry->proto == "icmp6") { - string type, code, id, tmp; - split('/', entry->state, type, tmp); - split(' ', tmp, code, tmp); - split('(', tmp, tmp, id); - split(')', id, id, tmp); - - nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, atoi(type.c_str())); - nfct_set_attr_u8(ct, ATTR_ICMP_CODE, atoi(code.c_str())); - nfct_set_attr_u16(ct, ATTR_ICMP_ID, atoi(id.c_str())); - } - - ret = nfct_query(cth, NFCT_Q_DESTROY, ct); - if (ret < 0) { - string msg = "Failed to delete state: "; - msg += strerror(errno); - c_warn(win, msg.c_str(), flags); - } - -} - - -/* - * CORE FUNCTIONS - */ - -/* - * Callback for conntrack - */ -int conntrack_hook(enum nf_conntrack_msg_type nf_type, struct nf_conntrack *ct, - void *tmp) -{ - - /* - * start by getting our struct back - */ - struct hook_data *data = static_cast(tmp); - - /* - * and pull out the pieces - */ - vector *stable = data->stable; - flags_t *flags = data->flags; - max_t *max = data->max; - counters_t *counts = data->counts; - const filters_t *filters = data->filters; - - // our table entry - tentry_t *entry = new tentry_t; - - // some vars - struct protoent* pe = NULL; - int seconds, minutes, hours; - char ttlc[11]; - ostringstream buffer; - - /* - * Clear the entry - */ - entry->sname = ""; - entry->dname = ""; - entry->srcpt = 0; - entry->dstpt = 0; - entry->proto = ""; - entry->ttl = ""; - entry->state = ""; - - /* - * First, we read stuff into the array that's always the - * same regardless of protocol - */ - - short int pr = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); - pe = getprotobynumber(pr); - if (pe == NULL) { - buffer << pr; - entry->proto = buffer.str(); - buffer.str(""); - } else { - entry->proto = pe->p_name; - /* - * if proto is "ipv6-icmp" we can just say "icmp6" to save space... - * it's more common/standard anyway - */ - if (entry->proto == "ipv6-icmp") - entry->proto = "icmp6"; - } - - // ttl - seconds = nfct_get_attr_u32(ct, ATTR_TIMEOUT); - minutes = seconds/60; - hours = minutes/60; - minutes = minutes%60; - seconds = seconds%60; - // Format it with snprintf and store it in the table - snprintf(ttlc,11, "%3i:%02i:%02i", hours, minutes, seconds); - entry->ttl = ttlc; - - entry->family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO); - // Everything has addresses - if (entry->family == AF_INET) { - memcpy(entry->src.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC), - sizeof(uint8_t[16])); - memcpy(entry->dst.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV4_DST), - sizeof(uint8_t[16])); - } else if (entry->family == AF_INET6) { - memcpy(entry->src.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC), - sizeof(uint8_t[16])); - memcpy(entry->dst.s6_addr, nfct_get_attr(ct, ATTR_ORIG_IPV6_DST), - sizeof(uint8_t[16])); - } else { - fprintf(stderr, "UNKNOWN FAMILY!\n"); - exit(1); - } - - // Counters (summary, in + out) - entry->bytes = nfct_get_attr_u32(ct, ATTR_ORIG_COUNTER_BYTES) + - nfct_get_attr_u32(ct, ATTR_REPL_COUNTER_BYTES); - entry->packets = nfct_get_attr_u32(ct, ATTR_ORIG_COUNTER_PACKETS) + - nfct_get_attr_u32(ct, ATTR_REPL_COUNTER_PACKETS); - - if (digits(entry->bytes) > max->bytes) { - max->bytes = digits(entry->bytes); - } - if (digits(entry->packets) > max->packets) { - max->packets = digits(entry->packets); - } - - if (entry->proto.size() > max->proto) - max->proto = entry->proto.size(); - - // OK, proto dependent stuff - if (entry->proto == "tcp" || entry->proto == "udp") { - entry->srcpt = htons(nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); - entry->dstpt = htons(nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); - } - - if (entry->proto == "tcp") { - entry->state = states[nfct_get_attr_u8(ct, ATTR_TCP_STATE)]; - counts->tcp++; - } else if (entry->proto == "udp") { - entry->state = ""; - counts->udp++; - } else if (entry->proto == "icmp" || entry->proto == "icmp6") { - buffer.str(""); - buffer << (int)nfct_get_attr_u8(ct, ATTR_ICMP_TYPE) << "/" - << (int)nfct_get_attr_u8(ct, ATTR_ICMP_CODE) << " (" - << nfct_get_attr_u16(ct, ATTR_ICMP_ID) << ")"; - entry->state = buffer.str(); - counts->icmp++; - if (entry->state.size() > max->state) - max->state = entry->state.size(); - } else { - counts->other++; - } - - /* - * FILTERING - */ - - /* - * FIXME: Filtering needs to be pulled into it's own function. - */ - struct in_addr lb; - struct in6_addr lb6; - inet_pton(AF_INET, "127.0.0.1", &lb); - inet_pton(AF_INET6, "::1", &lb6); - size_t entrysize = entry->family == AF_INET - ? sizeof(in_addr) - : sizeof(in6_addr); - if (flags->skiplb && (entry->family == AF_INET - ? !memcmp(&(entry->src), &lb, sizeof(in_addr)) - : !memcmp(&(entry->src), &lb6, sizeof(in6_addr)))) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->skipdns && (entry->dstpt == 53)) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_src && (memcmp(&(entry->src), &(filters->src), entrysize))) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_srcpt && (entry->srcpt != filters->srcpt)) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_dst && (memcmp(&(entry->dst), &(filters->dst), entrysize))) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - if (flags->filter_dstpt && (entry->dstpt != filters->dstpt)) { - counts->skipped++; - return NFCT_CB_CONTINUE; - } - - /* - * RESOLVE - */ - - // Resolve names - if necessary - or generate strings of address, - // and calculate max sizes - stringify_entry(entry, *max, *flags); - - /* - * Add this to the array - */ - stable->push_back(entry); - - return NFCT_CB_CONTINUE; -} - -/* - * This is the core of this program - build a table of states. - * - * For the new libnetfilter_conntrack code, the bulk of build_table was moved - * to the conntrack callback function. - */ -void build_table(flags_t &flags, const filters_t &filters, vector - &stable, counters_t &counts, max_t &max) -{ - /* - * Variables - */ - int res=0; - vector fields(MAXFIELDS); - static struct nfct_handle *cth; - u_int8_t family = AF_UNSPEC; - - /* - * This is the ugly struct for the nfct hook, that holds pointers to - * all of the things the callback will need to fill our table - */ - struct hook_data hook; - hook.stable = &stable; - hook.flags = &flags; - hook.max = &max; - hook.counts = &counts; - hook.filters = &filters; - - /* - * Initialization - */ - // Nuke the tentry_t's we made before deleting the vector of pointers - for ( - vector::iterator it = stable.begin(); - it != stable.end(); - it++ - ) { - delete *it; - } - stable.clear(); - counts.tcp = counts.udp = counts.icmp = counts.other = counts.skipped = 0; - - cth = nfct_open(CONNTRACK, 0); - if (!cth) { - end_curses(); - printf("ERROR: couldn't establish conntrack connection\n"); - exit(2); - } - nfct_callback_register(cth, NFCT_T_ALL, conntrack_hook, (void *)&hook); - res = nfct_query(cth, NFCT_Q_DUMP, &family); - if (res < 0) { - end_curses(); - printf("ERROR: Couldn't retreive conntrack table: %s\n", strerror(errno)); - exit(2); - } - nfct_close(cth); -} - -/* - * This sorts the table based on the current sorting preference - */ -void sort_table(const int &sortby, const bool &lookup, const int &sort_factor, - vector &stable, string &sorting) -{ - switch (sortby) { - case SORT_SRC: - if (lookup) { - std::sort(stable.begin(), stable.end(), srcname_sort); - sorting = "SrcName"; - } else { - std::sort(stable.begin(), stable.end(), src_sort); - sorting = "SrcIP"; - } - break; - - case SORT_SRC_PT: - std::sort(stable.begin(), stable.end(), srcpt_sort); - sorting = "SrcPort"; - break; - - case SORT_DST: - if (lookup) { - std::sort(stable.begin(), stable.end(), dstname_sort); - sorting = "DstName"; - } else { - std::sort(stable.begin(), stable.end(), dst_sort); - sorting = "DstIP"; - } - break; - - case SORT_DST_PT: - std::sort(stable.begin(), stable.end(), dstpt_sort); - sorting = "DstPort"; - break; - - case SORT_PROTO: - std::sort(stable.begin(), stable.end(), proto_sort); - sorting = "Prt"; - break; - - case SORT_STATE: - std::sort(stable.begin(), stable.end(), state_sort); - sorting = "State"; - break; - - case SORT_TTL: - std::sort(stable.begin(), stable.end(), ttl_sort); - sorting = "TTL"; - break; - - case SORT_BYTES: - std::sort(stable.begin(), stable.end(), bytes_sort); - sorting = "Bytes"; - break; - - case SORT_PACKETS: - std::sort(stable.begin(), stable.end(), packets_sort); - sorting = "Packets"; - break; - - default: - //we should never get here - sorting = "??unknown??"; - break; - - } //switch - - if (sort_factor == -1) - sorting = sorting + " reverse"; - -} - -void print_headers(const flags_t &flags, const string &format, - const string &sorting, const filters_t &filters, - const counters_t &counts, const screensize_t &ssize, - int table_size, WINDOW *mainwin) -{ - if (flags.single) { - cout << "IP Tables State Top -- Sort by: " << sorting << endl; - } else { - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - wmove(mainwin,0, ssize.x/2-15); - wattron(mainwin, A_BOLD); - wprintw(mainwin, "IPTState - IPTables State Top\n"); - - wprintw(mainwin, "Version: "); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-13s", VERSION); - - wattron(mainwin, A_BOLD); - wprintw(mainwin, "Sort: "); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-16s", sorting.c_str()); - - wattron(mainwin, A_BOLD); - wprintw(mainwin, "b"); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-19s", ": change sorting"); - - wattron(mainwin, A_BOLD); - wprintw(mainwin, "h"); - wattroff(mainwin, A_BOLD); - wprintw(mainwin, "%-s\n", ": help"); - } - - /* - * If enabled, print totals - */ - if (flags.totals) { - if (flags.single) - printf(TOTALS_FORMAT, table_size+counts.skipped, counts.tcp, counts.udp, - counts.icmp, counts.other, counts.skipped); - else - wprintw(mainwin, TOTALS_FORMAT, table_size+counts.skipped, counts.tcp, - counts.udp, counts.icmp, counts.other, counts.skipped); - } - - /* - * If any, print filters - */ - char tmp[NAMELEN]; - if (flags.filter_src || flags.filter_dst || flags.filter_srcpt - || flags.filter_dstpt) { - - if (flags.single) { - printf("Filters: "); - } else { - wattron(mainwin, A_BOLD); - wprintw(mainwin, "Filters: "); - wattroff(mainwin, A_BOLD); - } - - bool printed_a_filter = false; - - if (flags.filter_src) { - inet_ntop(filters.srcfam, &filters.src, tmp, NAMELEN-1); - if (flags.single) - printf("src: %s", tmp); - else - wprintw(mainwin, "src: %s", tmp); - printed_a_filter = true; - } - if (flags.filter_srcpt) { - if (printed_a_filter) { - if (flags.single) - printf(", "); - else - waddstr(mainwin, ", "); - } - if (flags.single) - printf("sport: %lu", filters.srcpt); - else - wprintw(mainwin, "sport: %lu", filters.srcpt); - printed_a_filter = true; - } - if (flags.filter_dst) { - if (printed_a_filter) { - if (flags.single) - printf(", "); - else - waddstr(mainwin, ", "); - } - inet_ntop(filters.dstfam, &filters.dst, tmp, NAMELEN-1); - if (flags.single) - printf("dst: %s", tmp); - else - wprintw(mainwin, "dst: %s", tmp); - printed_a_filter = true; - } - if (flags.filter_dstpt) { - if (printed_a_filter) { - if (flags.single) - printf(", "); - else - waddstr(mainwin, ", "); - } - if (flags.single) - printf("dport: %lu", filters.dstpt); - else - wprintw(mainwin, "dport: %lu", filters.dstpt); - printed_a_filter = true; - } - if (flags.single) - printf("\n"); - else - wprintw(mainwin, "\n"); - } - - /* - * Print column headers - */ - if (flags.single) { - if (flags.counters) - printf(format.c_str(), "Source", "Destination", "Prt", "State", "TTL", - "B", "P"); - else - printf(format.c_str(), "Source", "Destination", "Prt", "State", "TTL"); - } else { - wattron(mainwin, A_BOLD); - if (flags.counters) - wprintw(mainwin, format.c_str(), "Source", "Destination", "Prt", - "State", "TTL", "B", "P"); - else - wprintw(mainwin, format.c_str(), "Source", "Destination", "Prt", - "State", "TTL"); - wattroff(mainwin, A_BOLD); - } - -} - - -void truncate(string &string, int length, bool mark, char direction) -{ - int s = (direction == 'f') ? string.size() - length : 0; - - string = string.substr(s, length); - if (mark) { - int m = (direction == 'f') ? 0 : string.size() - 1; - string[m] = '+'; - } -} - -/* - * Based on the format pre-chosen, truncate src/dst as needed, and then - * generate the host:port strings and drop them off in the src/dst string - * objects passed in. - */ -void format_src_dst(tentry_t *table, string &src, string &dst, - const flags_t &flags, const max_t &max) -{ - ostringstream buffer; - bool have_port = table->proto == "tcp" || table->proto == "udp"; - char direction; - unsigned int length; - - // What length would we currently use? - length = table->sname.size(); - if (have_port) - length += table->spname.size() + 1; - - // If it's too long, figure out how room we have and truncate it - if (length > max.src) { - length = max.src; - if (have_port) - length -= 1 + table->spname.size(); - direction = (flags.lookup) ? 'e' : 'f'; - truncate(table->sname, length, flags.tag_truncate, direction); - } - - // ... and repeat - length = table->dname.size(); - if (have_port) - length += table->dpname.size() + 1; - if (length > max.dst) { - length = max.dst; - if (have_port) - length -= 1 + table->dpname.size(); - direction = (flags.lookup) ? 'f' : 'e'; - truncate(table->dname, length, flags.tag_truncate, direction); - } - - buffer << table->sname; - if (have_port) - buffer << ":" << table->spname; - src = buffer.str(); - buffer.str(""); - buffer << table->dname; - if (have_port) - buffer << ":" << table->dpname; - dst = buffer.str(); - buffer.str(""); -} - -/* - * An abstraction of priting a line for both single/curses modes - */ -void printline(tentry_t *table, const flags_t &flags, const string &format, - const max_t &max, WINDOW *mainwin, const bool curr) -{ - ostringstream buffer; - buffer.str(""); - string src, dst, b, p; - - // Generate strings for src/dest, truncating and marking as necessary - format_src_dst(table, src, dst, flags, max); - - if (flags.counters) { - buffer << table->bytes; - b = buffer.str(); - buffer.str(""); - buffer << table->packets; - p = buffer.str(); - buffer.str(""); - } - - if (flags.single) { - if (flags.counters) - printf(format.c_str(), src.c_str(), dst.c_str(), table->proto.c_str(), - table->state.c_str(), table->ttl.c_str(), b.c_str(), p.c_str()); - else - printf(format.c_str(), src.c_str(), dst.c_str(), table->proto.c_str(), - table->state.c_str(), table->ttl.c_str()); - } else { - int color = 0; - if (!flags.nocolor) { - if (table->proto == "tcp") - color = 1; - else if (table->proto == "udp") - color = 2; - else if (table->proto == "icmp" || table->proto == "icmp6") - color = 3; - if (curr) - color += 4; - wattron(mainwin, COLOR_PAIR(color)); - } - if (flags.counters) - wprintw(mainwin, format.c_str(), src.c_str(), dst.c_str(), - table->proto.c_str(), table->state.c_str(), table->ttl.c_str(), - b.c_str(), p.c_str()); - else - wprintw(mainwin, format.c_str(), src.c_str(), dst.c_str(), - table->proto.c_str(), table->state.c_str(), table->ttl.c_str()); - - if (!flags.nocolor && color != 0) - wattroff(mainwin, COLOR_PAIR(color)); - } -} - -/* - * This does all the work of actually printing the table including - * various bits of formatting. It handles both curses and non-curses runs. - */ -void print_table(vector &stable, const flags_t &flags, - const string &format, const string &sorting, - const filters_t &filters, const counters_t &counts, - const screensize_t &ssize, const max_t &max, WINDOW *mainwin, - unsigned int &curr) -{ - /* - * Print headers - */ - print_headers(flags, format, sorting, filters, counts, ssize, stable.size(), - mainwin); - - /* - * Print the state table - */ - unsigned int limit = (stable.size() < NLINES) ? stable.size() : NLINES; - for (unsigned int tmpint=0; tmpint < limit; tmpint++) { - printline(stable[tmpint], flags, format, max, mainwin, (curr == tmpint)); - if (!flags.single && flags.noscroll - && (tmpint >= ssize.y-4 || (flags.totals && tmpint >= ssize.y-5))) - break; - - } - - /* - * We don't want to lave things on the screen we didn't draw - * this time. - */ - if (!flags.single) - wclrtobot(mainwin); - -} - -/* - * Dynamically build a format to fit the most amount of data on the screen - */ -void determine_format(WINDOW *mainwin, max_t &max, screensize_t &ssize, - string &format, flags_t &flags) -{ - - /* - * NOTE: When doing proper dynamic format building, we fill the - * entire screen, so curses puts in a newline for us. However - * with "staticsize" we must add a newline. Also with "single" - * mode we must add it as well since there's no curses there. - * - * Thus DEFAULT_FORMAT (only used for staticsize) has it, and - * at the bottom of this function we add a \n if flags.single - * is set. - */ - if (flags.staticsize) { - format = DEFAULT_FORMAT; - max.src = DEFAULT_SRC; - max.dst = DEFAULT_DST; - max.proto = DEFAULT_PROTO; - max.state = DEFAULT_STATE; - max.ttl = DEFAULT_TTL; - return; - } - - ssize = get_size(flags.single); - - /* The screen must be 85 chars wide to be able to fit in - * counters when we display IP addresses... - * - * in lookup mode, we can truncate names, but in IP mode, - * truncation makes no sense, so we just disable counters if - * we run into this. - */ - if (ssize.x < 85 && flags.counters && !flags.lookup) { - string prompt = "Window too narrow for counters! Disabling."; - c_warn(mainwin, prompt, flags); - flags.counters = false; - } - - /* what's left is the above three, plus 4 spaces - * (one between each of 5 fields) - */ - unsigned int left = ssize.x - max.ttl - max.state - max.proto - 4; - if (flags.counters) - left -= (max.bytes + max.packets + 2); - - /* - * The rest is *prolly* going to be divided between src - * and dst, so we see if that works. If 'left' is odd though - * we give the extra space to src. - */ - unsigned int src, dst; - src = dst = left / 2; - bool left_odd = false; - if ((left % 2) == 1) { - left_odd = true; - src++; - } - if ((max.src + max.dst) < left) { - /* - * This means we can fit without an truncation, but it doesn't - * necessarily mean that we can just give half to src and half - * to dst... so lets figure that out. - */ - - if (max.src < src && max.dst < dst) { - /* - * This case applies if: - * we're even and they both fit in left/2 - * OR - * we're odd and dst fits in left/2 - * and src fits in left/2+1 - * - * Since we've already calculated src/dst that way - * we just combine this check as they both require - * the same outcome. - */ - } else if (left_odd && (src < left / 2) && (dst < left / 2 + 1)) { - /* - * If src can fit in left/2 and dst in left/2+1 - * then we switch them. - */ - src = dst; - dst++; - } else if (max.src > max.dst) { - /* - * If we're here, we can fit them, but we can't fit them - * and still keep the two columns relatively equal. Ah - * well. - * - * Either max gets the bigger chunk and everything else - * to dst... - */ - src = max.src; - dst = left - max.src; - } else { - /* - * ...or the other way around - */ - dst = max.dst; - src = left - max.dst; - } - } else if (max.src < src) { - /* - * If we're here, we do have to truncate, but if one column is - * very small, we should not give it more space than it needs. - */ - src = max.src; - dst = left - max.src; - } else if (max.dst < dst) { - /* - * same as above. - */ - dst = max.dst; - src = left - max.dst; - } - - /* - * If nothing matched, then they're both bigger than left/2, so we'll - * leave the default we set above. - */ - - ostringstream buffer; - buffer << "\%-" << src << "s \%-" << dst << "s \%-" << max.proto << "s \%-" - << max.state << "s \%-" << max.ttl << "s"; - - if (flags.counters) - buffer << " \%-" << max.bytes << "s \%-" << max.packets << "s"; - - if (flags.single) - buffer << "\n"; - - format = buffer.str(); - - max.dst = dst; - max.src = src; -} - -/* - * Interactive help - */ -void interactive_help(const string &sorting, const flags_t &flags, - const filters_t &filters) -{ - - /* - * This is the max we need the pad to be, and thus how - * big we're going to create the pad. - * - * In many cases we'd make the pad very very large and not - * worry about it. However, in this case: - * 1. We know exactly how big we need it to be, and it's - * not going to change interactively. - * 2. We want to draw a "box" around the window and if the - * pad is huge then the box will get drawn around that. - * - * So... we have 32 lines of help, plus a top and bottom border, - * thus maxrows is 34. - * - * Our help text is not wider than 80, so we'll se that standard - * width. - * - * If the screen is bigger than this, we deal with it below. - */ - unsigned int maxrows = 41; - unsigned int maxcols = 80; - - /* - * The actual screen size - */ - screensize_t ssize = get_size(flags.single); - - /* - * If the biggest we think we'll need is smaller than the screen, - * then lets grow the pad to the size of the screen so that the - * main window isn't peeking through. - */ - if (maxrows < ssize.y) - maxrows = ssize.y; - if (maxcols < ssize.x) - maxcols = ssize.x; - - /* - * Where we are withing the pad (for printing). We can't just print - * newlines and expect it to work. Cause, well, it doesn't. You have - * to tell it where on the pad to print, specifically. - */ - unsigned int x, y; - x = y = 0; - - /* - * The current position on the pad we're showing (top left) - */ - unsigned int px, py; - px = py = 0; - - /* - * As noted above, we create the biggest pad we might need - */ - static WINDOW *helpwin; - helpwin = newpad(maxrows, maxcols); - - /* - * Create a box, and then add one to "x" and "y" so we don't write - * on the line, - */ - box(helpwin, ACS_VLINE, ACS_HLINE); - x++; - y++; - - - /* - * we want arrow keys to work - */ - keypad(helpwin, true); - - // Prolly not needed - wmove(helpwin, 0, 0); - - // Print opener - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, "IPTState "); - waddstr(helpwin, VERSION); - wattroff(helpwin, A_BOLD); - // this is \n - y++; - - // We don't want anything except the title up against the - // border - x++; - - string nav = "Up/j, Down/k, Left/h, Right/l, PageUp/^u, PageDown/^d, "; - nav += " Home, or End"; - // Print instructions first - mvwaddstr(helpwin, y++, x, "Navigation:"); - mvwaddstr(helpwin, y++, x, nav.c_str()); - mvwaddstr(helpwin, y++, x, " Press any other key to continue..."); - y++; - - // Print settings - mvwaddstr(helpwin, y++, x, "Current settings:"); - - mvwaddstr(helpwin, y++, x, " Sorting by: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin, sorting.c_str()); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Dynamic formatting: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(!flags.staticsize) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Skip loopback states: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.skiplb) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Resolve hostnames: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.lookup) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Mark truncated hostnames: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.tag_truncate) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Colors: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(!flags.nocolor) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Skip outgoing DNS lookup states: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.skipdns) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Enable scroll: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(!flags.noscroll) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Display totals: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.totals) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - mvwaddstr(helpwin, y++, x, " Display counters: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin,(flags.counters) ? "yes" : "no"); - wattroff(helpwin, A_BOLD); - - char tmp[NAMELEN]; - if (flags.filter_src) { - inet_ntop(filters.srcfam, &filters.src, tmp, - filters.srcfam == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)); - mvwaddstr(helpwin, y++, x, " Source filter: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin, tmp); - wattroff(helpwin, A_BOLD); - } - if (flags.filter_dst) { - inet_ntop(filters.dstfam, &filters.dst, tmp, - filters.dstfam == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)); - mvwaddstr(helpwin, y++, x, " Destination filter: "); - wattron(helpwin, A_BOLD); - waddstr(helpwin, tmp); - wattroff(helpwin, A_BOLD); - } - if (flags.filter_srcpt) { - mvwaddstr(helpwin, y++, x, " Source port filter: "); - wattron(helpwin, A_BOLD); - wprintw(helpwin, "%lu", filters.srcpt); - wattroff(helpwin, A_BOLD); - } - if (flags.filter_dstpt) { - mvwaddstr(helpwin, y++, x, " Destination port filter: "); - wattron(helpwin, A_BOLD); - wprintw(helpwin, "%lu", filters.dstpt); - wattroff(helpwin, A_BOLD); - } - - y++; - - // Print commands - mvwaddstr(helpwin, y++, x, "Interactive commands:"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " c"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tUse colors"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " C"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of bytes/packets counters"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " b"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tSort by next column"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " B"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tSort by previous column"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " d"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange destination filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " D"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange destination port filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " f"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of loopback states"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " h"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tDisplay this help message"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " l"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle DNS lookups"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " L"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of outgoing DNS states"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " m"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle marking truncated hostnames with a '+'"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " o"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle dynamic or old formatting"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " p"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle scrolling"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " q"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tQuit"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " r"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle reverse sorting"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " R"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange the refresh rate"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " s"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange source filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " S"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tChange source port filter"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " t"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tToggle display of totals"); - - wattron(helpwin, A_BOLD); - mvwaddstr(helpwin, y++, x, " x"); - wattroff(helpwin, A_BOLD); - waddstr(helpwin, "\tDelete the currently highlighted state from netfilter"); - - y++; - - wmove(helpwin, 0, 0); - - /* - * refresh from wherever we are the pad - * and the top of the window to the bottom of the window. - */ - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - // kill line buffering - cbreak(); - // nodelay with a 0 here causes getch() to block until key is pressed. - nodelay( helpwin, 0 ); - int c; - while ((c = wgetch(helpwin))) { - switch (c) { - case ERR: - continue; - break; - case KEY_DOWN: - case 'j': - /* - * py is the top of the window, - * ssize.y is the height of the window, - * so py+ssize.y is the bottom of the window. - * - * Since y is the bottom of the text we've - * written, if - * py+ssize.y == y - * then the bottom of the screen as at the - * bottom of the text, no more scrolling. - */ - if (py + ssize.y < y) - py++; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_UP: - case 'k': - if (py > 0) - py--; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_RIGHT: - case 'l': - /* - * px is the left of the window, - * ssize.x is the width of the window, - * so px+ssize.x os the right side of the window. - * - * So if px+ssize.x == 80 (more than the width - * of our text), no more scrolling. - */ - if (px + ssize.x < 80) - px++; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_LEFT: - case 'h': - if (px > 0) - px--; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_HOME: - case KEY_SHOME: - case KEY_FIND: - px = py = 0; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_END: - py = y-ssize.y; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case 4: - case KEY_NPAGE: - case KEY_SNEXT: - if (flags.noscroll) - break; - /* - * If the screen is bigger than the text, - * ignore - */ - if (y < ssize.y) - break; - /* - * Otherwise, if the bottom of the screen - * (current position + screen size - * == py + ssize.y) - * were to go down one screen (thus: - * py + ssize.y*2), - * and that is bigger than the whole text, just - * go to the bottom. - * - * Otherwise, go down a screen size. - */ - if ((py + (ssize.y * 2)) > y) { - py = y-ssize.y; - } else { - py += ssize.y; - } - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case 21: - case KEY_PPAGE: - case KEY_SPREVIOUS: - if (flags.noscroll) - break; - /* - * If we're at the top, ignore this. - */ - if (py == 0) - break; - /* - * Otherwise if we're less than a page from the - * top, go to the top, else, go up a page. - */ - if (py < ssize.y) - py = 0; - else - py -= ssize.y; - prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - - case 'q': - default: - goto out; - break; - } - if (need_resize) { - goto out; - } - } -out: - // once a key is pressed, tear down the help window. - delwin(helpwin); - refresh(); - halfdelay(1); -} - - -/* - * MAIN - */ -int main(int argc, char *argv[]) -{ - - // Variables - string line, src, dst, srcpt, dstpt, proto, code, type, state, ttl, mins, - secs, hrs, sorting, tmpstring, format, prompt; - ostringstream ostream; - vector stable; - int tmpint = 0, sortby = 0, rate = 1, hdrs = 0; - unsigned int py = 0, px = 0, curr_state = 0; - timeval selecttimeout; - fd_set readfd; - flags_t flags; - counters_t counts; - screensize_t ssize; - filters_t filters; - max_t max; - - /* - * Initialize - */ - flags.single = flags.totals = flags.lookup = flags.skiplb = flags.staticsize - = flags.skipdns = flags.tag_truncate = flags.filter_src - = flags.filter_dst = flags.filter_srcpt = flags.filter_dstpt - = flags.noscroll = flags.nocolor = flags.counters = false; - ssize.x = ssize.y = 0; - counts.tcp = counts.udp = counts.icmp = counts.other = counts.skipped = 0; - filters.src = filters.dst = in6addr_any; - filters.srcpt = filters.dstpt = 0; - max.src = max.dst = max.proto = max.state = max.ttl = 0; - px = py = 0; - - static struct option long_options[] = { - {"counters", no_argument , 0, 'C'}, - {"dst-filter", required_argument, 0, 'd'}, - {"dstpt-filter", required_argument, 0, 'D'}, - {"help", no_argument, 0, 'h'}, - {"lookup", no_argument, 0, 'l'}, - {"mark-truncated", no_argument, 0, 'm'}, - {"no-color", no_argument, 0, 'c'}, - {"no-dynamic", no_argument, 0, 'o'}, - {"no-dns", no_argument, 0, 'L'}, - {"no-loopback", no_argument, 0, 'f'}, - {"no-scroll", no_argument, 0, 'p'}, - {"rate", required_argument, 0, 'R'}, - {"reverse", no_argument, 0, 'r'}, - {"single", no_argument, 0, '1'}, - {"sort", required_argument, 0, 'b'}, - {"src-filter", required_argument, 0, 's'}, - {"srcpt-filter", required_argument, 0, 'S'}, - {"totals", no_argument, 0, 't'}, - {"version", no_argument, 0, 'v'}, - {0, 0, 0,0} - }; - int option_index = 0; - - // Command Line Arguments - while ((tmpint = getopt_long(argc, argv, "Cd:D:hlmcoLfpR:r1b:s:S:tv", - long_options, &option_index)) != EOF) { - switch (tmpint) { - case 0: - /* Apparently this test is needed?! Seems lame! */ - if (long_options[option_index].flag != 0) - break; - - /* - * Long-only options go here, like so: - * - * tmpstring = long_options[option_index].name; - * if (tmpstring == "srcpt-filter") { - * ... - * } else if (...) { - * ... - * } - * - */ - - break; - // --counters - case 'C': - flags.counters = true; - break; - // --dst-filter - case 'd': - if (optarg == NULL) - break; - // See check_ip() note above - if (!check_ip(optarg, &filters.dst, &filters.dstfam)) { - cerr << "Invalid IP address: " << optarg - << endl; - exit(1); - } - flags.filter_dst = true; - break; - // --dstpt-filter - case 'D': - /* - * even though this won't be an IP address - * aton() won't complain about anything that's - * just digits, so it's an easy check. - */ - if (optarg == NULL) - break; - flags.filter_dstpt = true; - filters.dstpt = atoi(optarg); - break; - // --help - case 'h': - help(); - break; - // --lookup - case 'l': - flags.lookup = true; - // and also set skipdns for sanity - flags.skipdns = true; - break; - // --mark-truncated - case 'm': - flags.tag_truncate = true; - break; - // --color - case 'c': - flags.nocolor = false; - break; - // --no-dynamic - case 'o': - flags.staticsize = true; - break; - // --no-dns - case 'L': - flags.skipdns = true; - break; - // --no-loopback - case 'f': - flags.skiplb = true; - break; - // --no-scroll - case 'p': - flags.noscroll = true; - break; - // --reverse - case 'r': - sort_factor = -1; - break; - // --rate - case 'R': - rate = atoi(optarg); - break; - // --sort - case 'b': - if (*optarg == 'd') - sortby = SORT_DST; - else if (*optarg == 'D') { - sortby = SORT_DST_PT; - } else if (*optarg == 'S') - sortby = SORT_SRC_PT; - else if (*optarg == 'p') - sortby = SORT_PROTO; - else if (*optarg == 's') - sortby = SORT_STATE; - else if (*optarg == 't') - sortby = SORT_TTL; - else if (*optarg == 'b' && flags.counters) - sortby = SORT_BYTES; - else if (*optarg == 'P' && flags.counters) - sortby = SORT_PACKETS; - break; - // --single - case '1': - flags.single = true; - break; - // --src-filter - case 's': - if (optarg == NULL) - break; - if (!check_ip(optarg, &filters.src, &filters.srcfam)) { - cerr << "Invalid IP address: " << optarg << endl; - exit(1); - } - flags.filter_src = true; - break; - // --srcpt-filter - case 'S': - if (optarg == NULL) - break; - flags.filter_srcpt = true; - filters.srcpt = atoi(optarg); - break; - // --totals - case 't': - flags.totals = true; - break; - // --version - case 'v': - version(); - exit(0); - break; - // catch-all - default: - // getopts should already have printed a message - exit(1); - break; - } - } - - if (rate < 0 || rate > 60) { - rate = 1; - } - - // Initialize Curses Stuff - static WINDOW *mainwin = NULL; - if (!flags.single) { - mainwin = start_curses(flags); - keypad(mainwin, true); - } - - /* - * We want to keep going until the user stops us - * unless they use single run mode - * in which case, we'll deal with that down below - */ - while (1) { - - /* - * We get the screensize_t up-front so we can die if the - * screen doesn't meet our minimum requirements without making - * the user wait while we gather and process all the data. - * We'll do it again afterwards just in case - */ - - ssize = get_size(flags.single); - - if (ssize.x < 72) { - term_too_small(); - } - - // And our header size - hdrs = 3; - if (flags.totals) { - hdrs++; - } - if (flags.filter_src || flags.filter_dst || flags.filter_srcpt - || flags.filter_dstpt) { - hdrs++; - } - - // clear maxes - initialize_maxes(max, flags); - - // Build our table - build_table(flags, filters, stable, counts, max); - - /* - * Now that we have the new table, make sure our page/cursor - * positions still make sense. - */ - if (curr_state > stable.size() - 1) { - curr_state = stable.size() - 1; - } - - /* - * The bottom of the screen is stable.size()+hdrs+1 - * (the +1 is so we can have a blank line at the end) - * but we want to never have py be more than that - ssize.y - * so we're showing a page full of states. - */ - int bottom = stable.size() + hdrs + 1 - ssize.y; - if (bottom < 0) - bottom = 0; - if (py > (unsigned)bottom) - py = bottom; - - /* - * Originally I strived to do this the "right" way by calling - * nfct_is_set(ct, ATTR_ORIG_COUNGERS) to determine if - * counters were enabled. BUT, if counters are not enabled, - * nfct_get_attr() returns NULL, so this test is just as - * valid. - * - * Conversely checking is_set and then get_attr() inside our - * callback is twice the calls per-state if they are enabled, - * for no additional benefit. - */ - if (flags.counters && stable.size() > 0 && stable[0]->bytes == 0) { - prompt = "Counters requested, but not enabled in the"; - prompt += " kernel!"; - flags.counters = 0; - c_warn(mainwin, prompt, flags); - } - - // Sort our table - sort_table(sortby, flags.lookup, sort_factor, stable, sorting); - - /* - * From here on out 'max' is no longer "the maximum size of - * this field throughout the table", but is instead the actual - * size to print each field. - * - * BTW, we do "get_size" again here incase the window changed - * while we were off parsing and sorting data. - */ - determine_format(mainwin, max, ssize, format, flags); - - /* - * Now we print out the table in whichever format we're - * configured for - */ - print_table(stable, flags, format, sorting, filters, counts, ssize, max, - mainwin, curr_state); - - // Exit if we're only supposed to run once - if (flags.single) - exit(0); - - // Otherwise refresh the curses display - if (flags.noscroll) { - refresh(); - } else { - prefresh(mainwin, py, px, 0, 0, ssize.y-1, ssize.x-1); - } - - //check for key presses for one second - //or whatever the user said - selecttimeout.tv_sec = rate; - selecttimeout.tv_usec = 0; - // I don't care about fractions of seconds. I don't want them. - FD_ZERO(&readfd); - FD_SET(0, &readfd); - select(1,&readfd, NULL, NULL, &selecttimeout); - if (FD_ISSET(0, &readfd)) { - tmpint = wgetch(mainwin); - switch (tmpint) { - // This is ^L - case 12: - handle_resize(mainwin, flags, ssize); - break; - /* - * This is at the top because the rest are in - * order of longopts, and q isn't in longopts - */ - case 'q': - goto out; - break; - /* - * General option toggles - */ - case 'c': - /* - * we only want to pay attention to this - * command if colors are available - */ - if (has_colors()) - flags.nocolor = !flags.nocolor; - break; - case 'C': - flags.counters = !flags.counters; - if (sortby >= SORT_BYTES) - sortby = SORT_BYTES-1; - break; - case 'h': - interactive_help(sorting, flags, filters); - break; - case 'l': - flags.lookup = !flags.lookup; - /* - * If we just turned on lookup, also turn on filtering DNS states. - * They can turn it off if they want, but generally this is the safer - * approach. - */ - if (flags.lookup) { - flags.skipdns = true; - } - break; - case 'm': - flags.tag_truncate = !flags.tag_truncate; - break; - case 'o': - flags.staticsize = !flags.staticsize; - break; - case 'L': - flags.skipdns = !flags.skipdns; - break; - case 'f': - flags.skiplb = !flags.skiplb; - break; - case 'p': - switch_scroll(flags, mainwin); - break; - case 'r': - sort_factor = -sort_factor; - break; - case 'b': - if (sortby < SORT_MAX) { - sortby++; - if (!flags.counters && sortby >= SORT_BYTES) - sortby = 0; - } else { - sortby = 0; - } - break; - case 'B': - if (sortby > 0) { - sortby--; - } else { - if (flags.counters) - sortby=SORT_MAX; - else - sortby=SORT_BYTES-1; - } - break; - case 't': - flags.totals = !flags.totals; - break; - /* - * Update-filters - */ - case 'd': - prompt = "New Destination Filter? (leave blank"; - prompt += " for none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_dst = false; - filters.dst = in6addr_any; - } else { - if (!check_ip(tmpstring.c_str(), &filters.dst, &filters.dstfam)) { - prompt = "Invalid IP,"; - prompt += " ignoring!"; - c_warn(mainwin, prompt, flags); - } else { - flags.filter_dst = true; - } - } - break; - case 'D': - prompt = "New dstpt filter? (leave blank for"; - prompt += " none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_dstpt = false; - filters.dstpt = 0; - } else { - flags.filter_dstpt = true; - filters.dstpt = atoi(tmpstring.c_str()); - } - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - break; - case 'R': - prompt = "Rate: "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring != "") { - int i = atoi(tmpstring.c_str()); - if (i < 1) { - prompt = "Invalid rate,"; - prompt += " ignoring!"; - c_warn(mainwin, prompt, flags); - } else { - rate = i; - } - } - break; - case 's': - prompt = "New src filter? (leave blank for"; - prompt += " none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_src = false; - filters.src = in6addr_any; - } else { - if (!check_ip(tmpstring.c_str(), &filters.src, &filters.srcfam)) { - prompt = "Invalid IP,"; - prompt += " ignoring!"; - c_warn(mainwin, prompt, flags); - } else { - flags.filter_src = true; - } - } - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - break; - case 'S': - prompt = "New srcpt filter? (leave blank for"; - prompt += " none): "; - get_input(mainwin, tmpstring, prompt, flags); - if (tmpstring == "") { - flags.filter_srcpt = false; - filters.srcpt = 0; - } else { - flags.filter_srcpt = true; - filters.srcpt = atoi(tmpstring.c_str()); - } - wmove(mainwin, 0, 0); - wclrtoeol(mainwin); - break; - case 'x': - delete_state(mainwin, stable[curr_state], flags); - break; - /* - * Window navigation - */ - case KEY_DOWN: - case 'j': - if (flags.noscroll) - break; - /* - * GENERAL NOTE: - * py is the top of the window, - * ssize.y is the height of the window, - * so py+ssize.y is the bottom of the window. - * - * BOTTOM OF SCROLLING: - * Since stable.size()+hdrs+1 is - * the bottom of the text we've written, if - * py+ssize.y == stable.size()+hdrs+1 - * then the bottom of the screen as at the - * bottom of the text, no more scrolling. - * - * However, we only want to scroll the page - * when the cursor is at the bottom, i.e. - * when curr_state+4 == py+ssize.y - */ - - /* - * If we have room to scroll down AND if cur is - * at the bottom of a page scroll down. - */ - if ((py + ssize.y <= stable.size() + hdrs + 1) - && (curr_state + 4 == py + ssize.y)) - py++; - - /* - * As long as the cursor isn't at the end, - * move it down one. - */ - if (curr_state < stable.size() - 1) - curr_state++; - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_UP: - case 'k': - if (flags.noscroll) - break; - - /* - * This one is tricky. - * - * First thing we need to know is when the cursor - * is at the top of the page. This is simply when - * curr_state+hdrs+1 cursor location), is - * exactly one more than the top of the window - * (py), * i.e. when curr_state+hdrs+1 == py+1. - * - * PAGE SCROLLING: - * IF we're not page-scrolled all the way up - * (i.e. py > 0) - * AND the cursor is at the top of the page - * OR the cursor is at the top of the list, - * AND we're not yet at the top (showing - * the headers). - * THEN we scroll up. - * - * CURSOR SCROLLING: - * Unlike KEY_DOWN, we don't break just because - * the cursor can't move anymore - on the way - * ip we may still have page-scrolling to do. So - * test to make sure we're not at state 0, and - * if so, we scroll up. - */ - - /* - * Basically: - * IF the cursor bumps the top of the screen - * OR we need to scroll up for headers - */ - if ((py > 0 && (curr_state + hdrs + 1) == (py + 1)) - || (curr_state == 0 && py > 0)) - py--; - - if (curr_state > 0) - curr_state--; - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - // 4 is ^d - case 4: - case KEY_NPAGE: - case KEY_SNEXT: - if (flags.noscroll) - break; - /* - * If the screen is bigger than the text, - * and the cursor is at the bottom, ignore. - */ - if (stable.size() + hdrs + 1 < ssize.y && curr_state == stable.size()) - break; - - /* - * Otherwise, if the bottom of the screen - * (current position + screen size - * == py + ssize.y) - * were to go down one screen (thus: - * py + ssize.y*2), - * and that is bigger than the whole pad, just - * go to the bottom. - * - * Otherwise, go down a screen size. - */ - if (py + ssize.y * 2 > stable.size() + hdrs + 1) { - py = stable.size() + hdrs + 1 - ssize.y; - } else { - py += ssize.y; - } - - /* - * For the cursor, we try to move it down one - * screen as well, but if that's too far, - * we bring it up to the largest number it can - * be. - */ - curr_state += ssize.y; - if (curr_state > stable.size()) { - curr_state = stable.size(); - } - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - // 21 is ^u - case 21: - case KEY_PPAGE: - case KEY_SPREVIOUS: - if (flags.noscroll) - break; - /* - * If we're at the top, ignore - */ - if (py == 0 && curr_state == 0) - break; - /* - * Otherwise if we're less than a page from the - * top, go to the top, else go up a page. - */ - if (py < ssize.y) { - py = 0; - } else { - py -= ssize.y; - } - - /* - * We bring the cursor up a page too, unless - * that's too far. - */ - if (curr_state < ssize.y) { - curr_state = 0; - } else { - curr_state -= ssize.y; - } - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_HOME: - if (flags.noscroll) - break; - px = py = curr_state = 0; - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - case KEY_END: - if (flags.noscroll) - break; - py = stable.size() + hdrs + 1 - ssize.y; - if (py < 0) - py = 0; - curr_state = stable.size(); - prefresh(mainwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); - break; - } - } - /* - * If we got a sigwinch, we need to redraw - */ - if (need_resize) { - handle_resize(mainwin, flags, ssize); - need_resize = false; - } - } // end while(1) - - out: - - /* - * The user has broken out of the loop, take down the curses - */ - end_curses(); - - // And we're done - return(0); - -} // end main