diff --git a/Makefile b/Makefile index 8c0da25..8c80416 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,11 @@ sbin := $(DESTDIR)/sbin man8 := $(DESTDIR)/share/man/man8/ all: nethogs decpcap_test + +runtests: test + ./test + + # nethogs_testsum CFLAGS=-g -Wall -Wextra @@ -29,8 +34,11 @@ install: nethogs nethogs.8 install -d -m 755 $(man8) install -m 644 nethogs.8 $(man8) -nethogs: nethogs.cpp $(OBJS) - $(CXX) $(CFLAGS) nethogs.cpp $(OBJS) -o nethogs -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" +test: test.cpp + $(CXX) $(CFLAGS) test.cpp -o test -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" + +nethogs: main.cpp nethogs.cpp $(OBJS) + $(CXX) $(CFLAGS) main.cpp $(OBJS) -o nethogs -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" nethogs_testsum: nethogs_testsum.cpp $(OBJS) $(CXX) $(CFLAGS) -g nethogs_testsum.cpp $(OBJS) -o nethogs_testsum -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" @@ -62,3 +70,4 @@ cui.o: cui.cpp cui.h nethogs.h clean: rm -f $(OBJS) rm -f nethogs + rm -f test diff --git a/README b/README index f4a2fe1..2dc6cd0 100644 --- a/README +++ b/README @@ -18,8 +18,28 @@ Ideas/ToDo for new releases: * Make it work correctly on machines with multiple IP addresses * Integrate into another tool?? +== CODING STANDARDS == + +Can anyone recommend a sensible set? :) + +For now: +* '{' +** on a new line for function definitions +** on a new line for enums +** on the same line for conditionals/loops +** omitted when possible +* use tab for indentation +* use doxygen/javadoc-style comments. + * for multiline doxygen docs, add a newline after '/**' +* case +** classes: camelcased, start uppercase +** enums: camelcased, start uppercase +** functions: camelcased, start lowercase +** local variables: camelcased, start lowercase + + == LICENSE == -Copyright 2004-2005, 2008, 2010-2011 Arnout Engelen +Copyright 2004-2005, 2008, 2010-2012 Arnout Engelen License: nethogs may be redistributed under the terms of the GPLv2 or any later version. See the COPYING file for the license text. diff --git a/connection.cpp b/connection.cpp index eb037c7..cfc03d2 100644 --- a/connection.cpp +++ b/connection.cpp @@ -25,17 +25,7 @@ #include #include "nethogs.h" #include "connection.h" - -class ConnList -{ -public: - ConnList (Connection * m_val = NULL, ConnList * m_next = NULL) - { - val = m_val; next = m_next; - } - Connection * val; - ConnList * next; -}; +#include "process.h" ConnList * connections = NULL; @@ -127,22 +117,22 @@ Connection::~Connection () ConnList * prev_conn = NULL; while (curr_conn != NULL) { - if (curr_conn->val == this) + if (curr_conn->getVal() == this) { ConnList * todelete = curr_conn; - curr_conn = curr_conn->next; + curr_conn = curr_conn->getNext(); if (prev_conn == NULL) { connections = curr_conn; } else { - prev_conn->next = curr_conn; + prev_conn->setNext(curr_conn); } delete (todelete); } else { prev_conn = curr_conn; - curr_conn = curr_conn->next; + curr_conn = curr_conn->getNext(); } } } @@ -186,12 +176,12 @@ Connection * findConnection (Packet * packet) while (current != NULL) { /* the reference packet is always *outgoing* */ - if (packet->match(current->val->refpacket)) + if (packet->match(current->getVal()->refpacket)) { - return current->val; + return current->getVal(); } - current = current->next; + current = current->getNext(); } // Try again, now with the packet inverted: @@ -201,13 +191,13 @@ Connection * findConnection (Packet * packet) while (current != NULL) { /* the reference packet is always *outgoing* */ - if (invertedPacket->match(current->val->refpacket)) + if (invertedPacket->match(current->getVal()->refpacket)) { delete invertedPacket; - return current->val; + return current->getVal(); } - current = current->next; + current = current->getNext(); } delete invertedPacket; diff --git a/cui.cpp b/cui.cpp index 5d9541b..6f8595d 100644 --- a/cui.cpp +++ b/cui.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -34,8 +35,7 @@ std::string * caption; -//extern char [] version; -const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION; +extern const char version[]; extern ProcList * processes; extern timeval curtime; @@ -79,20 +79,33 @@ private: uid_t m_uid; }; -char * uid2username (uid_t uid) +#include + +std::string itoa(int i) +{ + std::stringstream out; + out << i; + return out.str(); +} + +/** + * @returns the username that corresponds to this uid + */ +std::string uid2username (uid_t uid) { struct passwd * pwd = NULL; - /* getpwuid() allocates space for this itself, - * which we shouldn't free */ + errno = NULL; + + /* points to a static memory area, should not be freed */ pwd = getpwuid(uid); if (pwd == NULL) - { - assert(false); - return strdup ("unlisted"); - } else { - return strdup(pwd->pw_name); - } + if (errno == 0) + return itoa(uid); + else + forceExit(false, "Error calling getpwuid(3) for uid %d: %d %s", uid, errno, strerror(errno)); + else + return std::string(pwd->pw_name); } @@ -111,9 +124,8 @@ void Line::show (int row, unsigned int proglen) mvprintw (3+row, 0, "?"); else mvprintw (3+row, 0, "%d", m_pid); - char * username = uid2username(m_uid); - mvprintw (3+row, 6, "%s", username); - free (username); + std::string username = uid2username(m_uid); + mvprintw (3+row, 6, "%s", username.c_str()); if (strlen (m_name) > proglen) { // truncate oversized names char * tmp = strdup(m_name); @@ -191,7 +203,7 @@ void init_ui () cbreak(); nodelay(screen, TRUE); caption = new std::string ("NetHogs"); - caption->append(version); + caption->append(getVersion()); //caption->append(", running at "); } @@ -430,7 +442,7 @@ void do_refresh() } else { - forceExit("Invalid viewmode", -1); + forceExit(false, "Invalid viewMode: %d", viewMode); } uid_t uid = curproc->getVal()->getUid(); #ifndef NDEBUG diff --git a/decpcap.h b/decpcap.h index 8f7dd2c..a64255a 100644 --- a/decpcap.h +++ b/decpcap.h @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ - +#ifndef __DECPCAP_H +#define __DECPCAP_H #include #include @@ -84,3 +85,5 @@ int dp_datalink(struct dp_handle * handle); int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf); char * dp_geterr (struct dp_handle * handle); + +#endif diff --git a/devices.h b/devices.h index 68d4fda..6652305 100644 --- a/devices.h +++ b/devices.h @@ -19,6 +19,8 @@ * */ +#ifndef __DEVICES_H +#define __DEVICES_H #include // NULL @@ -33,3 +35,5 @@ public: }; device * determine_default_device(); + +#endif diff --git a/inode2prog.h b/inode2prog.h index 92d8e31..120dc5f 100644 --- a/inode2prog.h +++ b/inode2prog.h @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ - +#ifndef __INODE2PROG_h +#define __INODE2PROG_h /* this should be called quickly after the packet * arrived, since the inode may disappear from the table @@ -39,3 +40,5 @@ void prg_cache_clear(); // reread the inode-to-prg_node-mapping void reread_mapping (); + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..139dd98 --- /dev/null +++ b/main.cpp @@ -0,0 +1,179 @@ +#include "nethogs.cpp" + +static void versiondisplay(void) +{ + std::cerr << version << "\n"; +} + +static void help(void) +{ + //std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))] [device [device [device ...]]]\n"; + std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [device [device [device ...]]]\n"; + std::cerr << " -V : prints version.\n"; + std::cerr << " -d : delay for update refresh rate in seconds. default is 1.\n"; + std::cerr << " -t : tracemode.\n"; + //std::cerr << " -f : format of packets on interface, default is eth.\n"; + std::cerr << " -b : bughunt mode - implies tracemode.\n"; + std::cerr << " -p : sniff in promiscious mode (not recommended).\n"; + std::cerr << " device : device(s) to monitor. default is eth0\n"; + std::cerr << std::endl; + std::cerr << "When nethogs is running, press:\n"; + std::cerr << " q: quit\n"; + std::cerr << " m: switch between total and kb/s mode\n"; +} + +int main (int argc, char** argv) +{ + process_init(); + + device * devices = NULL; + //dp_link_type linktype = dp_link_ethernet; + int promisc = 0; + + int opt; + while ((opt = getopt(argc, argv, "Vhbtpd:")) != -1) { + switch(opt) { + case 'V': + versiondisplay(); + exit(0); + case 'h': + help(); + exit(0); + case 'b': + bughuntmode = true; + tracemode = true; + break; + case 't': + tracemode = true; + break; + case 'p': + promisc = 1; + break; + case 'd': + refreshdelay=atoi(optarg); + break; + /* + case 'f': + argv++; + if (strcmp (optarg, "ppp") == 0) + linktype = dp_link_ppp; + else if (strcmp (optarg, "eth") == 0) + linktype = dp_link_ethernet; + } + break; + */ + default: + help(); + exit(EXIT_FAILURE); + } + } + + while (optind < argc) { + devices = new device (strdup(argv[optind++]), devices); + } + + if (devices == NULL) + { + devices = determine_default_device(); + } + + if ((!tracemode) && (!DEBUG)){ + init_ui(); + } + + if (NEEDROOT && (getuid() != 0)) + forceExit(false, "You need to be root to run NetHogs!"); + + char errbuf[PCAP_ERRBUF_SIZE]; + + handle * handles = NULL; + device * current_dev = devices; + while (current_dev != NULL) { + getLocal(current_dev->name, tracemode); + if ((!tracemode) && (!DEBUG)){ + //caption->append(current_dev->name); + //caption->append(" "); + } + + dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, promisc, 100, errbuf); + if (newhandle != NULL) + { + dp_addcb (newhandle, dp_packet_ip, process_ip); + dp_addcb (newhandle, dp_packet_ip6, process_ip6); + dp_addcb (newhandle, dp_packet_tcp, process_tcp); + dp_addcb (newhandle, dp_packet_udp, process_udp); + + /* The following code solves sf.net bug 1019381, but is only available + * in newer versions (from 0.8 it seems) of libpcap + * + * update: version 0.7.2, which is in debian stable now, should be ok + * also. + */ + if (dp_setnonblock (newhandle, 1, errbuf) == -1) + { + fprintf(stderr, "Error putting libpcap in nonblocking mode\n"); + } + handles = new handle (newhandle, current_dev->name, handles); + } + else + { + fprintf(stderr, "Error opening handler for device %s\n", current_dev->name); + } + + current_dev = current_dev->next; + } + + signal (SIGALRM, &alarm_cb); + signal (SIGINT, &quit_cb); + alarm (refreshdelay); + + fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); + + // Main loop: + // + // Walks though the 'handles' list, which contains handles opened in non-blocking mode. + // This causes the CPU utilisation to go up to 100%. This is tricky: + while (1) + { + bool packets_read = false; + + handle * current_handle = handles; + while (current_handle != NULL) + { + struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); + userdata->sa_family = AF_UNSPEC; + currentdevice = current_handle->devicename; + int retval = dp_dispatch (current_handle->content, -1, (u_char *)userdata, sizeof (struct dpargs)); + if (retval == -1 || retval == -2) + { + std::cerr << "Error dispatching" << std::endl; + } + else if (retval != 0) + { + packets_read = true; + } + free (userdata); + current_handle = current_handle->next; + } + + if ((!DEBUG)&&(!tracemode)) + { + // handle user input + ui_tick(); + } + + if (needrefresh) + { + do_refresh(); + needrefresh = false; + } + + // If no packets were read at all this iteration, pause to prevent 100% + // CPU utilisation; + if (!packets_read) + { + usleep(100); + } + } +} + diff --git a/nethogs.cpp b/nethogs.cpp index 53ff9ce..a1920fb 100644 --- a/nethogs.cpp +++ b/nethogs.cpp @@ -106,6 +106,11 @@ struct dpargs { in6_addr ip6_dst; }; +const char* getVersion() +{ + return version; +} + int process_tcp (u_char * userdata, const dp_header * header, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; struct tcphdr * tcp = (struct tcphdr *) m_packet; @@ -222,7 +227,7 @@ void quit_cb (int /* i */) exit(0); } -void forceExit(const char *msg, int errcode, ...) +void forceExit(bool success, const char *msg, ...) { if ((!tracemode)&&(!DEBUG)){ exit_ui(); @@ -234,30 +239,10 @@ void forceExit(const char *msg, int errcode, ...) va_end(argp); std::cerr << std::endl; - exit(errcode); -} - -static void versiondisplay(void) -{ - std::cerr << version << "\n"; -} - -static void help(void) -{ - //std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))] [device [device [device ...]]]\n"; - std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [device [device [device ...]]]\n"; - std::cerr << " -V : prints version.\n"; - std::cerr << " -d : delay for update refresh rate in seconds. default is 1.\n"; - std::cerr << " -t : tracemode.\n"; - //std::cerr << " -f : format of packets on interface, default is eth.\n"; - std::cerr << " -b : bughunt mode - implies tracemode.\n"; - std::cerr << " -p : sniff in promiscious mode (not recommended).\n"; - std::cerr << " device : device(s) to monitor. default is eth0\n"; - std::cerr << std::endl; - std::cerr << "When nethogs is running, press:\n"; - std::cerr << " q: quit\n"; - std::cerr << " m: switch between total and kb/s mode\n"; - + if (success) + exit(EXIT_SUCCESS); + else + exit(EXIT_FAILURE); } class handle { @@ -271,158 +256,3 @@ public: handle * next; }; -int main (int argc, char** argv) -{ - process_init(); - - device * devices = NULL; - //dp_link_type linktype = dp_link_ethernet; - int promisc = 0; - - int opt; - while ((opt = getopt(argc, argv, "Vhbtpd:")) != -1) { - switch(opt) { - case 'V': - versiondisplay(); - exit(0); - case 'h': - help(); - exit(0); - case 'b': - bughuntmode = true; - tracemode = true; - break; - case 't': - tracemode = true; - break; - case 'p': - promisc = 1; - break; - case 'd': - refreshdelay=atoi(optarg); - break; - /* - case 'f': - argv++; - if (strcmp (optarg, "ppp") == 0) - linktype = dp_link_ppp; - else if (strcmp (optarg, "eth") == 0) - linktype = dp_link_ethernet; - } - break; - */ - default: - help(); - exit(EXIT_FAILURE); - } - } - - while (optind < argc) { - devices = new device (strdup(argv[optind++]), devices); - } - - if (devices == NULL) - { - devices = determine_default_device(); - } - - if ((!tracemode) && (!DEBUG)){ - init_ui(); - } - - if (NEEDROOT && (getuid() != 0)) - forceExit("You need to be root to run NetHogs!", -2); - - char errbuf[PCAP_ERRBUF_SIZE]; - - handle * handles = NULL; - device * current_dev = devices; - while (current_dev != NULL) { - getLocal(current_dev->name, tracemode); - if ((!tracemode) && (!DEBUG)){ - //caption->append(current_dev->name); - //caption->append(" "); - } - - dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, promisc, 100, errbuf); - if (newhandle != NULL) - { - dp_addcb (newhandle, dp_packet_ip, process_ip); - dp_addcb (newhandle, dp_packet_ip6, process_ip6); - dp_addcb (newhandle, dp_packet_tcp, process_tcp); - dp_addcb (newhandle, dp_packet_udp, process_udp); - - /* The following code solves sf.net bug 1019381, but is only available - * in newer versions (from 0.8 it seems) of libpcap - * - * update: version 0.7.2, which is in debian stable now, should be ok - * also. - */ - if (dp_setnonblock (newhandle, 1, errbuf) == -1) - { - fprintf(stderr, "Error putting libpcap in nonblocking mode\n"); - } - handles = new handle (newhandle, current_dev->name, handles); - } - else - { - fprintf(stderr, "Error opening handler for device %s\n", current_dev->name); - } - - current_dev = current_dev->next; - } - - signal (SIGALRM, &alarm_cb); - signal (SIGINT, &quit_cb); - alarm (refreshdelay); - - fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); - - // Main loop: - // - // Walks though the 'handles' list, which contains handles opened in non-blocking mode. - // This causes the CPU utilisation to go up to 100%. This is tricky: - while (1) - { - bool packets_read = false; - - handle * current_handle = handles; - while (current_handle != NULL) - { - struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); - userdata->sa_family = AF_UNSPEC; - currentdevice = current_handle->devicename; - int retval = dp_dispatch (current_handle->content, -1, (u_char *)userdata, sizeof (struct dpargs)); - if (retval == -1 || retval == -2) - { - std::cerr << "Error dispatching" << std::endl; - } - else if (retval != 0) - { - packets_read = true; - } - free (userdata); - current_handle = current_handle->next; - } - - if ((!DEBUG)&&(!tracemode)) - { - // handle user input - ui_tick(); - } - - if (needrefresh) - { - do_refresh(); - needrefresh = false; - } - - // If no packets were read at all this iteration, pause to prevent 100% - // CPU utilisation; - if (!packets_read) - { - usleep(100); - } - } -} - diff --git a/nethogs.h b/nethogs.h index a39d99f..cbca93b 100644 --- a/nethogs.h +++ b/nethogs.h @@ -63,7 +63,9 @@ #define PROGNAME_WIDTH 512 -void forceExit(const char *msg, int errcode, ...); +#define NORETURN __attribute__ ((__noreturn__)) + +void forceExit(bool success, const char *msg, ...) NORETURN; class local_addr { public: @@ -128,4 +130,6 @@ private: void quit_cb (int i); +const char* getVersion(); + #endif diff --git a/packet.cpp b/packet.cpp index 36e19e1..fa36838 100644 --- a/packet.cpp +++ b/packet.cpp @@ -60,13 +60,14 @@ void getLocal (const char *device, bool tracemode) struct ifreq iFreq; struct sockaddr_in *saddr; - if((sock=socket(AF_INET, SOCK_RAW, htons(0x0806)))<0){ - forceExit("creating socket failed while establishing local IP - are you root?", -3); - } + if((sock=socket(AF_INET, SOCK_RAW, htons(0x0806)))<0) + forceExit(false, "creating socket failed while establishing local IP - are you root?"); + strcpy(iFreq.ifr_name, device); - if(ioctl(sock, SIOCGIFADDR, &iFreq)<0){ - forceExit("ioctl failed while establishing local IP for selected device %s. You may specify the device on the command line.", -4, device); - } + + if(ioctl(sock, SIOCGIFADDR, &iFreq)<0) + forceExit(false, "ioctl failed while establishing local IP for selected device %s. You may specify the device on the command line.", device); + saddr=(struct sockaddr_in*)&iFreq.ifr_addr; local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs);