add REMOTESERVICE to port monitor peekables
[monky] / src / libtcp-portmon.c
1 /* -------------------------------------------------------------------------
2  * libtcp-portmon.c:  tcp port monitoring library.               
3  *
4  * Copyright (C) 2005  Philip Kovacs kovacsp3@comcast.net
5  * 
6  * $Id$
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  * --------------------------------------------------------------------------- */
22
23 #include "libtcp-portmon.h"
24
25 /* -------------------------------------------------------------------
26  * IMPLEMENTATION INTERFACE
27  *
28  * Implementation-specific interface begins here.  Clients should not
29  * manipulate these structures directly, nor call the defined helper
30  * functions.  Use the "Client interface" functions defined at bottom.
31  * ------------------------------------------------------------------- */
32
33 /* ----------------------------------
34  * Copy a tcp_connection_t
35  *
36  * Returns 0 on success, -1 otherwise.
37  * ----------------------------------*/
38 int copy_tcp_connection( 
39         tcp_connection_t *                      p_dest_connection,
40         const tcp_connection_t *                p_source_connection
41         )
42 {
43    if ( !p_dest_connection || !p_source_connection )
44         return (-1);
45
46    p_dest_connection->local_addr = p_source_connection->local_addr;
47    p_dest_connection->local_port = p_source_connection->local_port;
48    p_dest_connection->remote_addr = p_source_connection->remote_addr;
49    p_dest_connection->remote_port = p_source_connection->remote_port;
50    p_dest_connection->age = p_source_connection->age;
51
52    return 0;
53 }
54
55 /* -----------------------------------------------------------------------------
56  * Open-addressed hash implementation requires that we supply two hash functions
57  * and a match function to compare two hash elements for identity.
58  * ----------------------------------------------------------------------------- */
59
60 /* --------------------------------------------------
61  * Functions to hash the connections within a monitor
62  * --------------------------------------------------*/
63
64 #define CONNECTION_HASH_KEY_LEN 17
65
66 /* ----------------------------------------------------------------------------------
67  * First connection hash function: DJB with a 65521 prime modulus to govern the range. 
68  * ----------------------------------------------------------------------------------*/
69 int connection_hash_function_1( const void *p_data )
70 {
71    tcp_connection_t *p_conn;
72    char key[CONNECTION_HASH_KEY_LEN];
73    unsigned int hash = 5381;
74    unsigned int i    = 0;
75
76    if ( !p_data )
77            return -1;
78    
79    memset(key,0,sizeof(key));
80
81    /* p_data is a pointer to tcp_connection_t */
82    p_conn = (tcp_connection_t *)p_data; 
83
84    /* key is a hex representation of the connection */
85    snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X", 
86                    p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
87 #ifdef HASH_DEBUG
88    fprintf(stderr,"--- key=[%s]\n",key);
89 #endif
90
91    for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
92    {
93       hash = ((hash << 5) + hash) + (key[i]);
94    }
95
96    return (hash & 0x7FFFFFFF) % 65521;
97 }
98
99 /* -------------------------------------------------------------------------
100  * Second connection hash function: DEK, modified to return odd numbers only,
101  * as required for open-address hashing using double-hash probing.
102  * Also uses a 65521 prime modulus to govern the range. 
103  * -------------------------------------------------------------------------*/
104 int connection_hash_function_2( const void *p_data )
105 {
106    tcp_connection_t *p_conn;
107    char key[CONNECTION_HASH_KEY_LEN];
108    unsigned int hash = CONNECTION_HASH_KEY_LEN-1;
109    unsigned int i    = 0;
110
111    if ( !p_data )
112            return -1;
113
114    memset(key,0,sizeof(key));
115
116    /* p_data is a pointer to a tcp_connection_t */
117    p_conn = (tcp_connection_t *)p_data;
118
119    /* key is a hex representation of the connection */
120    snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X", 
121                    p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
122
123    for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
124    {
125       hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
126    }
127    return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
128 }
129
130 /* -------------------------------------------------------------------------
131  * Connection Match function returns non-zero if hash elements are identical. 
132  * -------------------------------------------------------------------------*/
133 int connection_match_function( const void *p_data1, const void *p_data2 )
134 {
135    tcp_connection_t *p_conn1, *p_conn2;
136    
137    if ( !p_data1 || !p_data2 )
138            return 0;
139
140    /* p_data1, p_data2 are pointers to tcp_connection_t */
141    p_conn1 = (tcp_connection_t *)p_data1;
142    p_conn2 = (tcp_connection_t *)p_data2;
143
144    return (p_conn1->local_addr == p_conn2->local_addr &&
145            p_conn1->local_port == p_conn2->local_port &&
146            p_conn1->remote_addr ==  p_conn2->remote_addr &&
147            p_conn1->remote_port == p_conn2->remote_port );
148 }
149
150 /* --------------------------------------------------
151  * Functions to hash the monitors within a collection
152  * --------------------------------------------------*/
153
154 #define MONITOR_HASH_KEY_LEN 9
155
156 /* -------------------------------------------------------------------------------
157  * First monitor hash function: DJB with a 65521 prime modulus to govern the range.
158  * -------------------------------------------------------------------------------*/
159 int monitor_hash_function_1( const void *p_data )
160 {
161    tcp_port_monitor_t *p_monitor;
162    char key[MONITOR_HASH_KEY_LEN];
163    unsigned int hash = 5381;
164    unsigned int i    = 0;
165
166    if ( !p_data )
167            return -1;
168    
169    memset(key,0,sizeof(key));
170
171    /* p_data is a pointer to tcp_port_monitor_t */
172    p_monitor = (tcp_port_monitor_t *)p_data;
173
174    /* key is a hex representation of the starting port concatenated to the ending port */
175    snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
176                    p_monitor->port_range_begin, p_monitor->port_range_end );
177 #ifdef HASH_DEBUG
178    fprintf(stderr,"--- key=[%s]\n",key);
179 #endif
180
181    for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
182    {
183       hash = ((hash << 5) + hash) + (key[i]);
184    }
185
186    return (hash & 0x7FFFFFFF) % 65521;
187 }
188
189 /* -----------------------------------------------------------------------
190  * Second monitor hash function: DEK, modified to return odd numbers only,
191  * as required for open-address hashing using double-hash probing.
192  * Also uses a 65521 prime modulus to govern the range.
193  * -----------------------------------------------------------------------*/
194 int monitor_hash_function_2( const void *p_data )
195 {
196    tcp_port_monitor_t *p_monitor;
197    char key[MONITOR_HASH_KEY_LEN];
198    unsigned int hash = MONITOR_HASH_KEY_LEN-1;
199    unsigned int i    = 0;
200
201    if ( !p_data )
202            return -1;
203
204    memset(key,0,sizeof(key));
205
206    /* p_data is a pointer to a tcp_port_monitor_t */
207    p_monitor = (tcp_port_monitor_t *)p_data;
208
209    /* key is a hex representation of the starting port concatenated to the ending port */
210    snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
211                    p_monitor->port_range_begin, p_monitor->port_range_end );
212
213    for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
214    {
215       hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
216    }
217    return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
218 }
219
220 /* ----------------------------------------------------------------------
221  * Monitor match function returns non-zero if hash elements are identical.
222  * ----------------------------------------------------------------------*/
223 int monitor_match_function( const void *p_data1, const void *p_data2 )
224 {
225    tcp_port_monitor_t *p_monitor1, *p_monitor2;
226
227    if ( !p_data1 || !p_data2 )
228            return 0;
229
230    /* p_data1, p_data2 are pointers to tcp_connection_t */
231    p_monitor1 = (tcp_port_monitor_t *)p_data1;
232    p_monitor2 = (tcp_port_monitor_t *)p_data2;
233
234    return (p_monitor1->port_range_begin == p_monitor2->port_range_begin &&
235            p_monitor1->port_range_end == p_monitor2->port_range_end);
236 }
237
238 /* ---------------------------------------------------------------------------
239  * Port monitor utility functions implementing tcp_port_monitor_function_ptr_t
240  * ---------------------------------------------------------------------------*/
241 void destroy_tcp_port_monitor(
242         tcp_port_monitor_t *                    p_monitor,
243         void *                                  p_void 
244         )
245 {
246    tcp_connection_node_t *p_node, *p_temp;
247
248    if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
249       return;
250
251    /* destroy the monitor's hash */
252    hash_destroy(&p_monitor->hash);
253
254    /* destroy the monitor's peek array */
255    free( p_monitor->p_peek );
256
257    /* destroy the monitor's connection list */
258    for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; )
259    {
260            /* p_temp is for the next iteration */
261            p_temp = p_node->p_next;
262            
263            free( p_node );
264
265            p_node = p_temp;
266    }
267
268    /* destroy the monitor */
269    free( p_monitor );
270    p_monitor=NULL;
271 }
272
273 void age_tcp_port_monitor(
274         tcp_port_monitor_t *                    p_monitor,
275         void *                                  p_void
276         )
277 {
278    /* Run through the monitor's connections and decrement the age variable. 
279     * If the age goes negative, we remove the connection from the monitor. 
280     * Function takes O(n) time on the number of connections. */
281
282    tcp_connection_node_t *p_node, *p_temp;
283    tcp_connection_t *p_conn;
284    void *p_cast;
285
286    if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
287            return;
288
289    if ( !p_monitor->p_peek )
290            return;
291
292    for ( p_node = p_monitor->connection_list.p_head; p_node != NULL; )
293    {
294            if ( --p_node->connection.age >= 0 ) {
295                    p_node = p_node->p_next;
296                    continue;
297            }
298
299            /* connection on p_node is old.  remove connection from the hash. */
300            p_conn = &p_node->connection;
301            p_cast = (void *)p_conn;
302            if ( hash_remove( &p_monitor->hash, &p_cast ) != 0 ) {
303 #ifdef HASH_DEBUG
304                    fprintf(stderr, "--- hash_remove error\n");
305 #endif
306                    return;
307            }
308
309            /* splice p_node out of the connection_list */
310            if ( p_node->p_prev != NULL )
311                    p_node->p_prev->p_next = p_node->p_next;
312            if ( p_node->p_next != NULL )
313                    p_node->p_next->p_prev = p_node->p_prev;
314
315            /* correct the list head and tail if necessary */
316            if ( p_monitor->connection_list.p_head == p_node )
317                    p_monitor->connection_list.p_head = p_node->p_next;
318            if ( p_monitor->connection_list.p_tail == p_node )
319                    p_monitor->connection_list.p_tail = p_node->p_prev;
320
321            /* p_temp is for the next iteration */
322            p_temp = p_node->p_next;
323
324            /* destroy the node */
325            free( p_node );
326
327            p_node = p_temp;
328    }
329 }
330
331 void maintain_tcp_port_monitor_hash(
332         tcp_port_monitor_t *                    p_monitor,
333         void *                                  p_void
334         )
335 {
336    /* Check the number of vacated slots in the hash.  If it exceeds our maximum
337     * threshold (should be about 1/4 of the hash table), then the hash table
338     * performance degrades from O(1) toward O(n) as the number of vacated slots
339     * climbs.  This is avoided by clearing the hash and reinserting the entries.
340     * The benefit of open-addressing hashing does come with this price --
341     * you must rebalance it occasionally. */
342
343     tcp_connection_node_t *p_node;
344     double vacated_load;
345
346     if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
347         return;
348
349     vacated_load = (double)p_monitor->hash.vacated / (double)p_monitor->hash.positions;
350 #ifdef HASH_DEBUG
351     fprintf(stderr,"--- num vacated is %d, vacated factor is %.3f\n", p_monitor->hash.vacated, vacated_load );
352 #endif
353     if ( vacated_load <= TCP_CONNECTION_HASH_MAX_VACATED_RATIO )
354     {
355             /* hash is fine and needs no rebalancing */
356             return;
357     }
358
359 #ifdef HASH_DEBUG
360     fprintf(stderr,"--- rebuilding hash\n");
361 #endif
362
363     /* rebuild the hash  */
364     memset( p_monitor->hash.pp_table, 0, p_monitor->hash.positions * sizeof(void **));
365     p_monitor->hash.size = 0;
366     p_monitor->hash.vacated = 0;
367
368     for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next )
369     {
370             if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
371             {
372 #ifdef HASH_DEBUG
373                 fprintf(stderr,"--- hash_insert error\n");
374 #endif
375                 ;       
376             }
377     }
378 }
379
380 void rebuild_tcp_port_monitor_peek_table(
381         tcp_port_monitor_t *                    p_monitor,
382         void *                                  p_void
383         )
384 {
385    /* Run through the monitor's connections and rebuild the peek table
386     * of connection pointers.  This is done so peeking into the monitor
387     * can be done in O(1) time instead of O(n) time for each peek. */
388
389    tcp_connection_node_t *p_node;
390    int i = 0;
391
392    if ( !p_monitor || p_void )  /* p_void should be NULL in this context */
393         return;
394
395    /* zero out the peek array */
396    memset( p_monitor->p_peek, 0, p_monitor->hash.positions * sizeof(tcp_connection_t *) );
397
398    for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next, i++ )
399    {
400            p_monitor->p_peek[i] = &p_node->connection;
401    }
402 }
403
404 void show_connection_to_tcp_port_monitor(
405         tcp_port_monitor_t *                    p_monitor,
406         void *                                  p_void
407         )
408 {
409    /* The monitor gets to look at each connection to see if it falls within
410     * the monitor's port range of interest.  Connections of interest are first
411     * looked up in the hash to see if they are already there.  If they are, we
412     * reset the age of the connection so it is not deleted.  If the connection 
413     * is not in the hash, we add it, but only if the hash is not saturated.  
414     * The function takes O(1) time. */
415
416    tcp_connection_node_t *p_node;
417    void *p_cast;
418
419    if ( !p_monitor || !p_void )
420         return;
421
422    /* This p_connection is on caller's stack and not the heap.  If we are interested,
423     * we will create a copy of the connection (on the heap) and add it to our list. */
424    tcp_connection_t *p_connection = (tcp_connection_t *)p_void;
425    
426    /* inspect the local port number of the connection to see if we're interested. */
427    if ( (p_monitor->port_range_begin <= p_connection->local_port) &&
428         (p_connection->local_port <= p_monitor->port_range_end) )
429    {
430         /* the connection is in the range of the monitor. */
431
432         /* first check the hash to see if the connection is already there. */
433         p_cast = (void *)p_connection;
434         if ( hash_lookup( &p_monitor->hash, &p_cast ) == 0 )
435         {
436                 p_connection = (tcp_connection_t *)p_cast;
437                 /* it's already in the hash.  reset the age of the connection. */
438                 if ( p_connection != NULL )
439                 {
440                         p_connection->age = TCP_CONNECTION_STARTING_AGE;
441                 }
442
443                 return;
444         }
445
446         /* Connection is not yet in the hash.  We will try to add it, but only if the hash is not
447          * yet saturated.  We assume the hash is saturated (and therefore ignore this connection)
448          * if our load factor cap is now exceeded.  The benefit of limiting connections in this way
449          * is that the hash will continue to function at an average (1) speed by keeping the load
450          * load factor down.  Of course the downside is that each port monitor has a strict maximum 
451          * connection limit. */
452
453         if ( (double)p_monitor->hash.size / (double)p_monitor->hash.positions >= TCP_CONNECTION_HASH_MAX_LOAD_RATIO )
454         {
455                 /* hash exceeds our load limit is now "full" */
456                 return;
457         }
458
459         /* create a new connection node */
460         if ( (p_node = (tcp_connection_node_t *) calloc(1, sizeof(tcp_connection_node_t))) == NULL )
461                 return;
462
463         /* copy the connection data */
464         if ( copy_tcp_connection( &p_node->connection, p_connection ) != 0 )
465         {
466                 /* error copying the connection data. deallocate p_node to avoid leaks and return. */
467                 free( p_node );
468                 return;
469         }
470
471         p_node->connection.age = TCP_CONNECTION_STARTING_AGE;
472         p_node->p_next = NULL;
473
474         /* insert it into the monitor's hash table */
475         if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
476         {
477                 /* error inserting into hash.  delete the connection node we just created, so no leaks. */
478 #ifdef HASH_DEBUG
479                 fprintf(stderr, "--- hash_insert error\n");
480 #endif
481                 free(p_node);
482                 return;
483         }
484
485         /* append the node to the monitor's connection list */
486         if ( p_monitor->connection_list.p_tail == NULL )  /* assume p_head is NULL too */
487         {
488                 p_monitor->connection_list.p_head = p_node;
489                 p_monitor->connection_list.p_tail = p_node;
490                 p_node->p_prev = NULL;
491         }
492         else
493         {
494                 p_monitor->connection_list.p_tail->p_next = p_node;
495                 p_node->p_prev = p_monitor->connection_list.p_tail;
496                 p_monitor->connection_list.p_tail = p_node;
497         }
498    }
499 }
500
501 /* ---------------------------------------------------------------------------------------
502  * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in the collection. 
503  * ---------------------------------------------------------------------------------------*/
504 void for_each_tcp_port_monitor_in_collection(
505         tcp_port_monitor_collection_t *         p_collection,
506         tcp_port_monitor_function_ptr_t         p_function,
507         void *                                  p_function_args
508         )
509 {
510    tcp_port_monitor_node_t * p_current_node, * p_next_node;
511    
512    if ( !p_collection || !p_function )
513         return;
514
515    /* for each monitor in the collection */
516    for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
517    {
518         p_next_node = p_current_node->p_next;  /* do this first! */
519
520         if ( p_current_node->p_monitor )
521         {
522             /* apply the function with the given arguments */
523             (*p_function)( p_current_node->p_monitor, p_function_args );
524         }
525
526         p_current_node = p_next_node;
527    }
528   
529 }
530
531 /* ----------------------------------------------------------------------------------------
532  * Calculate an efficient hash size based on the desired number of elements and load factor.
533  * ---------------------------------------------------------------------------------------- */
534 int calc_efficient_hash_size(
535         int                                     min_elements,
536         int                                     max_hash_size,
537         double                                  max_load_factor
538         )
539 {
540    double min_size, hash_size, log_base_2;
541    
542    /* the size of the hash will the smallest power of two such that the minimum number
543       of desired elements does not exceed the maximum load factor. */                 
544    
545    min_size = (double)min_elements / max_load_factor;   /* starting point */
546      
547    /* now adjust size up to nearest power of two */
548    log_base_2 = (double) (int) ( log(min_size) / log(2) ) ;  /* lop off fractional portion of log */
549
550    hash_size = pow(2,log_base_2) >= min_size ? min_size : pow(2,(double)++log_base_2);
551
552    /* respect the maximum */
553    hash_size = hash_size <= max_hash_size ? hash_size : max_hash_size;
554
555    /*
556    fprintf(stderr,"hash size is %d, based on %d min_elements and %.02f max load, %d maximum\n",
557                    (int)hash_size, min_elements, max_load_factor, max_hash_size);
558    */
559
560    return hash_size;
561 }
562                 
563 /* ----------------------------------------------------------------------
564  * CLIENT INTERFACE 
565  *
566  * Clients should call only those functions below this line.
567  * ---------------------------------------------------------------------- */
568
569 /* ----------------------------------
570  * Client operations on port monitors
571  * ---------------------------------- */
572
573 /* Clients should first try to "find_tcp_port_monitor" before creating one
574    so that there are no redundant monitors. */
575 tcp_port_monitor_t * create_tcp_port_monitor(
576         in_port_t                               port_range_begin, 
577         in_port_t                               port_range_end,
578         tcp_port_monitor_args_t *               p_creation_args
579         )
580 {
581    tcp_port_monitor_t * p_monitor;
582
583    /* create the monitor */
584    p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t) );
585    if ( !p_monitor )
586         return NULL;
587
588    /* create the monitor's connection hash */
589    if ( hash_create( &p_monitor->hash, 
590                         p_creation_args && p_creation_args->min_port_monitor_connections > 0 ?
591                                 calc_efficient_hash_size( p_creation_args->min_port_monitor_connections,
592                                                           TCP_CONNECTION_HASH_SIZE_MAX,
593                                                           TCP_CONNECTION_HASH_MAX_LOAD_RATIO ) :
594                                 TCP_CONNECTION_HASH_SIZE_DEFAULT,
595                         &connection_hash_function_1, &connection_hash_function_2,
596                         &connection_match_function, NULL ) != 0 ) 
597    {
598         /* we failed to create the hash, so destroy the monitor completely so we don't leak */
599         destroy_tcp_port_monitor(p_monitor,NULL);
600         return NULL;
601    }
602
603    /* create the monitor's peek array */
604    if ( (p_monitor->p_peek = (tcp_connection_t **) calloc( p_monitor->hash.positions, sizeof(tcp_connection_t *))) == NULL )
605    {
606         /* we failed to create the peek array, so destroy the monitor completely, again, so we don't leak */
607         destroy_tcp_port_monitor(p_monitor,NULL);
608         return NULL ;
609    }
610
611    p_monitor->port_range_begin = port_range_begin;
612    p_monitor->port_range_end = port_range_end;
613
614    p_monitor->connection_list.p_head = NULL;
615    p_monitor->connection_list.p_tail = NULL;
616
617    return p_monitor;
618 }
619
620 /* Clients use this function to get connection data from the indicated port monitor.
621    The requested monitor value is copied into a client-supplied char buffer.
622    Returns 0 on success, -1 otherwise. */
623 int peek_tcp_port_monitor(
624         const tcp_port_monitor_t *              p_monitor,
625         int                                     item,
626         int                                     connection_index,
627         char *                                  p_buffer,
628         size_t                                  buffer_size
629         )
630 {
631    struct hostent *p_hostent;
632    struct servent *p_servent;
633    struct in_addr net;
634
635    if ( !p_monitor || !p_buffer || connection_index < 0 )
636         return(-1);
637
638    memset(p_buffer, 0, buffer_size);
639    memset(&net, 0, sizeof(net));
640
641    /* if the connection index is out of range, we simply return with no error
642     * having first cleared the client-supplied buffer. */
643    if ( (item!=COUNT) && (connection_index > p_monitor->hash.size - 1) )
644            return(0);
645                    
646    switch (item) {
647
648    case COUNT:
649    
650         snprintf( p_buffer, buffer_size, "%d" , p_monitor->hash.size );
651         break;
652
653    case REMOTEIP:
654
655         net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
656         snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
657         break;
658
659    case REMOTEHOST:
660
661         p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->remote_addr, sizeof(in_addr_t), AF_INET);
662         /* if no host name found, just use ip address. */
663         if ( !p_hostent || !p_hostent->h_name )
664         {
665                 net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
666                 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
667                 break;
668         }
669         snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
670         break;
671
672    case REMOTEPORT:
673
674         snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->remote_port );                          
675         break;
676
677    case REMOTESERVICE:
678
679         p_servent = getservbyport( htons(p_monitor->p_peek[ connection_index ]->remote_port ), "tcp" );
680         /* if no service name found for the port, just use the port number. */
681         if ( !p_servent || !p_servent->s_name ) {
682             snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->remote_port );
683         } else {
684             snprintf( p_buffer, buffer_size, "%s", p_servent->s_name );
685         }
686         break;
687
688    case LOCALIP:
689
690         net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
691         snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
692         break;
693
694    case LOCALHOST:
695
696         p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->local_addr, sizeof(in_addr_t), AF_INET);
697         /* if no host name found, just use ip address. */
698         if ( !p_hostent || !p_hostent->h_name )
699         {
700                 net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
701                 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
702                 break;
703         }
704         snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
705         break;
706
707    case LOCALPORT: 
708
709         snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
710         break;        
711
712    case LOCALSERVICE:
713
714         p_servent = getservbyport( htons(p_monitor->p_peek[ connection_index ]->local_port ), "tcp" );
715         /* if no service name found for the port, just use the port number. */
716         if ( !p_servent || !p_servent->s_name )
717         {
718                 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
719                 break;
720         }
721         snprintf( p_buffer, buffer_size, "%s", p_servent->s_name );
722         break;
723
724    default:
725         return(-1);
726    }
727
728    return(0);
729 }
730
731 /* --------------------------------
732  * Client operations on collections
733  * -------------------------------- */
734
735 /* Create a monitor collection.  Do this one first. */
736 tcp_port_monitor_collection_t * create_tcp_port_monitor_collection(
737         tcp_port_monitor_collection_args_t *    p_creation_args
738         )
739 {
740    tcp_port_monitor_collection_t * p_collection;
741
742    p_collection = (tcp_port_monitor_collection_t *) calloc( 1, sizeof( tcp_port_monitor_collection_t ) );
743    if ( !p_collection )
744            return NULL;
745
746    /* create the collection's monitor hash */
747    if ( hash_create( &p_collection->hash, 
748                         p_creation_args && p_creation_args->min_port_monitors > 0 ?
749                                 calc_efficient_hash_size( p_creation_args->min_port_monitors,
750                                                           TCP_MONITOR_HASH_SIZE_MAX,
751                                                           TCP_MONITOR_HASH_MAX_LOAD_RATIO ) :
752                                 TCP_MONITOR_HASH_SIZE_DEFAULT,
753                         &monitor_hash_function_1, &monitor_hash_function_2,
754                         &monitor_match_function, NULL ) != 0 )
755    {
756          /* we failed to create the hash, so destroy the monitor completely so we don't leak */
757          destroy_tcp_port_monitor_collection(p_collection);
758          return NULL;
759    }
760
761    p_collection->monitor_list.p_head = NULL;
762    p_collection->monitor_list.p_tail = NULL;
763
764    return p_collection;
765 }
766
767 /* Destroy the monitor collection (and the monitors inside).  Do this one last. */
768 void destroy_tcp_port_monitor_collection( 
769         tcp_port_monitor_collection_t *         p_collection
770         )
771 {
772    tcp_port_monitor_node_t * p_current_node, * p_next_node;
773
774    if ( !p_collection )
775            return;
776
777    /* destroy the collection's hash */
778    hash_destroy( &p_collection->hash );
779
780    /* destroy the monitors */
781    for_each_tcp_port_monitor_in_collection(
782         p_collection,
783         &destroy_tcp_port_monitor,
784         NULL
785         );
786
787    /* next destroy the empty monitor nodes */
788    for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
789    {
790         p_next_node = p_current_node->p_next;  /* do this first! */
791
792         free( p_current_node );
793         p_current_node = p_next_node;
794    }
795    
796    free( p_collection );
797    p_collection=NULL;
798 }
799
800 /* Updates the tcp statistics for all monitors within a collection */
801 void update_tcp_port_monitor_collection(
802         tcp_port_monitor_collection_t *         p_collection
803         )
804 {
805         FILE *fp;
806         char buf[256];
807         tcp_connection_t conn;
808         unsigned long inode,uid,state;
809
810         if ( !p_collection )
811                 return;
812
813         /* age the connections in all port monitors. */
814         for_each_tcp_port_monitor_in_collection(
815                 p_collection,
816                 &age_tcp_port_monitor,
817                 NULL
818                 );
819
820         /* read tcp data from /proc/net/tcp */
821         if ( ( fp = fopen("/proc/net/tcp", "r" ) ) == NULL )
822                 return;
823
824         /* ignore field name line */
825         fgets(buf, 255, fp);
826
827         /* read all tcp connections */
828         while (fgets (buf, sizeof (buf), fp) != NULL) {
829
830                 if ( sscanf (buf, "%*d: %lx:%hx %lx:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
831                         (unsigned long *)&conn.local_addr, &conn.local_port,
832                         (unsigned long *)&conn.remote_addr, &conn.remote_port,
833                         (unsigned long *)&state, (unsigned long *)&uid, (unsigned long *)&inode) != 7 )
834
835                         fprintf( stderr, "/proc/net/tcp: bad file format\n" );
836
837                 if ((inode == 0) || (state != TCP_ESTABLISHED)) continue;
838
839                 /* show the connection to each port monitor. */
840                 for_each_tcp_port_monitor_in_collection(
841                         p_collection,
842                         &show_connection_to_tcp_port_monitor,
843                         (void *) &conn
844                         );
845         }
846
847         fclose(fp);
848
849         /* check the health of the monitor hashes and rebuild them if nedded */
850         for_each_tcp_port_monitor_in_collection(
851                 p_collection,
852                 &maintain_tcp_port_monitor_hash,
853                 NULL
854                 );
855
856         /* rebuild the connection peek tables of all monitors so clients can peek in O(1) time */
857         for_each_tcp_port_monitor_in_collection(
858                 p_collection,
859                 &rebuild_tcp_port_monitor_peek_table,
860                 NULL
861                 );
862 }
863
864 /* After clients create a monitor, use this to add it to the collection.
865    Returns 0 on success, -1 otherwise. */
866 int insert_tcp_port_monitor_into_collection( 
867         tcp_port_monitor_collection_t *         p_collection,
868         tcp_port_monitor_t *                    p_monitor
869         )
870 {
871    tcp_port_monitor_node_t * p_node;
872
873    if ( !p_collection || !p_monitor )
874         return (-1);
875
876    /* create a container node for this monitor */
877    p_node = (tcp_port_monitor_node_t *) calloc( 1, sizeof(tcp_port_monitor_node_t) );
878    if ( !p_node )
879         return (-1);
880
881    /* populate the node */
882    p_node->p_monitor = p_monitor;
883    p_node->p_next = NULL;
884            
885    /* add a pointer to this monitor to the collection's hash */
886    if ( hash_insert( &p_collection->hash, (void *)p_monitor ) != 0 )
887    {
888         /* error inserting into hash.  destroy the monitor's container node so no leaks */
889         free( p_node );
890         return (-1);
891    }
892
893    /* tail of the container gets this node */
894    if ( !p_collection->monitor_list.p_tail )
895         p_collection->monitor_list.p_tail = p_node;
896    else
897    {
898         /* p_next of the tail better be NULL */
899         if ( p_collection->monitor_list.p_tail->p_next != NULL )
900            return (-1);
901
902         /* splice node onto tail */
903         p_collection->monitor_list.p_tail->p_next = p_node;
904         p_collection->monitor_list.p_tail = p_node;
905    }
906
907    /* if this was the first element added */
908    if ( !p_collection->monitor_list.p_head )
909         p_collection->monitor_list.p_head = p_collection->monitor_list.p_tail;
910
911    return 0;
912 }
913
914 /* Clients need a way to find monitors */
915 tcp_port_monitor_t * find_tcp_port_monitor( 
916         const tcp_port_monitor_collection_t *   p_collection, 
917         in_port_t                               port_range_begin,
918         in_port_t                               port_range_end
919         )
920 {
921    tcp_port_monitor_t monitor,*p_monitor;
922    void *p_cast;
923
924    if ( !p_collection )
925         return NULL;
926
927    /* need a monitor object to use for searching the hash */
928    monitor.port_range_begin = port_range_begin;
929    monitor.port_range_end = port_range_end;
930    p_monitor = &monitor;
931    p_cast = (void *)p_monitor;
932
933    /* simple hash table lookup */
934    if ( hash_lookup( &p_collection->hash, &p_cast ) == 0 )
935    {
936            /* found the monitor and p_cast now points to it */
937            p_monitor = (tcp_port_monitor_t *)p_cast;
938            return( p_monitor );
939    }
940
941    return NULL;  /* monitor not found */
942 }