diff options
Diffstat (limited to 'client/connection.c')
| -rw-r--r-- | client/connection.c | 292 | 
1 files changed, 292 insertions, 0 deletions
diff --git a/client/connection.c b/client/connection.c new file mode 100644 index 0000000..fb04e41 --- /dev/null +++ b/client/connection.c @@ -0,0 +1,292 @@ +#include <gtk/gtk.h> +#include <netdb.h> +#include <time.h> +#include <linux/ip.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <string.h> +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "connection.h" +#include "interface.h" +#include "support.h" +#include "daemon.h" +#include "mainwin.h" +#include "log.h" +#include "rulewin.h" +#include "ruleset.h" + +#include "format.h" + +GSList *queued_conn_list = NULL; +guint queued_conn_count = 0; + +conn_info_t *conn_current = NULL; + +guint total_conn_count = 0; + +static GtkWidget* get_window(void) { +    static GtkWidget *cw = NULL; + +    if (!cw) { +        GdkColor color; +        cw = create_connection_window(); +        gdk_color_parse ("black", &color); +        gtk_widget_modify_bg(lookup_widget(cw, "title_eventbox"), GTK_STATE_NORMAL, &color); +    } + +    return cw; +} + +void conn_fill_info(conn_info_t *c) { +    struct protoent *pe = NULL; +    struct servent *se = NULL; +    static gchar txt[256]; +    char *t; + +    c->from_string = g_strdup(format_hostname(c->src_ip_address)); +    c->to_string = g_strdup(format_hostname(c->dst_ip_address)); + +    c->port_string = NULL; +     +    if (c->protocol == IPPROTO_ICMP) +        snprintf(t = txt, sizeof(txt), "ICMP %s", icmp_type_str(c->icmp_type)); +    else if (c->protocol == IPPROTO_TCP || c->protocol == IPPROTO_UDP) { +        gchar *pp = c->protocol == IPPROTO_TCP ? "TCP" : "UDP"; + +        if ((se = getservbyport(htons(c->port), NULL))) +            snprintf(t = txt, sizeof(txt), "%s Port#%u (%s)", pp, c->port, c->port_string = g_strdup(se->s_name)); +        else +            snprintf(t = txt, sizeof(txt), "%s Port#%u", pp, c->port); +    } else if ((pe = getprotobynumber(c->protocol))) +        t = pe->p_name; +    else +        t = "Unknown"; + +    c->type_string = g_strdup(t); + +    strncpy(txt, ctime(&c->timestamp), sizeof(txt)); +    if (txt[0]) // remove trailing \n +        txt[strlen(txt)-1] = 0; +    c->timestamp_string = g_strdup(txt); +} + +void conn_free(conn_info_t*c) { +    g_free(c->from_string); +    g_free(c->to_string); +    g_free(c->type_string); +    g_free(c->port_string); +    g_free(c->timestamp_string); +    g_free(c); +} + +void conn_set_sticky() { +    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(get_main_window(), "sticky_button")))) +        gtk_window_stick(GTK_WINDOW(get_window())); +    else +        gtk_window_unstick(GTK_WINDOW(get_window())); +} + +void conn_show_info(conn_info_t *c) { +    GtkWidget* window; +    GtkTooltips *tooltips; +    static gchar txt[256]; +    static gchar fn[PATH_MAX]; +    char *d; +    GdkPixbuf *pixbuf; + +    window = get_window(); + +    snprintf(txt, sizeof(txt), "<span size=\"xx-large\" color=\"white\"><b>%s Connection</b></span>", c->direction == DIR_INCOMING ? "Incoming" : (c->direction == DIR_OUTGOING ? "Outgoing" : "Passing")); +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "title_label")), txt); + +    gtk_image_set_from_stock(GTK_IMAGE(lookup_widget(window, "title_image")), c->direction == DIR_INCOMING ? GTK_STOCK_GO_FORWARD : (c->direction == DIR_OUTGOING ? GTK_STOCK_GO_BACK : GTK_STOCK_NEW), GTK_ICON_SIZE_DND); + +    if (c->direction == DIR_INCOMING) +        d = c->device_in; +    else if (c->direction == DIR_OUTGOING) +        d = c->device_out; +    else +        snprintf(d = txt, sizeof(txt), "%s => %s", c->device_in, c->device_out); +     +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "interface_label")), d); +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "from_label")), c->from_string); +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "to_label")), c->to_string); +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "type_label")), c->type_string); + +    tooltips = GTK_TOOLTIPS(lookup_widget(window, "tooltips")); +    gtk_tooltips_set_tip(tooltips, lookup_widget(window, "from_eventbox"), format_ip_address(c->src_ip_address), NULL); +    gtk_tooltips_set_tip(tooltips, lookup_widget(window, "to_eventbox"), format_ip_address(c->dst_ip_address), NULL); +    gtk_tooltips_set_delay(tooltips, 0); +     +    snprintf(txt, sizeof(txt), "%lu", c->id); +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "id_label")), txt); + +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "time_label")), c->timestamp_string); + +    gtk_widget_set_sensitive(lookup_widget(window, "reject_button"), !c->broadcast); + +    gtk_label_set_label(GTK_LABEL(lookup_widget(window, "broadcast_label")), c->broadcast ? "Yes" : "No"); + +    gtk_image_set_from_stock(GTK_IMAGE(lookup_widget(window, "type_image")), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_DND); +    if (c->port_string) { +        snprintf(fn, sizeof(fn), "%s/pixmaps/%s.png", "..", c->port_string); +         +        if ((pixbuf = gdk_pixbuf_new_from_file(fn, NULL))) { +            gtk_image_set_from_pixbuf(GTK_IMAGE(lookup_widget(window, "type_image")), pixbuf); +            g_object_unref(pixbuf); +        } +    } +         +    gtk_widget_show_all(window); +    conn_set_sticky(); + +    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); +    gtk_window_present(GTK_WINDOW(window)); +} + + +void conn_show_count() { +    static gchar txt[256]; +    gchar *p; + +    if (queued_conn_count) +        snprintf(p = txt, sizeof(txt), "Connection (%i in queue)", queued_conn_count); +    else +        p = "Connection"; + +    gtk_window_set_title(GTK_WINDOW(get_window()), p); +} + +void conn_pop() { +    g_assert(!conn_current && queued_conn_list); + +    conn_current = (conn_info_t*) queued_conn_list->data; +    queued_conn_list = g_slist_remove(queued_conn_list, conn_current); +    queued_conn_count--; +    conn_show_count(); +    conn_show_info(conn_current); +} + +static conn_info_t* ipq2ci(ipq_packet_msg_t* m) { +    conn_info_t* ci = g_new0(conn_info_t, 1); +    struct iphdr *ip = (struct iphdr*) m->payload; + +    ci->id = m->packet_id; +    ci->direction = (m->indev_name[0] && m->outdev_name[0]) ? DIR_PASSING : (m->indev_name[0] ? DIR_INCOMING : DIR_OUTGOING); +    strncpy(ci->device_in, m->indev_name, IFNAMSIZ); ci->device_in[IFNAMSIZ] = 0; +    strncpy(ci->device_out, m->outdev_name, IFNAMSIZ); ci->device_out[IFNAMSIZ] = 0; + +    if (m->data_len < sizeof(struct iphdr)) { +        g_message("Packet too small for complete IP header"); +        g_free(ci); +        return NULL; +    } +     +    ci->src_ip_address = ip->saddr; +    ci->dst_ip_address = ip->daddr; +    ci->protocol = ip->protocol; + +    ci->broadcast = is_broadcast(ci->dst_ip_address); + +    ci->port = 0; +    ci->icmp_type = 0; +     +    if (ci->protocol == IPPROTO_TCP || ci->protocol == IPPROTO_UDP) +        ci->port = ntohs(*((guint16*) (m->payload + sizeof(struct iphdr) + 2))); +    else if (ci->protocol == IPPROTO_ICMP) +        ci->icmp_type = *((guint8*) (m->payload + sizeof(struct iphdr))); +     +    ci->timestamp = m->timestamp_sec; +     +    return ci; +} + +guint verdict2nr(verdict_t v) { +    switch (v) { +        case VERDICT_REJECT : return ruleset.icmp_reject_code+2; +        case VERDICT_QUERY :  +        case VERDICT_DROP : return 0;  +        case VERDICT_ACCEPT : return 1; +    } + +    return 0; +} + +void conn_new(ipq_packet_msg_t *m) { +    conn_info_t *ci; + +    if ((ci = ipq2ci(m))) { +        queued_conn_list = g_slist_append(queued_conn_list, ci); +        queued_conn_count++; +        total_conn_count++; +        conn_fill_info(ci); +        log_widget_append(ci); +         +        conn_show_count(); +         +        if (!conn_current) +            conn_pop(); +         +        mainwin_update_status_bar(); +    } +} + +void conn_verdict(verdict_t v) { +    if (!conn_current) +        return; + +    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(get_window(), "queue_check_button")))) { +        for (;;) { +            verdict_t _v; + +            if (v == VERDICT_REJECT && conn_current->broadcast) +                _v = VERDICT_DROP; +            else +                _v = v; + +            daemon_verdict(conn_current->id, verdict2nr(_v)); +            log_widget_verdict(conn_current, _v); +            conn_free(conn_current); + +            if (!queued_conn_list) +                break; + +            conn_current = (conn_info_t*) queued_conn_list->data; +            queued_conn_list = g_slist_remove(queued_conn_list, conn_current); +            queued_conn_count--; +        } + +         +        conn_current = NULL; +        g_assert(queued_conn_count == 0); + +        gtk_widget_hide(get_window()); +         +    } else { + +        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(get_window(), "rule_check_button")))) { +            rule_t *rule; + +            rule = rule_new_from_conn_info(conn_current); +            rule->verdict = v; +            rulewin_show(rule); +            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(get_window(), "rule_check_button")), FALSE); +        } +     +        daemon_verdict(conn_current->id, verdict2nr(v)); +        log_widget_verdict(conn_current, v); +        conn_free(conn_current); +        conn_current = NULL; +         +        if (!queued_conn_list) +            gtk_widget_hide(get_window()); +        else +            conn_pop(); +    } + +    mainwin_update_status_bar(); +}  | 
