Removing old svn keywords.
[monky] / src / libtcp-portmon.c
1 /* libtcp-portmon.c:  tcp port monitoring library.
2  *
3  * Copyright (C) 2005-2007 Philip Kovacs pkovacs@users.sourceforge.net
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
18  * USA. */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "libtcp-portmon.h"
25 #include <glib/gprintf.h>
26
27 /* -------------------------------------------------------------------
28  * IMPLEMENTATION INTERFACE
29  *
30  * Implementation-specific interface begins here.  Clients should not
31  * manipulate these structures directly, nor call the defined helper
32  * functions.  Use the "Client interface" functions defined at bottom.
33  * ------------------------------------------------------------------- */
34
35 /* -----------------------------------
36  * Copy a tcp_connection_t
37  *
38  * Returns 0 on success, -1 otherwise.
39  * ----------------------------------- */
40 int copy_tcp_connection(tcp_connection_t *p_dest_connection,
41                 const tcp_connection_t *p_source_connection)
42 {
43         if (!p_dest_connection || !p_source_connection) {
44                 return -1;
45         }
46
47         g_strlcpy(p_dest_connection->key, p_source_connection->key,
48                 sizeof(p_dest_connection->key));
49         p_dest_connection->local_addr = p_source_connection->local_addr;
50         p_dest_connection->local_port = p_source_connection->local_port;
51         p_dest_connection->remote_addr = p_source_connection->remote_addr;
52         p_dest_connection->remote_port = p_source_connection->remote_port;
53         p_dest_connection->age = p_source_connection->age;
54
55         return 0;
56 }
57
58 /* -------------------------------------------
59  * Port monitor utility functions implementing
60  * tcp_port_monitor_function_ptr_t
61  * ------------------------------------------- */
62 void destroy_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void)
63 {
64         tcp_connection_node_t *p_node, *p_temp;
65
66         if (!p_monitor || p_void) {     /* p_void should be NULL in this context */
67                 return;
68         }
69
70         /* destroy the monitor's peek array */
71         free(p_monitor->p_peek);
72
73         /* destroy the monitor's connection list */
74         for (p_node = p_monitor->connection_list.p_head; p_node != NULL; ) {
75                 /* p_temp is for the next iteration */
76                 p_temp = p_node->p_next;
77
78                 free(p_node);
79
80                 p_node = p_temp;
81         }
82
83         /* destroy the monitor's hash */
84         g_hash_table_destroy(p_monitor->hash);
85         p_monitor->hash = NULL;
86
87         /* destroy the monitor */
88         free(p_monitor);
89         p_monitor = NULL;
90 }
91
92 void age_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void)
93 {
94         /* Run through the monitor's connections and decrement the age variable.
95          * If the age goes negative, we remove the connection from the monitor.
96          * Function takes O(n) time on the number of connections. */
97
98         tcp_connection_node_t *p_node, *p_temp;
99         tcp_connection_t *p_conn;
100
101         if (!p_monitor || p_void) {     /* p_void should be NULL in this context */
102                 return;
103         }
104
105         if (!p_monitor->p_peek) {
106                 return;
107         }
108
109         for (p_node = p_monitor->connection_list.p_head; p_node; ) {
110                 if (--p_node->connection.age >= 0) {
111                         p_node = p_node->p_next;
112                         continue;
113                 }
114
115                 /* connection on p_node is old.  remove connection from the hash. */
116                 p_conn = &p_node->connection;
117 #ifdef HASH_DEBUG
118                 fprintf(stderr, "monitor hash removal of connection [%s]", p_conn->key);
119                 if (!g_hash_table_remove(p_monitor->hash,
120                                 (gconstpointer) p_conn->key)) {
121                         fprintf(stderr, " - ERROR NOT FOUND\n");
122                         return;
123                 }
124                 fprintf(stderr, " - OK\n");
125 #else
126                 if (!g_hash_table_remove(p_monitor->hash,
127                                 (gconstpointer) p_conn->key)) {
128                         return;
129                 }
130 #endif
131
132                 /* splice p_node out of the connection_list */
133                 if (p_node->p_prev != NULL) {
134                         p_node->p_prev->p_next = p_node->p_next;
135                 }
136                 if (p_node->p_next != NULL) {
137                         p_node->p_next->p_prev = p_node->p_prev;
138                 }
139
140                 /* correct the list head and tail if necessary */
141                 if (p_monitor->connection_list.p_head == p_node) {
142                         p_monitor->connection_list.p_head = p_node->p_next;
143                 }
144                 if (p_monitor->connection_list.p_tail == p_node) {
145                         p_monitor->connection_list.p_tail = p_node->p_prev;
146                 }
147
148                 /* p_temp is for the next iteration */
149                 p_temp = p_node->p_next;
150
151                 /* destroy the node */
152                 free(p_node);
153
154                 p_node = p_temp;
155         }
156 }
157
158 void rebuild_tcp_port_monitor_peek_table(tcp_port_monitor_t *p_monitor,
159                 void *p_void)
160 {
161         /* Run through the monitor's connections and rebuild the peek table of
162          * connection pointers.  This is done so peeking into the monitor can be
163          * done in O(1) time instead of O(n) time for each peek. */
164
165         tcp_connection_node_t *p_node;
166         int i = 0;
167
168         if (!p_monitor || p_void) {     /* p_void should be NULL in this context */
169                 return;
170         }
171
172         /* zero out the peek array */
173         memset(p_monitor->p_peek, 0, p_monitor->max_port_monitor_connections *
174                 sizeof(tcp_connection_t *));
175
176         for (p_node = p_monitor->connection_list.p_head; p_node != NULL;
177                         p_node = p_node->p_next, i++) {
178                 p_monitor->p_peek[i] = &p_node->connection;
179         }
180 }
181
182 void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor,
183                 void *p_void)
184 {
185         /* The monitor gets to look at each connection to see if it falls within
186          * the monitor's port range of interest.  Connections of interest are first
187          * looked up in the hash to see if they are already there.  If they are, we
188          * reset the age of the connection so it is not deleted.  If the connection
189          * is not in the hash, we add it, but only if we haven't exceeded the
190          * maximum connection limit for the monitor.
191          * The function takes O(1) time. */
192
193         tcp_connection_node_t *p_node;
194         tcp_connection_t *p_connection, *p_conn_hash;
195
196         if (!p_monitor || !p_void) {
197                 return;
198         }
199
200         /* This p_connection is on caller's stack and not the heap.
201          * If we are interested, we will create a copy of the connection
202          * (on the heap) and add it to our list. */
203         p_connection = (tcp_connection_t *) p_void;
204
205         /* inspect the local port number of the connection to see if we're
206          * interested. */
207         if ((p_monitor->port_range_begin <= p_connection->local_port)
208                         && (p_connection->local_port <= p_monitor->port_range_end)) {
209                 /* the connection is in the range of the monitor. */
210
211                 /* first check the hash to see if the connection is already there. */
212                 if ((p_conn_hash = g_hash_table_lookup(p_monitor->hash,
213                                 (gconstpointer) p_connection->key))) {
214                         /* it's already in the hash.  reset the age of the connection. */
215                         p_conn_hash->age = TCP_CONNECTION_STARTING_AGE;
216
217                         return;
218                 }
219
220                 /* Connection is not yet in the hash.
221                  * Add it if max_connections not exceeded. */
222                 if (g_hash_table_size(p_monitor->hash)
223                                 >= p_monitor->max_port_monitor_connections) {
224                         return;
225                 }
226
227                 /* create a new connection node */
228                 if ((p_node = (tcp_connection_node_t *)
229                                 calloc(1, sizeof(tcp_connection_node_t))) == NULL) {
230                         return;
231                 }
232
233                 /* copy the connection data */
234                 if (copy_tcp_connection(&p_node->connection, p_connection) != 0) {
235                         /* error copying the connection data. deallocate p_node to
236                          * avoid leaks and return. */
237                         free(p_node);
238                         return;
239                 }
240
241                 p_node->connection.age = TCP_CONNECTION_STARTING_AGE;
242                 p_node->p_next = NULL;
243
244                 /* insert it into the monitor's hash table */
245 #ifdef HASH_DEBUG
246                 fprintf(stderr, "monitor hash insert of connection [%s]\n",
247                         p_node->connection.key);
248 #endif
249                 g_hash_table_insert(p_monitor->hash,
250                         (gpointer) p_node->connection.key, (gpointer) &p_node->connection);
251
252                 /* append the node to the monitor's connection list */
253                 if (p_monitor->connection_list.p_tail == NULL) {
254                         /* assume p_head is NULL too */
255                         p_monitor->connection_list.p_head = p_node;
256                         p_monitor->connection_list.p_tail = p_node;
257                         p_node->p_prev = NULL;
258                 } else {
259                         p_monitor->connection_list.p_tail->p_next = p_node;
260                         p_node->p_prev = p_monitor->connection_list.p_tail;
261                         p_monitor->connection_list.p_tail = p_node;
262                 }
263         }
264 }
265
266 /* ------------------------------------------------------------------------
267  * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in
268  * the collection.
269  * ------------------------------------------------------------------------ */
270 void for_each_tcp_port_monitor_in_collection(
271                 tcp_port_monitor_collection_t *p_collection,
272                 tcp_port_monitor_function_ptr_t p_function, void *p_function_args)
273 {
274         tcp_port_monitor_node_t *p_current_node, *p_next_node;
275
276         if (!p_collection || !p_function) {
277                 return;
278         }
279
280         /* for each monitor in the collection */
281         for (p_current_node = p_collection->monitor_list.p_head;
282                         p_current_node != NULL; p_current_node = p_next_node) {
283                 p_next_node = p_current_node->p_next;   /* do this first! */
284
285                 if (p_current_node->p_monitor) {
286                         /* apply the function with the given arguments */
287                         p_function(p_current_node->p_monitor, p_function_args);
288                 }
289         }
290 }
291
292 /* ----------------------------------------------------------------------
293  * CLIENT INTERFACE
294  *
295  * Clients should call only those functions below this line.
296  * ---------------------------------------------------------------------- */
297
298 /* ----------------------------------
299  * Client operations on port monitors
300  * ---------------------------------- */
301
302 /* Clients should first try to "find_tcp_port_monitor" before creating one
303  * so that there are no redundant monitors. */
304 tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin,
305                 in_port_t port_range_end, tcp_port_monitor_args_t *p_creation_args)
306 {
307         tcp_port_monitor_t *p_monitor;
308
309         /* create the monitor */
310         p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t));
311         if (!p_monitor) {
312                 return NULL;
313         }
314
315         p_monitor->max_port_monitor_connections =
316                 p_creation_args->max_port_monitor_connections;
317
318         /* build the monitor key for the collection hash */
319         g_sprintf(p_monitor->key, ":%04X :%04X", port_range_begin, port_range_end);
320
321         /* create the monitor's connection hash */
322         if ((p_monitor->hash = g_hash_table_new(g_str_hash, g_str_equal)) == NULL) {
323                 /* we failed to create the hash, so destroy the monitor completely
324                  * so we don't leak */
325                 destroy_tcp_port_monitor(p_monitor, NULL);
326                 return NULL;
327         }
328
329         /* create the monitor's peek array */
330         if ((p_monitor->p_peek = (tcp_connection_t **)
331                         calloc(p_monitor->max_port_monitor_connections,
332                         sizeof(tcp_connection_t *))) == NULL) {
333                 /* we failed to create the peek array,
334                  * so destroy the monitor completely, again, so we don't leak */
335                 destroy_tcp_port_monitor(p_monitor, NULL);
336                 return NULL;
337         }
338
339         p_monitor->port_range_begin = port_range_begin;
340         p_monitor->port_range_end = port_range_end;
341
342         p_monitor->connection_list.p_head = NULL;
343         p_monitor->connection_list.p_tail = NULL;
344
345         return p_monitor;
346 }
347
348 /* Clients use this function to get connection data from the indicated
349  * port monitor.
350  * The requested monitor value is copied into a client-supplied char buffer.
351  * Returns 0 on success, -1 otherwise. */
352 int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item,
353                 int connection_index, char *p_buffer, size_t buffer_size)
354 {
355         struct in_addr net;
356         struct sockaddr_in sa;
357
358         sa.sin_family = AF_INET;
359         
360         if (!p_monitor || !p_buffer || connection_index < 0) {
361                 return -1;
362         }
363
364         memset(p_buffer, 0, buffer_size);
365         memset(&net, 0, sizeof(net));
366
367         /* if the connection index is out of range, we simply return with no error,
368          * having first cleared the client-supplied buffer. */
369         if ((item != COUNT) && (connection_index
370                         > (int) g_hash_table_size(p_monitor->hash) - 1)) {
371                 return 0;
372         }
373
374         switch (item) {
375
376                 case COUNT:
377
378                         snprintf(p_buffer, buffer_size, "%d",
379                                 g_hash_table_size(p_monitor->hash));
380                         break;
381
382                 case REMOTEIP:
383
384                         net.s_addr = p_monitor->p_peek[connection_index]->remote_addr;
385                         snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net));
386                         break;
387
388                 case REMOTEHOST:
389
390                         memcpy(&sa.sin_addr.s_addr, &p_monitor->p_peek[connection_index]->remote_addr, sizeof(sa.sin_addr.s_addr));
391                         getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), p_buffer, buffer_size, NULL, 0, 0);
392                         break;
393
394                 case REMOTEPORT:
395
396                         snprintf(p_buffer, buffer_size, "%d",
397                                 p_monitor->p_peek[connection_index]->remote_port);
398                         break;
399
400                 case REMOTESERVICE:
401
402                         sa.sin_port=htons(p_monitor->p_peek[connection_index]->remote_port);
403                         getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST);
404                         break;
405
406                 case LOCALIP:
407
408                         net.s_addr = p_monitor->p_peek[connection_index]->local_addr;
409                         snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net));
410                         break;
411
412                 case LOCALHOST:
413
414                         memcpy(&sa.sin_addr.s_addr, &p_monitor->p_peek[connection_index]->local_addr, sizeof(sa.sin_addr.s_addr));
415                         getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), p_buffer, buffer_size, NULL, 0, 0);
416                         break;
417
418                 case LOCALPORT:
419
420                         snprintf(p_buffer, buffer_size, "%d",
421                                 p_monitor->p_peek[connection_index]->local_port);
422                         break;
423
424                 case LOCALSERVICE:
425
426                         sa.sin_port=htons(p_monitor->p_peek[connection_index]->local_port);
427                         getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST);
428                         break;
429
430                 default:
431                         return -1;
432         }
433
434         return 0;
435 }
436
437 /* --------------------------------
438  * Client operations on collections
439  * -------------------------------- */
440
441 /* Create a monitor collection.  Do this one first. */
442 tcp_port_monitor_collection_t *create_tcp_port_monitor_collection(void)
443 {
444         tcp_port_monitor_collection_t *p_collection;
445
446         p_collection = (tcp_port_monitor_collection_t *)
447                 calloc(1, sizeof(tcp_port_monitor_collection_t));
448         if (!p_collection) {
449                 return NULL;
450         }
451
452         /* create the collection's monitor hash */
453         if ((p_collection->hash = g_hash_table_new(g_str_hash, g_str_equal))
454                         == NULL) {
455                 /* we failed to create the hash,
456                  * so destroy the monitor completely so we don't leak */
457                 destroy_tcp_port_monitor_collection(p_collection);
458                 return NULL;
459         }
460
461         p_collection->monitor_list.p_head = NULL;
462         p_collection->monitor_list.p_tail = NULL;
463
464         return p_collection;
465 }
466
467 /* Destroy the monitor collection (and the monitors inside).
468  * Do this one last. */
469 void destroy_tcp_port_monitor_collection(
470                 tcp_port_monitor_collection_t *p_collection)
471 {
472         tcp_port_monitor_node_t *p_current_node, *p_next_node;
473
474         if (!p_collection) {
475                 return;
476         }
477
478         /* destroy the monitors */
479         for_each_tcp_port_monitor_in_collection(p_collection,
480                 &destroy_tcp_port_monitor, NULL);
481
482         /* next destroy the empty monitor nodes */
483         for (p_current_node = p_collection->monitor_list.p_head;
484                         p_current_node != NULL; p_current_node = p_next_node) {
485                 p_next_node = p_current_node->p_next;   /* do this first! */
486
487                 free(p_current_node);
488         }
489
490         /* destroy the collection's hash */
491         g_hash_table_destroy(p_collection->hash);
492         p_collection->hash = NULL;
493
494         free(p_collection);
495         p_collection = NULL;
496 }
497
498 /* Updates the tcp statistics for all monitors within a collection */
499 void update_tcp_port_monitor_collection(
500                 tcp_port_monitor_collection_t *p_collection)
501 {
502         FILE *fp;
503         char buf[256];
504         tcp_connection_t conn;
505         unsigned long inode, uid, state;
506
507         if (!p_collection) {
508                 return;
509         }
510
511         /* age the connections in all port monitors. */
512         for_each_tcp_port_monitor_in_collection(p_collection,
513                 &age_tcp_port_monitor, NULL);
514
515         /* read tcp data from /proc/net/tcp */
516         if ((fp = fopen("/proc/net/tcp", "r")) == NULL) {
517                 return;
518         }
519
520         /* ignore field name line */
521         fgets(buf, 255, fp);
522
523         /* read all tcp connections */
524         while (fgets(buf, sizeof(buf), fp) != NULL) {
525
526                 if (sscanf(buf,
527                                 "%*d: %x:%hx %x:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
528                                 (unsigned int *) &conn.local_addr, &conn.local_port,
529                                 (unsigned int *) &conn.remote_addr, &conn.remote_port,
530                                 (unsigned long *) &state, (unsigned long *) &uid,
531                                 (unsigned long *) &inode) != 7) {
532                         fprintf(stderr, "/proc/net/tcp: bad file format\n");
533                 }
534                 /** TCP_ESTABLISHED equals 1, but is not (always??) included **/
535                 //if ((inode == 0) || (state != TCP_ESTABLISHED)) {
536                 if((inode == 0) || (state != 1)) {
537                         continue;
538                 }
539
540                 /* build hash key */
541                 g_sprintf(conn.key, "%08X:%04X %08X:%04X", conn.local_addr,
542                         conn.local_port, conn.remote_addr, conn.remote_port);
543
544                 /* show the connection to each port monitor. */
545                 for_each_tcp_port_monitor_in_collection(p_collection,
546                         &show_connection_to_tcp_port_monitor, (void *) &conn);
547         }
548
549         fclose(fp);
550
551         /* rebuild the connection peek tables of all monitors
552          * so clients can peek in O(1) time */
553         for_each_tcp_port_monitor_in_collection(p_collection,
554                 &rebuild_tcp_port_monitor_peek_table, NULL);
555 }
556
557 /* After clients create a monitor, use this to add it to the collection.
558  * Returns 0 on success, -1 otherwise. */
559 int insert_tcp_port_monitor_into_collection(
560                 tcp_port_monitor_collection_t *p_collection,
561                 tcp_port_monitor_t *p_monitor)
562 {
563         tcp_port_monitor_node_t *p_node;
564
565         if (!p_collection || !p_monitor) {
566                 return -1;
567         }
568
569         /* create a container node for this monitor */
570         p_node = (tcp_port_monitor_node_t *)
571                 calloc(1, sizeof(tcp_port_monitor_node_t));
572         if (!p_node) {
573                 return -1;
574         }
575
576         /* populate the node */
577         p_node->p_monitor = p_monitor;
578         p_node->p_next = NULL;
579
580         /* add a pointer to this monitor to the collection's hash */
581 #ifdef HASH_DEBUG
582         fprintf(stderr, "collection hash insert of monitor [%s]\n", p_monitor->key);
583 #endif
584         g_hash_table_insert(p_collection->hash, (gpointer) p_monitor->key,
585                 (gpointer) p_monitor);
586
587         /* tail of the container gets this node */
588         if (!p_collection->monitor_list.p_tail) {
589                 p_collection->monitor_list.p_tail = p_node;
590         } else {
591                 /* p_next of the tail better be NULL */
592                 if (p_collection->monitor_list.p_tail->p_next != NULL) {
593                         return -1;
594                 }
595
596                 /* splice node onto tail */
597                 p_collection->monitor_list.p_tail->p_next = p_node;
598                 p_collection->monitor_list.p_tail = p_node;
599         }
600
601         /* if this was the first element added */
602         if (!p_collection->monitor_list.p_head) {
603                 p_collection->monitor_list.p_head = p_collection->monitor_list.p_tail;
604         }
605
606         return 0;
607 }
608
609 /* Clients need a way to find monitors */
610 tcp_port_monitor_t *find_tcp_port_monitor(
611                 const tcp_port_monitor_collection_t *p_collection,
612                 in_port_t port_range_begin, in_port_t port_range_end)
613 {
614         tcp_port_monitor_t *p_monitor;
615         gchar key[12];
616
617         if (!p_collection) {
618                 return NULL;
619         }
620
621         /* is monitor in hash? */
622         g_sprintf(key, ":%04X :%04X", port_range_begin, port_range_end);
623         p_monitor = g_hash_table_lookup(p_collection->hash, (gconstpointer) key);
624         return p_monitor;
625 }