]> git.meshlink.io Git - catta/commitdiff
initial commit
authorLennart Poettering <lennart@poettering.net>
Thu, 23 Dec 2004 16:08:40 +0000 (16:08 +0000)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Dec 2004 16:08:40 +0000 (16:08 +0000)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@3 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe

17 files changed:
Makefile [new file with mode: 0644]
address.c [new file with mode: 0644]
address.h [new file with mode: 0644]
cache.h [new file with mode: 0644]
flx.h [new file with mode: 0644]
iface.c [new file with mode: 0644]
iface.h [new file with mode: 0644]
local-addr.h [new file with mode: 0644]
local.c [new file with mode: 0644]
main.c [new file with mode: 0644]
netlink.c [new file with mode: 0644]
netlink.h [new file with mode: 0644]
server.c [new file with mode: 0644]
server.h [new file with mode: 0644]
socket.h [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f5af624
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+CFLAGS=-g -O0 -Wall -W -pipe $(shell pkg-config --cflags glib-2.0)
+LIBS=$(shell pkg-config --libs glib-2.0)
+
+flexmdns: main.o iface.o netlink.o server.o address.o util.o local.o
+       $(CC) -o $@ $^ $(LIBS)
+
+*.o: *.h
+
+clean:
+       rm -f *.o flexmdns
diff --git a/address.c b/address.c
new file mode 100644 (file)
index 0000000..633f40e
--- /dev/null
+++ b/address.c
@@ -0,0 +1,80 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "address.h"
+
+guint flx_address_get_size(const flxAddress *a) {
+    g_assert(a);
+
+    if (a->family == AF_INET)
+        return 4;
+    else if (a->family == AF_INET6)
+        return 16;
+
+    return 0;
+}
+
+gint flx_address_cmp(const flxAddress *a, const flxAddress *b) {
+    g_assert(a);
+    g_assert(b);
+    
+    if (a->family != b->family)
+        return -1;
+
+    return memcmp(a, b, flx_address_get_size(a));
+}
+
+gchar *flx_address_snprint(char *s, guint length, const flxAddress *a) {
+    g_assert(s);
+    g_assert(length);
+    g_assert(a);
+    return (gchar*) inet_ntop(a->family, a->data, s, length);
+}
+
+gchar* flx_reverse_lookup_name(const flxAddress *a) {
+    g_assert(a);
+
+    if (a->family == AF_INET) {
+        guint32 n = ntohl(a->ipv4.address);
+        return g_strdup_printf("%u.%u.%u.%u.in-addr.arpa", n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24);
+    } else if (a->family == AF_INET6) {
+        
+        return g_strdup_printf("%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.int",
+                               a->ipv6.address[15] & 0xF,
+                               a->ipv6.address[15] >> 4,
+                               a->ipv6.address[14] & 0xF,
+                               a->ipv6.address[14] >> 4,
+                               a->ipv6.address[13] & 0xF,
+                               a->ipv6.address[13] >> 4,
+                               a->ipv6.address[12] & 0xF,
+                               a->ipv6.address[12] >> 4,
+                               a->ipv6.address[11] & 0xF,
+                               a->ipv6.address[11] >> 4,
+                               a->ipv6.address[10] & 0xF,
+                               a->ipv6.address[10] >> 4,
+                               a->ipv6.address[9] & 0xF,
+                               a->ipv6.address[9] >> 4,
+                               a->ipv6.address[8] & 0xF,
+                               a->ipv6.address[8] >> 4,
+                               a->ipv6.address[7] & 0xF,
+                               a->ipv6.address[7] >> 4,
+                               a->ipv6.address[6] & 0xF,
+                               a->ipv6.address[6] >> 4,
+                               a->ipv6.address[5] & 0xF,
+                               a->ipv6.address[5] >> 4,
+                               a->ipv6.address[4] & 0xF,
+                               a->ipv6.address[4] >> 4,
+                               a->ipv6.address[3] & 0xF,
+                               a->ipv6.address[3] >> 4,
+                               a->ipv6.address[2] & 0xF,
+                               a->ipv6.address[2] >> 4,
+                               a->ipv6.address[1] & 0xF,
+                               a->ipv6.address[1] >> 4,
+                               a->ipv6.address[0] & 0xF,
+                               a->ipv6.address[0] >> 4);
+    }
+
+    return NULL;
+}
diff --git a/address.h b/address.h
new file mode 100644 (file)
index 0000000..28ccb4b
--- /dev/null
+++ b/address.h
@@ -0,0 +1,31 @@
+#ifndef fooaddresshfoo
+#define fooaddresshfoo
+
+#include <glib.h>
+
+typedef struct {
+    guint32 address;
+} flxIPv4Address;
+
+typedef struct {
+    guint8 address[16];
+} flxIPv6Address;
+
+typedef struct {
+    guint family;
+
+    union {
+        flxIPv6Address ipv6;
+        flxIPv4Address ipv4;
+        guint8 data[0];
+    };
+} flxAddress;
+
+guint flx_address_get_size(const flxAddress *a);
+gint flx_address_cmp(const flxAddress *a, const flxAddress *b);
+
+gchar *flx_address_snprint(char *s, guint length, const flxAddress *a);
+
+gchar* flx_reverse_lookup_name(const flxAddress *a);
+
+#endif
diff --git a/cache.h b/cache.h
new file mode 100644 (file)
index 0000000..f2014df
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,22 @@
+#ifndef foocachehfoo
+#define foocachehfoo
+
+typedef enum {
+    FLX_CACHE_VALID,
+    FLX_CACHE_EXPIRY1,
+    FLX_CACHE_EXPIRY2,
+    FLX_CACHE_EXPIRY3
+        
+} flxCacheEntry;
+
+typedef struct flxCacheEntry {
+    GTimeVal timestamp;
+    flxRecord rr;
+    gint interface;
+    flxAddress origin;
+
+    flxCacheEntryState state;
+    
+} flxCacheEntry;
+
+#endif
diff --git a/flx.h b/flx.h
new file mode 100644 (file)
index 0000000..8c46673
--- /dev/null
+++ b/flx.h
@@ -0,0 +1,61 @@
+#ifndef fooflxhfoo
+#define fooflxhfoo
+
+#include <stdio.h>
+#include <glib.h>
+
+struct _flxServer;
+typedef struct _flxServer flxServer;
+
+typedef struct  {
+    gchar *name;
+    guint16 type;
+    guint16 class;
+    gpointer data;
+    guint16 size;
+    guint32 ttl;
+} flxRecord;
+
+typedef struct {
+    gchar *name;
+    guint16 type;
+    guint16 class;
+} flxQuery;
+
+flxServer *flx_server_new(GMainContext *c);
+void flx_server_free(flxServer* s);
+
+gint flx_server_get_next_id(flxServer *s);
+
+void flx_server_add_rr(flxServer *s, gint id, gint interface, const flxRecord *rr);
+void flx_server_add(flxServer *s, gint id, const gchar *name, gint interface, guint16 type, gconstpointer data, guint size);
+
+void flx_server_remove(flxServer *s, gint id);
+
+const flxRecord *flx_server_iterate(flxServer *s, gint id, void **state);
+
+flxRecord *flx_record_copy_normalize(flxRecord *ret_dest, const flxRecord*src);
+
+void flx_server_dump(flxServer *s, FILE *f);
+
+struct _flxLocalAddrSource;
+typedef struct _flxLocalAddrSource flxLocalAddrSource;
+
+flxLocalAddrSource *flx_local_addr_source_new(flxServer *s);
+void flx_local_addr_source_free(flxLocalAddrSource *l);
+
+#define FLX_DNS_TYPE_A 0x01
+#define FLX_DNS_TYPE_AAAA 0x1C
+#define FLX_DNS_TYPE_PTR 0x0C
+#define FLX_DNS_TYPE_HINFO 0x0D
+#define FLX_DNS_TYPE_CNAME 0x05
+#define FLX_DNS_TYPE_NS 0x02
+#define FLX_DNS_TYPE_SOA 0x06
+#define FLX_DNS_TYPE_MX 0x0F
+#define FLX_DNS_TYPE_TXT 0x10
+
+#define FLX_DNS_CLASS_IN 0x01
+
+#define FLX_DEFAULT_TTL (120*60)
+
+#endif
diff --git a/iface.c b/iface.c
new file mode 100644 (file)
index 0000000..01f0637
--- /dev/null
+++ b/iface.c
@@ -0,0 +1,446 @@
+#include <string.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <errno.h>
+
+#include "iface.h"
+#include "netlink.h"
+
+typedef struct _interface_callback_info {
+    void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata);
+    gpointer userdata;
+    struct _interface_callback_info *next;
+} interface_callback_info;
+
+typedef struct _address_callback_info {
+    void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *i, gpointer userdata);
+    gpointer userdata;
+    struct _address_callback_info *next;
+} address_callback_info;
+
+struct _flxInterfaceMonitor {
+    flxNetlink *netlink;
+    GHashTable *hash_table;
+    interface_callback_info *interface_callbacks;
+    address_callback_info *address_callbacks;
+    flxInterface *interfaces;
+    guint query_addr_seq, query_link_seq;
+    enum { LIST_IFACE, LIST_ADDR, LIST_DONE } list;
+};
+
+static void run_interface_callbacks(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i) {
+    interface_callback_info *c;
+    g_assert(m);
+    g_assert(i);
+
+    for (c = m->interface_callbacks; c; c = c->next) {
+        g_assert(c->callback);
+        c->callback(m, change, i, c->userdata);
+    }
+}
+
+static void run_address_callbacks(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a) {
+    address_callback_info *c;
+    g_assert(m);
+    g_assert(a);
+
+    for (c = m->address_callbacks; c; c = c->next) {
+        g_assert(c->callback);
+        c->callback(m, change, a, c->userdata);
+    }
+}
+
+static void free_address(flxInterfaceMonitor *m, flxInterfaceAddress *a) {
+    g_assert(m);
+    g_assert(a);
+    g_assert(a->interface);
+
+    if (a->prev)
+        a->prev->next = a->next;
+    else
+        a->interface->addresses = a->next;
+
+    if (a->next)
+        a->next->prev = a->prev;
+
+    g_free(a);
+}
+
+static void free_interface(flxInterfaceMonitor *m, flxInterface *i) {
+    g_assert(m);
+    g_assert(i);
+
+    while (i->addresses)
+        free_address(m, i->addresses);
+
+    if (i->prev)
+        i->prev->next = i->next;
+    else
+        m->interfaces = i->next;
+
+    if (i->next)
+        i->next->prev = i->prev;
+
+    g_hash_table_remove(m->hash_table, &i->index);
+    
+    g_free(i->name);
+    g_free(i);
+}
+
+static flxInterfaceAddress* get_address(flxInterfaceMonitor *m, flxInterface *i, const flxAddress *raddr) {
+    flxInterfaceAddress *ia;
+    
+    g_assert(m);
+    g_assert(i);
+    g_assert(raddr);
+
+    for (ia = i->addresses; ia; ia = ia->next) {
+        if (flx_address_cmp(&ia->address, raddr) == 0)
+            return ia;
+    }
+
+    return NULL;
+}
+
+static int netlink_list_items(flxNetlink *nl, guint16 type, guint *ret_seq) {
+    struct nlmsghdr *n;
+    struct rtgenmsg *gen;
+    guint8 req[1024];
+    
+    memset(&req, 0, sizeof(req));
+    n = (struct nlmsghdr*) req;
+    n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+    n->nlmsg_type = type;
+    n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+    n->nlmsg_pid = 0;
+
+    gen = NLMSG_DATA(n);
+    memset(gen, 0, sizeof(struct rtgenmsg));
+    gen->rtgen_family = AF_UNSPEC;
+
+    return flx_netlink_send(nl, n, ret_seq);
+}
+
+static void callback(flxNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
+    flxInterfaceMonitor *m = userdata;
+    
+    g_assert(m);
+    g_assert(n);
+    g_assert(m->netlink == nl);
+
+    if (n->nlmsg_type == RTM_NEWLINK) {
+        struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
+        flxInterface *i;
+        struct rtattr *a = NULL;
+        size_t l;
+        int changed;
+
+        if (ifinfomsg->ifi_family != AF_UNSPEC)
+            return;
+
+        if ((i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
+            changed = 1;
+        else {
+            i = g_new0(flxInterface, 1);
+            i->index = ifinfomsg->ifi_index;
+            i->addresses = NULL;
+            if ((i->next = m->interfaces))
+                i->next->prev = i;
+            m->interfaces = i;
+            i->prev = NULL;
+            g_hash_table_insert(m->hash_table, &i->index, i);
+            changed = 0;
+        }
+        
+        i->flags = ifinfomsg->ifi_flags;
+
+        l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+        a = IFLA_RTA(ifinfomsg);
+
+        while (RTA_OK(a, l)) {
+            switch(a->rta_type) {
+                case IFLA_IFNAME:
+                    g_free(i->name);
+                    i->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
+                    break;
+                    
+                default:
+                    ;
+            }
+
+            a = RTA_NEXT(a, l);
+        }
+
+        run_interface_callbacks(m, changed ? FLX_INTERFACE_CHANGE : FLX_INTERFACE_NEW, i);
+        
+    } else if (n->nlmsg_type == RTM_DELLINK) {
+        struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
+        flxInterface *i;
+        flxInterfaceAddress *a;
+
+        if (ifinfomsg->ifi_family != AF_UNSPEC)
+            return;
+        
+        if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
+            return;
+
+        for (a = i->addresses; a; a = a->next)
+            run_address_callbacks(m, FLX_INTERFACE_REMOVE, a);
+
+        run_interface_callbacks(m, FLX_INTERFACE_REMOVE, i);
+
+        free_interface(m, i);
+        
+    } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
+
+        struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
+        flxInterface *i;
+        struct rtattr *a = NULL;
+        size_t l;
+        int changed;
+        flxAddress raddr;
+        int raddr_valid = 0;
+
+        if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
+            return;
+
+        if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifaddrmsg->ifa_index)))
+            return;
+
+        raddr.family = ifaddrmsg->ifa_family;
+
+        l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+        a = IFA_RTA(ifaddrmsg);
+
+        while (RTA_OK(a, l)) {
+            switch(a->rta_type) {
+                case IFA_ADDRESS:
+                    if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
+                        (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4))
+                        return;
+
+                    memcpy(raddr.data, RTA_DATA(a), RTA_PAYLOAD(a));
+                    raddr_valid = 1;
+                    break;
+                    
+                default:
+                    ;
+            }
+
+            a = RTA_NEXT(a, l);
+        }
+
+        if (!raddr_valid)
+            return;
+
+        if (n->nlmsg_type == RTM_NEWADDR) {
+            flxInterfaceAddress *addr;
+            
+            if ((addr = get_address(m, i, &raddr)))
+                changed = 1;
+            else {
+                addr = g_new0(flxInterfaceAddress, 1);
+                addr->address = raddr;
+                addr->interface = i;
+                if ((addr->next = i->addresses))
+                    addr->next->prev = addr;
+                i->addresses = addr;
+                addr->prev = NULL;
+                
+                changed = 0;
+            }
+            
+            addr->flags = ifaddrmsg->ifa_flags;
+            addr->scope = ifaddrmsg->ifa_scope;
+            
+            run_address_callbacks(m, changed ? FLX_INTERFACE_CHANGE : FLX_INTERFACE_NEW, addr);
+        } else {
+            flxInterfaceAddress *addr;
+            
+            if (!(addr = get_address(m, i, &raddr)))
+                return;
+
+            run_address_callbacks(m, FLX_INTERFACE_REMOVE, addr);
+            free_address(m, addr);
+        }
+                
+    } else if (n->nlmsg_type == NLMSG_DONE) {
+
+        if (m->list == LIST_IFACE) {
+            m->list = LIST_DONE;
+            
+            if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0) {
+                g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
+            } else
+                m->list = LIST_ADDR;
+        } else
+            m->list = LIST_DONE;
+        
+    } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
+        struct nlmsgerr *e = NLMSG_DATA (n);
+                    
+        if (e->error)
+            g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
+    }
+}
+
+flxInterfaceMonitor *flx_interface_monitor_new(GMainContext *c) {
+    flxInterfaceMonitor *m = NULL;
+
+    m = g_new0(flxInterfaceMonitor, 1);
+    if (!(m->netlink = flx_netlink_new(c, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
+        goto fail;
+
+    m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
+    m->interface_callbacks = NULL;
+    m->address_callbacks = NULL;
+    m->interfaces = NULL;
+
+    if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
+        goto fail;
+
+    m->list = LIST_IFACE;
+    
+    return m;
+
+fail:
+    flx_interface_monitor_free(m);
+    return NULL;
+}
+
+void flx_interface_monitor_free(flxInterfaceMonitor *m) {
+    g_assert(m);
+
+    if (m->netlink)
+        flx_netlink_free(m->netlink);
+
+    if (m->hash_table)
+        g_hash_table_destroy(m->hash_table);
+
+    while (m->interface_callbacks) {
+        interface_callback_info *c = m->interface_callbacks;
+        m->interface_callbacks = c->next;
+        g_free(c);
+    }
+
+    while (m->address_callbacks) {
+        address_callback_info *c = m->address_callbacks;
+        m->address_callbacks = c->next;
+        g_free(c);
+    }
+    
+    g_free(m);
+}
+
+
+const flxInterface* flx_interface_monitor_get_interface(flxInterfaceMonitor *m, gint index) {
+    g_assert(m);
+    g_assert(index > 0);
+
+    return g_hash_table_lookup(m->hash_table, &index);
+}
+
+void flx_interface_monitor_add_interface_callback(
+    flxInterfaceMonitor *m,
+    void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata),
+    gpointer userdata) {
+    
+    interface_callback_info *info;
+    
+    g_assert(m);
+    g_assert(callback);
+
+    info = g_new(interface_callback_info, 1);
+    info->callback = callback;
+    info->userdata = userdata;
+    info->next = m->interface_callbacks;
+    m->interface_callbacks = info;
+}
+
+void flx_interface_monitor_remove_interface_callback(
+    flxInterfaceMonitor *m,
+    void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata),
+    gpointer userdata) {
+
+    interface_callback_info *info, *prev;
+
+    g_assert(m);
+    g_assert(callback);
+
+    info = m->interface_callbacks;
+    prev = NULL;
+    
+    while (info) {
+        if (info->callback == callback && info->userdata == userdata) {
+            interface_callback_info *c = info;
+            
+            if (prev)
+                prev->next = c->next;
+            else
+                m->interface_callbacks = c->next;
+            
+            info = c->next;
+            g_free(c);
+        } else {
+            prev = info;
+            info = info->next;
+        }
+    }
+}
+
+void flx_interface_monitor_add_address_callback(
+    flxInterfaceMonitor *m,
+    void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata),
+    gpointer userdata) {
+
+    address_callback_info *info;
+    
+    g_assert(m);
+    g_assert(callback);
+
+    info = g_new(address_callback_info, 1);
+    info->callback = callback;
+    info->userdata = userdata;
+    info->next = m->address_callbacks;
+    m->address_callbacks = info;
+}
+
+
+void flx_interface_monitor_remove_address_callback(
+    flxInterfaceMonitor *m,
+    void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata),
+    gpointer userdata) {
+
+    address_callback_info *info, *prev;
+
+    g_assert(m);
+    g_assert(callback);
+
+    info = m->address_callbacks;
+    prev = NULL;
+    
+    while (info) {
+        if (info->callback == callback && info->userdata == userdata) {
+            address_callback_info *c = info;
+            
+            if (prev)
+                prev->next = c->next;
+            else
+                m->address_callbacks = c->next;
+            
+            info = c->next;
+            g_free(c);
+        } else {
+            prev = info;
+            info = info->next;
+        }
+    }
+
+}
+
+const flxInterface* flx_interface_monitor_get_first(flxInterfaceMonitor *m) {
+    g_assert(m);
+    return m->interfaces;
+}
diff --git a/iface.h b/iface.h
new file mode 100644 (file)
index 0000000..3476645
--- /dev/null
+++ b/iface.h
@@ -0,0 +1,63 @@
+#ifndef fooifacehfoo
+#define fooifacehfoo
+
+#include <glib.h>
+
+#include "address.h"
+
+struct _flxInterfaceMonitor;
+typedef struct _flxInterfaceMonitor flxInterfaceMonitor;
+
+struct _flxInterfaceAddress;
+typedef struct _flxInterfaceAddress flxInterfaceAddress;
+
+struct _flxInterface;
+typedef struct _flxInterface flxInterface;
+
+struct _flxInterface {
+    gchar *name;
+    gint index;
+    guint flags;
+
+    flxInterfaceAddress *addresses;
+    flxInterface *next, *prev;
+};
+
+struct _flxInterfaceAddress {
+    guchar flags;
+    guchar scope;
+    flxAddress address;
+    
+    flxInterface *interface;
+    flxInterfaceAddress *next, *prev;
+};
+
+typedef enum { FLX_INTERFACE_NEW, FLX_INTERFACE_REMOVE, FLX_INTERFACE_CHANGE } flxInterfaceChange;
+
+flxInterfaceMonitor *flx_interface_monitor_new(GMainContext *c);
+void flx_interface_monitor_free(flxInterfaceMonitor *m);
+
+const flxInterface* flx_interface_monitor_get_interface(flxInterfaceMonitor *m, gint index);
+const flxInterface* flx_interface_monitor_get_first(flxInterfaceMonitor *m);
+
+void flx_interface_monitor_add_interface_callback(
+    flxInterfaceMonitor *m,
+    void (*cb)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata),
+    gpointer userdata);
+
+void flx_interface_monitor_remove_interface_callback(
+    flxInterfaceMonitor *m,
+    void (*cb)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata),
+    gpointer userdata);
+
+void flx_interface_monitor_add_address_callback(
+    flxInterfaceMonitor *m,
+    void (*cb)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata),
+    gpointer userdata);
+
+void flx_interface_monitor_remove_address_callback(
+    flxInterfaceMonitor *m,
+    void (*cb)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata),
+    gpointer userdata);
+
+#endif
diff --git a/local-addr.h b/local-addr.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/local.c b/local.c
new file mode 100644 (file)
index 0000000..4a63274
--- /dev/null
+++ b/local.c
@@ -0,0 +1,148 @@
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <net/if.h>
+
+#include "flx.h"
+#include "server.h"
+#include "util.h"
+#include "iface.h"
+
+typedef struct {
+    flxServer *server;
+    flxAddress address;
+    gint id;
+} addr_info;
+
+struct _flxLocalAddrSource {
+    flxServer *server;
+    GHashTable *hash_table;
+    gint hinfo_id;
+    gchar *hostname;
+};
+
+static gboolean addr_equal(gconstpointer a, gconstpointer b) {
+    return flx_address_cmp(a, b) == 0;
+}
+
+static guint hash(gconstpointer v, guint l) {
+    const guint8 *c;
+    guint hash = 0;
+
+    for (c = v; l > 0; c++, l--)
+        hash = 31 * hash + *c;
+
+    return hash;
+}
+
+static guint addr_hash(gconstpointer v) {
+    const flxAddress *a = v;
+
+    return hash(a, sizeof(a->family) + flx_address_get_size(a));
+}
+
+static void add_addr(flxLocalAddrSource *l, const flxInterfaceAddress *a) {
+    addr_info *ai;
+    gchar *r;
+    g_assert(l);
+    g_assert(a);
+
+    if (!(a->interface->flags & IFF_UP) ||
+        !(a->interface->flags & IFF_RUNNING) ||
+        (a->interface->flags & IFF_LOOPBACK))
+        return;
+
+    if (a->scope != RT_SCOPE_UNIVERSE)
+        return;
+    
+    ai = g_new(addr_info, 1);
+    ai->server = l->server;
+    ai->address = a->address;
+    
+    ai->id = flx_server_get_next_id(l->server);
+    flx_server_add(l->server, ai->id, l->hostname,
+                   ai->address.family == AF_INET ? FLX_DNS_TYPE_A : FLX_DNS_TYPE_AAAA,
+                   ai->address.data, flx_address_get_size(&ai->address));
+
+    r = flx_reverse_lookup_name(&ai->address);
+    flx_server_add(l->server, ai->id, r,
+                   FLX_DNS_TYPE_PTR,
+                   l->hostname, strlen(l->hostname)+1);
+    g_free(r);
+
+    g_hash_table_replace(l->hash_table, &ai->address, ai);
+
+}
+
+/* Called whenever a new address becomes available, is changed or removed on the local machine */
+static void addr_callback(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata) {
+    flxLocalAddrSource *l = userdata;
+    g_assert(m);
+    g_assert(a);
+    g_assert(l);
+
+    if (change == FLX_INTERFACE_REMOVE)
+        g_hash_table_remove(l->hash_table, &a->address);
+    else if (change == FLX_INTERFACE_NEW)
+        add_addr(l, a);
+}
+
+static void destroy(gpointer data) {
+    addr_info *ai = data;
+    flx_server_remove(ai->server, ai->id);
+    g_free(ai);
+}
+
+flxLocalAddrSource *flx_local_addr_source_new(flxServer *s) {
+    flxLocalAddrSource *l;
+    const flxInterface *i;
+    struct utsname utsname;
+    gint length;
+    gchar *e, *hn, *c;
+
+    l = g_new(flxLocalAddrSource, 1);
+    l->server = s;
+    l->hash_table = g_hash_table_new_full(addr_hash, addr_equal, NULL, destroy);
+
+    hn = flx_get_host_name();
+    if ((e = strchr(hn, '.')))
+        *e = 0;
+
+    l->hostname = g_strdup_printf("%s.local.", hn);
+    g_free(hn);
+
+    flx_interface_monitor_add_address_callback(s->monitor, addr_callback, l);
+
+    for (i = flx_interface_monitor_get_first(s->monitor); i; i = i->next) {
+        flxInterfaceAddress *a;
+
+        for (a = i->addresses; a; a = a->next)
+            add_addr(l, a);
+    }
+
+    l->hinfo_id = flx_server_get_next_id(l->server);
+
+    uname(&utsname);
+    c = g_strdup_printf("%s%c%s%n", g_strup(utsname.machine), 0, g_strup(utsname.sysname), &length);
+    
+    flx_server_add(l->server, l->hinfo_id, l->hostname,
+                   FLX_DNS_TYPE_HINFO,
+                   c, length+1);
+    g_free(c);
+    
+    return l;
+}
+
+void flx_local_addr_source_free(flxLocalAddrSource *l) {
+    g_assert(l);
+    
+    flx_interface_monitor_remove_address_callback(l->server->monitor, addr_callback, l);
+    g_hash_table_destroy(l->hash_table);
+    flx_server_remove(l->server, l->hinfo_id);
+    g_free(l->hostname);
+    g_free(l);
+}
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..9bba48c
--- /dev/null
+++ b/main.c
@@ -0,0 +1,37 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "flx.h"
+
+static GMainLoop *loop = NULL;
+
+static gboolean timeout (gpointer data) {
+    g_main_loop_quit(loop);
+    return FALSE;
+}
+
+int main(int argc, char *argv[]) {
+    flxServer *flx;
+    flxLocalAddrSource *l;
+    guint32 ip;
+
+    flx = flx_server_new(NULL);
+
+    l = flx_local_addr_source_new(flx);
+    
+/*     ip = inet_addr("127.0.0.1"); */
+/*     flx_server_add(flx, flx_server_get_next_id(flx), "foo.local", FLX_DNS_TYPE_A, &ip, sizeof(ip)); */
+
+    g_timeout_add(1000, timeout, NULL);
+    
+    loop = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(loop);
+    g_main_loop_unref(loop);
+
+    flx_server_dump(flx, stdout);
+
+    flx_local_addr_source_free(l);
+    flx_server_free(flx);
+    return 0;
+}
diff --git a/netlink.c b/netlink.c
new file mode 100644 (file)
index 0000000..a04e6c9
--- /dev/null
+++ b/netlink.c
@@ -0,0 +1,154 @@
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "netlink.h"
+
+struct _flxNetlink {
+    GMainContext *context;
+    gint fd;
+    guint seq;
+    GPollFD poll_fd;
+    GSource *source;
+    void (*callback) (flxNetlink *nl, struct nlmsghdr *n, gpointer userdata);
+    gpointer userdata;
+    GSourceFuncs source_funcs;
+};
+
+static gboolean work(flxNetlink *nl) {
+    g_assert(nl);
+
+    for (;;) {
+        guint8 replybuf[64*1024];
+        ssize_t bytes;
+        struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
+
+        if ((bytes = recv(nl->fd, replybuf, sizeof(replybuf), MSG_DONTWAIT)) < 0) {
+
+            if (errno == EAGAIN || errno == EINTR)
+                break;
+
+            g_warning("NETLINK: recv() failed");
+            return FALSE;
+        }
+
+        if (nl->callback) {
+            for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+                if (!NLMSG_OK(p, bytes)) {
+                    g_warning("NETLINK: packet truncated");
+                    return FALSE;
+                }
+
+                nl->callback(nl, p, nl->userdata);
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+    g_assert(source);
+    g_assert(timeout);
+    
+    *timeout = -1;
+    return FALSE;
+}
+
+static gboolean check_func(GSource *source) {
+    flxNetlink* nl;
+    g_assert(source);
+
+    nl = *((flxNetlink**) (((guint8*) source) + sizeof(GSource)));
+    g_assert(nl);
+    
+    return nl->poll_fd.revents & G_IO_IN;
+}
+
+static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
+    flxNetlink* nl;
+    g_assert(source);
+
+    nl = *((flxNetlink**) (((guint8*) source) + sizeof(GSource)));
+    g_assert(nl);
+    
+    return work(nl);
+}
+
+flxNetlink *flx_netlink_new(GMainContext *context, guint32 groups, void (*cb) (flxNetlink *nl, struct nlmsghdr *n, gpointer userdata), gpointer userdata) {
+    int fd;
+    struct sockaddr_nl addr;
+    flxNetlink *nl;
+
+    g_assert(context);
+    g_assert(cb);
+
+    if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+        g_critical("NETLINK: socket(PF_NETLINK): %s", strerror(errno));
+        return NULL;
+    }
+    
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_groups = groups;
+    addr.nl_pid = getpid();
+
+    if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(fd);
+        g_critical("bind(): %s", strerror(errno));
+        return NULL;
+    }
+
+    nl = g_new(flxNetlink, 1);
+    nl->context = context;
+    g_main_context_ref(context);
+    nl->fd = fd;
+    nl->seq = 0;
+    nl->callback = cb;
+    nl->userdata = userdata;
+
+    memset(&nl->source_funcs, 0, sizeof(nl->source_funcs));
+    nl->source_funcs.prepare = prepare_func;
+    nl->source_funcs.check = check_func;
+    nl->source_funcs.dispatch = dispatch_func,
+
+    nl->source = g_source_new(&nl->source_funcs, sizeof(GSource) + sizeof(flxNetlink*));
+    *((flxNetlink**) (((guint8*) nl->source) + sizeof(GSource))) = nl;
+
+    memset(&nl->poll_fd, 0, sizeof(GPollFD));
+    nl->poll_fd.fd = fd;
+    nl->poll_fd.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
+    g_source_add_poll(nl->source, &nl->poll_fd);
+    
+    g_source_attach(nl->source, nl->context);
+    
+    return nl;
+}
+
+void flx_netlink_free(flxNetlink *nl) {
+    g_assert(nl);
+    
+    g_source_destroy(nl->source);
+    g_source_unref(nl->source);
+    g_main_context_unref(nl->context);
+    close(nl->fd);
+    g_free(nl);
+}
+
+int flx_netlink_send(flxNetlink *nl, struct nlmsghdr *m, guint *ret_seq) {
+    g_assert(nl);
+    g_assert(m);
+    
+    m->nlmsg_seq = nl->seq++;
+    m->nlmsg_flags |= NLM_F_ACK;
+
+    if (send(nl->fd, m, m->nlmsg_len, 0) < 0) {
+        g_warning("NETLINK: send(): %s\n", strerror(errno));
+        return -1;
+    }
+
+    if (ret_seq)
+        *ret_seq = m->nlmsg_seq;
+
+    return 0;
+}
diff --git a/netlink.h b/netlink.h
new file mode 100644 (file)
index 0000000..9efbce4
--- /dev/null
+++ b/netlink.h
@@ -0,0 +1,18 @@
+#ifndef foonetlinkhfoo
+#define foonetlinkhfoo
+
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include <glib.h>
+
+struct _flxNetlink;
+typedef struct _flxNetlink flxNetlink;
+
+flxNetlink *flx_netlink_new(GMainContext *c, guint32 groups, void (*cb) (flxNetlink *n, struct nlmsghdr *m, gpointer userdata), gpointer userdata);
+void flx_netlink_free(flxNetlink *n);
+
+int flx_netlink_send(flxNetlink *n, struct nlmsghdr *m, guint *ret_seq);
+
+#endif
diff --git a/server.c b/server.c
new file mode 100644 (file)
index 0000000..6ab1f1f
--- /dev/null
+++ b/server.c
@@ -0,0 +1,232 @@
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "server.h"
+#include "util.h"
+
+flxServer *flx_server_new(GMainContext *c) {
+    flxServer *s = g_new(flxServer, 1);
+
+    if (c) {
+        g_main_context_ref(c);
+        s->context = c;
+    } else
+        s->context = g_main_context_default();
+    
+    s->current_id = 1;
+    s->rrset_by_id = g_hash_table_new(g_int_hash, g_int_equal);
+    s->rrset_by_name = g_hash_table_new(g_str_hash, g_str_equal);
+    s->entries = NULL;
+
+    s->monitor = flx_interface_monitor_new(s->context);
+    
+    return s;
+}
+
+void flx_server_free(flxServer* s) {
+    g_assert(s);
+
+    flx_interface_monitor_free(s->monitor);
+
+    flx_server_remove(s, 0);
+    
+    g_hash_table_destroy(s->rrset_by_id);
+    g_hash_table_destroy(s->rrset_by_name);
+    g_main_context_unref(s->context);
+    g_free(s);
+}
+
+gint flx_server_get_next_id(flxServer *s) {
+    g_assert(s);
+
+    return s->current_id++;
+}
+
+void flx_server_add_rr(flxServer *s, gint id, gint interface, const flxRecord *rr) {
+    flxEntry *e;
+    g_assert(s);
+    g_assert(rr);
+    g_assert(rr->name);
+    g_assert(rr->data);
+    g_assert(rr->size);
+
+    e = g_new(flxEntry, 1);
+    flx_record_copy_normalize(&e->rr, rr);
+    e->id = id;
+    e->interface = interface;
+
+    /* Insert into linked list */
+    e->prev = NULL;
+    if ((e->next = s->entries))
+        e->next->prev = e;
+    s->entries = e;
+
+    /* Insert into hash table indexed by id */
+    e->prev_by_id = NULL;
+    if ((e->next_by_id = g_hash_table_lookup(s->rrset_by_id, &id)))
+        e->next_by_id->prev = e;
+    g_hash_table_replace(s->rrset_by_id, &e->id, e);
+
+    /* Insert into hash table indexed by name */
+    e->prev_by_name = NULL;
+    if ((e->next_by_name = g_hash_table_lookup(s->rrset_by_name, e->rr.name)))
+        e->next_by_name->prev = e;
+    g_hash_table_replace(s->rrset_by_name, e->rr.name, e);
+}
+
+void flx_server_add(flxServer *s, gint id, const gchar *name, gint interface, guint16 type, gconstpointer data, guint size) {
+    flxRecord rr;
+    g_assert(s);
+    g_assert(name);
+    g_assert(data);
+    g_assert(size);
+
+    rr.name = (gchar*) name;
+    rr.type = type;
+    rr.class = FLX_DNS_CLASS_IN;
+    rr.data = (gpointer) data;
+    rr.size = size;
+    rr.interface = interface;
+    rr.ttl = FLX_DEFAULT_TTL;
+    flx_server_add_rr(s, id, 0, &rr);
+}
+
+const flxRecord *flx_server_iterate(flxServer *s, gint id, void **state) {
+    flxEntry **e = (flxEntry**) state;
+    g_assert(s);
+    g_assert(e);
+
+    if (e)
+        *e = id > 0 ? (*e)->next_by_id : (*e)->next;
+    else
+        *e = id > 0 ? g_hash_table_lookup(s->rrset_by_id, &id) : s->entries;
+        
+    if (!*e)
+        return NULL;
+
+    return &(*e)->rr;
+}
+
+static void free_entry(flxServer*s, flxEntry *e) {
+    g_assert(e);
+
+    /* Remove from linked list */
+    if (e->prev)
+        e->prev->next = e->next;
+    else
+        s->entries = e->next;
+    
+    if (e->next)
+        e->next->prev = e->prev;
+
+    /* Remove from hash table indexed by id */
+    if (e->prev_by_id)
+        e->prev_by_id = e->next_by_id;
+    else {
+        if (e->next_by_id)
+            g_hash_table_replace(s->rrset_by_id, &e->next_by_id->id, e->next_by_id);
+        else
+            g_hash_table_remove(s->rrset_by_id, &e->id);
+    }
+
+    if (e->next_by_id)
+        e->next_by_id->prev_by_id = e->prev_by_id;
+
+    /* Remove from hash table indexed by name */
+    if (e->prev_by_name)
+        e->prev_by_name = e->next_by_name;
+    else {
+        if (e->next_by_name)
+            g_hash_table_replace(s->rrset_by_name, &e->next_by_name->rr.name, e->next_by_name);
+        else
+            g_hash_table_remove(s->rrset_by_name, &e->rr.name);
+    }
+    
+    if (e->next_by_name)
+        e->next_by_name->prev_by_name = e->prev_by_name;
+}
+
+void flx_server_remove(flxServer *s, gint id) {
+    g_assert(s);
+
+    if (id <= 0) {
+        while (s->entries)
+            free_entry(s, s->entries);
+    } else {
+        flxEntry *e;
+
+        while ((e = g_hash_table_lookup(s->rrset_by_id, &id)))
+            free_entry(s, e);
+    }
+}
+
+flxRecord *flx_record_copy_normalize(flxRecord *ret_dest, const flxRecord*src) {
+    g_assert(ret_dest);
+    g_assert(src);
+
+    *ret_dest = *src;
+    ret_dest->name = flx_normalize_name(src->name);
+    ret_dest->data = g_memdup(src->data, src->size);
+
+    return ret_dest;    
+}
+
+static const gchar *dns_class_to_string(guint16 class) {
+    if (class == FLX_DNS_CLASS_IN)
+        return "IN";
+
+    return NULL;
+}
+
+static const gchar *dns_type_to_string(guint16 type) {
+    switch (type) {
+        case FLX_DNS_TYPE_A:
+            return "A";
+        case FLX_DNS_TYPE_AAAA:
+            return "AAAA";
+        case FLX_DNS_TYPE_PTR:
+            return "PTR";
+        case FLX_DNS_TYPE_HINFO:
+            return "HINFO";
+        case FLX_DNS_TYPE_TXT:
+            return "TXT";
+        default:
+            return NULL;
+    }
+}
+
+void flx_server_dump(flxServer *s, FILE *f) {
+    flxEntry *e;
+    g_assert(s);
+    g_assert(f);
+
+    for (e = s->entries; e; e = e->next) {
+        char t[256];
+        fprintf(f, "%-40s %-8s %-8s ", e->rr.name, dns_class_to_string(e->rr.class), dns_type_to_string(e->rr.type));
+
+        t[0] = 0;
+        
+        if (e->rr.class == FLX_DNS_CLASS_IN) {
+            if (e->rr.type == FLX_DNS_TYPE_A)
+                inet_ntop(AF_INET, e->rr.data, t, sizeof(t));
+            else if (e->rr.type == FLX_DNS_TYPE_AAAA)
+                inet_ntop(AF_INET6, e->rr.data, t, sizeof(t));
+            else if (e->rr.type == FLX_DNS_TYPE_PTR)
+                g_strlcpy(t, e->rr.data, sizeof(t));
+            else if (e->rr.type == FLX_DNS_TYPE_HINFO) {
+                char *s2;
+
+                if ((s2 = memchr(e->rr.data, 0, e->rr.size))) {
+                    s2++;
+                    if (memchr(s2, 0, e->rr.size - ((char*) s2 - (char*) e->rr.data)))
+                        snprintf(t, sizeof(t), "'%s' '%s'", (char*) e->rr.data, s2);
+                }
+                
+            }
+        }
+            
+        fprintf(f, "%s\n", t);
+    }
+}
+
diff --git a/server.h b/server.h
new file mode 100644 (file)
index 0000000..7c96e78
--- /dev/null
+++ b/server.h
@@ -0,0 +1,33 @@
+#ifndef fooflxserverhfoo
+#define fooflxserverhfoo
+
+#include "flx.h"
+#include "iface.h"
+
+struct _flxEntry {
+    flxRecord rr;
+    gint id;
+    gint interface;
+
+    int unique;
+
+    struct _flxEntry *next, *prev;
+    struct _flxEntry *next_by_name, *prev_by_name;
+    struct _flxEntry *next_by_id, *prev_by_id;
+};
+
+typedef struct _flxEntry flxEntry;
+
+struct _flxServer {
+    GMainContext *context;
+    flxInterfaceMonitor *monitor;
+
+    gint current_id;
+    
+    GHashTable *rrset_by_id;
+    GHashTable *rrset_by_name;
+
+    flxEntry *entries;
+};
+
+#endif
diff --git a/socket.h b/socket.h
new file mode 100644 (file)
index 0000000..8d8b128
--- /dev/null
+++ b/socket.h
@@ -0,0 +1,11 @@
+#ifndef foosockethfoo
+#define foosockethfoo
+
+int flx_open_socket(int iface);
+
+
+int flx_send_packet(int fd, int iface, struct flx_dns_packet *p);
+
+
+
+#endif
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..7d3c3b6
--- /dev/null
+++ b/util.c
@@ -0,0 +1,26 @@
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+gchar *flx_get_host_name(void) {
+    char t[256];
+    gethostname(t, sizeof(t));
+    return g_strndup(t, sizeof(t));
+}
+
+gchar *flx_normalize_name(gchar *s) {
+    size_t l;
+    g_assert(s);
+
+    l = strlen(s);
+
+    if (!l)
+        return g_strdup(".");
+
+    if (s[l-1] == '.')
+        return g_strdup(s);
+    
+    return g_strdup_printf("%s.", s);
+}
+
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..a5d9feb
--- /dev/null
+++ b/util.h
@@ -0,0 +1,9 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+#include <glib.h>
+
+gchar *flx_normalize_name(gchar *s);
+gchar *flx_get_host_name(void);
+
+#endif