-/* -------------------------------------------------------------------------
- * libtcp-portmon.c: tcp port monitoring library.
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=c
*
- * Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net
- *
- * $Id$
+ * libtcp-portmon.c: tcp port monitoring library.
+ *
+ * Copyright (C) 2005-2007 Philip Kovacs pkovacs@users.sourceforge.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
*
* 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
- * --------------------------------------------------------------------------- */
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "libtcp-portmon.h"
+#include <glib/gprintf.h>
/* -------------------------------------------------------------------
* IMPLEMENTATION INTERFACE
* functions. Use the "Client interface" functions defined at bottom.
* ------------------------------------------------------------------- */
-/* ----------------------------------
+/* -----------------------------------
* Copy a tcp_connection_t
*
* Returns 0 on success, -1 otherwise.
- * ----------------------------------*/
-int copy_tcp_connection(
- tcp_connection_t * p_dest_connection,
- const tcp_connection_t * p_source_connection
- )
+ * ----------------------------------- */
+int copy_tcp_connection(tcp_connection_t *p_dest_connection,
+ const tcp_connection_t *p_source_connection)
{
- if ( !p_dest_connection || !p_source_connection )
- return (-1);
+ if (!p_dest_connection || !p_source_connection) {
+ return -1;
+ }
- p_dest_connection->local_addr = p_source_connection->local_addr;
- p_dest_connection->local_port = p_source_connection->local_port;
- p_dest_connection->remote_addr = p_source_connection->remote_addr;
- p_dest_connection->remote_port = p_source_connection->remote_port;
- p_dest_connection->age = p_source_connection->age;
+ p_dest_connection->local_addr = p_source_connection->local_addr;
+ p_dest_connection->local_port = p_source_connection->local_port;
+ p_dest_connection->remote_addr = p_source_connection->remote_addr;
+ p_dest_connection->remote_port = p_source_connection->remote_port;
+ p_dest_connection->age = p_source_connection->age;
- return 0;
+ return 0;
}
-/* -----------------------------------------------------------------------------
- * 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 )
+/* -------------------------------------------
+ * 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_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
+ tcp_connection_node_t *p_node, *p_temp;
- for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
- {
- hash = ((hash << 5) + hash) + (key[i]);
- }
-
- return (hash & 0x7FFFFFFF) % 65521;
-}
+ if (!p_monitor || p_void) { /* p_void should be NULL in this context */
+ return;
+ }
-/* -------------------------------------------------------------------------
- * 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;
+ /* destroy the monitor's peek array */
+ free(p_monitor->p_peek);
- if ( !p_data )
- return -1;
+ /* 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;
- memset(key,0,sizeof(key));
+ free(p_node);
- /* p_data is a pointer to a tcp_connection_t */
- p_conn = (tcp_connection_t *)p_data;
+ p_node = p_temp;
+ }
- /* 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);
+ /* destroy the monitor's hash */
+ g_hash_table_destroy(p_monitor->hash);
+ p_monitor->hash = NULL;
- for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
- {
- hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
- }
- return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
+ /* destroy the monitor */
+ free(p_monitor);
+ p_monitor = NULL;
}
-/* -------------------------------------------------------------------------
- * Connection Match function returns non-zero if hash elements are identical.
- * -------------------------------------------------------------------------*/
-int connection_match_function( const void *p_data1, const void *p_data2 )
+void age_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void)
{
- 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
- * --------------------------------------------------*/
+ /* 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. */
-#define MONITOR_HASH_KEY_LEN 9
+ tcp_connection_node_t *p_node, *p_temp;
+ tcp_connection_t *p_conn;
-/* -------------------------------------------------------------------------------
- * 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
+ if (!p_monitor || p_void) { /* p_void should be NULL in this context */
+ return;
+ }
- for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
- {
- hash = ((hash << 5) + hash) + (key[i]);
- }
+ if (!p_monitor->p_peek) {
+ return;
+ }
- return (hash & 0x7FFFFFFF) % 65521;
-}
+ for (p_node = p_monitor->connection_list.p_head; p_node; ) {
+ if (--p_node->connection.age >= 0) {
+ p_node = p_node->p_next;
+ continue;
+ }
-/* -----------------------------------------------------------------------
- * 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;
+ /* connection on p_node is old. remove connection from the hash. */
+ p_conn = &p_node->connection;
+#ifdef HASH_DEBUG
+ fprintf(stderr, "monitor hash removal of connection [%s]", p_conn->key);
+ if (!g_hash_table_remove(p_monitor->hash,
+ (gconstpointer) p_conn->key)) {
+ fprintf(stderr, " - ERROR NOT FOUND\n");
+ return;
+ }
+ fprintf(stderr, " - OK\n");
+#else
+ if (!g_hash_table_remove(p_monitor->hash,
+ (gconstpointer) p_conn)) {
+ return;
+ }
+#endif
- if ( !p_data )
- return -1;
+ /* 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;
+ }
- memset(key,0,sizeof(key));
+ /* 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_data is a pointer to a tcp_port_monitor_t */
- p_monitor = (tcp_port_monitor_t *)p_data;
+ /* p_temp is for the next iteration */
+ p_temp = p_node->p_next;
- /* 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 );
+ /* destroy the node */
+ free(p_node);
- for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
- {
- hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
- }
- return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
+ p_node = p_temp;
+ }
}
-/* ----------------------------------------------------------------------
- * Monitor match function returns non-zero if hash elements are identical.
- * ----------------------------------------------------------------------*/
-int monitor_match_function( const void *p_data1, const void *p_data2 )
+void rebuild_tcp_port_monitor_peek_table(tcp_port_monitor_t *p_monitor,
+ void *p_void)
{
- tcp_port_monitor_t *p_monitor1, *p_monitor2;
+ /* Run through the monitor'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_data1 || !p_data2 )
- return 0;
+ if (!p_monitor || p_void) { /* p_void should be NULL in this context */
+ return;
+ }
- /* 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;
+ /* zero out the peek array */
+ memset(p_monitor->p_peek, 0, p_monitor->max_port_monitor_connections *
+ sizeof(tcp_connection_t *));
- return (p_monitor1->port_range_begin == p_monitor2->port_range_begin &&
- p_monitor1->port_range_end == p_monitor2->port_range_end);
+ 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;
+ }
}
-/* ---------------------------------------------------------------------------
- * 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
- )
+void show_connection_to_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);
+ /* 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 we haven't exceeded the
+ * maximum connection limit for the monitor.
+ * The function takes O(1) time. */
+
+ tcp_connection_node_t *p_node;
+ tcp_connection_t *p_connection, *p_conn_hash;
+
+ if (!p_monitor || !p_void) {
+ return;
+ }
- /* destroy the monitor's peek array */
- free( p_monitor->p_peek );
+ /* This p_connection is on caller's stack and not the heap.
+ * If we are interested, we will create a copy of the connection
+ * (on the heap) and add it to our list. */
+ p_connection = (tcp_connection_t *) p_void;
- /* 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 );
+ /* 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. */
- p_node = p_temp;
- }
+ /* first check the hash to see if the connection is already there. */
+ if ((p_conn_hash = g_hash_table_lookup(p_monitor->hash,
+ (gconstpointer) p_connection))) {
+ /* it's already in the hash. reset the age of the connection. */
+ p_conn_hash->age = TCP_CONNECTION_STARTING_AGE;
- /* destroy the monitor */
- free( p_monitor );
- p_monitor=NULL;
-}
-
-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;
- }
+ 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;
+ /* Connection is not yet in the hash.
+ * Add it if max_connections not exceeded. */
+ if (g_hash_table_size(p_monitor->hash)
+ >= p_monitor->max_port_monitor_connections) {
+ return;
+ }
- /* 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;
+ /* create a new connection node */
+ if ((p_node = (tcp_connection_node_t *)
+ calloc(1, sizeof(tcp_connection_node_t))) == NULL) {
+ return;
+ }
- /* p_temp is for the next iteration */
- p_temp = p_node->p_next;
+ /* copy the connection data */
+ if (copy_tcp_connection(&p_node->connection, p_connection) != 0) {
+ /* error copying the connection data. deallocate p_node to
+ * avoid leaks and return. */
+ free(p_node);
+ return;
+ }
- /* destroy the node */
- free( p_node );
+ p_node->connection.age = TCP_CONNECTION_STARTING_AGE;
+ p_node->p_next = NULL;
- p_node = p_temp;
- }
+ /* insert it into the monitor's hash table */
+#ifdef HASH_DEBUG
+ fprintf(stderr, "monitor hash insert of connection [%s]\n",
+ p_node->connection.key);
+#endif
+ g_hash_table_insert(p_monitor->hash,
+ (gpointer) &p_node->connection, (gpointer) &p_node->connection);
+
+ /* 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;
+ }
+ }
}
-void maintain_tcp_port_monitor_hash(
- tcp_port_monitor_t * p_monitor,
- void * p_void
- )
+/* ------------------------------------------------------------------------
+ * 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)
{
- /* 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_port_monitor_node_t *p_current_node, *p_next_node;
- tcp_connection_node_t *p_node;
- double vacated_load;
+ if (!p_collection || !p_function) {
+ return;
+ }
- if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
- return;
+ /* for each monitor in the collection */
+ for (p_current_node = p_collection->monitor_list.p_head;
+ p_current_node != NULL; p_current_node = p_next_node) {
+ p_next_node = p_current_node->p_next; /* do this first! */
- 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_CONNECTION_HASH_MAX_VACATED_RATIO )
- {
- /* hash is fine and needs no rebalancing */
- return;
- }
-
-#ifdef HASH_DEBUG
- fprintf(stderr,"--- rebuilding hash\n");
-#endif
+ if (p_current_node->p_monitor) {
+ /* apply the function with the given arguments */
+ p_function(p_current_node->p_monitor, p_function_args);
+ }
+ }
+}
- /* 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;
+static const unsigned char prefix_4on6[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff
+};
- 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
- ;
- }
- }
-}
+union sockaddr_in46 {
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ struct sockaddr sa;
+};
-void rebuild_tcp_port_monitor_peek_table(
- tcp_port_monitor_t * p_monitor,
- void * p_void
- )
+/* checks whether the address is a IPv4-mapped IPv6 address */
+static int is_4on6(const struct in6_addr *addr)
{
- /* Run through the monitor'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;
+ return ! memcmp(&addr->s6_addr, prefix_4on6, sizeof(prefix_4on6));
+}
- 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, p_monitor->hash.positions * sizeof(tcp_connection_t *) );
+/* converts the address to appropriate textual representation (IPv6, IPv4 or fqdn) */
+static void print_host(char *p_buffer, size_t buffer_size, const struct in6_addr *addr, int fqdn)
+{
+ union sockaddr_in46 sa;
+ socklen_t slen;
+
+ memset(&sa, 0, sizeof(sa));
+
+ if(is_4on6(addr)) {
+ sa.sa4.sin_family = AF_INET;
+ memcpy(&sa.sa4.sin_addr.s_addr, &addr->s6_addr[12], 4);
+ slen = sizeof(sa.sa4);
+ } else {
+ sa.sa6.sin6_family = AF_INET6;
+ memcpy(&sa.sa6.sin6_addr, addr, sizeof(struct in6_addr));
+ slen = sizeof(sa.sa6);
+ }
- 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;
- }
+ getnameinfo(&sa.sa, slen, p_buffer, buffer_size, NULL, 0, fqdn?0:NI_NUMERICHOST);
}
-void show_connection_to_tcp_port_monitor(
- tcp_port_monitor_t * p_monitor,
- void * p_void
- )
+/* converts the textual representation of an IPv4 or IPv6 address to struct in6_addr */
+static void string_to_addr(struct in6_addr *addr, const char *p_buffer)
{
- /* 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;
-
- /* This p_connection is on caller's stack and not the heap. If we are interested,
- * we will create a copy of the connection (on the heap) and add it to our list. */
- 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_CONNECTION_STARTING_AGE;
- }
+ size_t i;
- return;
+ if(strlen(p_buffer) < 32) { //IPv4 address
+ i = sizeof(prefix_4on6);
+ memcpy(addr->s6_addr, prefix_4on6, i);
+ } else {
+ i = 0;
}
- /* 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. */
-
- if ( (double)p_monitor->hash.size / (double)p_monitor->hash.positions >= TCP_CONNECTION_HASH_MAX_LOAD_RATIO )
- {
- /* hash exceeds our load limit is now "full" */
- return;
+ for( ; i < sizeof(addr->s6_addr); i+=4, p_buffer+=8) {
+ sscanf(p_buffer, "%8x", (unsigned *)&addr->s6_addr[i]);
}
+}
- /* create a new connection node */
- if ( (p_node = (tcp_connection_node_t *) calloc(1, sizeof(tcp_connection_node_t))) == NULL )
- return;
+/* hash function for tcp_connections */
+static guint tcp_connection_hash(gconstpointer A)
+{
+ const tcp_connection_t *a = (const tcp_connection_t *) A;
+ guint hash = 0;
+ size_t i;
+
+ hash = hash*47 + a->local_port;
+ hash = hash*47 + a->remote_port;
+ for(i = 0; i < sizeof(a->local_addr.s6_addr); ++i)
+ hash = hash*47 + a->local_addr.s6_addr[i];
+ for(i = 0; i < sizeof(a->remote_addr.s6_addr); ++i)
+ hash = hash*47 + a->remote_addr.s6_addr[i];
+
+ return hash;
+}
- /* copy the connection data */
- if ( copy_tcp_connection( &p_node->connection, p_connection ) != 0 )
- {
- /* error copying the connection data. deallocate p_node to avoid leaks and return. */
- free( p_node );
- return;
- }
+/* comparison function for tcp_connections */
+static gboolean tcp_connection_equal(gconstpointer A, gconstpointer B)
+{
+ const tcp_connection_t *a = (const tcp_connection_t *) A;
+ const tcp_connection_t *b = (const tcp_connection_t *) B;
- p_node->connection.age = TCP_CONNECTION_STARTING_AGE;
- p_node->p_next = NULL;
+ return a->local_port == b->local_port && a->remote_port == b->remote_port &&
+ ! memcmp(&a->local_addr, &b->local_addr, sizeof(a->local_addr)) &&
+ ! memcmp(&a->remote_addr.s6_addr, &b->remote_addr, sizeof(a->remote_addr));
+}
- /* 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);
+/* adds connections from file to the collection */
+static void process_file(tcp_port_monitor_collection_t *p_collection, const char *file)
+{
+ FILE *fp;
+ char buf[256];
+ char local_addr[40];
+ char remote_addr[40];
+ tcp_connection_t conn;
+ unsigned long inode, uid, state;
+
+ if ((fp = fopen(file, "r")) == NULL) {
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;
- }
- }
-}
+ /* ignore field name line */
+ fgets(buf, 255, fp);
-/* ---------------------------------------------------------------------------------------
- * 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;
+ /* read all tcp connections */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
- /* 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 (sscanf(buf,
+ "%*d: %39[0-9a-fA-F]:%hx %39[0-9a-fA-F]:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
+ local_addr, &conn.local_port,
+ remote_addr, &conn.remote_port,
+ (unsigned long *) &state, (unsigned long *) &uid,
+ (unsigned long *) &inode) != 7) {
+ fprintf(stderr, "/proc/net/tcp: bad file format\n");
+ }
+ /** TCP_ESTABLISHED equals 1, but is not (always??) included **/
+ //if ((inode == 0) || (state != TCP_ESTABLISHED)) {
+ if((inode == 0) || (state != 1)) {
+ continue;
+ }
+
+ string_to_addr(&conn.local_addr, local_addr);
+ string_to_addr(&conn.remote_addr, remote_addr);
- if ( p_current_node->p_monitor )
- {
- /* apply the function with the given arguments */
- (*p_function)( p_current_node->p_monitor, p_function_args );
+ /* show the connection to each port monitor. */
+ for_each_tcp_port_monitor_in_collection(p_collection,
+ &show_connection_to_tcp_port_monitor, (void *) &conn);
}
- p_current_node = p_next_node;
- }
-
+ fclose(fp);
}
-/* ----------------------------------------------------------------------------------------
- * Calculate an efficient hash size based on the desired number of elements and load factor.
- * ---------------------------------------------------------------------------------------- */
-int calc_efficient_hash_size(
- int min_elements,
- int max_hash_size,
- double max_load_factor
- )
-{
- double min_size, hash_size, log_base_2;
-
- /* the size of the hash will the smallest power of two such that the minimum number
- of desired elements does not exceed the maximum load factor. */
-
- min_size = (double)min_elements / max_load_factor; /* starting point */
-
- /* now adjust size up to nearest power of two */
- log_base_2 = (double) (int) ( log(min_size) / log(2) ) ; /* lop off fractional portion of log */
-
- hash_size = pow(2,log_base_2) >= min_size ? min_size : pow(2,(double)++log_base_2);
-
- /* respect the maximum */
- hash_size = hash_size <= max_hash_size ? hash_size : max_hash_size;
-
- /*
- fprintf(stderr,"hash size is %d, based on %d min_elements and %.02f max load, %d maximum\n",
- (int)hash_size, min_elements, max_load_factor, max_hash_size);
- */
-
- return hash_size;
-}
-
/* ----------------------------------------------------------------------
- * CLIENT INTERFACE
+ * CLIENT INTERFACE
*
* Clients should call only those functions below this line.
* ---------------------------------------------------------------------- */
* ---------------------------------- */
/* 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_args_t * p_creation_args
- )
+ * 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_args_t *p_creation_args)
{
- 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,
- p_creation_args && p_creation_args->min_port_monitor_connections > 0 ?
- calc_efficient_hash_size( p_creation_args->min_port_monitor_connections,
- TCP_CONNECTION_HASH_SIZE_MAX,
- TCP_CONNECTION_HASH_MAX_LOAD_RATIO ) :
- TCP_CONNECTION_HASH_SIZE_DEFAULT,
- &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( p_monitor->hash.positions, 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;
-}
+ tcp_port_monitor_t *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(
- const 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;
+ /* create the monitor */
+ p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t));
+ if (!p_monitor) {
+ return NULL;
+ }
- if ( !p_monitor || !p_buffer || connection_index < 0 )
- return(-1);
+ p_monitor->max_port_monitor_connections =
+ p_creation_args->max_port_monitor_connections;
- memset(p_buffer, 0, buffer_size);
- memset(&net, 0, sizeof(net));
+ /* build the monitor key for the collection hash */
+ g_sprintf(p_monitor->key, ":%04X :%04X", port_range_begin, port_range_end);
- /* 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) {
+ /* create the monitor's connection hash */
+ if ((p_monitor->hash = g_hash_table_new(tcp_connection_hash, tcp_connection_equal)) == NULL) {
+ /* 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;
+ }
- case COUNT:
-
- snprintf( p_buffer, buffer_size, "%d" , p_monitor->hash.size );
- break;
+ /* create the monitor's peek array */
+ if ((p_monitor->p_peek = (tcp_connection_t **)
+ calloc(p_monitor->max_port_monitor_connections,
+ 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;
+ }
- case REMOTEIP:
+ p_monitor->port_range_begin = port_range_begin;
+ p_monitor->port_range_end = port_range_end;
- net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
- snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
- break;
+ p_monitor->connection_list.p_head = NULL;
+ p_monitor->connection_list.p_tail = NULL;
- case REMOTEHOST:
+ 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(const tcp_port_monitor_t *p_monitor, int item,
+ int connection_index, char *p_buffer, size_t buffer_size)
+{
+ struct sockaddr_in sa;
- 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;
+ if (!p_monitor || !p_buffer || connection_index < 0) {
+ return -1;
}
- snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
- break;
- case REMOTEPORT:
+ memset(p_buffer, 0, buffer_size);
+ memset(&sa, 0, sizeof(sa));
- snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->remote_port );
- break;
+ sa.sin_family = AF_INET;
- case LOCALIP:
+ /* 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
+ > (int) g_hash_table_size(p_monitor->hash) - 1)) {
+ return 0;
+ }
- net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
- snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
- break;
+ switch (item) {
- case LOCALHOST:
+ case COUNT:
- 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;
+ snprintf(p_buffer, buffer_size, "%d",
+ g_hash_table_size(p_monitor->hash));
+ break;
- case LOCALPORT:
+ case REMOTEIP:
- snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
- break;
+ print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 0);
+ break;
- case LOCALSERVICE:
+ case REMOTEHOST:
- 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;
+ print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 1);
+ break;
+
+ case REMOTEPORT:
+
+ snprintf(p_buffer, buffer_size, "%d",
+ p_monitor->p_peek[connection_index]->remote_port);
+ break;
+
+ case REMOTESERVICE:
+
+ sa.sin_port=htons(p_monitor->p_peek[connection_index]->remote_port);
+ getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST);
+ break;
+
+ case LOCALIP:
+
+ print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 0);
+ break;
+
+ case LOCALHOST:
+
+ print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 1);
+ break;
+
+ case LOCALPORT:
- default:
- return(-1);
- }
+ snprintf(p_buffer, buffer_size, "%d",
+ p_monitor->p_peek[connection_index]->local_port);
+ break;
- return(0);
+ case LOCALSERVICE:
+
+ sa.sin_port=htons(p_monitor->p_peek[connection_index]->local_port);
+ getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST);
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
}
/* --------------------------------
* -------------------------------- */
/* Create a monitor collection. Do this one first. */
-tcp_port_monitor_collection_t * create_tcp_port_monitor_collection(
- tcp_port_monitor_collection_args_t * p_creation_args
- )
+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,
- p_creation_args && p_creation_args->min_port_monitors > 0 ?
- calc_efficient_hash_size( p_creation_args->min_port_monitors,
- TCP_MONITOR_HASH_SIZE_MAX,
- TCP_MONITOR_HASH_MAX_LOAD_RATIO ) :
- TCP_MONITOR_HASH_SIZE_DEFAULT,
- &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;
+ 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 ((p_collection->hash = g_hash_table_new(g_str_hash, g_str_equal))
+ == NULL) {
+ /* 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
- )
+/* 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 );
- p_collection=NULL;
+ tcp_port_monitor_node_t *p_current_node, *p_next_node;
+
+ if (!p_collection) {
+ return;
+ }
+
+ /* 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_current_node = p_next_node) {
+ p_next_node = p_current_node->p_next; /* do this first! */
+
+ free(p_current_node);
+ }
+
+ /* destroy the collection's hash */
+ g_hash_table_destroy(p_collection->hash);
+ p_collection->hash = NULL;
+
+ free(p_collection);
+ p_collection = NULL;
}
/* Updates the tcp statistics for all monitors within a collection */
void update_tcp_port_monitor_collection(
- tcp_port_monitor_collection_t * p_collection
- )
+ tcp_port_monitor_collection_t *p_collection)
{
- FILE *fp;
- char buf[256];
- tcp_connection_t conn;
- unsigned long inode,uid,state;
-
- if ( !p_collection )
+ if (!p_collection) {
return;
+ }
+
+ process_file(p_collection, "/proc/net/tcp");
+ process_file(p_collection, "/proc/net/tcp6");
/* 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;
+ for_each_tcp_port_monitor_in_collection(p_collection,
+ &age_tcp_port_monitor, NULL);
- /* ignore field name line */
- fgets(buf, 255, fp);
+ /* 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);
+}
- /* read all tcp connections */
- while (fgets (buf, sizeof (buf), fp) != 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 ( sscanf (buf, "%*d: %lx:%hx %lx:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
- (unsigned long *)&conn.local_addr, &conn.local_port,
- (unsigned long *)&conn.remote_addr, &conn.remote_port,
- (unsigned long *)&state, (unsigned long *)&uid, (unsigned long *)&inode) != 7 )
+ if (!p_collection || !p_monitor) {
+ return -1;
+ }
- fprintf( stderr, "/proc/net/tcp: bad file format\n" );
+ /* 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;
+ }
- if ((inode == 0) || (state != TCP_ESTABLISHED)) continue;
+ /* populate the node */
+ p_node->p_monitor = p_monitor;
+ p_node->p_next = NULL;
- /* show the connection to each port monitor. */
- for_each_tcp_port_monitor_in_collection(
- p_collection,
- &show_connection_to_tcp_port_monitor,
- (void *) &conn
- );
- }
+ /* add a pointer to this monitor to the collection's hash */
+#ifdef HASH_DEBUG
+ fprintf(stderr, "collection hash insert of monitor [%s]\n", p_monitor->key);
+#endif
+ g_hash_table_insert(p_collection->hash, (gpointer) p_monitor->key,
+ (gpointer) p_monitor);
+
+ /* 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;
+ }
- fclose(fp);
+ /* splice node onto tail */
+ p_collection->monitor_list.p_tail->p_next = p_node;
+ p_collection->monitor_list.p_tail = p_node;
+ }
- /* 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
- );
-}
+ /* 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;
+ }
-/* 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;
+ return 0;
}
/* Clients need a way to find monitors */
-tcp_port_monitor_t * find_tcp_port_monitor(
- const tcp_port_monitor_collection_t * p_collection,
- in_port_t port_range_begin,
- in_port_t port_range_end
- )
+tcp_port_monitor_t *find_tcp_port_monitor(
+ const 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 */
+ tcp_port_monitor_t *p_monitor;
+ gchar key[12];
+
+ if (!p_collection) {
+ return NULL;
+ }
+
+ /* is monitor in hash? */
+ g_sprintf(key, ":%04X :%04X", port_range_begin, port_range_end);
+ p_monitor = g_hash_table_lookup(p_collection->hash, (gconstpointer) key);
+ return p_monitor;
}