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