make a 'runtests' make target that runs some 'unit test'-like tests. Fix crash when uid's without a password entry are encountered, return the uid as a string instead.

This commit is contained in:
Arnout Engelen
2012-03-15 22:31:28 +00:00
parent 2c04ca9267
commit 4ff973bbd7
11 changed files with 284 additions and 229 deletions

View File

@@ -9,6 +9,11 @@ sbin := $(DESTDIR)/sbin
man8 := $(DESTDIR)/share/man/man8/ man8 := $(DESTDIR)/share/man/man8/
all: nethogs decpcap_test all: nethogs decpcap_test
runtests: test
./test
# nethogs_testsum # nethogs_testsum
CFLAGS=-g -Wall -Wextra CFLAGS=-g -Wall -Wextra
@@ -29,8 +34,11 @@ install: nethogs nethogs.8
install -d -m 755 $(man8) install -d -m 755 $(man8)
install -m 644 nethogs.8 $(man8) install -m 644 nethogs.8 $(man8)
nethogs: nethogs.cpp $(OBJS) test: test.cpp
$(CXX) $(CFLAGS) nethogs.cpp $(OBJS) -o nethogs -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" $(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) 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)\" $(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: clean:
rm -f $(OBJS) rm -f $(OBJS)
rm -f nethogs rm -f nethogs
rm -f test

22
README
View File

@@ -18,8 +18,28 @@ Ideas/ToDo for new releases:
* Make it work correctly on machines with multiple IP addresses * Make it work correctly on machines with multiple IP addresses
* Integrate into another tool?? * 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 == == LICENSE ==
Copyright 2004-2005, 2008, 2010-2011 Arnout Engelen <arnouten@bzzt.net> Copyright 2004-2005, 2008, 2010-2012 Arnout Engelen <arnouten@bzzt.net>
License: nethogs may be redistributed under the terms of the GPLv2 or any License: nethogs may be redistributed under the terms of the GPLv2 or any
later version. See the COPYING file for the license text. later version. See the COPYING file for the license text.

View File

@@ -25,17 +25,7 @@
#include <malloc.h> #include <malloc.h>
#include "nethogs.h" #include "nethogs.h"
#include "connection.h" #include "connection.h"
#include "process.h"
class ConnList
{
public:
ConnList (Connection * m_val = NULL, ConnList * m_next = NULL)
{
val = m_val; next = m_next;
}
Connection * val;
ConnList * next;
};
ConnList * connections = NULL; ConnList * connections = NULL;
@@ -127,22 +117,22 @@ Connection::~Connection ()
ConnList * prev_conn = NULL; ConnList * prev_conn = NULL;
while (curr_conn != NULL) while (curr_conn != NULL)
{ {
if (curr_conn->val == this) if (curr_conn->getVal() == this)
{ {
ConnList * todelete = curr_conn; ConnList * todelete = curr_conn;
curr_conn = curr_conn->next; curr_conn = curr_conn->getNext();
if (prev_conn == NULL) if (prev_conn == NULL)
{ {
connections = curr_conn; connections = curr_conn;
} else { } else {
prev_conn->next = curr_conn; prev_conn->setNext(curr_conn);
} }
delete (todelete); delete (todelete);
} }
else else
{ {
prev_conn = curr_conn; 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) while (current != NULL)
{ {
/* the reference packet is always *outgoing* */ /* 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: // Try again, now with the packet inverted:
@@ -201,13 +191,13 @@ Connection * findConnection (Packet * packet)
while (current != NULL) while (current != NULL)
{ {
/* the reference packet is always *outgoing* */ /* the reference packet is always *outgoing* */
if (invertedPacket->match(current->val->refpacket)) if (invertedPacket->match(current->getVal()->refpacket))
{ {
delete invertedPacket; delete invertedPacket;
return current->val; return current->getVal();
} }
current = current->next; current = current->getNext();
} }
delete invertedPacket; delete invertedPacket;

44
cui.cpp
View File

@@ -25,6 +25,7 @@
#include <pwd.h> #include <pwd.h>
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
@@ -34,8 +35,7 @@
std::string * caption; std::string * caption;
//extern char [] version; extern const char version[];
const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION;
extern ProcList * processes; extern ProcList * processes;
extern timeval curtime; extern timeval curtime;
@@ -79,20 +79,33 @@ private:
uid_t m_uid; uid_t m_uid;
}; };
char * uid2username (uid_t uid) #include <sstream>
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; struct passwd * pwd = NULL;
/* getpwuid() allocates space for this itself, errno = NULL;
* which we shouldn't free */
/* points to a static memory area, should not be freed */
pwd = getpwuid(uid); pwd = getpwuid(uid);
if (pwd == NULL) if (pwd == NULL)
{ if (errno == 0)
assert(false); return itoa(uid);
return strdup ("unlisted"); else
} else { forceExit(false, "Error calling getpwuid(3) for uid %d: %d %s", uid, errno, strerror(errno));
return strdup(pwd->pw_name); else
} return std::string(pwd->pw_name);
} }
@@ -111,9 +124,8 @@ void Line::show (int row, unsigned int proglen)
mvprintw (3+row, 0, "?"); mvprintw (3+row, 0, "?");
else else
mvprintw (3+row, 0, "%d", m_pid); mvprintw (3+row, 0, "%d", m_pid);
char * username = uid2username(m_uid); std::string username = uid2username(m_uid);
mvprintw (3+row, 6, "%s", username); mvprintw (3+row, 6, "%s", username.c_str());
free (username);
if (strlen (m_name) > proglen) { if (strlen (m_name) > proglen) {
// truncate oversized names // truncate oversized names
char * tmp = strdup(m_name); char * tmp = strdup(m_name);
@@ -191,7 +203,7 @@ void init_ui ()
cbreak(); cbreak();
nodelay(screen, TRUE); nodelay(screen, TRUE);
caption = new std::string ("NetHogs"); caption = new std::string ("NetHogs");
caption->append(version); caption->append(getVersion());
//caption->append(", running at "); //caption->append(", running at ");
} }
@@ -430,7 +442,7 @@ void do_refresh()
} }
else else
{ {
forceExit("Invalid viewmode", -1); forceExit(false, "Invalid viewMode: %d", viewMode);
} }
uid_t uid = curproc->getVal()->getUid(); uid_t uid = curproc->getVal()->getUid();
#ifndef NDEBUG #ifndef NDEBUG

