Added tcp port monitor support
authorPhilip Kovacs <pkovacs@users.sourceforge.net>
Mon, 31 Oct 2005 05:17:06 +0000 (05:17 +0000)
committerPhilip Kovacs <pkovacs@users.sourceforge.net>
Mon, 31 Oct 2005 05:17:06 +0000 (05:17 +0000)
git-svn-id: https://conky.svn.sourceforge.net/svnroot/conky/trunk/conky@357 7f574dfc-610e-0410-a909-a81674777703

ChangeLog
configure.in
src/Makefile.am
src/common.c
src/conky.c
src/conky.h
src/hash.c [new file with mode: 0644]
src/hash.h [new file with mode: 0644]
src/libtcp-portmon.c [new file with mode: 0644]
src/libtcp-portmon.h [new file with mode: 0644]

index 4742947..9fc3fb0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 # $Id$
 
+2005-10-30
+       * Added tcp port monitor support (pkovacs)
+
 2005-10-28
        * Added WM_CLASS patch from sf.net patch 1340825
 
index f16af11..a787026 100644 (file)
@@ -107,6 +107,24 @@ if test x$want_mpd = xyes; then
 fi
 
 dnl
+dnl PORT_MONITORS
+dnl
+
+want_portmon=no
+AC_ARG_ENABLE(portmon,
+[  --enable-portmon        enable if you want tcp (ip4) port monitoring [[default=no]]],
+  [want_portmon="$enableval"])
+
+AM_CONDITIONAL(BUILD_PORT_MONITORS, test x$want_portmon = xyes)
+if test x$want_portmon = xyes; then
+  AC_CHECK_HEADERS([netdb.h netinet/in.h netinet/tcp.h sys/socket.h arpa/inet.h], [], [PORT_MONITORS_MISSING=yes])
+  if test "x$PORT_MONITORS_MISSING" = xyes; then
+         AC_MSG_ERROR([missing a needed network header for port monitoring])
+  fi
+  AC_DEFINE(TCP_PORT_MONITOR, 1, [Define if you want tcp port monitoring support])
+fi
+
+dnl
 dnl Cairo
 dnl
 
index eb02c52..f04c04a 100644 (file)
@@ -26,6 +26,10 @@ if BUILD_NETBSD
 netbsd = netbsd.c
 endif
 
+if BUILD_PORT_MONITORS
+port_monitors = libtcp-portmon.h libtcp-portmon.c hash.h hash.c
+endif
+
 if BUILD_CAIRO
 cairo = cairo.c
 endif
@@ -38,7 +42,7 @@ if BUILD_X11
 x11 = x11.c
 endif
 
-conky_SOURCES = common.c fs.c $(linux) mail.c mixer.c $(seti) $(mpd) $(solaris) $(freebsd) $(netbsd) $(cairo) conky.c conky.h $(x11) $(mldonkey) remoted.c remoted.h remotec.c remotec.h
+conky_SOURCES = common.c fs.c $(linux) mail.c mixer.c $(seti) $(mpd) $(solaris) $(freebsd) $(netbsd) $(port_monitors) $(cairo) conky.c conky.h $(x11) $(mldonkey) remoted.c remoted.h remotec.c remotec.h
 
 AM_LDFLAGS = $(X11_LIBS) $(XFT_LIBS) $(CAIRO_LIBS) $(PTHREAD_LIBS) -lm
 
index c5c792a..2810677 100644 (file)
@@ -244,6 +244,10 @@ void update_stuff()
                update_fs_stats();
                last_fs_update = current_update_time;
        }
+#ifdef TCP_PORT_MONITOR
+       if (NEED(INFO_TCP_PORT_MONITOR))
+               update_tcp_port_monitor_collection( info.p_tcp_port_monitor_collection );
+#endif
 }
 
 int round_to_int(float f)
index 8724591..020e3ce 100644 (file)
@@ -884,6 +884,9 @@ enum text_object_type {
        OBJ_mpd_track,
        OBJ_mpd_percent,
 #endif
+#ifdef TCP_PORT_MONITOR
+       OBJ_tcp_portmon,
+#endif
 };
 
 struct text_object {
@@ -945,6 +948,14 @@ struct text_object {
                struct {
                        int a, b;
                } pair;         /* 2 */
+#ifdef TCP_PORT_MONITOR
+               struct {
+                       in_port_t  port_range_begin;  /* starting port to monitor */
+                       in_port_t  port_range_end;    /* ending port to monitor */
+                       int        item;              /* enum value in tcp_port_monitor.h, e.g. COUNT, REMOTEIP, etc. */
+                       int        connection_index;  /* 0 to n-1 connections. */
+               } tcp_port_monitor;
+#endif
        } data;
 };
 
@@ -1736,6 +1747,89 @@ int a = stippled_borders, b = 1;
         (void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
        END
 #endif
+#ifdef TCP_PORT_MONITOR
+       OBJ(tcp_portmon, INFO_TCP_PORT_MONITOR) 
+               int argc, port_begin, port_end, item, connection_index;
+               char itembuf[32];
+               memset(itembuf,0,sizeof(itembuf));
+               /* massive argument checking */
+               if (!arg) {
+                       CRIT_ERR("tcp_portmon: needs arguments");
+               }
+               argc=sscanf(arg, "%d %d %31s %d", &port_begin, &port_end, itembuf, &connection_index);
+               if ( (argc != 3) && (argc != 4) ) 
+               {
+                       CRIT_ERR("tcp_portmon: requires 3 or 4 arguments");
+               }
+               if ( (port_begin<1) || (port_begin>65535) || (port_end<1) || (port_end>65535) )
+               {
+                       CRIT_ERR("tcp_portmon: port values must be from 1 to 65535");
+               }
+               if ( port_begin > port_end )
+               {
+                       CRIT_ERR("tcp_portmon: starting port must be <= ending port");
+               }
+               if ( strncmp(itembuf,"count",31) == 0 )
+                       item=COUNT;
+               else if ( strncmp(itembuf,"rip",31) == 0 )
+                       item=REMOTEIP;
+               else if ( strncmp(itembuf,"rhost",31) == 0 )
+                       item=REMOTEHOST;
+               else if ( strncmp(itembuf,"rport",31) == 0 )
+                       item=REMOTEPORT;
+               else if ( strncmp(itembuf,"lip",31) == 0 )
+                       item=LOCALIP;
+               else if ( strncmp(itembuf,"lhost",31) == 0 )
+                       item=LOCALHOST;
+               else if ( strncmp(itembuf,"lport",31) == 0 )
+                       item=LOCALPORT;
+               else if ( strncmp(itembuf,"lservice",31) == 0 )
+                       item=LOCALSERVICE;
+               else
+               {
+                       CRIT_ERR("tcp_portmon: invalid item specified"); 
+               }
+               if ( (argc==3) && (item!=COUNT) )
+               {
+                       CRIT_ERR("tcp_portmon: 3 argument form valid only for \"count\" item");
+               }
+               if ( (argc==4) && (connection_index<0) )
+               {
+                       CRIT_ERR("tcp_portmon: connection index must be non-negative");
+               }
+               /* ok, args looks good. save the text object data */
+               obj->data.tcp_port_monitor.port_range_begin = (in_addr_t)port_begin;
+               obj->data.tcp_port_monitor.port_range_end = (in_addr_t)port_end;
+               obj->data.tcp_port_monitor.item = item;
+               obj->data.tcp_port_monitor.connection_index = connection_index;
+
+               /* if the port monitor collection hasn't been created, we must create it */
+               if ( !info.p_tcp_port_monitor_collection )
+               {
+                       info.p_tcp_port_monitor_collection = create_tcp_port_monitor_collection();
+                       if ( !info.p_tcp_port_monitor_collection )
+                       {
+                               CRIT_ERR("tcp_portmon: unable to create port monitor collection");
+                       }
+               }
+
+               /* if a port monitor for this port does not exist, create one and add it to the collection */
+               if ( find_tcp_port_monitor( info.p_tcp_port_monitor_collection, port_begin, port_end ) == NULL )
+               {
+                       tcp_port_monitor_t * p_monitor = create_tcp_port_monitor( port_begin, port_end );
+                       if ( !p_monitor )
+                       {
+                               CRIT_ERR("tcp_portmon: unable to create port monitor");
+                       }
+                       /* add the newly created monitor to the collection */
+                       if ( insert_tcp_port_monitor_into_collection( info.p_tcp_port_monitor_collection,
+                                                                     p_monitor ) != 0 )
+                       {
+                               CRIT_ERR("tcp_portmon: unable to add port monitor to collection");
+                       }
+               }
+       END
+#endif
        {
                char buf[256];
                ERR("unknown variable %s", s);
@@ -3046,6 +3140,30 @@ static void generate_text()
                                        }
                                }
                        }
