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