+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);
+}
+