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