| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <cerrno> |
| #include <cmath> |
| #include <csignal> |
| #include <cstdlib> |
| #include <cstring> |
| #include <ctime> |
| #include <fstream> |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| #include <algorithm> |
| |
| |
| #include <arpa/inet.h> |
| #include <getopt.h> |
| extern "C" { |
| #include <libnetfilter_conntrack/libnetfilter_conntrack.h> |
| }; |
| #include <netdb.h> |
| #include <ncurses.h> |
| #include <unistd.h> |
| using namespace std; |
| |
| #define VERSION "2.2.6" |
| |
| |
| |
| |
| |
| |
| #define MAXCONS 16384 |
| #define MAXLINES 32767 |
| #if MAXCONS < MAXLINES |
| #define NLINES MAXCONS |
| #else |
| #define NLINES MAXLINES |
| #endif |
| #define MAXFIELDS 20 |
| |
| #define DEFAULT_FORMAT "%-21s %-21s %-7s %-12s %-9s\n" |
| |
| #define DEFAULT_SRC 21 |
| #define DEFAULT_DST 21 |
| #define DEFAULT_PROTO 7 |
| #define DEFAULT_STATE 12 |
| #define DEFAULT_TTL 9 |
| |
| #define TOTALS_FORMAT \ |
| "Total States: %i -- TCP: %i UDP: %i ICMP: %i Other: %i (Filtered: %i)\n" |
| |
| #define TRUNC_FRONT 0 |
| #define TRUNC_END 1 |
| |
| #define NAMELEN 100 |
| |
| #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 |
| |
| |
| |
| |
| |
| |
| |
| |
| int sort_factor = 1; |
| bool need_resize = false; |
| |
| |
| static const char *states[] = { |
| "NONE", |
| "SYN_SENT", |
| "SYN_RECV", |
| "ESTABLISHED", |
| "FIN_WAIT", |
| "CLOSE_WAIT", |
| "LAST_ACK", |
| "TIME_WAIT", |
| "CLOSE", |
| "LISTEN" |
| }; |
| |
| |
| |
| |
| |
| |
| 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; |
| }; |
| |
| struct screensize_t { |
| unsigned int x, y; |
| }; |
| |
| struct flags_t { |
| bool single, totals, lookup, skiplb, staticsize, skipdns, tag_truncate, |
| filter_src, filter_dst, filter_srcpt, filter_dstpt, noscroll, nocolor, |
| counters; |
| }; |
| |
| struct counters_t { |
| unsigned int total, tcp, udp, icmp, other, skipped; |
| }; |
| |
| struct filters_t { |
| in6_addr src, dst; |
| uint8_t srcfam, dstfam; |
| unsigned long srcpt, dstpt; |
| }; |
| |
| struct max_t { |
| unsigned int src, dst, proto, state, ttl; |
| unsigned long bytes, packets; |
| }; |
| struct hook_data { |
| vector<tentry_t*> *stable; |
| flags_t *flags; |
| max_t *max; |
| counters_t *counts; |
| const filters_t *filters; |
| }; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| |
| void splita(char s, string line, vector<string> &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; |
| } |
| |
| |
| |
| |
| unsigned int digits(unsigned long x) |
| { |
| return (unsigned int) floor(log10((double)x))+1; |
| } |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| void version() |
| { |
| cout << "IPTables State Top Version " << VERSION << endl << endl; |
| } |
| |
| void help() |
| { |
| cout << "IPTables State Top Version " << VERSION << endl; |
| cout << "Usage: iptstate [<options>]\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 <IP>\n"; |
| cout << "\tOnly show states with a destination of <IP>\n"; |
| cout << "\tNote, that this must be an IP, hostname matching is" |
| << " not yet supported.\n\n"; |
| cout << " -D --dstpt-filter <port>\n"; |
| cout << "\tOnly show states with a destination port of <port>\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 <seconds>\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 <column>\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 <IP>\n"; |
| cout << "\tOnly show states with a source of <IP>\n"; |
| cout << "\tNote, that this must be an IP, hostname matching is" |
| << " not yet supported.\n\n"; |
| cout << " -S, --srcpt-filter <port>\n"; |
| cout << "\tOnly show states with a source port of <port>\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); |
| } |
| |
| |
| |
| |
| 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(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| bool src_sort(tentry_t *one, tentry_t *two) |
| { |
| |
| |
| |
| |
| |
| |
| 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) |
| { |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| void end_curses() |
| { |
| curs_set(1); |
| nocbreak(); |
| endwin(); |
| cout << endl; |
| } |
| |
| |
| |
| |
| void winch_handler(int sig) |
| { |
| sigset_t mask_set; |
| sigset_t old_set; |
| |
| signal(28, winch_handler); |
| |
| sigfillset(&mask_set); |
| sigprocmask(SIG_SETMASK, &mask_set, &old_set); |
| |
| need_resize = true; |
| } |
| |
| |
| |
| |
| void kill_handler(int sig) |
| { |
| end_curses(); |
| printf("Caught signal %d, cleaning up.\n", sig); |
| exit(0); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static WINDOW* start_curses(flags_t &flags) |
| { |
| int y, x; |
| initscr(); |
| cbreak(); |
| noecho(); |
| halfdelay(1); |
| |
| |
| |
| |
| |
| |
| |
| signal(28, winch_handler); |
| |
| signal(2, kill_handler); |
| signal(15, kill_handler); |
| |
| if (has_colors()) { |
| start_color(); |
| |
| init_pair(1, COLOR_GREEN, COLOR_BLACK); |
| |
| init_pair(2, COLOR_YELLOW, COLOR_BLACK); |
| |
| init_pair(3, COLOR_RED, COLOR_BLACK); |
| |
| init_pair(4, COLOR_BLACK, COLOR_RED); |
| |
| 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; |
| } |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| void term_too_small() |
| { |
| end_curses(); |
| cout << "I'm sorry, your terminal must be atleast 72 columns" |
| << "wide to run iptstate\n"; |
| exit(3); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void switch_scroll(flags_t &flags, WINDOW *&mainwin) |
| { |
| int x, y; |
| if (flags.noscroll) { |
| getmaxyx(stdscr, y, x); |
| |
| erase(); |
| |
| wmove(mainwin, 0, 0); |
| mainwin = newpad(NLINES, x); |
| wmove(mainwin, 0, 0); |
| keypad(mainwin,1); |
| halfdelay(1); |
| } else { |
| |
| delwin(mainwin); |
| mainwin = stdscr; |
| keypad(mainwin,1); |
| halfdelay(1); |
| } |
| |
| flags.noscroll = !flags.noscroll; |
| } |
| |
| |
| |
| |
| void get_input(WINDOW *win, string &input, const string &prompt, |
| const flags_t &flags) |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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': |
| |
| case 7: |
| if (ch == 7) |
| input = ""; |
| if (!flags.nocolor) |
| wattroff(cmd, COLOR_PAIR(4)); |
| delwin(cmd); |
| noecho(); |
| wmove(win, 0, 0); |
| return; |
| break; |
| |
| 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); |
| } |
| } |
| |
| |
| |
| |
| |
| void c_warn(WINDOW *win, const string &warning, const flags_t &flags) |
| { |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| void initialize_maxes(max_t &max, flags_t &flags) |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (flags.lookup) { |
| max.src = 6; |
| max.dst = 11; |
| } else { |
| max.src = max.dst = 21; |
| } |
| |
| |
| |
| |
| max.proto = 3; |
| |
| |
| |
| |
| |
| max.state = 11; |
| |
| max.ttl = 9; |
| |
| |
| max.bytes = 2; |
| max.packets = 2; |
| } |
| |
| |
| |
| |
| void handle_resize(WINDOW *&win, const flags_t &flags, screensize_t &ssize) |
| { |
| if (flags.noscroll) { |
| endwin(); |
| refresh(); |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| delwin(win); |
| endwin(); |
| |
| |
| |
| refresh(); |
| getmaxyx(stdscr, ssize.y, ssize.x); |
| win = newpad(NLINES, ssize.x); |
| keypad(win, true); |
| wmove(win, 0, 0); |
| |
| return; |
| } |
| |
| |
| |
| |
| 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)); |
| } |
| |
| |
| 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); |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int conntrack_hook(enum nf_conntrack_msg_type nf_type, struct nf_conntrack *ct, |
| void *tmp) |
| { |
| |
| |
| |
| |
| struct hook_data *data = static_cast<struct hook_data *>(tmp); |
| |
| |
| |
| |
| vector<tentry_t*> *stable = data->stable; |
| flags_t *flags = data->flags; |
| max_t *max = data->max; |
| counters_t *counts = data->counts; |
| const filters_t *filters = data->filters; |
| |
| |
| tentry_t *entry = new tentry_t; |
| |
| |
| struct protoent* pe = NULL; |
| int seconds, minutes, hours; |
| char ttlc[11]; |
| ostringstream buffer; |
| |
| |
| |
| |
| entry->sname = ""; |
| entry->dname = ""; |
| entry->srcpt = 0; |
| entry->dstpt = 0; |
| entry->proto = ""; |
| entry->ttl = ""; |
| entry->state = ""; |
| |
| |
| |
| |
| |
| |
| 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 (entry->proto == "ipv6-icmp") |
| entry->proto = "icmp6"; |
| } |
| |
| |
| seconds = nfct_get_attr_u32(ct, ATTR_TIMEOUT); |
| minutes = seconds/60; |
| hours = minutes/60; |
| minutes = minutes%60; |
| seconds = seconds%60; |
| |
| snprintf(ttlc,11, "%3i:%02i:%02i", hours, minutes, seconds); |
| entry->ttl = ttlc; |
| |
| entry->family = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO); |
| |
| 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); |
| } |
| |
| |
| 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(); |
| |
| |
| 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++; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| stringify_entry(entry, *max, *flags); |
| |
| |
| |
| |
| stable->push_back(entry); |
| |
| return NFCT_CB_CONTINUE; |
| } |
| |
| |
| |
| |
| |
| |
| |
| void build_table(flags_t &flags, const filters_t &filters, vector<tentry_t*> |
| &stable, counters_t &counts, max_t &max) |
| { |
| |
| |
| |
| int res=0; |
| vector<string> fields(MAXFIELDS); |
| static struct nfct_handle *cth; |
| u_int8_t family = AF_UNSPEC; |
| |
| |
| |
| |
| |
| struct hook_data hook; |
| hook.stable = &stable; |
| hook.flags = &flags; |
| hook.max = &max; |
| hook.counts = &counts; |
| hook.filters = &filters; |
| |
| |
| |
| |
| |
| for ( |
| vector<tentry_t*>::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); |
| } |
| |
| |
| |
| |
| void sort_table(const int &sortby, const bool &lookup, const int &sort_factor, |
| vector<tentry_t*> &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: |
| |
| sorting = "??unknown??"; |
| break; |
| |
| } |
| |
| 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 (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); |
| } |
| |
| |
| |
| |
| 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"); |
| } |
| |
| |
| |
| |
| 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] = '+'; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| 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; |
| |
| |
| length = table->sname.size(); |
| if (have_port) |
| length += table->spname.size() + 1; |
| |
| |
| 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); |
| } |
| |
| |
| 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(""); |
| } |
| |
| |
| |
| |
| 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; |
| |
| |
| 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)); |
| } |
| } |
| |
| |
| |
| |
| |
| void print_table(vector<tentry_t*> &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(flags, format, sorting, filters, counts, ssize, stable.size(), |
| mainwin); |
| |
| |
| |
| |
| 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; |
| |
| } |
| |
| |
| |
| |
| |
| if (!flags.single) |
| wclrtobot(mainwin); |
| |
| } |
| |
| |
| |
| |
| void determine_format(WINDOW *mainwin, max_t &max, screensize_t &ssize, |
| string &format, flags_t &flags) |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| |
| |
| |
| |
| |
| |
| |
| |
| if (ssize.x < 85 && flags.counters && !flags.lookup) { |
| string prompt = "Window too narrow for counters! Disabling."; |
| c_warn(mainwin, prompt, flags); |
| flags.counters = false; |
| } |
| |
| |
| |
| |
| unsigned int left = ssize.x - max.ttl - max.state - max.proto - 4; |
| if (flags.counters) |
| left -= (max.bytes + max.packets + 2); |
| |
| |
| |
| |
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| |
| if (max.src < src && max.dst < dst) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } else if (left_odd && (src < left / 2) && (dst < left / 2 + 1)) { |
| |
| |
| |
| |
| src = dst; |
| dst++; |
| } else if (max.src > max.dst) { |
| |
| |
| |
| |
| |
| |
| |
| |
| src = max.src; |
| dst = left - max.src; |
| } else { |
| |
| |
| |
| dst = max.dst; |
| src = left - max.dst; |
| } |
| } else if (max.src < src) { |
| |
| |
| |
| |
| src = max.src; |
| dst = left - max.src; |
| } else if (max.dst < dst) { |
| |
| |
| |
| dst = max.dst; |
| src = left - max.dst; |
| } |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| void interactive_help(const string &sorting, const flags_t &flags, |
| const filters_t &filters) |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| unsigned int maxrows = 41; |
| unsigned int maxcols = 80; |
| |
| |
| |
| |
| screensize_t ssize = get_size(flags.single); |
| |
| |
| |
| |
| |
| |
| if (maxrows < ssize.y) |
| maxrows = ssize.y; |
| if (maxcols < ssize.x) |
| maxcols = ssize.x; |
| |
| |
| |
| |
| |
| |
| unsigned int x, y; |
| x = y = 0; |
| |
| |
| |
| |
| unsigned int px, py; |
| px = py = 0; |
| |
| |
| |
| |
| static WINDOW *helpwin; |
| helpwin = newpad(maxrows, maxcols); |
| |
| |
| |
| |
| |
| box(helpwin, ACS_VLINE, ACS_HLINE); |
| x++; |
| y++; |
| |
| |
| |
| |
| |
| keypad(helpwin, true); |
| |
| |
| wmove(helpwin, 0, 0); |
| |
| |
| wattron(helpwin, A_BOLD); |
| mvwaddstr(helpwin, y++, x, "IPTState "); |
| waddstr(helpwin, VERSION); |
| wattroff(helpwin, A_BOLD); |
| |
| y++; |
| |
| |
| |
| x++; |
| |
| string nav = "Up/j, Down/k, Left/h, Right/l, PageUp/^u, PageDown/^d, "; |
| nav += " Home, or End"; |
| |
| mvwaddstr(helpwin, y++, x, "Navigation:"); |
| mvwaddstr(helpwin, y++, x, nav.c_str()); |
| mvwaddstr(helpwin, y++, x, " Press any other key to continue..."); |
| y++; |
| |
| |
| 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++; |
| |
| |
| 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); |
| |
| |
| |
| |
| |
| prefresh(helpwin, py, px, 0, 0, ssize.y - 1, ssize.x - 1); |
| |
| cbreak(); |
| |
| nodelay( helpwin, 0 ); |
| int c; |
| while ((c = wgetch(helpwin))) { |
| switch (c) { |
| case ERR: |
| continue; |
| break; |
| case KEY_DOWN: |
| case 'j': |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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': |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 (y < ssize.y) |
| break; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 (py == 0) |
| break; |
| |
| |
| |
| |
| 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: |
| |
| delwin(helpwin); |
| refresh(); |
| halfdelay(1); |
| } |
| |
| |
| |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| |
| |
| string line, src, dst, srcpt, dstpt, proto, code, type, state, ttl, mins, |
| secs, hrs, sorting, tmpstring, format, prompt; |
| ostringstream ostream; |
| vector<tentry_t*> 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; |
| |
| |
| |
| |
| 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; |
| |
| |
| while ((tmpint = getopt_long(argc, argv, "Cd:D:hlmcoLfpR:r1b:s:S:tv", |
| long_options, &option_index)) != EOF) { |
| switch (tmpint) { |
| case 0: |
| |
| if (long_options[option_index].flag != 0) |
| break; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| break; |
| |
| case 'C': |
| flags.counters = true; |
| break; |
| |
| case 'd': |
| if (optarg == NULL) |
| break; |
| |
| if (!check_ip(optarg, &filters.dst, &filters.dstfam)) { |
| cerr << "Invalid IP address: " << optarg |
| << endl; |
| exit(1); |
| } |
| flags.filter_dst = true; |
| break; |
| |
| case 'D': |
| |
| |
| |
| |
| |
| if (optarg == NULL) |
| break; |
| flags.filter_dstpt = true; |
| filters.dstpt = atoi(optarg); |
| break; |
| |
| case 'h': |
| help(); |
| break; |
| |
| case 'l': |
| flags.lookup = true; |
| |
| flags.skipdns = true; |
| break; |
| |
| case 'm': |
| flags.tag_truncate = true; |
| break; |
| |
| case 'c': |
| flags.nocolor = false; |
| break; |
| |
| case 'o': |
| flags.staticsize = true; |
| break; |
| |
| case 'L': |
| flags.skipdns = true; |
| break; |
| |
| case 'f': |
| flags.skiplb = true; |
| break; |
| |
| case 'p': |
| flags.noscroll = true; |
| break; |
| |
| case 'r': |
| sort_factor = -1; |
| break; |
| |
| case 'R': |
| rate = atoi(optarg); |
| break; |
| |
| 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; |
| |
| case '1': |
| flags.single = true; |
| break; |
| |
| 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; |
| |
| case 'S': |
| if (optarg == NULL) |
| break; |
| flags.filter_srcpt = true; |
| filters.srcpt = atoi(optarg); |
| break; |
| |
| case 't': |
| flags.totals = true; |
| break; |
| |
| case 'v': |
| version(); |
| exit(0); |
| break; |
| |
| default: |
| |
| exit(1); |
| break; |
| } |
| } |
| |
| if (rate < 0 || rate > 60) { |
| rate = 1; |
| } |
| |
| |
| static WINDOW *mainwin = NULL; |
| if (!flags.single) { |
| mainwin = start_curses(flags); |
| keypad(mainwin, true); |
| } |
| |
| |
| |
| |
| |
| |
| while (1) { |
| |
| |
| |
| |
| |
| |
| |
| |
| ssize = get_size(flags.single); |
| |
| if (ssize.x < 72) { |
| term_too_small(); |
| } |
| |
| |
| hdrs = 3; |
| if (flags.totals) { |
| hdrs++; |
| } |
| if (flags.filter_src || flags.filter_dst || flags.filter_srcpt |
| || flags.filter_dstpt) { |
| hdrs++; |
| } |
| |
| |
| initialize_maxes(max, flags); |
| |
| |
| build_table(flags, filters, stable, counts, max); |
| |
| |
| |
| |
| |
| if (curr_state > stable.size() - 1) { |
| curr_state = stable.size() - 1; |
| } |
| |
| |
| |
| |
| |
| |
| |
| int bottom = stable.size() + hdrs + 1 - ssize.y; |
| if (bottom < 0) |
| bottom = 0; |
| if (py > (unsigned)bottom) |
| py = bottom; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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_table(sortby, flags.lookup, sort_factor, stable, sorting); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| determine_format(mainwin, max, ssize, format, flags); |
| |
| |
| |
| |
| |
| print_table(stable, flags, format, sorting, filters, counts, ssize, max, |
| mainwin, curr_state); |
| |
| |
| if (flags.single) |
| exit(0); |
| |
| |
| if (flags.noscroll) { |
| refresh(); |
| } else { |
| prefresh(mainwin, py, px, 0, 0, ssize.y-1, ssize.x-1); |
| } |
| |
| |
| |
| selecttimeout.tv_sec = rate; |
| selecttimeout.tv_usec = 0; |
| |
| FD_ZERO(&readfd); |
| FD_SET(0, &readfd); |
| select(1,&readfd, NULL, NULL, &selecttimeout); |
| if (FD_ISSET(0, &readfd)) { |
| tmpint = wgetch(mainwin); |
| switch (tmpint) { |
| |
| case 12: |
| handle_resize(mainwin, flags, ssize); |
| break; |
| |
| |
| |
| |
| case 'q': |
| goto out; |
| break; |
| |
| |
| |
| case 'c': |
| |
| |
| |
| |
| 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 (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; |
| |
| |
| |
| 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; |
| |
| |
| |
| case KEY_DOWN: |
| case 'j': |
| if (flags.noscroll) |
| break; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if ((py + ssize.y <= stable.size() + hdrs + 1) |
| && (curr_state + 4 == py + ssize.y)) |
| py++; |
| |
| |
| |
| |
| |
| 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; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| |
| case 4: |
| case KEY_NPAGE: |
| case KEY_SNEXT: |
| if (flags.noscroll) |
| break; |
| |
| |
| |
| |
| if (stable.size() + hdrs + 1 < ssize.y && curr_state == stable.size()) |
| break; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (py + ssize.y * 2 > stable.size() + hdrs + 1) { |
| py = stable.size() + hdrs + 1 - ssize.y; |
| } else { |
| py += ssize.y; |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| |
| case 21: |
| case KEY_PPAGE: |
| case KEY_SPREVIOUS: |
| if (flags.noscroll) |
| break; |
| |
| |
| |
| if (py == 0 && curr_state == 0) |
| break; |
| |
| |
| |
| |
| if (py < ssize.y) { |
| py = 0; |
| } else { |
| py -= ssize.y; |
| } |
| |
| |
| |
| |
| |
| 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 (need_resize) { |
| handle_resize(mainwin, flags, ssize); |
| need_resize = false; |
| } |
| } |
| |
| out: |
| |
| |
| |
| |
| end_curses(); |
| |
| |
| return(0); |
| |
| } |