X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Flibtcp-portmon.c;h=bedcae5f51a58d401dd60ce1fd759d3497741be5;hb=841c0c962fd208c69a21f4f278167dde597dc278;hp=63095f80ef98d0693ab7c100735fed0671b0f750;hpb=3d26a4880e92df2c6004d85c1be458e35c6bfc3a;p=monky diff --git a/src/libtcp-portmon.c b/src/libtcp-portmon.c index 63095f8..bedcae5 100644 --- a/src/libtcp-portmon.c +++ b/src/libtcp-portmon.c @@ -1,6 +1,7 @@ -/* $Id$ */ - -/* libtcp-portmon.c: tcp port monitoring library. +/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- + * vim: ts=4 sw=4 noet ai cindent syntax=c + * + * libtcp-portmon.c: tcp port monitoring library. * * Copyright (C) 2005-2007 Philip Kovacs pkovacs@users.sourceforge.net * @@ -17,10 +18,16 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 - * USA. */ + * USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif -#include #include "libtcp-portmon.h" +#include /* ------------------------------------------------------------------- * IMPLEMENTATION INTERFACE @@ -42,8 +49,6 @@ int copy_tcp_connection(tcp_connection_t *p_dest_connection, return -1; } - g_strlcpy(p_dest_connection->key, p_source_connection->key, - sizeof(p_dest_connection->key)); p_dest_connection->local_addr = p_source_connection->local_addr; p_dest_connection->local_port = p_source_connection->local_port; p_dest_connection->remote_addr = p_source_connection->remote_addr; @@ -122,7 +127,7 @@ void age_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void) fprintf(stderr, " - OK\n"); #else if (!g_hash_table_remove(p_monitor->hash, - (gconstpointer) p_conn->key)) { + (gconstpointer) p_conn)) { return; } #endif @@ -208,7 +213,7 @@ void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor, /* first check the hash to see if the connection is already there. */ if ((p_conn_hash = g_hash_table_lookup(p_monitor->hash, - (gconstpointer) p_connection->key))) { + (gconstpointer) p_connection))) { /* it's already in the hash. reset the age of the connection. */ p_conn_hash->age = TCP_CONNECTION_STARTING_AGE; @@ -245,7 +250,7 @@ void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor, p_node->connection.key); #endif g_hash_table_insert(p_monitor->hash, - (gpointer) p_node->connection.key, (gpointer) &p_node->connection); + (gpointer) &p_node->connection, (gpointer) &p_node->connection); /* append the node to the monitor's connection list */ if (p_monitor->connection_list.p_tail == NULL) { @@ -287,6 +292,136 @@ void for_each_tcp_port_monitor_in_collection( } } +static const unsigned char prefix_4on6[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff +}; + +union sockaddr_in46 { + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + struct sockaddr sa; +}; + +/* checks whether the address is a IPv4-mapped IPv6 address */ +static int is_4on6(const struct in6_addr *addr) +{ + return ! memcmp(&addr->s6_addr, prefix_4on6, sizeof(prefix_4on6)); +} + + +/* converts the address to appropriate textual representation (IPv6, IPv4 or fqdn) */ +static void print_host(char *p_buffer, size_t buffer_size, const struct in6_addr *addr, int fqdn) +{ + union sockaddr_in46 sa; + socklen_t slen; + + memset(&sa, 0, sizeof(sa)); + + if(is_4on6(addr)) { + sa.sa4.sin_family = AF_INET; + memcpy(&sa.sa4.sin_addr.s_addr, &addr->s6_addr[12], 4); + slen = sizeof(sa.sa4); + } else { + sa.sa6.sin6_family = AF_INET6; + memcpy(&sa.sa6.sin6_addr, addr, sizeof(struct in6_addr)); + slen = sizeof(sa.sa6); + } + + getnameinfo(&sa.sa, slen, p_buffer, buffer_size, NULL, 0, fqdn?0:NI_NUMERICHOST); +} + +/* converts the textual representation of an IPv4 or IPv6 address to struct in6_addr */ +static void string_to_addr(struct in6_addr *addr, const char *p_buffer) +{ + size_t i; + + if(strlen(p_buffer) < 32) { //IPv4 address + i = sizeof(prefix_4on6); + memcpy(addr->s6_addr, prefix_4on6, i); + } else { + i = 0; + } + + for( ; i < sizeof(addr->s6_addr); i+=4, p_buffer+=8) { + sscanf(p_buffer, "%8x", (unsigned *)&addr->s6_addr[i]); + } +} + +/* hash function for tcp_connections */ +static guint tcp_connection_hash(gconstpointer A) +{ + const tcp_connection_t *a = (const tcp_connection_t *) A; + guint hash = 0; + size_t i; + + hash = hash*47 + a->local_port; + hash = hash*47 + a->remote_port; + for(i = 0; i < sizeof(a->local_addr.s6_addr); ++i) + hash = hash*47 + a->local_addr.s6_addr[i]; + for(i = 0; i < sizeof(a->remote_addr.s6_addr); ++i) + hash = hash*47 + a->remote_addr.s6_addr[i]; + + return hash; +} + +/* comparison function for tcp_connections */ +static gboolean tcp_connection_equal(gconstpointer A, gconstpointer B) +{ + const tcp_connection_t *a = (const tcp_connection_t *) A; + const tcp_connection_t *b = (const tcp_connection_t *) B; + + return a->local_port == b->local_port && a->remote_port == b->remote_port && + ! memcmp(&a->local_addr, &b->local_addr, sizeof(a->local_addr)) && + ! memcmp(&a->remote_addr.s6_addr, &b->remote_addr, sizeof(a->remote_addr)); +} + +/* adds connections from file to the collection */ +static void process_file(tcp_port_monitor_collection_t *p_collection, const char *file) +{ + FILE *fp; + char buf[256]; + char local_addr[40]; + char remote_addr[40]; + tcp_connection_t conn; + unsigned long inode, uid, state; + + if ((fp = fopen(file, "r")) == NULL) { + return; + } + + /* ignore field name line */ + fgets(buf, 255, fp); + + /* read all tcp connections */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + + if (sscanf(buf, + "%*d: %39[0-9a-fA-F]:%hx %39[0-9a-fA-F]:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu", + local_addr, &conn.local_port, + remote_addr, &conn.remote_port, + (unsigned long *) &state, (unsigned long *) &uid, + (unsigned long *) &inode) != 7) { + fprintf(stderr, "/proc/net/tcp: bad file format\n"); + } + /** TCP_ESTABLISHED equals 1, but is not (always??) included **/ + //if ((inode == 0) || (state != TCP_ESTABLISHED)) { + if((inode == 0) || (state != 1)) { + continue; + } + + string_to_addr(&conn.local_addr, local_addr); + string_to_addr(&conn.remote_addr, remote_addr); + + /* show the connection to each port monitor. */ + for_each_tcp_port_monitor_in_collection(p_collection, + &show_connection_to_tcp_port_monitor, (void *) &conn); + } + + fclose(fp); +} + /* ---------------------------------------------------------------------- * CLIENT INTERFACE * @@ -317,7 +452,7 @@ tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin, g_sprintf(p_monitor->key, ":%04X :%04X", port_range_begin, port_range_end); /* create the monitor's connection hash */ - if ((p_monitor->hash = g_hash_table_new(g_str_hash, g_str_equal)) == NULL) { + if ((p_monitor->hash = g_hash_table_new(tcp_connection_hash, tcp_connection_equal)) == NULL) { /* we failed to create the hash, so destroy the monitor completely * so we don't leak */ destroy_tcp_port_monitor(p_monitor, NULL); @@ -350,17 +485,17 @@ tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin, int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, int connection_index, char *p_buffer, size_t buffer_size) { - struct hostent *p_hostent; - struct servent *p_servent; - struct in_addr net; + struct sockaddr_in sa; if (!p_monitor || !p_buffer || connection_index < 0) { return -1; } memset(p_buffer, 0, buffer_size); - memset(&net, 0, sizeof(net)); + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + /* if the connection index is out of range, we simply return with no error, * having first cleared the client-supplied buffer. */ if ((item != COUNT) && (connection_index @@ -378,22 +513,12 @@ int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, case REMOTEIP: - net.s_addr = p_monitor->p_peek[connection_index]->remote_addr; - snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net)); + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 0); break; case REMOTEHOST: - p_hostent = gethostbyaddr((const void *) - &p_monitor->p_peek[connection_index]->remote_addr, - sizeof(in_addr_t), AF_INET); - /* if no host name found, just use ip address. */ - if (!p_hostent || !p_hostent->h_name) { - net.s_addr = p_monitor->p_peek[connection_index]->remote_addr; - snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net)); - break; - } - snprintf(p_buffer, buffer_size, "%s", p_hostent->h_name); + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 1); break; case REMOTEPORT: @@ -404,36 +529,18 @@ int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, case REMOTESERVICE: - p_servent = getservbyport( - htons(p_monitor->p_peek[connection_index]->remote_port), "tcp"); - /* if no service name found for the port, - * just use the port number. */ - if (!p_servent || !p_servent->s_name) { - snprintf(p_buffer, buffer_size, "%d", - p_monitor->p_peek[connection_index]->remote_port); - } else { - snprintf(p_buffer, buffer_size, "%s", p_servent->s_name); - } + sa.sin_port=htons(p_monitor->p_peek[connection_index]->remote_port); + getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST); break; case LOCALIP: - net.s_addr = p_monitor->p_peek[connection_index]->local_addr; - snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net)); + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 0); break; case LOCALHOST: - p_hostent = gethostbyaddr((const void *) - &p_monitor->p_peek[connection_index]->local_addr, - sizeof(in_addr_t), AF_INET); - /* if no host name found, just use ip address. */ - if (!p_hostent || !p_hostent->h_name) { - net.s_addr = p_monitor->p_peek[connection_index]->local_addr; - snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net)); - break; - } - snprintf(p_buffer, buffer_size, "%s", p_hostent->h_name); + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 1); break; case LOCALPORT: @@ -444,16 +551,8 @@ int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, case LOCALSERVICE: - p_servent = getservbyport( - htons(p_monitor->p_peek[connection_index]->local_port), "tcp"); - /* if no service name found for the port, - * just use the port number. */ - if (!p_servent || !p_servent->s_name) { - snprintf(p_buffer, buffer_size, "%d", - p_monitor->p_peek[connection_index]->local_port); - break; - } - snprintf(p_buffer, buffer_size, "%s", p_servent->s_name); + sa.sin_port=htons(p_monitor->p_peek[connection_index]->local_port); + getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST); break; default: @@ -528,54 +627,17 @@ void destroy_tcp_port_monitor_collection( void update_tcp_port_monitor_collection( tcp_port_monitor_collection_t *p_collection) { - FILE *fp; - char buf[256]; - tcp_connection_t conn; - unsigned long inode, uid, state; - if (!p_collection) { return; } + process_file(p_collection, "/proc/net/tcp"); + process_file(p_collection, "/proc/net/tcp6"); + /* age the connections in all port monitors. */ for_each_tcp_port_monitor_in_collection(p_collection, &age_tcp_port_monitor, NULL); - /* read tcp data from /proc/net/tcp */ - if ((fp = fopen("/proc/net/tcp", "r")) == NULL) { - return; - } - - /* ignore field name line */ - fgets(buf, 255, fp); - - /* read all tcp connections */ - while (fgets(buf, sizeof(buf), fp) != NULL) { - - if (sscanf(buf, - "%*d: %x:%hx %x:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu", - (unsigned int *) &conn.local_addr, &conn.local_port, - (unsigned int *) &conn.remote_addr, &conn.remote_port, - (unsigned long *) &state, (unsigned long *) &uid, - (unsigned long *) &inode) != 7) { - fprintf(stderr, "/proc/net/tcp: bad file format\n"); - } - - if ((inode == 0) || (state != TCP_ESTABLISHED)) { - continue; - } - - /* build hash key */ - g_sprintf(conn.key, "%08X:%04X %08X:%04X", conn.local_addr, - conn.local_port, conn.remote_addr, conn.remote_port); - - /* show the connection to each port monitor. */ - for_each_tcp_port_monitor_in_collection(p_collection, - &show_connection_to_tcp_port_monitor, (void *) &conn); - } - - fclose(fp); - /* rebuild the connection peek tables of all monitors * so clients can peek in O(1) time */ for_each_tcp_port_monitor_in_collection(p_collection,