From: Lennart Poettering Date: Thu, 23 Dec 2004 16:08:40 +0000 (+0000) Subject: initial commit X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=33ccd714ea9469b5b7d3b36bbe468ba1b0f31dfc;p=catta initial commit git-svn-id: file:///home/lennart/svn/public/avahi/trunk@3 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..633f40e --- /dev/null +++ b/address.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#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 index 0000000..28ccb4b --- /dev/null +++ b/address.h @@ -0,0 +1,31 @@ +#ifndef fooaddresshfoo +#define fooaddresshfoo + +#include + +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 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 index 0000000..8c46673 --- /dev/null +++ b/flx.h @@ -0,0 +1,61 @@ +#ifndef fooflxhfoo +#define fooflxhfoo + +#include +#include + +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 index 0000000..01f0637 --- /dev/null +++ b/iface.c @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include + +#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 index 0000000..3476645 --- /dev/null +++ b/iface.h @@ -0,0 +1,63 @@ +#ifndef fooifacehfoo +#define fooifacehfoo + +#include + +#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 index 0000000..e69de29 diff --git a/local.c b/local.c new file mode 100644 index 0000000..4a63274 --- /dev/null +++ b/local.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..9bba48c --- /dev/null +++ b/main.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#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 index 0000000..a04e6c9 --- /dev/null +++ b/netlink.c @@ -0,0 +1,154 @@ +#include +#include +#include + +#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 index 0000000..9efbce4 --- /dev/null +++ b/netlink.h @@ -0,0 +1,18 @@ +#ifndef foonetlinkhfoo +#define foonetlinkhfoo + +#include +#include +#include + +#include + +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 index 0000000..6ab1f1f --- /dev/null +++ b/server.c @@ -0,0 +1,232 @@ +#include +#include +#include + +#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 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 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 index 0000000..7d3c3b6 --- /dev/null +++ b/util.c @@ -0,0 +1,26 @@ +#include +#include + +#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 index 0000000..a5d9feb --- /dev/null +++ b/util.h @@ -0,0 +1,9 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +#include + +gchar *flx_normalize_name(gchar *s); +gchar *flx_get_host_name(void); + +#endif