--- /dev/null
+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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+}
+
--- /dev/null
+#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
--- /dev/null
+#ifndef foosockethfoo
+#define foosockethfoo
+
+int flx_open_socket(int iface);
+
+
+int flx_send_packet(int fd, int iface, struct flx_dns_packet *p);
+
+
+
+#endif
--- /dev/null
+#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);
+}
+
--- /dev/null
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+#include <glib.h>
+
+gchar *flx_normalize_name(gchar *s);
+gchar *flx_get_host_name(void);
+
+#endif