+#ifdef TCP_PORT_MONITOR
+                       OBJ(tcp_portmon)
+                       {
+                               /* grab a pointer to this port monitor */
+                               tcp_port_monitor_t * p_monitor = 
+                                       find_tcp_port_monitor( info.p_tcp_port_monitor_collection,
+                                                               obj->data.tcp_port_monitor.port_range_begin,
+                                                               obj->data.tcp_port_monitor.port_range_end );
+                               if ( !p_monitor ) {
+                                       snprintf(p, n, "monitor not found");
+                                       break;
+                               }
+
+                               /* now grab the text of interest */
+                               if ( peek_tcp_port_monitor( p_monitor, 
+                                                           obj->data.tcp_port_monitor.item, 
+                                                           obj->data.tcp_port_monitor.connection_index,
+                                                           p, n ) != 0 )
+                               {
+                                       snprintf(p, n, "monitor peek error");
+                                       break;
+                               }
+                       }
+#endif
 
                        break;
                }
@@ -4158,6 +4276,10 @@ static void reload_handler(int a)
                XClearWindow(display, RootWindow(display, screen)); // clear the window first
 
 #endif /* X11 */
+#ifdef TCP_PORT_MONITOR
+               destroy_tcp_port_monitor_collection( info.p_tcp_port_monitor_collection );
+               info.p_tcp_port_monitor_collection = NULL; 
+#endif
                extract_variable_text(text);
                free(text);
                text = NULL;
@@ -4201,6 +4323,10 @@ static void clean_up()
 #ifdef SETI
        free(seti_dir);
 #endif
+#ifdef TCP_PORT_MONITOR
+       destroy_tcp_port_monitor_collection( info.p_tcp_port_monitor_collection );
+       info.p_tcp_port_monitor_collection = NULL;
+#endif
 }
 
 static void term_handler(int a)
@@ -4283,7 +4409,7 @@ static void set_default_configurations(void)
        maximum_width = 0;
 #ifdef OWN_WINDOW
        own_window = 0;
-    strcpy(wm_class_name, "conky");
+       strcpy(wm_class_name, "conky"); 
 #endif
        stippled_borders = 0;
        border_margin = 3;
@@ -4642,10 +4768,10 @@ else if (strcasecmp(name, a) == 0 || strcasecmp(name, b) == 0)
                CONF("own_window") {
                        own_window = string_to_bool(value);
                }
-        CONF("wm_class_name") {
-            strncpy(wm_class_name, value, sizeof(wm_class_name)-1);
-            wm_class_name[sizeof(wm_class_name)-1] = 0;
-        }
+               CONF("wm_class_name") {
+                       strncpy(wm_class_name, value, sizeof(wm_class_name)-1);
+                       wm_class_name[sizeof(wm_class_name)-1] = 0;
+               }
                CONF("own_window_transparent") {
                        set_transparent = string_to_bool(value);
                }
@@ -4741,6 +4867,8 @@ static const char *getopt_string = "vVdt:f:u:i:hc:w:x:y:a:"
 
 int main(int argc, char **argv)
 {
+       memset(&info, 0, sizeof(info) );
+
        /* handle command line parameters that don't change configs */
 #ifdef X11
        char *s;
@@ -4951,7 +5079,7 @@ int main(int argc, char **argv)
 #if defined OWN_WINDOW
        init_window
            (own_window,
-         wm_class_name,
+            wm_class_name,
             text_width + border_margin * 2 + 1,
             text_height + border_margin * 2 + 1,
             on_bottom, fixed_pos, set_transparent, background_colour);
index 034bf7b..88160fb 100644 (file)
@@ -114,6 +114,10 @@ struct mpd_s {
 };
 #endif
 
+#ifdef TCP_PORT_MONITOR
+#include "libtcp-portmon.h"
+#endif
+
 enum {
        INFO_CPU = 0,
        INFO_MAIL = 1,
@@ -142,6 +146,9 @@ enum {
        INFO_WIFI = 19,
        INFO_DISKIO = 20,
        INFO_I8K = 21,
+#ifdef TCP_PORT_MONITOR
+        INFO_TCP_PORT_MONITOR = 22,
+#endif
 };
 
 
@@ -186,6 +193,9 @@ struct information {
        struct process *cpu[10];
        struct process *memu[10];
        unsigned long looped;
+#ifdef TCP_PORT_MONITOR
+        tcp_port_monitor_collection_t * p_tcp_port_monitor_collection;
+#endif
 };
 
 int out_to_console;
diff --git a/src/hash.c b/src/hash.c
new file mode 100644 (file)
index 0000000..a773b73
--- /dev/null
@@ -0,0 +1,192 @@
+#ifdef HASH_DEBUG
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "hash.h"
+
+/* Create and initialize a hash table on the heap.
+   Returns 0 on success, -1 otherwise. */
+int hash_create( hash_table_t *p_hash_table,
+                 int positions,
+                 int (*p_hash_fun1)(const void *p_data),
+                 int (*p_hash_fun2)(const void *p_data),
+                 int (*p_match_fun)(const void *p_data1, const void *p_data2),
+                 void (*p_destroy_fun)(void *p_data)
+        )
+{
+    if ( ( p_hash_table->pp_table = (void **)calloc(positions,  sizeof(void *))) == NULL )
+               return -1;
+
+    p_hash_table->positions = positions;
+    p_hash_table->size = 0;
+    p_hash_table->vacated = 0;
+
+    /* sentinel address indicating a vacated slot */
+    p_hash_table->p_vacated = &p_hash_table->sentinel_vacated;
+
+    p_hash_table->p_hash_fun1 = p_hash_fun1;
+    p_hash_table->p_hash_fun2 = p_hash_fun2;
+
+    p_hash_table->p_match_fun = p_match_fun;
+    p_hash_table->p_destroy_fun = p_destroy_fun;
+
+    return 0;
+}
+
+
+/* Destroy a hash table */
+void hash_destroy( hash_table_t *p_hash_table )
+{
+   int i;
+
+   if ( !p_hash_table )
+       return;
+
+   /* Destroy the elements the hash points to, if a destroy function was provided */
+   if (p_hash_table->p_destroy_fun != NULL) {
+
+       for (i = 0; i < p_hash_table->positions; i++) {
+
+               if ( p_hash_table->pp_table[i] != NULL && p_hash_table->pp_table[i] != p_hash_table->p_vacated )
+                  p_hash_table->p_destroy_fun( p_hash_table->pp_table[i] );
+       }
+   }
+
+   free( p_hash_table->pp_table );
+   memset( p_hash_table, 0, sizeof(hash_table_t) );
+
+   return;
+}
+
+/* Insert an element into a hash table.
+   Returns 0 on successful insert, 1 if data already in hash and -1 if unable to insert. */
+int hash_insert( hash_table_t *p_hash_table, const void *p_data )
+{
+   void *temp;
+   int position, i;
+   int hashed_1, hashed_2;
+
+   if ( !p_hash_table )
+       return -1;
+
+   if ( p_hash_table->size == p_hash_table->positions )
+       return -1;
+
+   temp = (void *)p_data;
+
+   if ( hash_lookup( p_hash_table, &temp ) == 0 )
+       return 1;
+
+   /* Loudon's original algorithm needlessly repeated running the hash algorithms with each iteration
+      to find a slot.   Just running the hash algorithms once is enough since they are deterministic. */
+   hashed_1 = p_hash_table->p_hash_fun1( p_data );
+   hashed_2 = p_hash_table->p_hash_fun2( p_data );
+
+   for ( i = 0; i < p_hash_table->positions; i++ ) {
+
+       position = ( hashed_1 + (i * hashed_2) ) % p_hash_table->positions;
+#ifdef HASH_DEBUG
+               printf("--- hash_insert: probe %d, position %d\n",i,position);
+#endif
+
+       if ( p_hash_table->pp_table[ position ] == NULL ) /* empty slot */
+       {
+               p_hash_table->pp_table[ position ] = (void *)p_data;
+               p_hash_table->size++;
+               return 0;       
+       }
+
+       if ( p_hash_table->pp_table[ position ] == p_hash_table->p_vacated ) /* vacated slot */
+       {
+               p_hash_table->pp_table[ position ] = (void *)p_data;
+               p_hash_table->size++;
+               p_hash_table->vacated--;
+               return 0;       
+       }
+   }
+
+   /* hash functions not selected correctly since the above algorithm should visit all slots in the hash. */
+   return -1;
+}
+
+/* Delete an element from a hash table.
+   Returns 0 on successful delete, -1 if not found. */
+int hash_remove( hash_table_t *p_hash_table, void **pp_data)
+{
+   int position, i;
+   int hashed_1, hashed_2;
+
+   if ( !p_hash_table || !pp_data )
+        return -1; 
+
+   hashed_1 = p_hash_table->p_hash_fun1( *pp_data );
+   hashed_2 = p_hash_table->p_hash_fun2( *pp_data );
+
+   for (i = 0; i < p_hash_table->positions; i++) {
+
+       position= ( hashed_1 + (i * hashed_2) ) % p_hash_table->positions;
+#ifdef HASH_DEBUG
+       printf("--- hash_remove: probe %d, position %d\n",i,position);
+#endif
+
+       if ( p_hash_table->pp_table[ position ] == NULL ) {
+
+               return -1;
+
+        }
+
+       else if ( p_hash_table->pp_table[ position ] == p_hash_table->p_vacated ) {
+
+               continue;
+
+        }
+
+       else if ( p_hash_table->p_match_fun( p_hash_table->pp_table[ position ], *pp_data)) {
+
+               *pp_data = p_hash_table->pp_table[ position ];
+               p_hash_table->pp_table[ position ] = p_hash_table->p_vacated;
+               p_hash_table->vacated++;
+               p_hash_table->size--;
+               return 0;
+       }
+   }
+
+   return -1;
+}
+
+/* Lookup an element in a hash table.
+   Returns 0 if found (data passed back byref), -1 if not found. */
+int hash_lookup(const hash_table_t *p_hash_table, void **pp_data)
+{
+   int position, i;
+   int hashed_1, hashed_2;
+
+   if ( !p_hash_table || !pp_data )
+       return -1;
+
+   hashed_1 = p_hash_table->p_hash_fun1( *pp_data );
+   hashed_2 = p_hash_table->p_hash_fun2( *pp_data );
+
+   for (i = 0; i < p_hash_table->positions; i++) {
+
+       position= ( hashed_1 + (i * hashed_2) ) % p_hash_table->positions;
+#ifdef HASH_DEBUG
+        printf("--- hash_lookup: probe %d, position %d\n",i,position);
+#endif
+       if ( p_hash_table->pp_table[ position ] == NULL ) {
+
+               return -1;
+
+       }
+
+       else if ( p_hash_table->p_match_fun(p_hash_table->pp_table[ position ], *pp_data) ) {
+
+               *pp_data = p_hash_table->pp_table[ position ];
+               return 0;
+
+       }
+   }
+
+   return -1;
+}
diff --git a/src/hash.h b/src/hash.h
new file mode 100644 (file)
index 0000000..930851c
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef HASH_H
+#define HASH_H
+
+/* ------------------------------------------------------
+ * Open-addressed hash using double hash probing
+ *
+ * for i in 0 to m-1: 
+ *     h(k, i) = ( h1(k) + i*h2(k) ) mod m 
+ *
+ * requires: 1) m must be a power of two
+ *           2) h2(k) must return an odd number
+ *
+ * Besed on code published in _Mastering Algorithms in C_
+ * by Kyle Loudon (O'Reilly 1999).
+ * Modified by Philip Kovacs (kovacsp3@comcast.net)
+ * ------------------------------------------------------ */
+
+typedef struct _hash_table_t {
+       int     positions;
+       int     size;
+       int     vacated;
+       int     sentinel_vacated;
+       void    *p_vacated;
+       int     (*p_hash_fun1)(const void *p_data);
+       int     (*p_hash_fun2)(const void *p_data);
+       int     (*p_match_fun)(const void *p_data1, const void *p_data2);
+       void    (*p_destroy_fun)(void *p_data);
+       void    **pp_table;
+} hash_table_t;
+
+/* Create and initialize a hash table on the heap.
+   Returns 0 on success, -1 otherwise. */
+int hash_create( hash_table_t *p_hash_table,
+                int positions,
+                int (*p_hash_fun1)(const void *p_data),
+                int (*p_hash_fun2)(const void *p_data),
+                int (*p_match_fun)(const void *p_data1, const void *p_data2),
+                void (*p_destroy_fun)(void *p_data) 
+       );
+
+/* Destroy a hash table */
+void hash_destroy( hash_table_t *p_hash_table );
+
+/* Insert an element into a hash table.
+   Returns 0 on successful insert, 1 if data already in hash and -1 if unable to insert. */
+int hash_insert( hash_table_t *p_hash_table, const void *p_data);
+
+/* Delete an element from a hash table.
+   Returns 0 on successful delete, -1 if not found. */
+int hash_remove( hash_table_t *p_hash_table, void **pp_data);
+
+/* Lookup an element in a hash table.
+   Returns 0 if found (data passed back byref), -1 if not found. */
+int hash_lookup(const hash_table_t *p_hash_table, void **pp_data);
+
+/* Return size of a hash table */
+#define hash_size(p_hash_table) ((p_hash_table)->size)
+
+#endif
diff --git a/src/libtcp-portmon.c b/src/libtcp-portmon.c
new file mode 100644 (file)
index 0000000..02b34fd
--- /dev/null
@@ -0,0 +1,853 @@
+/* -------------------------------------------------------------------------
+ * libtcp-portmon.c:  tcp port monitoring library.               
+ *
+ * Copyright (C) 2005  Philip Kovacs kovacsp3@comcast.net
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * --------------------------------------------------------------------------- */
+
+#include "libtcp-portmon.h"
+
+/* -------------------------------------------------------------------
+ * IMPLEMENTATION INTERFACE
+ *
+ * Implementation-specific interface begins here.  Clients should not
+ * manipulate these structures directly, nor call the defined helper
+ * functions.  Use the "Client interface" functions defined at bottom.
+ * ------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * Open-addressed hash implementation requires that we supply two hash functions
+ * and a match function to compare two hash elements for identity.
+ * ----------------------------------------------------------------------------- */
+
+/* --------------------------------------------------
+ * Functions to hash the connections within a monitor
+ * --------------------------------------------------*/
+
+#define CONNECTION_HASH_KEY_LEN 17
+
+/* ----------------------------------------------------------------------------------
+ * First connection hash function: DJB with a 65521 prime modulus to govern the range. 
+ * ----------------------------------------------------------------------------------*/
+int connection_hash_function_1( const void *p_data )
+{
+   tcp_connection_t *p_conn;
+   char key[CONNECTION_HASH_KEY_LEN];
+   unsigned int hash = 5381;
+   unsigned int i    = 0;
+
+   if ( !p_data )
+          return -1;
+   
+   memset(key,0,sizeof(key));
+
+   /* p_data is a pointer to tcp_connection_t */
+   p_conn = (tcp_connection_t *)p_data; 
+
+   /* key is a hex representation of the connection */
+   snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X", 
+                  p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
+#ifdef HASH_DEBUG
+   fprintf(stderr,"--- key=[%s]\n",key);
+#endif
+
+   for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
+   {
+      hash = ((hash << 5) + hash) + (key[i]);
+   }
+
+   return (hash & 0x7FFFFFFF) % 65521;
+}
+
+/* -------------------------------------------------------------------------
+ * Second connection hash function: DEK, modified to return odd numbers only,
+ * as required for open-address hashing using double-hash probing.
+ * Also uses a 65521 prime modulus to govern the range. 
+ * -------------------------------------------------------------------------*/
+int connection_hash_function_2( const void *p_data )
+{
+   tcp_connection_t *p_conn;
+   char key[CONNECTION_HASH_KEY_LEN];
+   unsigned int hash = CONNECTION_HASH_KEY_LEN-1;
+   unsigned int i    = 0;
+
+   if ( !p_data )
+          return -1;
+
+   memset(key,0,sizeof(key));
+
+   /* p_data is a pointer to a tcp_connection_t */
+   p_conn = (tcp_connection_t *)p_data;
+
+   /* key is a hex representation of the connection */
+   snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X", 
+                  p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
+
+   for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
+   {
+      hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
+   }
+   return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
+}
+
+/* -------------------------------------------------------------------------
+ * Connection Match function returns non-zero if hash elements are identical. 
+ * -------------------------------------------------------------------------*/
+int connection_match_function( const void *p_data1, const void *p_data2 )
+{
+   tcp_connection_t *p_conn1, *p_conn2;
+   
+   if ( !p_data1 || !p_data2 )
+          return 0;
+
+   /* p_data1, p_data2 are pointers to tcp_connection_t */
+   p_conn1 = (tcp_connection_t *)p_data1;
+   p_conn2 = (tcp_connection_t *)p_data2;
+
+   return (p_conn1->local_addr == p_conn2->local_addr &&
+          p_conn1->local_port == p_conn2->local_port &&
+          p_conn1->remote_addr ==  p_conn2->remote_addr &&
+          p_conn1->remote_port == p_conn2->remote_port );
+}
+
+/* --------------------------------------------------
+ * Functions to hash the monitors within a collection
+ * --------------------------------------------------*/
+
+#define MONITOR_HASH_KEY_LEN 9
+
+/* -------------------------------------------------------------------------------
+ * First monitor hash function: DJB with a 65521 prime modulus to govern the range.
+ * -------------------------------------------------------------------------------*/
+int monitor_hash_function_1( const void *p_data )
+{
+   tcp_port_monitor_t *p_monitor;
+   char key[MONITOR_HASH_KEY_LEN];
+   unsigned int hash = 5381;
+   unsigned int i    = 0;
+
+   if ( !p_data )
+           return -1;
+   
+   memset(key,0,sizeof(key));
+
+   /* p_data is a pointer to tcp_port_monitor_t */
+   p_monitor = (tcp_port_monitor_t *)p_data;
+
+   /* key is a hex representation of the starting port concatenated to the ending port */
+   snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
+                   p_monitor->port_range_begin, p_monitor->port_range_end );
+#ifdef HASH_DEBUG
+   fprintf(stderr,"--- key=[%s]\n",key);
+#endif
+
+   for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
+   {
+      hash = ((hash << 5) + hash) + (key[i]);
+   }
+
+   return (hash & 0x7FFFFFFF) % 65521;
+}
+
+/* -----------------------------------------------------------------------
+ * Second monitor hash function: DEK, modified to return odd numbers only,
+ * as required for open-address hashing using double-hash probing.
+ * Also uses a 65521 prime modulus to govern the range.
+ * -----------------------------------------------------------------------*/
+int monitor_hash_function_2( const void *p_data )
+{
+   tcp_port_monitor_t *p_monitor;
+   char key[MONITOR_HASH_KEY_LEN];
+   unsigned int hash = MONITOR_HASH_KEY_LEN-1;
+   unsigned int i    = 0;
+
+   if ( !p_data )
+           return -1;
+
+   memset(key,0,sizeof(key));
+
+   /* p_data is a pointer to a tcp_port_monitor_t */
+   p_monitor = (tcp_port_monitor_t *)p_data;
+
+   /* key is a hex representation of the starting port concatenated to the ending port */
+   snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
+                   p_monitor->port_range_begin, p_monitor->port_range_end );
+
+   for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
+   {
+      hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
+   }
+   return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
+}
+
+/* ----------------------------------------------------------------------
+ * Monitor match function returns non-zero if hash elements are identical.
+ * ----------------------------------------------------------------------*/
+int monitor_match_function( const void *p_data1, const void *p_data2 )
+{
+   tcp_port_monitor_t *p_monitor1, *p_monitor2;
+
+   if ( !p_data1 || !p_data2 )
+           return 0;
+
+   /* p_data1, p_data2 are pointers to tcp_connection_t */
+   p_monitor1 = (tcp_port_monitor_t *)p_data1;
+   p_monitor2 = (tcp_port_monitor_t *)p_data2;
+
+   return (p_monitor1->port_range_begin == p_monitor1->port_range_begin &&
+          p_monitor2->port_range_end == p_monitor2->port_range_end);
+}
+
+/* ---------------------------------------------------------------------------
+ * Port monitor utility functions implementing tcp_port_monitor_function_ptr_t
+ * ---------------------------------------------------------------------------*/
+void destroy_tcp_port_monitor(
+        tcp_port_monitor_t *                    p_monitor,
+       void *                                  p_void 
+        )
+{
+   tcp_connection_node_t *p_node, *p_temp;
+
+   if ( !p_monitor || p_void )         /* p_void should be NULL in this context */
+      return;
+
+   /* destroy the monitor's hash */
+   hash_destroy(&p_monitor->hash);
+
+   /* destroy the monitor's peek array */
+   free( p_monitor->p_peek );
+
+   /* destroy the monitor's connection list */
+   for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; )
+   {
+          /* p_temp is for the next iteration */
+          p_temp = p_node->p_next;
+          
+          free( p_node );
+
+          p_node = p_temp;
+   }
+
+   /* destroy the monitor */
+   free( p_monitor );
+}
+
+void age_tcp_port_monitor(
+        tcp_port_monitor_t *                    p_monitor,
+       void *                                  p_void
+        )
+{
+   /* Run through the monitor's connections and decrement the age variable. 
+    * If the age goes negative, we remove the connection from the monitor. 
+    * Function takes O(n) time on the number of connections. */
+
+   tcp_connection_node_t *p_node, *p_temp;
+   tcp_connection_t *p_conn;
+   void *p_cast;
+
+   if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
+          return;
+
+   if ( !p_monitor->p_peek )
+          return;
+
+   for ( p_node = p_monitor->connection_list.p_head; p_node != NULL; )
+   {
+          if ( --p_node->connection.age >= 0 ) {
+                  p_node = p_node->p_next;
+                  continue;
+          }
+
+          /* connection on p_node is old.  remove connection from the hash. */
+          p_conn = &p_node->connection;
+          p_cast = (void *)p_conn;
+          if ( hash_remove( &p_monitor->hash, &p_cast ) != 0 ) {
+#ifdef HASH_DEBUG
+                  fprintf(stderr, "--- hash_remove error\n");
+#endif
+                  return;
+          }
+
+          /* splice p_node out of the connection_list */
+          if ( p_node->p_prev != NULL )
+                  p_node->p_prev->p_next = p_node->p_next;
+          if ( p_node->p_next != NULL )
+                  p_node->p_next->p_prev = p_node->p_prev;
+
+          /* correct the list head and tail if necessary */
+          if ( p_monitor->connection_list.p_head == p_node )
+                  p_monitor->connection_list.p_head = p_node->p_next;
+          if ( p_monitor->connection_list.p_tail == p_node )
+                  p_monitor->connection_list.p_tail = p_node->p_prev;
+
+          /* p_temp is for the next iteration */
+          p_temp = p_node->p_next;
+
+          /* destroy the node */
+          free( p_node );
+
+          p_node = p_temp;
+   }
+}
+
+void maintain_tcp_port_monitor_hash(
+       tcp_port_monitor_t *                    p_monitor,
+       void *                                  p_void
+       )
+{
+   /* Check the number of vacated slots in the hash.  If it exceeds our maximum
+    * threshold (should be about 1/4 of the hash table), then the hash table
+    * performance degrades from O(1) toward O(n) as the number of vacated slots
+    * climbs.  This is avoided by clearing the hash and reinserting the entries.
+    * The benefit of open-addressing hashing does come with this price --
+    * you must rebalance it occasionally. */
+
+    tcp_connection_node_t *p_node;
+    double vacated_load;
+
+    if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
+       return;
+
+    vacated_load = (double)p_monitor->hash.vacated / (double)p_monitor->hash.positions;
+#ifdef HASH_DEBUG
+    fprintf(stderr,"--- num vacated is %d, vacated factor is %.3f\n", p_monitor->hash.vacated, vacated_load );
+#endif
+    if ( vacated_load <= TCP_CONNECIION_HASH_MAX_VACATED_PCT )
+    {
+           /* hash is fine and needs no rebalancing */
+           return;
+    }
+
+#ifdef HASH_DEBUG
+    fprintf(stderr,"--- rebuilding hash\n");
+#endif
+
+    /* rebuild the hash  */
+    memset( p_monitor->hash.pp_table, 0, p_monitor->hash.positions * sizeof(void **));
+    p_monitor->hash.size = 0;
+    p_monitor->hash.vacated = 0;
+
+    for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next )
+    {
+           if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
+           {
+#ifdef HASH_DEBUG
+               fprintf(stderr,"--- hash_insert error\n");
+#endif
+               ;       
+           }
+    }
+}
+
+void rebuild_tcp_port_monitor_peek_table(
+       tcp_port_monitor_t *                    p_monitor,
+       void *                                  p_void
+       )
+{
+   /* Run through the monitori's connections and rebuild the peek table
+    * of connection pointers.  This is done so peeking into the monitor
+    * can be done in O(1) time instead of O(n) time for each peek. */
+
+   tcp_connection_node_t *p_node;
+   int i = 0;
+
+   if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
+       return;
+
+   /* zero out the peek array */
+   memset( p_monitor->p_peek, 0, TCP_CONNECTION_HASH_SIZE * sizeof(tcp_connection_t *) );
+
+   for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next, i++ )
+   {
+          p_monitor->p_peek[i] = &p_node->connection;
+   }
+}
+
+void show_connection_to_tcp_port_monitor(
+        tcp_port_monitor_t *                    p_monitor,
+        void *                                 p_void
+        )
+{
+   /* The monitor gets to look at each connection to see if it falls within
+    * the monitor's port range of interest.  Connections of interest are first
+    * looked up in the hash to see if they are already there.  If they are, we
+    * reset the age of the connection so it is not deleted.  If the connection 
+    * is not in the hash, we add it, but only if the hash is not saturated.  
+    * The function takes O(1) time. */
+
+   tcp_connection_node_t *p_node;
+   void *p_cast;
+
+   if ( !p_monitor || !p_void )
+       return;
+
+   tcp_connection_t *p_connection = (tcp_connection_t *)p_void;
+   
+   /* inspect the local port number of the connection to see if we're interested. */
+   if ( (p_monitor->port_range_begin <= p_connection->local_port) &&
+        (p_connection->local_port <= p_monitor->port_range_end) )
+   {
+       /* the connection is in the range of the monitor. */
+
+       /* first check the hash to see if the connection is already there. */
+       p_cast = (void *)p_connection;
+       if ( hash_lookup( &p_monitor->hash, &p_cast ) == 0 )
+       {
+               p_connection = (tcp_connection_t *)p_cast;
+               /* it's already in the hash.  reset the age of the connection. */
+               if ( p_connection != NULL )
+               {
+                       p_connection->age = TCP_CONNECIION_STARTING_AGE;
+               }
+
+               return;
+       }
+
+       /* Connection is not yet in the hash.  We will try to add it, but only if the hash is not
+        * yet saturated.  We assume the hash is saturated (and therefore ignore this connection)
+        * if our load factor cap is now exceeded.  The benefit of limiting connections in this way
+        * is that the hash will continue to function at an average (1) speed by keeping the load
+        * load factor down.  Of course the downside is that each port monitor has a strict maximum 
+        * connection limit.  Future versions should probably allow the client to set the hash size
+        * and load limits and/or provide for automatic resizing of hashes. */
+
+       if ( p_monitor->hash.size / p_monitor->hash.positions > TCP_CONNECTION_HASH_MAX_LOAD_PCT )
+       {
+               /* hash exceeds our load limit is now "full" */
+               return;
+       }
+
+       /* create a new connection node */
+       if ( (p_node = (tcp_connection_node_t *) calloc(1, sizeof(tcp_connection_node_t))) == NULL )
+               return;
+
+       p_node->connection = *p_connection;  /* bitwise copy of the struct */
+       p_node->connection.age = TCP_CONNECIION_STARTING_AGE;
+       p_node->p_next = NULL;
+
+       /* insert it into the monitor's hash table */
+       if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
+       {
+               /* error inserting into hash.  delete the connection node we just created, so no leaks. */
+#ifdef HASH_DEBUG
+               fprintf(stderr, "--- hash_insert error\n");
+#endif
+               free(p_node);
+               return;
+       }
+
+       /* append the node to the monitor's connection list */
+       if ( p_monitor->connection_list.p_tail == NULL )  /* assume p_head is NULL too */
+       {
+               p_monitor->connection_list.p_head = p_node;
+               p_monitor->connection_list.p_tail = p_node;
+               p_node->p_prev = NULL;
+       }
+       else
+       {
+               p_monitor->connection_list.p_tail->p_next = p_node;
+               p_node->p_prev = p_monitor->connection_list.p_tail;
+               p_monitor->connection_list.p_tail = p_node;
+       }
+   }
+}
+
+/* ---------------------------------------------------------------------------------------
+ * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in the collection. 
+ * ---------------------------------------------------------------------------------------*/
+void for_each_tcp_port_monitor_in_collection(
+       tcp_port_monitor_collection_t *         p_collection,
+       tcp_port_monitor_function_ptr_t         p_function,
+       void *                                  p_function_args
+       )
+{
+   tcp_port_monitor_node_t * p_current_node, * p_next_node;
+   
+   if ( !p_collection || !p_function )
+       return;
+
+   /* for each monitor in the collection */
+   for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
+   {
+        p_next_node = p_current_node->p_next;  /* do this first! */
+
+       if ( p_current_node->p_monitor )
+       {
+            /* apply the function with the given arguments */
+           (*p_function)( p_current_node->p_monitor, p_function_args );
+       }
+
+        p_current_node = p_next_node;
+   }
+  
+}
+
+
+/* ----------------------------------------------------------------------
+ * CLIENT INTERFACE 
+ *
+ * Clients should call only those functions below this line.
+ * ---------------------------------------------------------------------- */
+
+/* ----------------------------------
+ * Client operations on port monitors
+ * ---------------------------------- */
+
+/* Clients should first try to "find_tcp_port_monitor" before creating one
+   so that there are no redundant monitors. */
+tcp_port_monitor_t * create_tcp_port_monitor(
+       in_port_t                               port_range_begin, 
+       in_port_t                               port_range_end
+       )
+{
+   tcp_port_monitor_t * p_monitor;
+
+   /* create the monitor */
+   p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t) );
+   if ( !p_monitor )
+       return NULL;
+
+   /* create the monitor's connection hash */
+   if ( hash_create( &p_monitor->hash, TCP_CONNECTION_HASH_SIZE, 
+                       &connection_hash_function_1, &connection_hash_function_2,
+                       &connection_match_function, NULL ) != 0 ) 
+   {
+       /* we failed to create the hash, so destroy the monitor completely so we don't leak */
+       destroy_tcp_port_monitor(p_monitor,NULL);
+       return NULL;
+   }
+
+   /* create the monitor's peek array */
+   if ( (p_monitor->p_peek = (tcp_connection_t **) calloc( TCP_CONNECTION_HASH_SIZE, sizeof(tcp_connection_t *))) == NULL )
+   {
+       /* we failed to create the peek array, so destroy the monitor completely, again, so we don't leak */
+       destroy_tcp_port_monitor(p_monitor,NULL);
+       return NULL ;
+   }
+
+   p_monitor->port_range_begin = port_range_begin;
+   p_monitor->port_range_end = port_range_end;
+
+   p_monitor->connection_list.p_head = NULL;
+   p_monitor->connection_list.p_tail = NULL;
+
+   return p_monitor;
+}
+
+/* Clients use this function to get connection data from the indicated port monitor.
+   The requested monitor value is copied into a client-supplied char buffer.
+   Returns 0 on success, -1 otherwise. */
+int peek_tcp_port_monitor(
+        tcp_port_monitor_t *                    p_monitor,
+        int                                     item,
+        int                                     connection_index,
+        char *                                  p_buffer,
+        size_t                                  buffer_size
+        )
+{
+   struct hostent *p_hostent;
+   struct servent *p_servent;
+   struct in_addr net;
+
+   if ( !p_monitor || !p_buffer || connection_index < 0 )
+       return(-1);
+
+   memset(p_buffer, 0, buffer_size);
+   memset(&net, 0, sizeof(net));
+
+   /* if the connection index is out of range, we simply return with no error
+    * having first cleared the client-supplied buffer. */
+   if ( (item!=COUNT) && (connection_index > p_monitor->hash.size - 1) )
+          return(0);
+                  
+   switch (item) {
+
+   case COUNT:
+   
+       snprintf( p_buffer, buffer_size, "%d" , p_monitor->hash.size );
+       break;
+
+   case REMOTEIP:
+
+       net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
+       snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
+       break;
+
+   case REMOTEHOST:
+
+       p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->remote_addr, sizeof(in_addr_t), AF_INET);
+       /* if no host name found, just use ip address. */
+       if ( !p_hostent || !p_hostent->h_name )
+       {
+               net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
+               snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
+               break;
+       }
+       snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
+       break;
+
+   case REMOTEPORT:
+
+        snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->remote_port );                          
+       break;
+       
+   case LOCALIP:
+
+       net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
+       snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
+        break;
+
+   case LOCALHOST:
+
+       p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->local_addr, sizeof(in_addr_t), AF_INET);
+       /* if no host name found, just use ip address. */
+       if ( !p_hostent || !p_hostent->h_name )
+       {
+               net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
+               snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
+               break;
+       }
+       snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
+       break;
+
+   case LOCALPORT: 
+
+        snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
+        break;        
+
+   case LOCALSERVICE:
+
+       p_servent = getservbyport( htons(p_monitor->p_peek[ connection_index ]->local_port ), "tcp" );
+       /* if no service name found for the port, just use the port number. */
+       if ( !p_servent || !p_servent->s_name )
+       {
+               snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
+               break;
+       }
+       snprintf( p_buffer, buffer_size, "%s", p_servent->s_name );
+       break;
+
+   default:
+       return(-1);
+   }
+
+   return(0);
+}
+
+/* --------------------------------
+ * Client operations on collections
+ * -------------------------------- */
+
+/* Create a monitor collection.  Do this one first. */
+tcp_port_monitor_collection_t * create_tcp_port_monitor_collection( void )
+{
+   tcp_port_monitor_collection_t * p_collection;
+
+   p_collection = (tcp_port_monitor_collection_t *) calloc( 1, sizeof( tcp_port_monitor_collection_t ) );
+   if ( !p_collection )
+          return NULL;
+
+   /* create the collection's monitor hash */
+   if ( hash_create( &p_collection->hash, TCP_MONITOR_HASH_SIZE,
+                       &monitor_hash_function_1, &monitor_hash_function_2,
+                       &monitor_match_function, NULL ) != 0 )
+   {
+         /* we failed to create the hash, so destroy the monitor completely so we don't leak */
+         destroy_tcp_port_monitor_collection(p_collection);
+         return NULL;
+   }
+
+   p_collection->monitor_list.p_head = NULL;
+   p_collection->monitor_list.p_tail = NULL;
+
+   return p_collection;
+}
+
+/* Destroy the monitor collection (and the monitors inside).  Do this one last. */
+void destroy_tcp_port_monitor_collection( 
+       tcp_port_monitor_collection_t *         p_collection
+       )
+{
+   tcp_port_monitor_node_t * p_current_node, * p_next_node;
+
+   if ( !p_collection )
+          return;
+
+   /* destroy the collection's hash */
+   hash_destroy( &p_collection->hash );
+
+   /* destroy the monitors */
+   for_each_tcp_port_monitor_in_collection(
+        p_collection,
+       &destroy_tcp_port_monitor,
+       NULL
+       );
+
+   /* next destroy the empty monitor nodes */
+   for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
+   {
+       p_next_node = p_current_node->p_next;  /* do this first! */
+
+        free( p_current_node );
+       p_current_node = p_next_node;
+   }
+   
+   free( p_collection );
+}
+
+/* Updates the tcp statitics for all monitors within a collection */
+void update_tcp_port_monitor_collection(
+        tcp_port_monitor_collection_t *         p_collection
+        )
+{
+       FILE *fp;
+        char buf[256];
+        tcp_connection_t conn;
+        unsigned long state;
+
+       if ( !p_collection )
+               return;
+
+       /* age the connections in all port monitors. */
+       for_each_tcp_port_monitor_in_collection(
+               p_collection,
+               &age_tcp_port_monitor,
+               NULL
+               );
+
+       /* read tcp data from /proc/net/tcp */
+       if ( ( fp = fopen("/proc/net/tcp", "r" ) ) == NULL )
+               return;
+
+        /* ignore field name line */
+        fgets(buf, 255, fp);
+
+        /* read all tcp connections */
+        while (fgets (buf, sizeof (buf), fp) != NULL) {
+
+                if ( sscanf (buf, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
+                        (unsigned long *)&conn.local_addr, (unsigned long *)&conn.local_port,
+                        (unsigned long *)&conn.remote_addr, (unsigned long *)&conn.remote_port,
+                        (unsigned long *)&state, (unsigned long *)&conn.uid, (unsigned long *)&conn.inode) != 7 )
+
+                        fprintf( stderr, "/proc/net/tcp: bad file format\n" );
+
+                if ((conn.inode == 0) || (state != TCP_ESTABLISHED)) continue;
+
+               /* show the connection to each port monitor. */
+               for_each_tcp_port_monitor_in_collection(
+                       p_collection,
+                       &show_connection_to_tcp_port_monitor,
+                       (void *) &conn
+                       );
+        }
+
+       fclose(fp);
+
+       /* check the health of the monitor hashes and rebuild them if nedded */
+       for_each_tcp_port_monitor_in_collection(
+               p_collection,
+               &maintain_tcp_port_monitor_hash,
+               NULL
+               );
+
+       /* rebuild the connection peek tables of all monitors so clients can peek in O(1) time */
+       for_each_tcp_port_monitor_in_collection(
+               p_collection,
+               &rebuild_tcp_port_monitor_peek_table,
+               NULL
+               );
+}
+
+/* After clients create a monitor, use this to add it to the collection.
+   Returns 0 on success, -1 otherwise. */
+int insert_tcp_port_monitor_into_collection( 
+       tcp_port_monitor_collection_t *         p_collection,
+       tcp_port_monitor_t *                    p_monitor
+       )
+{
+   tcp_port_monitor_node_t * p_node;
+
+   if ( !p_collection || !p_monitor )
+       return (-1);
+
+   /* create a container node for this monitor */
+   p_node = (tcp_port_monitor_node_t *) calloc( 1, sizeof(tcp_port_monitor_node_t) );
+   if ( !p_node )
+       return (-1);
+
+   /* populate the node */
+   p_node->p_monitor = p_monitor;
+   p_node->p_next = NULL;
+          
+   /* add a pointer to this monitor to the collection's hash */
+   if ( hash_insert( &p_collection->hash, (void *)p_monitor ) != 0 )
+   {
+       /* error inserting into hash.  destroy the monitor's container node so no leaks */
+       free( p_node );
+       return (-1);
+   }
+
+   /* tail of the container gets this node */
+   if ( !p_collection->monitor_list.p_tail )
+       p_collection->monitor_list.p_tail = p_node;
+   else
+   {
+       /* p_next of the tail better be NULL */
+        if ( p_collection->monitor_list.p_tail->p_next != NULL )
+          return (-1);
+
+        /* splice node onto tail */
+       p_collection->monitor_list.p_tail->p_next = p_node;
+       p_collection->monitor_list.p_tail = p_node;
+   }
+
+   /* if this was the first element added */
+   if ( !p_collection->monitor_list.p_head )
+       p_collection->monitor_list.p_head = p_collection->monitor_list.p_tail;
+
+   return 0;
+}
+
+/* Clients need a way to find monitors */
+tcp_port_monitor_t * find_tcp_port_monitor( 
+       tcp_port_monitor_collection_t *         p_collection, 
+       in_port_t                               port_range_begin,
+       in_port_t                               port_range_end
+       )
+{
+   tcp_port_monitor_t monitor,*p_monitor;
+   void *p_cast;
+
+   if ( !p_collection )
+        return NULL;
+
+   /* need a monitor object to use for searching the hash */
+   monitor.port_range_begin = port_range_begin;
+   monitor.port_range_end = port_range_end;
+   p_monitor = &monitor;
+   p_cast = (void *)p_monitor;
+
+   /* simple hash table lookup */
+   if ( hash_lookup( &p_collection->hash, &p_cast ) == 0 )
+   {
+          /* found the monitor and p_cast now points to it */
+          p_monitor = (tcp_port_monitor_t *)p_cast;
+          return( p_monitor );
+   }
+
+   return NULL;  /* monitor not found */
+}
diff --git a/src/libtcp-portmon.h b/src/libtcp-portmon.h
new file mode 100644 (file)
index 0000000..6117e69
--- /dev/null
@@ -0,0 +1,278 @@
+/* -------------------------------------------------------------------------
+ * libtcp-portmon.h:  tcp port monitoring library.               
+ *
+ * Copyright (C) 2005  Philip Kovacs kovacsp3@comcast.net
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * --------------------------------------------------------------------------- */
+
+#ifndef LIBTCP_PORTMON_H
+#define LIBTCP_PORTMON_H
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include "hash.h"
+
+/* ------------------------------------------------------------------------------------------------
+ * Each port monitor contains a connection hash whose contents changes dynamically as the monitor 
+ * is presented with connections on each update cycle.   This implementation maintains the health
+ * of this hash by enforcing several rules.  First, the hash cannot contain more items than the
+ * TCP_CONNECTION_HASH_MAX_LOAD_PCT permits.  For example, a 256 element hash with a max load of 
+ * 0.5 cannot contain more than 128 connections.  Additional connections are ignored by the monitor.
+ * The load factor of 0.5 is low enough to keep the hash running at near O(1) performanace at all 
+ * times.  As elements are removed from the hash, the hash slots are tagged vacated, as required 
+ * by open address hashing.  The vacated tags are essential as they enable the hash to find elements
+ * for which there were collisions during insert (requiring additional probing for an open slot).
+ * The problem with vacated slots (even though they are reused) is that, as they increase in number,
+ * esp. past about 1/4 of all slots, the average number of probes the hash has to perform increases
+ * from O(1) on average to O(n) worst case. To keep the hash healthy, we simply rebuild it when the
+ * percentage of vacated slots gets too high (above TCP_CONNECTION_HASH_MAX_VACATED_PCT).  Rebuilding
+ * the hash takes O(n) on the number of elements, but it well worth it as it keeps the hash running
+ * at an average access time of O(1).
+ * ------------------------------------------------------------------------------------------------*/
+
+#define TCP_CONNECTION_HASH_SIZE 256                   /* connection hash size -- must be a power of two */
+#define TCP_CONNECTION_HASH_MAX_LOAD_PCT 0.5           /* disallow inserts after this % load is exceeded */
+#define TCP_CONNECIION_HASH_MAX_VACATED_PCT 0.25       /* rebalance hash after this % of vacated slots is exceeded */ 
+#define TCP_CONNECIION_STARTING_AGE 1                  /* connection deleted if unseen again after this # of refreshes */
+
+/* ----------------------------------------------------------------------------------------
+ * The tcp port monitor collection also contains a hash to track the monitors it contains.
+ * This hash, unlike the connection hash describes above, is not very dynamic.  Client of
+ * this library typically create a fixed number of monitors and let them run until program 
+ * termination.  For this reason, I haven't included any load governors or hash rebuilding
+ * steps as is done above.  You may store up to TCP_MONITOR_HASH_SIZE monitors in this hash,
+ * but you _should_ remember that keeping the load low (e.g. max of 0.5) keeps the monitor
+ * lookups at O(1).  
+ * ----------------------------------------------------------------------------------------*/
+
+/* TODO: Make TCP_CONNECTION_HASH_SIZE and TCP_MONITOR_HASH_SIZE variables the client can supply */
+
+#define TCP_MONITOR_HASH_SIZE 64                       /* monitor hash size -- must be a power of two */
+
+/* -------------------------------------------------------------------
+ * IMPLEMENTATION INTERFACE
+ *
+ * Implementation-specific interface begins here.  Clients should not 
+ * manipulate these structures directly, nor call the defined helper 
+ * functions.  Use the "Client interface" functions defined at bottom.
+ * ------------------------------------------------------------------- */
+
+/* The inventory of peekable items within the port monitor. */
+enum tcp_port_monitor_peekables { COUNT=0, REMOTEIP, REMOTEHOST, REMOTEPORT, LOCALIP, LOCALHOST, LOCALPORT, LOCALSERVICE };
+
+/* -----------------------
+ * A single tcp connection 
+ * ----------------------- */
+typedef struct _tcp_connection_t {
+        in_addr_t local_addr;
+        in_port_t local_port;
+        in_addr_t remote_addr;
+        in_port_t remote_port;
+        unsigned int uid;
+        unsigned int inode;
+       int age;
+} tcp_connection_t;
+
+/* ------------------------------------------------------------------------
+ * A tcp connection node/list
+ *
+ * Connections within each monitor are stored in a double-linked list.
+ * The age variable provides the mechanism for removing connections if they
+ * are not seen again in subsequent update cycles.
+ * ------------------------------------------------------------------------ */
+typedef struct _tcp_connection_node_t {
+       tcp_connection_t connection;
+       struct _tcp_connection_node_t * p_prev;
+       struct _tcp_connection_node_t * p_next;
+} tcp_connection_node_t;
+
+typedef struct _tcp_connection_list_t {
+       tcp_connection_node_t * p_head;
+       tcp_connection_node_t * p_tail;
+} tcp_connection_list_t;
+
+/* --------------
+ * A port monitor 
+ * -------------- */
+typedef struct _tcp_port_monitor_t {
+        in_port_t port_range_begin;
+        in_port_t port_range_end;              /* begin = end to monitor a single port */
+       tcp_connection_list_t connection_list;  /* list of connections for this monitor */
+       hash_table_t hash;                      /* hash table contains pointers into monitor's connection list */
+       tcp_connection_t **p_peek;              /* array of connection pointers for O(1) peeking by index */ 
+} tcp_port_monitor_t;
+
+/* -----------------------------------------------------------------------------
+ * Open-addressed hash implementation requires that we supply two hash functions
+ * and a match function to compare two hash elements for identity.
+ * ----------------------------------------------------------------------------- */
+
+/* --------------------------------------------------
+ * Functions to hash the connections within a monitor
+ * --------------------------------------------------*/
+
+/* First connection hash function */
+int connection_hash_function_1( const void * /* p_data */ );
+
+/* Second connection hash function */
+int connection_hash_function_2( const void * /* p_data */ );
+
+/* Connection match function returns non-zero if hash elements are identical. */
+int connection_match_function( const void * /* p_data1 */, const void * /* p_data2 */ );
+
+/* --------------------------------------------------
+ * Functions to hash the monitors within a collection
+ * --------------------------------------------------*/
+
+/* First monitor hash function */
+int monitor_hash_function_1( const void * /* p_data */ );
+
+/* Second monitor hash function */
+int monitor_hash_function_2( const void * /* p_data */ );
+
+/* Monitor match function returns non-zero if hash elements are identical. */
+int monitor_match_function( const void * /* p_data1 */, const void * /* p_data2 */ );
+
+/* ------------------------
+ * A port monitor node/list 
+ * ------------------------ */
+typedef struct _tcp_port_monitor_node_t {
+        tcp_port_monitor_t * p_monitor;
+        struct _tcp_port_monitor_node_t *p_next;
+} tcp_port_monitor_node_t;
+
+typedef struct __tcp_port_monitor_list_t {
+       tcp_port_monitor_node_t * p_head;
+       tcp_port_monitor_node_t * p_tail;
+} tcp_port_monitor_list_t;
+
+/* ---------------------------------------
+ * A port monitor utility function typedef
+ * ---------------------------------------*/ 
+typedef void (*tcp_port_monitor_function_ptr_t)( tcp_port_monitor_t * /* p_monitor */, void * /* p_void */ );
+
+/* ---------------------------------------------------------------------------
+ * Port monitor utility functions implementing tcp_port_monitor_function_ptr_t
+ * ---------------------------------------------------------------------------*/
+void destroy_tcp_port_monitor(
+        tcp_port_monitor_t *                    /* p_monitor */,
+       void *                                  /* p_void (use NULL for this function) */
+       );
+
+void age_tcp_port_monitor(
+       tcp_port_monitor_t *                    /* p_monitor */,
+       void *                                  /* p_void (use NULL for this function) */
+       );
+
+void maintain_tcp_port_monitor_hash(
+       tcp_port_monitor_t *                    /* p_monitor */,
+       void *                                  /* p_void (use NULL for this function) */
+       );
+
+void rebuild_tcp_port_monitor_peek_table(
+       tcp_port_monitor_t *                    /* p_monitor */,
+       void *                                  /* p_void (use NULL for this function) */
+       );
+
+void show_connection_to_tcp_port_monitor(
+       tcp_port_monitor_t *                    /* p_monitor */,
+       void *                                  /* p_connection (client should cast) */
+       );
+
+/* -----------------------------
+ * A tcp port monitor collection
+ * -----------------------------*/
+typedef struct _tcp_port_monitor_collection_t {
+       tcp_port_monitor_list_t monitor_list;   /* list of monitors for this collection */
+       hash_table_t hash;                      /* hash table contains pointers into collection's monitor list */
+} tcp_port_monitor_collection_t;
+
+/* ---------------------------------------------------------------------------------------
+ * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in the collection. 
+ * ---------------------------------------------------------------------------------------*/
+void for_each_tcp_port_monitor_in_collection(
+       tcp_port_monitor_collection_t *         /* p_collection */,
+       tcp_port_monitor_function_ptr_t         /* p_function */,
+       void *                                  /* p_function_args (for user arguments) */
+       );
+
+
+/* ----------------------------------------------------------------------
+ * CLIENT INTERFACE 
+ *
+ * Clients should call only those functions below this line.
+ * ---------------------------------------------------------------------- */
+
+/* ----------------------------------
+ * Client operations on port monitors
+ * ---------------------------------- */
+
+/* Clients should first try to "find_tcp_port_monitor" before creating one
+   so that there are no redundant monitors. */
+tcp_port_monitor_t * create_tcp_port_monitor(
+       in_port_t                               /* port_range_begin */, 
+       in_port_t                               /* port_range_end */ 
+       );
+
+/* Clients use this function to get connection data from the indicated port monitor.
+   The requested monitor value is copied into a client-supplied char buffer. 
+   Returns 0 on success, -1 otherwise. */
+int peek_tcp_port_monitor(
+       tcp_port_monitor_t *                    /* p_monitor */,
+       int                                     /* item, ( item of interest, from tcp_port_monitor_peekables enum ) */,
+       int                                     /* connection_index, ( 0 to number of connections in monitor - 1 )*/,
+       char *                                  /* p_buffer, buffer to receive requested value */,
+       size_t                                  /* buffer_size, size of p_buffer */
+       );
+
+/* --------------------------------
+ * Client operations on collections
+ * -------------------------------- */
+
+/* Create a monitor collection.  Do this one first. */
+tcp_port_monitor_collection_t * create_tcp_port_monitor_collection( void );
+
+/* Destroy the monitor collection (and everything it contains).  Do this one last. */
+void destroy_tcp_port_monitor_collection( 
+       tcp_port_monitor_collection_t *         /* p_collection */ 
+       );
+
+/* Updates the tcp statitics for all monitors within a collection */
+void update_tcp_port_monitor_collection(
+       tcp_port_monitor_collection_t *         /* p_collection */
+       );
+
+/* After clients create a monitor, use this to add it to the collection. 
+   Returns 0 on success, -1 otherwise. */
+int insert_tcp_port_monitor_into_collection( 
+       tcp_port_monitor_collection_t *         /* p_collection */, 
+       tcp_port_monitor_t *                    /* p_monitor */ 
+       );
+
+/* Clients need a way to find monitors */
+tcp_port_monitor_t * find_tcp_port_monitor( 
+       tcp_port_monitor_collection_t *         /* p_collection */, 
+       in_port_t                               /* port_range_begin */, 
+       in_port_t                               /* port_range_end */ 
+       );
+
+#endif