View File

@@ -18,7 +18,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* *
*/ */
#ifndef __DECPCAP_H
#define __DECPCAP_H
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@@ -84,3 +85,5 @@ int dp_datalink(struct dp_handle * handle);
int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf); int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf);
char * dp_geterr (struct dp_handle * handle); char * dp_geterr (struct dp_handle * handle);
#endif

View File

@@ -19,6 +19,8 @@
* *
*/ */
#ifndef __DEVICES_H
#define __DEVICES_H
#include <cstddef> // NULL #include <cstddef> // NULL
@@ -33,3 +35,5 @@ public:
}; };
device * determine_default_device(); device * determine_default_device();
#endif

View File

@@ -18,7 +18,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 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 /* this should be called quickly after the packet
* arrived, since the inode may disappear from the table * arrived, since the inode may disappear from the table
@@ -39,3 +40,5 @@ void prg_cache_clear();
// reread the inode-to-prg_node-mapping // reread the inode-to-prg_node-mapping
void reread_mapping (); void reread_mapping ();
#endif

179
main.cpp Normal file
View File

@@ -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);
}
}
}

View File

@@ -106,6 +106,11 @@ struct dpargs {
in6_addr ip6_dst; in6_addr ip6_dst;
}; };
const char* getVersion()
{
return version;
}
int process_tcp (u_char * userdata, const dp_header * header, const u_char * m_packet) { int process_tcp (u_char * userdata, const dp_header * header, const u_char * m_packet) {
struct dpargs * args = (struct dpargs *) userdata; struct dpargs * args = (struct dpargs *) userdata;
struct tcphdr * tcp = (struct tcphdr *) m_packet; struct tcphdr * tcp = (struct tcphdr *) m_packet;
@@ -222,7 +227,7 @@ void quit_cb (int /* i */)
exit(0); exit(0);
} }
void forceExit(const char *msg, int errcode, ...) void forceExit(bool success, const char *msg, ...)
{ {
if ((!tracemode)&&(!DEBUG)){ if ((!tracemode)&&(!DEBUG)){
exit_ui(); exit_ui();
@@ -234,30 +239,10 @@ void forceExit(const char *msg, int errcode, ...)
va_end(argp); va_end(argp);
std::cerr << std::endl; std::cerr << std::endl;
exit(errcode); if (success)
} exit(EXIT_SUCCESS);
else
static void versiondisplay(void) exit(EXIT_FAILURE);
{
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";
} }
class handle { class handle {
@@ -271,158 +256,3 @@ public:
handle * next; 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);
}
}
}

View File

@@ -63,7 +63,9 @@
#define PROGNAME_WIDTH 512 #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 { class local_addr {
public: public:
@@ -128,4 +130,6 @@ private:
void quit_cb (int i); void quit_cb (int i);
const char* getVersion();
#endif #endif

View File

@@ -60,13 +60,14 @@ void getLocal (const char *device, bool tracemode)
struct ifreq iFreq; struct ifreq iFreq;
struct sockaddr_in *saddr; struct sockaddr_in *saddr;
if((sock=socket(AF_INET, SOCK_RAW, htons(0x0806)))<0){ if((sock=socket(AF_INET, SOCK_RAW, htons(0x0806)))<0)
forceExit("creating socket failed while establishing local IP - are you root?", -3); forceExit(false, "creating socket failed while establishing local IP - are you root?");
}
strcpy(iFreq.ifr_name, device); 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; saddr=(struct sockaddr_in*)&iFreq.ifr_addr;
local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs); local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs);