From 33ccd714ea9469b5b7d3b36bbe468ba1b0f31dfc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Dec 2004 16:08:40 +0000 Subject: [PATCH] initial commit git-svn-id: file:///home/lennart/svn/public/avahi/trunk@3 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- Makefile | 10 ++ address.c | 80 +++++++++ address.h | 31 ++++ cache.h | 22 +++ flx.h | 61 +++++++ iface.c | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++ iface.h | 63 ++++++++ local-addr.h | 0 local.c | 148 +++++++++++++++++ main.c | 37 +++++ netlink.c | 154 ++++++++++++++++++ netlink.h | 18 +++ server.c | 232 +++++++++++++++++++++++++++ server.h | 33 ++++ socket.h | 11 ++ util.c | 26 +++ util.h | 9 ++ 17 files changed, 1381 insertions(+) create mode 100644 Makefile create mode 100644 address.c create mode 100644 address.h create mode 100644 cache.h create mode 100644 flx.h create mode 100644 iface.c create mode 100644 iface.h create mode 100644 local-addr.h create mode 100644 local.c create mode 100644 main.c create mode 100644 netlink.c create mode 100644 netlink.h create mode 100644 server.c create mode 100644 server.h create mode 100644 socket.h create mode 100644 util.c create mode 100644 util.h 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 -- 2.39.5