From f93eca3530bef2cc23ffe6c3a04493ad171c2aed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 May 2005 15:31:30 +0000 Subject: move the sources to libavahi-core/ git-svn-id: file:///home/lennart/svn/public/avahi/trunk@50 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- Makefile | 28 -- address.c | 126 ------ address.h | 40 -- announce.c | 397 ----------------- announce.h | 51 --- avahi.h | 143 ------ cache.c | 346 --------------- cache.h | 66 --- dns-test.c | 35 -- dns.c | 749 -------------------------------- dns.h | 82 ---- domain-test.c | 19 - iface.c | 562 ------------------------ iface.h | 102 ----- libavahi-core/Makefile | 28 ++ libavahi-core/address.c | 126 ++++++ libavahi-core/address.h | 40 ++ libavahi-core/announce.c | 397 +++++++++++++++++ libavahi-core/announce.h | 51 +++ libavahi-core/avahi.h | 143 ++++++ libavahi-core/cache.c | 346 +++++++++++++++ libavahi-core/cache.h | 66 +++ libavahi-core/dns-test.c | 35 ++ libavahi-core/dns.c | 749 ++++++++++++++++++++++++++++++++ libavahi-core/dns.h | 82 ++++ libavahi-core/domain-test.c | 19 + libavahi-core/iface.c | 562 ++++++++++++++++++++++++ libavahi-core/iface.h | 102 +++++ libavahi-core/llist.h | 50 +++ libavahi-core/main.c | 74 ++++ libavahi-core/netlink.c | 162 +++++++ libavahi-core/netlink.h | 20 + libavahi-core/prioq-test.c | 91 ++++ libavahi-core/prioq.c | 359 +++++++++++++++ libavahi-core/prioq.h | 35 ++ libavahi-core/psched.c | 715 ++++++++++++++++++++++++++++++ libavahi-core/psched.h | 78 ++++ libavahi-core/rr.c | 556 ++++++++++++++++++++++++ libavahi-core/rr.h | 117 +++++ libavahi-core/server.c | 1014 +++++++++++++++++++++++++++++++++++++++++++ libavahi-core/server.h | 79 ++++ libavahi-core/socket.c | 488 +++++++++++++++++++++ libavahi-core/socket.h | 27 ++ libavahi-core/strlst-test.c | 58 +++ libavahi-core/strlst.c | 218 ++++++++++ libavahi-core/strlst.h | 34 ++ libavahi-core/subscribe.c | 134 ++++++ libavahi-core/subscribe.h | 31 ++ libavahi-core/timeeventq.c | 160 +++++++ libavahi-core/timeeventq.h | 36 ++ libavahi-core/util.c | 214 +++++++++ libavahi-core/util.h | 26 ++ llist.h | 50 --- main.c | 74 ---- netlink.c | 162 ------- netlink.h | 20 - prioq-test.c | 91 ---- prioq.c | 359 --------------- prioq.h | 35 -- psched.c | 715 ------------------------------ psched.h | 78 ---- rr.c | 556 ------------------------ rr.h | 117 ----- server.c | 1014 ------------------------------------------- server.h | 79 ---- socket.c | 488 --------------------- socket.h | 27 -- strlst-test.c | 58 --- strlst.c | 218 ---------- strlst.h | 34 -- subscribe.c | 134 ------ subscribe.h | 31 -- timeeventq.c | 160 ------- timeeventq.h | 36 -- util.c | 214 --------- util.h | 26 -- 76 files changed, 7522 insertions(+), 7522 deletions(-) delete mode 100644 Makefile delete mode 100644 address.c delete mode 100644 address.h delete mode 100644 announce.c delete mode 100644 announce.h delete mode 100644 avahi.h delete mode 100644 cache.c delete mode 100644 cache.h delete mode 100644 dns-test.c delete mode 100644 dns.c delete mode 100644 dns.h delete mode 100644 domain-test.c delete mode 100644 iface.c delete mode 100644 iface.h create mode 100644 libavahi-core/Makefile create mode 100644 libavahi-core/address.c create mode 100644 libavahi-core/address.h create mode 100644 libavahi-core/announce.c create mode 100644 libavahi-core/announce.h create mode 100644 libavahi-core/avahi.h create mode 100644 libavahi-core/cache.c create mode 100644 libavahi-core/cache.h create mode 100644 libavahi-core/dns-test.c create mode 100644 libavahi-core/dns.c create mode 100644 libavahi-core/dns.h create mode 100644 libavahi-core/domain-test.c create mode 100644 libavahi-core/iface.c create mode 100644 libavahi-core/iface.h create mode 100644 libavahi-core/llist.h create mode 100644 libavahi-core/main.c create mode 100644 libavahi-core/netlink.c create mode 100644 libavahi-core/netlink.h create mode 100644 libavahi-core/prioq-test.c create mode 100644 libavahi-core/prioq.c create mode 100644 libavahi-core/prioq.h create mode 100644 libavahi-core/psched.c create mode 100644 libavahi-core/psched.h create mode 100644 libavahi-core/rr.c create mode 100644 libavahi-core/rr.h create mode 100644 libavahi-core/server.c create mode 100644 libavahi-core/server.h create mode 100644 libavahi-core/socket.c create mode 100644 libavahi-core/socket.h create mode 100644 libavahi-core/strlst-test.c create mode 100644 libavahi-core/strlst.c create mode 100644 libavahi-core/strlst.h create mode 100644 libavahi-core/subscribe.c create mode 100644 libavahi-core/subscribe.h create mode 100644 libavahi-core/timeeventq.c create mode 100644 libavahi-core/timeeventq.h create mode 100644 libavahi-core/util.c create mode 100644 libavahi-core/util.h delete mode 100644 llist.h delete mode 100644 main.c delete mode 100644 netlink.c delete mode 100644 netlink.h delete mode 100644 prioq-test.c delete mode 100644 prioq.c delete mode 100644 prioq.h delete mode 100644 psched.c delete mode 100644 psched.h delete mode 100644 rr.c delete mode 100644 rr.h delete mode 100644 server.c delete mode 100644 server.h delete mode 100644 socket.c delete mode 100644 socket.h delete mode 100644 strlst-test.c delete mode 100644 strlst.c delete mode 100644 strlst.h delete mode 100644 subscribe.c delete mode 100644 subscribe.h delete mode 100644 timeeventq.c delete mode 100644 timeeventq.h delete mode 100644 util.c delete mode 100644 util.h diff --git a/Makefile b/Makefile deleted file mode 100644 index ff3977e..0000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -#CC=gcc-2.95 -CFLAGS=-g -O1 -Wall -W -pipe $(shell pkg-config --cflags glib-2.0) -Wno-unused -LIBS=$(shell pkg-config --libs glib-2.0) - -all: strlst-test prioq-test domain-test dns-test flexmdns - -flexmdns: timeeventq.o main.o iface.o netlink.o server.o address.o util.o prioq.o cache.o rr.o dns.o socket.o psched.o announce.o subscribe.o strlst.o - $(CC) -o $@ $^ $(LIBS) - -#test-llist: test-llist.o -# $(CC) -o $@ $^ $(LIBS) - -prioq-test: prioq-test.o prioq.o - $(CC) -o $@ $^ $(LIBS) - -strlst-test: strlst-test.o strlst.o - $(CC) -o $@ $^ $(LIBS) - -domain-test: domain-test.o util.o - $(CC) -o $@ $^ $(LIBS) - -dns-test: dns-test.o util.o dns.o rr.o strlst.o - $(CC) -o $@ $^ $(LIBS) - -*.o: *.h - -clean: - rm -f *.o flexmdns prioq-test strlst-test domain-test dns-test diff --git a/address.c b/address.c deleted file mode 100644 index e5f8866..0000000 --- a/address.c +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include -#include - -#include "address.h" - -guint avahi_address_get_size(const AvahiAddress *a) { - g_assert(a); - - if (a->family == AF_INET) - return 4; - else if (a->family == AF_INET6) - return 16; - - return 0; -} - -gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) { - g_assert(a); - g_assert(b); - - if (a->family != b->family) - return -1; - - return memcmp(a->data.data, b->data.data, avahi_address_get_size(a)); -} - -gchar *avahi_address_snprint(char *s, guint length, const AvahiAddress *a) { - g_assert(s); - g_assert(length); - g_assert(a); - return (gchar*) inet_ntop(a->family, a->data.data, s, length); -} - -gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a) { - guint32 n = ntohl(a->address); - g_assert(a); - - return g_strdup_printf("%u.%u.%u.%u.in-addr.arpa", n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24); -} - -static gchar *reverse_lookup_name_ipv6(const AvahiIPv6Address *a, const gchar *suffix) { - - 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.%s", - a->address[15] & 0xF, - a->address[15] >> 4, - a->address[14] & 0xF, - a->address[14] >> 4, - a->address[13] & 0xF, - a->address[13] >> 4, - a->address[12] & 0xF, - a->address[12] >> 4, - a->address[11] & 0xF, - a->address[11] >> 4, - a->address[10] & 0xF, - a->address[10] >> 4, - a->address[9] & 0xF, - a->address[9] >> 4, - a->address[8] & 0xF, - a->address[8] >> 4, - a->address[7] & 0xF, - a->address[7] >> 4, - a->address[6] & 0xF, - a->address[6] >> 4, - a->address[5] & 0xF, - a->address[5] >> 4, - a->address[4] & 0xF, - a->address[4] >> 4, - a->address[3] & 0xF, - a->address[3] >> 4, - a->address[2] & 0xF, - a->address[2] >> 4, - a->address[1] & 0xF, - a->address[1] >> 4, - a->address[0] & 0xF, - a->address[0] >> 4, - suffix); -} - -gchar *avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a) { - return reverse_lookup_name_ipv6(a, "ip6.arpa"); -} - -gchar *avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a) { - return reverse_lookup_name_ipv6(a, "ip6.int"); -} - -AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr) { - g_assert(ret_addr); - g_assert(s); - - if (inet_pton(family, s, ret_addr->data.data) < 0) - return NULL; - - ret_addr->family = family; - - return ret_addr; -} - -AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) { - g_assert(sa); - g_assert(ret_addr); - - g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); - - ret_addr->family = sa->sa_family; - - if (sa->sa_family == AF_INET) - memcpy(&ret_addr->data.ipv4, &((struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4)); - else - memcpy(&ret_addr->data.ipv6, &((struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6)); - - return ret_addr; -} - -guint16 avahi_port_from_sockaddr(const struct sockaddr* sa) { - g_assert(sa); - - g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); - - if (sa->sa_family == AF_INET) - return ntohs(((struct sockaddr_in*) sa)->sin_port); - else - return ntohs(((struct sockaddr_in6*) sa)->sin6_port); -} diff --git a/address.h b/address.h deleted file mode 100644 index 2eea75d..0000000 --- a/address.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef fooaddresshfoo -#define fooaddresshfoo - -#include - -#include - -typedef struct { - guint32 address; -} AvahiIPv4Address; - -typedef struct { - guint8 address[16]; -} AvahiIPv6Address; - -typedef struct { - guchar family; - - union { - AvahiIPv6Address ipv6; - AvahiIPv4Address ipv4; - guint8 data[0]; - } data; -} AvahiAddress; - -guint avahi_address_get_size(const AvahiAddress *a); -gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); - -gchar *avahi_address_snprint(char *ret_s, guint length, const AvahiAddress *a); - -AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr); - -AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr); -guint16 avahi_port_from_sockaddr(const struct sockaddr* sa); - -gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a); -gchar* avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a); -gchar* avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a); - -#endif diff --git a/announce.c b/announce.c deleted file mode 100644 index 538ba2e..0000000 --- a/announce.c +++ /dev/null @@ -1,397 +0,0 @@ -#include "announce.h" -#include "util.h" - -#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250 -#define AVAHI_PROBE_JITTER_MSEC 250 -#define AVAHI_PROBE_INTERVAL_MSEC 250 - -static void remove_announcement(AvahiServer *s, AvahiAnnouncement *a) { - g_assert(s); - g_assert(a); - - if (a->time_event) - avahi_time_event_queue_remove(s->time_event_queue, a->time_event); - - AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_interface, a->interface->announcements, a); - AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_entry, a->entry->announcements, a); - - g_free(a); -} - -static void elapse_announce(AvahiTimeEvent *e, void *userdata); - -static void set_timeout(AvahiAnnouncement *a, const GTimeVal *tv) { - g_assert(a); - - if (!tv) { - if (a->time_event) { - avahi_time_event_queue_remove(a->server->time_event_queue, a->time_event); - a->time_event = NULL; - } - } else { - - if (a->time_event) - avahi_time_event_queue_update(a->server->time_event_queue, a->time_event, tv); - else - a->time_event = avahi_time_event_queue_add(a->server->time_event_queue, tv, elapse_announce, a); - } -} - -static void next_state(AvahiAnnouncement *a); - -void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately) { - AvahiEntry *e; - g_assert(g); - g_assert(!g->dead); - - /* Check whether all group members have been probed */ - - if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) - return; - - avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED); - - if (g->dead) - return; - - for (e = g->entries; e; e = e->entries_next) { - AvahiAnnouncement *a; - - for (a = e->announcements; a; a = a->by_entry_next) { - - if (a->state != AVAHI_WAITING) - continue; - - a->state = AVAHI_ANNOUNCING; - - if (immediately) { - /* Shortcut */ - - a->n_iteration = 1; - next_state(a); - } else { - GTimeVal tv; - a->n_iteration = 0; - avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); - set_timeout(a, &tv); - } - } - } -} - -static void next_state(AvahiAnnouncement *a) { - g_assert(a); - -/* g_message("%i -- %u", a->state, a->n_iteration); */ - - if (a->state == AVAHI_WAITING) { - - g_assert(a->entry->group); - - avahi_entry_group_check_probed(a->entry->group, TRUE); - - } else if (a->state == AVAHI_PROBING) { - - if (a->n_iteration >= 4) { - /* Probing done */ - - gchar *t; - - g_message("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record)); - g_free(t); - - if (a->entry->group) { - g_assert(a->entry->group->n_probing); - a->entry->group->n_probing--; - } - - if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING) - a->state = AVAHI_WAITING; - else { - a->state = AVAHI_ANNOUNCING; - a->n_iteration = 1; - } - - set_timeout(a, NULL); - next_state(a); - } else { - GTimeVal tv; - - avahi_interface_post_probe(a->interface, a->entry->record, FALSE); - - avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0); - set_timeout(a, &tv); - - a->n_iteration++; - } - - } else if (a->state == AVAHI_ANNOUNCING) { - - avahi_interface_post_response(a->interface, NULL, a->entry->record, a->entry->flags & AVAHI_ENTRY_UNIQUE, FALSE); - - if (++a->n_iteration >= 4) { - gchar *t; - /* Announcing done */ - - g_message("Enough announcements for record [%s]", t = avahi_record_to_string(a->entry->record)); - g_free(t); - - a->state = AVAHI_ESTABLISHED; - - set_timeout(a, NULL); - } else { - GTimeVal tv; - avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC); - - if (a->n_iteration < 10) - a->sec_delay *= 2; - - set_timeout(a, &tv); - } - } -} - -static void elapse_announce(AvahiTimeEvent *e, void *userdata) { - g_assert(e); - - next_state(userdata); -} - -AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncement *a; - - g_assert(s); - g_assert(e); - g_assert(i); - - for (a = e->announcements; a; a = a->by_entry_next) - if (a->interface == i) - return a; - - return NULL; -} - -static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { - AvahiAnnouncement *a; - GTimeVal tv; - gchar *t; - - g_assert(s); - g_assert(i); - g_assert(e); - g_assert(!e->dead); - -/* g_message("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */ -/* g_free(t); */ - - if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_commited(e)) - return; - - /* We don't want duplicate announcements */ - if (avahi_get_announcement(s, e, i)) - return; - - a = g_new(AvahiAnnouncement, 1); - a->server = s; - a->interface = i; - a->entry = e; - - if ((e->flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_NOPROBE)) - a->state = AVAHI_PROBING; - else if (!(e->flags & AVAHI_ENTRY_NOANNOUNCE)) { - - if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED) - a->state = AVAHI_ANNOUNCING; - else - a->state = AVAHI_WAITING; - - } else - a->state = AVAHI_ESTABLISHED; - - - g_message("New announcement on interface %s.%i for entry [%s] state=%i", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record), a->state); - g_free(t); - - a->n_iteration = 1; - a->sec_delay = 1; - a->time_event = NULL; - - if (a->state == AVAHI_PROBING) - if (e->group) - e->group->n_probing++; - - AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_interface, i->announcements, a); - AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_entry, e->announcements, a); - - if (a->state == AVAHI_PROBING) { - avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC); - set_timeout(a, &tv); - } else if (a->state == AVAHI_ANNOUNCING) { - avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); - set_timeout(a, &tv); - } -} - -void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { - AvahiEntry *e; - - g_assert(s); - g_assert(i); - - if (!i->announcing) - return; - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead) - new_announcement(s, i, e); -} - -static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { - AvahiEntry *e = userdata; - - g_assert(m); - g_assert(i); - g_assert(e); - g_assert(!e->dead); - - new_announcement(m->server, i, e); -} - -void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) { - g_assert(s); - g_assert(e); - g_assert(!e->dead); - - avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e); -} - -void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g) { - AvahiEntry *e; - - g_assert(s); - g_assert(g); - - for (e = g->entries; e; e = e->by_group_next) - if (!e->dead) - avahi_announce_entry(s, e); -} - -gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncement *a; - - g_assert(s); - g_assert(e); - g_assert(i); - g_assert(!e->dead); - - if (!(a = avahi_get_announcement(s, e, i))) - return FALSE; - - return a->state == AVAHI_ANNOUNCING || a->state == AVAHI_ESTABLISHED; -} - -gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncement *a; - - g_assert(s); - g_assert(e); - g_assert(i); - g_assert(!e->dead); - - if (!(a = avahi_get_announcement(s, e, i))) - return FALSE; - - return a->state == AVAHI_PROBING || a->state == AVAHI_WAITING; -} - -static AvahiRecord *make_goodbye_record(AvahiRecord *r) { -/* gchar *t; */ - AvahiRecord *g; - - g_assert(r); - -/* g_message("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */ -/* g_free(t); */ - - g = avahi_record_copy(r); - g_assert(g->ref == 1); - g->ttl = 0; - - return g; -} - -static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { - AvahiEntry *e = userdata; - AvahiRecord *g; - - g_assert(m); - g_assert(i); - g_assert(e); - g_assert(!e->dead); - - if (!avahi_interface_match(i, e->interface, e->protocol)) - return; - - if (e->flags & AVAHI_ENTRY_NOANNOUNCE) - return; - - if (!avahi_entry_registered(m->server, e, i)) - return; - - g = make_goodbye_record(e->record); - avahi_interface_post_response(i, NULL, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); - avahi_record_unref(g); -} - -void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean goodbye) { - g_assert(s); - g_assert(i); - -/* g_message("goodbye interface: %s.%u", i->hardware->name, i->protocol); */ - - if (goodbye && avahi_interface_relevant(i)) { - AvahiEntry *e; - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead) - send_goodbye_callback(s->monitor, i, e); - } - - while (i->announcements) - remove_announcement(s, i->announcements); - -/* g_message("goodbye interface done: %s.%u", i->hardware->name, i->protocol); */ - -} - -void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean goodbye) { - g_assert(s); - g_assert(e); - -/* g_message("goodbye entry: %p", e); */ - - if (goodbye && !e->dead) - avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e); - - while (e->announcements) - remove_announcement(s, e->announcements); - -/* g_message("goodbye entry done: %p", e); */ - -} - -void avahi_goodbye_all(AvahiServer *s, gboolean goodbye) { - AvahiEntry *e; - - g_assert(s); - -/* g_message("goodbye all"); */ - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead) - avahi_goodbye_entry(s, e, goodbye); - -/* g_message("goodbye all done"); */ - -} - diff --git a/announce.h b/announce.h deleted file mode 100644 index cd96977..0000000 --- a/announce.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef fooannouncehfoo -#define fooannouncehfoo - -#include - -typedef struct _AvahiAnnouncement AvahiAnnouncement; - -#include "llist.h" -#include "iface.h" -#include "server.h" -#include "timeeventq.h" - -typedef enum { - AVAHI_PROBING, - AVAHI_WAITING, /* wait for other records in group */ - AVAHI_ANNOUNCING, - AVAHI_ESTABLISHED -} AvahiAnnouncementState; - -struct _AvahiAnnouncement { - AvahiServer *server; - AvahiInterface *interface; - AvahiEntry *entry; - - AvahiTimeEvent *time_event; - - AvahiAnnouncementState state; - guint n_iteration; - guint sec_delay; - - AVAHI_LLIST_FIELDS(AvahiAnnouncement, by_interface); - AVAHI_LLIST_FIELDS(AvahiAnnouncement, by_entry); -}; - -void avahi_announce_interface(AvahiServer *s, AvahiInterface *i); -void avahi_announce_entry(AvahiServer *s, AvahiEntry *e); -void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g); - -void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately); - -gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); -gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); - -void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean send); -void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean send); - -void avahi_goodbye_all(AvahiServer *s, gboolean send); - -AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); - -#endif diff --git a/avahi.h b/avahi.h deleted file mode 100644 index 1c59faf..0000000 --- a/avahi.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef fooAvahihfoo -#define fooAvahihfoo - -#include -#include - -typedef struct _AvahiServer AvahiServer; -typedef struct _AvahiEntry AvahiEntry; -typedef struct _AvahiEntryGroup AvahiEntryGroup; - -#include "address.h" -#include "rr.h" - -typedef enum { - AVAHI_ENTRY_NULL = 0, - AVAHI_ENTRY_UNIQUE = 1, - AVAHI_ENTRY_NOPROBE = 2, - AVAHI_ENTRY_NOANNOUNCE = 4 -} AvahiEntryFlags; - -typedef enum { - AVAHI_ENTRY_GROUP_UNCOMMITED, - AVAHI_ENTRY_GROUP_REGISTERING, - AVAHI_ENTRY_GROUP_ESTABLISHED, - AVAHI_ENTRY_GROUP_COLLISION -} AvahiEntryGroupState; - -typedef void (*AvahiEntryGroupCallback) (AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata); - -AvahiServer *avahi_server_new(GMainContext *c); -void avahi_server_free(AvahiServer* s); - -const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state); -void avahi_server_dump(AvahiServer *s, FILE *f); - -AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata); -void avahi_entry_group_free(AvahiEntryGroup *g); -void avahi_entry_group_commit(AvahiEntryGroup *g); -AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g); - -void avahi_server_add( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - AvahiRecord *r); - -void avahi_server_add_ptr( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - const gchar *dest); - -void avahi_server_add_address( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - AvahiAddress *a); - -void avahi_server_add_text( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - ... /* text records, terminated by NULL */); - -void avahi_server_add_text_va( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - va_list va); - -void avahi_server_add_text_strlst( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - AvahiStringList *strlst); - -void avahi_server_add_service( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - const gchar *type, - const gchar *name, - const gchar *domain, - const gchar *host, - guint16 port, - ... /* text records, terminated by NULL */); - -void avahi_server_add_service_va( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - const gchar *type, - const gchar *name, - const gchar *domain, - const gchar *host, - guint16 port, - va_list va); - -void avahi_server_add_service_strlst( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - const gchar *type, - const gchar *name, - const gchar *domain, - const gchar *host, - guint16 port, - AvahiStringList *strlst); - -typedef enum { - AVAHI_SUBSCRIPTION_NEW, - AVAHI_SUBSCRIPTION_REMOVE, - AVAHI_SUBSCRIPTION_CHANGE -} AvahiSubscriptionEvent; - -typedef struct _AvahiSubscription AvahiSubscription; - -typedef void (*AvahiSubscriptionCallback)(AvahiSubscription *s, AvahiRecord *record, gint interface, guchar protocol, AvahiSubscriptionEvent event, gpointer userdata); - -AvahiSubscription *avahi_subscription_new(AvahiServer *s, AvahiKey *key, gint interface, guchar protocol, AvahiSubscriptionCallback callback, gpointer userdata); -void avahi_subscription_free(AvahiSubscription *s); - -#endif diff --git a/cache.c b/cache.c deleted file mode 100644 index c2d680e..0000000 --- a/cache.c +++ /dev/null @@ -1,346 +0,0 @@ -#include - -#include "util.h" -#include "cache.h" - -static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { - AvahiCacheEntry *t; - - g_assert(c); - g_assert(e); - -/* g_message("removing from cache: %p %p", c, e); */ - - /* Remove from hash table */ - t = g_hash_table_lookup(c->hash_table, e->record->key); - AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e); - if (t) - g_hash_table_replace(c->hash_table, t->record->key, t); - else - g_hash_table_remove(c->hash_table, e->record->key); - - /* Remove from linked list */ - AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e); - - if (e->time_event) - avahi_time_event_queue_remove(c->server->time_event_queue, e->time_event); - - avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_REMOVE); - - avahi_record_unref(e->record); - - g_free(e); -} - -AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) { - AvahiCache *c; - g_assert(server); - - c = g_new(AvahiCache, 1); - c->server = server; - c->interface = iface; - c->hash_table = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal); - - AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries); - - return c; -} - -void avahi_cache_free(AvahiCache *c) { - g_assert(c); - - while (c->entries) - remove_entry(c, c->entries); - - g_hash_table_destroy(c->hash_table); - - g_free(c); -} - -AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k) { - g_assert(c); - g_assert(k); - - g_assert(!avahi_key_is_pattern(k)); - - return g_hash_table_lookup(c->hash_table, k); -} - -gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata) { - gpointer ret; - - g_assert(c); - g_assert(pattern); - g_assert(cb); - - if (avahi_key_is_pattern(pattern)) { - AvahiCacheEntry *e, *n; - - for (e = c->entries; e; e = n) { - n = e->entry_next; - - if (avahi_key_pattern_match(pattern, e->record->key)) - if ((ret = cb(c, pattern, e, userdata))) - return ret; - } - - } else { - AvahiCacheEntry *e, *n; - - for (e = avahi_cache_lookup_key(c, pattern); e; e = n) { - n = e->by_key_next; - - if ((ret = cb(c, pattern, e, userdata))) - return ret; - } - } - - return NULL; -} - -static gpointer lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { - g_assert(c); - g_assert(pattern); - g_assert(e); - - if (avahi_record_equal_no_ttl(e->record, userdata)) - return e; - - return NULL; -} - -AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r) { - AvahiCacheEntry *e; - - g_assert(c); - g_assert(r); - - return avahi_cache_walk(c, r->key, lookup_record_callback, r); -} - -static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent); - -static void elapse_func(AvahiTimeEvent *t, void *userdata) { - AvahiCacheEntry *e = userdata; - - g_assert(t); - g_assert(e); - - if (e->state == AVAHI_CACHE_FINAL) { - remove_entry(e->cache, e); - g_message("Removing entry from cache due to expiration"); - } else { - guint percent = 0; - - switch (e->state) { - case AVAHI_CACHE_VALID: - e->state = AVAHI_CACHE_EXPIRY1; - percent = 85; - break; - - case AVAHI_CACHE_EXPIRY1: - e->state = AVAHI_CACHE_EXPIRY2; - percent = 90; - break; - case AVAHI_CACHE_EXPIRY2: - e->state = AVAHI_CACHE_EXPIRY3; - percent = 95; - break; - - case AVAHI_CACHE_EXPIRY3: - e->state = AVAHI_CACHE_FINAL; - percent = 100; - break; - - default: - ; - } - - g_assert(percent > 0); - - g_message("Requesting cache entry update at %i%%.", percent); - - /* Request a cache update, if we are subscribed to this entry */ - if (avahi_is_subscribed(e->cache->server, e->record->key)) - avahi_interface_post_query(e->cache->interface, e->record->key, TRUE); - - /* Check again later */ - next_expiry(e->cache, e, percent); - } -} - -static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) { - g_assert(c); - g_assert(e); - - if (e->time_event) - avahi_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry); - else - e->time_event = avahi_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e); -} - -static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent) { - gulong usec; - - g_assert(c); - g_assert(e); - g_assert(percent > 0 && percent <= 100); - - e->expiry = e->timestamp; - - usec = e->record->ttl * 10000; - - /* 2% jitter */ - usec = g_random_int_range(usec*percent, usec*(percent+2)); - - g_time_val_add(&e->expiry, usec); - update_time_event(c, e); -} - -void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a) { - AvahiCacheEntry *e, *t; - gchar *txt; - - g_assert(c); - g_assert(r && r->ref >= 1); - - g_message("cache update: %s", (txt = avahi_record_to_string(r))); - g_free(txt); - - if (r->ttl == 0) { - - /* This is a goodbye request */ - - if ((e = avahi_cache_lookup_record(c, r))) { - - e->state = AVAHI_CACHE_FINAL; - g_get_current_time(&e->timestamp); - e->expiry = e->timestamp; - g_time_val_add(&e->expiry, 1000000); /* 1s */ - update_time_event(c, e); - } - - } else { - - /* This is an update request */ - - if ((t = e = avahi_cache_lookup_key(c, r->key))) { - - if (unique) { - - /* For unique records, remove all entries but one */ - while (e->by_key_next) - remove_entry(c, e->by_key_next); - - } else { - - /* For non-unique record, look for exactly the same entry */ - for (; e; e = e->by_key_next) - if (avahi_record_equal_no_ttl(e->record, r)) - break; - } - } - - if (e) { - -/* g_message("found matching cache entry"); */ - - /* We are the first in the linked list so let's replace the hash table key with the new one */ - if (e->by_key_prev == NULL) - g_hash_table_replace(c->hash_table, r->key, e); - - /* Notify subscribers */ - if (!avahi_record_equal_no_ttl(e->record, r)) - avahi_subscription_notify(c->server, c->interface, r, AVAHI_SUBSCRIPTION_CHANGE); - - /* Update the record */ - avahi_record_unref(e->record); - e->record = avahi_record_ref(r); - - } else { - /* No entry found, therefore we create a new one */ - -/* g_message("couldn't find matching cache entry"); */ - - e = g_new(AvahiCacheEntry, 1); - e->cache = c; - e->time_event = NULL; - e->record = avahi_record_ref(r); - - /* Append to hash table */ - AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, t, e); - g_hash_table_replace(c->hash_table, e->record->key, t); - - /* Append to linked list */ - AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); - - /* Notify subscribers */ - avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_NEW); - } - - e->origin = *a; - g_get_current_time(&e->timestamp); - next_expiry(c, e, 80); - e->state = AVAHI_CACHE_VALID; - } -} - -static gpointer drop_key_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) { - g_assert(c); - g_assert(pattern); - g_assert(e); - - remove_entry(c, e); - return NULL; -} - -void avahi_cache_drop_key(AvahiCache *c, AvahiKey *k) { - g_assert(c); - g_assert(k); - - avahi_cache_walk(c, k, drop_key_callback, NULL); -} - -void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r) { - AvahiCacheEntry *e; - - g_assert(c); - g_assert(r); - - if ((e = avahi_cache_lookup_record(c, r))) - remove_entry(c, e); -} - -static void func(gpointer key, gpointer data, gpointer userdata) { - AvahiCacheEntry *e = data; - AvahiKey *k = key; - gchar *t; - - t = avahi_record_to_string(e->record); - fprintf((FILE*) userdata, "%s\n", t); - g_free(t); -} - -void avahi_cache_dump(AvahiCache *c, FILE *f) { - g_assert(c); - g_assert(f); - - fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n"); - g_hash_table_foreach(c->hash_table, func, f); -} - -gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { - GTimeVal now; - guint age; - - g_assert(c); - g_assert(e); - - g_get_current_time(&now); - - age = avahi_timeval_diff(&now, &e->timestamp)/1000000; - - g_message("age: %u, ttl/2: %u", age, e->record->ttl); - - return age >= e->record->ttl/2; -} diff --git a/cache.h b/cache.h deleted file mode 100644 index de0ec9f..0000000 --- a/cache.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef foocachehfoo -#define foocachehfoo - -#include - -struct _AvahiCache; -typedef struct _AvahiCache AvahiCache; - -#include "prioq.h" -#include "server.h" -#include "llist.h" -#include "timeeventq.h" - -typedef enum { - AVAHI_CACHE_VALID, - AVAHI_CACHE_EXPIRY1, - AVAHI_CACHE_EXPIRY2, - AVAHI_CACHE_EXPIRY3, - AVAHI_CACHE_FINAL -} AvahiCacheEntryState; - -typedef struct AvahiCacheEntry AvahiCacheEntry; - -struct AvahiCacheEntry { - AvahiCache *cache; - AvahiRecord *record; - GTimeVal timestamp; - GTimeVal expiry; - - AvahiAddress origin; - - AvahiCacheEntryState state; - AvahiTimeEvent *time_event; - - AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key); - AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry); -}; - -struct _AvahiCache { - AvahiServer *server; - - AvahiInterface *interface; - - GHashTable *hash_table; - - AVAHI_LLIST_HEAD(AvahiCacheEntry, entries); -}; - -AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface); -void avahi_cache_free(AvahiCache *c); - -AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k); -AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r); - -void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a); - -void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r); - -void avahi_cache_dump(AvahiCache *c, FILE *f); - -typedef gpointer AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata); -gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata); - -gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e); - -#endif diff --git a/dns-test.c b/dns-test.c deleted file mode 100644 index d8c7b53..0000000 --- a/dns-test.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "dns.h" -#include "util.h" - -int main(int argc, char *argv[]) { - gchar t[256], *a, *b, *c, *d; - AvahiDnsPacket *p; - - p = avahi_dns_packet_new(8000); - - avahi_dns_packet_append_name(p, a = "hello.hello.hello.de."); - avahi_dns_packet_append_name(p, b = "this is a test.hello.de."); - avahi_dns_packet_append_name(p, c = "this\\.is\\.a\\.test\\.with\\.dots.hello.de."); - avahi_dns_packet_append_name(p, d = "this\\\\is another\\ \\test.hello.de."); - - avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size); - - avahi_dns_packet_consume_name(p, t, sizeof(t)); - g_message(">%s<", t); - g_assert(avahi_domain_equal(a, t)); - - avahi_dns_packet_consume_name(p, t, sizeof(t)); - g_message(">%s<", t); - g_assert(avahi_domain_equal(b, t)); - - avahi_dns_packet_consume_name(p, t, sizeof(t)); - g_message(">%s<", t); - g_assert(avahi_domain_equal(c, t)); - - avahi_dns_packet_consume_name(p, t, sizeof(t)); - g_message(">%s<", t); - g_assert(avahi_domain_equal(d, t)); - - avahi_dns_packet_free(p); - return 0; -} diff --git a/dns.c b/dns.c deleted file mode 100644 index e485ed8..0000000 --- a/dns.c +++ /dev/null @@ -1,749 +0,0 @@ -#include - -#include -#include -#include - -#include "dns.h" -#include "util.h" - -AvahiDnsPacket* avahi_dns_packet_new(guint max_size) { - AvahiDnsPacket *p; - - if (max_size <= 0) - max_size = AVAHI_DNS_PACKET_MAX_SIZE; - else if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) - max_size = AVAHI_DNS_PACKET_HEADER_SIZE; - - p = g_malloc(sizeof(AvahiDnsPacket) + max_size); - p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; - p->max_size = max_size; - p->name_table = NULL; - - memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); - return p; -} - -AvahiDnsPacket* avahi_dns_packet_new_query(guint max_size) { - AvahiDnsPacket *p; - - p = avahi_dns_packet_new(max_size); - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); - return p; -} - -AvahiDnsPacket* avahi_dns_packet_new_response(guint max_size) { - AvahiDnsPacket *p; - - p = avahi_dns_packet_new(max_size); - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)); - return p; -} - -void avahi_dns_packet_free(AvahiDnsPacket *p) { - g_assert(p); - - if (p->name_table) - g_hash_table_destroy(p->name_table); - - g_free(p); -} - -void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v) { - g_assert(p); - g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE); - - ((guint16*) AVAHI_DNS_PACKET_DATA(p))[index] = g_htons(v); -} - -guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index) { - g_assert(p); - g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE); - - return g_ntohs(((guint16*) AVAHI_DNS_PACKET_DATA(p))[index]); -} - -/* Read the first label from string *name, unescape "\" and write it to dest */ -gchar *avahi_unescape_label(gchar *dest, guint size, const gchar **name) { - guint i = 0; - gchar *d; - - g_assert(dest); - g_assert(size > 0); - g_assert(name); - g_assert(*name); - - d = dest; - - for (;;) { - if (i >= size) - return NULL; - - if (**name == '.') { - (*name)++; - break; - } - - if (**name == 0) - break; - - if (**name == '\\') { - (*name) ++; - - if (**name == 0) - break; - } - - *(d++) = *((*name) ++); - i++; - } - - g_assert(i < size); - - *d = 0; - - return dest; -} - -guint8* avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name) { - guint8 *d, *saved_ptr = NULL; - guint saved_size; - - g_assert(p); - g_assert(name); - - saved_size = p->size; - saved_ptr = avahi_dns_packet_extend(p, 0); - - while (*name) { - guint n; - guint8* prev; - const gchar *pname; - gchar label[64]; - - /* Check whether we can compress this name. */ - - if (p->name_table && (prev = g_hash_table_lookup(p->name_table, name))) { - guint index; - - g_assert(prev >= AVAHI_DNS_PACKET_DATA(p)); - index = (guint) (prev - AVAHI_DNS_PACKET_DATA(p)); - - g_assert(index < p->size); - - if (index < 0x4000) { - guint16 *t; - if (!(t = (guint16*) avahi_dns_packet_extend(p, sizeof(guint16)))) - return NULL; - - *t = g_htons((0xC000 | index)); - return saved_ptr; - } - } - - pname = name; - - if (!(avahi_unescape_label(label, sizeof(label), &name))) - goto fail; - - if (!(d = avahi_dns_packet_append_string(p, label))) - goto fail; - - if (!p->name_table) - p->name_table = g_hash_table_new_full((GHashFunc) avahi_domain_hash, (GEqualFunc) avahi_domain_equal, g_free, NULL); - - g_hash_table_insert(p->name_table, g_strdup(pname), d); - } - - if (!(d = avahi_dns_packet_extend(p, 1))) - goto fail; - - *d = 0; - - return saved_ptr; - -fail: - p->size = saved_size; - return NULL; -} - -guint8* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v) { - guint8 *d; - g_assert(p); - - if (!(d = avahi_dns_packet_extend(p, sizeof(guint16)))) - return NULL; - - *((guint16*) d) = g_htons(v); - return d; -} - -guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v) { - guint8 *d; - g_assert(p); - - if (!(d = avahi_dns_packet_extend(p, sizeof(guint32)))) - return NULL; - - *((guint32*) d) = g_htonl(v); - - return d; -} - -guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer b, guint l) { - guint8* d; - - g_assert(p); - g_assert(b); - g_assert(l); - - if (!(d = avahi_dns_packet_extend(p, l))) - return NULL; - - memcpy(d, b, l); - return d; -} - -guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s) { - guint8* d; - guint k; - - g_assert(p); - g_assert(s); - - if ((k = strlen(s)) >= 255) - k = 255; - - if (!(d = avahi_dns_packet_extend(p, k+1))) - return NULL; - - *d = (guint8) k; - memcpy(d+1, s, k); - - return d; -} - -guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l) { - guint8 *d; - - g_assert(p); - - if (p->size+l > p->max_size) - return NULL; - - d = AVAHI_DNS_PACKET_DATA(p) + p->size; - p->size += l; - - return d; -} - -gint avahi_dns_packet_check_valid(AvahiDnsPacket *p) { - guint16 flags; - g_assert(p); - - if (p->size < 12) - return -1; - - flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); - - if (flags & AVAHI_DNS_FLAG_OPCODE || flags & AVAHI_DNS_FLAG_RCODE) - return -1; - - return 0; -} - -gint avahi_dns_packet_is_query(AvahiDnsPacket *p) { - g_assert(p); - - return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); -} - -/* Read a label from a DNS packet, escape "\" and ".", append \0 */ -static gchar *escape_label(guint8* src, guint src_length, gchar **ret_name, guint *ret_name_length) { - gchar *r; - - g_assert(src); - g_assert(ret_name); - g_assert(*ret_name); - g_assert(ret_name_length); - g_assert(*ret_name_length > 0); - - r = *ret_name; - - while (src_length > 0) { - if (*src == '.' || *src == '\\') { - if (*ret_name_length < 3) - return NULL; - - *((*ret_name) ++) = '\\'; - (*ret_name_length) --; - } - - if (*ret_name_length < 2) - return NULL; - - *((*ret_name)++) = *src; - (*ret_name_length) --; - - src_length --; - src++; - } - - **ret_name = 0; - - return r; -} - -static gint consume_labels(AvahiDnsPacket *p, guint index, gchar *ret_name, guint l) { - gint ret = 0; - int compressed = 0; - int first_label = 1; - g_assert(p && ret_name && l); - - for (;;) { - guint8 n; - - if (index+1 > p->size) - return -1; - - n = AVAHI_DNS_PACKET_DATA(p)[index]; - - if (!n) { - index++; - if (!compressed) - ret++; - - if (l < 1) - return -1; - *ret_name = 0; - - return ret; - - } else if (n <= 63) { - /* Uncompressed label */ - index++; - if (!compressed) - ret++; - - if (index + n > p->size) - return -1; - - if ((guint) n + 1 > l) - return -1; - - if (!first_label) { - *(ret_name++) = '.'; - l--; - } else - first_label = 0; - - if (!(escape_label(AVAHI_DNS_PACKET_DATA(p) + index, n, &ret_name, &l))) - return -1; - - index += n; - - if (!compressed) - ret += n; - } else if ((n & 0xC0) == 0xC0) { - /* Compressed label */ - - if (index+2 > p->size) - return -1; - - index = ((guint) (AVAHI_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[index+1]; - - if (!compressed) - ret += 2; - - compressed = 1; - } else - return -1; - } -} - -gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l) { - gint r; - - if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) - return -1; - - p->rindex += r; - return 0; -} - -gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v) { - g_assert(p); - g_assert(ret_v); - - if (p->rindex + sizeof(guint16) > p->size) - return -1; - - *ret_v = g_ntohs(*((guint16*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex))); - p->rindex += sizeof(guint16); - - return 0; -} - -gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v) { - g_assert(p); - g_assert(ret_v); - - if (p->rindex + sizeof(guint32) > p->size) - return -1; - - *ret_v = g_ntohl(*((guint32*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex))); - p->rindex += sizeof(guint32); - - return 0; -} - -gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l) { - g_assert(p); - g_assert(ret_data); - g_assert(l > 0); - - if (p->rindex + l > p->size) - return -1; - - memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); - p->rindex += l; - - return 0; -} - -gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l) { - guint k; - - g_assert(p); - g_assert(ret_string); - g_assert(l > 0); - - if (p->rindex >= p->size) - return -1; - - k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; - - if (p->rindex+1+k > p->size) - return -1; - - if (l > k+1) - l = k+1; - - memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); - ret_string[l-1] = 0; - - - p->rindex += 1+k; - - return 0; - -} - -gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { - g_assert(p); - - if (p->rindex > p->size) - return NULL; - - return AVAHI_DNS_PACKET_DATA(p) + p->rindex; -} - -gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length) { - g_assert(p); - - if (p->rindex + length > p->size) - return -1; - - p->rindex += length; - return 0; -} - -AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush) { - gchar name[257], buf[257]; - guint16 type, class; - guint32 ttl; - guint16 rdlength; - gconstpointer data; - AvahiRecord *r = NULL; - gconstpointer start; - - g_assert(p); - g_assert(ret_cache_flush); - -/* g_message("consume_record()"); */ - - if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || - avahi_dns_packet_consume_uint16(p, &type) < 0 || - avahi_dns_packet_consume_uint16(p, &class) < 0 || - avahi_dns_packet_consume_uint32(p, &ttl) < 0 || - avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || - p->rindex + rdlength > p->size) - goto fail; - -/* g_message("name = %s, rdlength = %u", name, rdlength); */ - - *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); - class &= ~AVAHI_DNS_CACHE_FLUSH; - - start = avahi_dns_packet_get_rptr(p); - - r = avahi_record_new_full(name, class, type); - - switch (type) { - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - -/* g_message("ptr"); */ - - if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) - goto fail; - - r->data.ptr.name = g_strdup(buf); - break; - - - case AVAHI_DNS_TYPE_SRV: - -/* g_message("srv"); */ - - if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || - avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || - avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || - avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) - goto fail; - - r->data.srv.name = g_strdup(buf); - break; - - case AVAHI_DNS_TYPE_HINFO: - -/* g_message("hinfo"); */ - - if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) - goto fail; - - r->data.hinfo.cpu = g_strdup(buf); - - if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) - goto fail; - - r->data.hinfo.os = g_strdup(buf); - break; - - case AVAHI_DNS_TYPE_TXT: - -/* g_message("txt"); */ - - if (rdlength > 0) { - r->data.txt.string_list = avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength); - - if (avahi_dns_packet_skip(p, rdlength) < 0) - goto fail; - } else - r->data.txt.string_list = NULL; - - break; - - case AVAHI_DNS_TYPE_A: - -/* g_message("A"); */ - - if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) - goto fail; - - break; - - case AVAHI_DNS_TYPE_AAAA: - -/* g_message("aaaa"); */ - - if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) - goto fail; - - break; - - default: - -/* g_message("generic"); */ - - if (rdlength > 0) { - - r->data.generic.data = g_memdup(avahi_dns_packet_get_rptr(p), rdlength); - - if (avahi_dns_packet_skip(p, rdlength) < 0) - goto fail; - } - - break; - } - -/* g_message("%i == %u ?", (guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start, rdlength); */ - - /* Check if we read enough data */ - if ((guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start != rdlength) - goto fail; - - r->ttl = ttl; - - return r; - -fail: - if (r) - avahi_record_unref(r); - - return NULL; -} - -AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response) { - gchar name[256]; - guint16 type, class; - - g_assert(p); - g_assert(ret_unicast_response); - - if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || - avahi_dns_packet_consume_uint16(p, &type) < 0 || - avahi_dns_packet_consume_uint16(p, &class) < 0) - return NULL; - - *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); - class &= ~AVAHI_DNS_UNICAST_RESPONSE; - - return avahi_key_new(name, class, type); -} - -guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response) { - guint8 *t; - guint size; - - g_assert(p); - g_assert(k); - - size = p->size; - - if (!(t = avahi_dns_packet_append_name(p, k->name)) || - !avahi_dns_packet_append_uint16(p, k->type) || - !avahi_dns_packet_append_uint16(p, k->class | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { - p->size = size; - return NULL; - } - - return t; -} - -guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush) { - guint8 *t, *l, *start; - guint size; - - g_assert(p); - g_assert(r); - - size = p->size; - - if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || - !avahi_dns_packet_append_uint16(p, r->key->type) || - !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->class | AVAHI_DNS_CACHE_FLUSH) : (r->key->class &~ AVAHI_DNS_CACHE_FLUSH)) || - !avahi_dns_packet_append_uint32(p, r->ttl) || - !(l = avahi_dns_packet_append_uint16(p, 0))) - goto fail; - - start = avahi_dns_packet_extend(p, 0); - - switch (r->key->type) { - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME : - - if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) - goto fail; - - break; - - case AVAHI_DNS_TYPE_SRV: - - if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || - !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || - !avahi_dns_packet_append_uint16(p, r->data.srv.port) || - !avahi_dns_packet_append_name(p, r->data.srv.name)) - goto fail; - - break; - - case AVAHI_DNS_TYPE_HINFO: - if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || - !avahi_dns_packet_append_string(p, r->data.hinfo.os)) - goto fail; - - break; - - case AVAHI_DNS_TYPE_TXT: { - - guint8 *data; - guint size; - - size = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); - -/* g_message("appending string: %u %p", size, r->data.txt.string_list); */ - - if (!(data = avahi_dns_packet_extend(p, size))) - goto fail; - - avahi_string_list_serialize(r->data.txt.string_list, data, size); - break; - } - - - case AVAHI_DNS_TYPE_A: - - if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) - goto fail; - - break; - - case AVAHI_DNS_TYPE_AAAA: - - if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) - goto fail; - - break; - - default: - - if (r->data.generic.size && - avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) - goto fail; - - break; - } - - - - - size = avahi_dns_packet_extend(p, 0) - start; - g_assert(size <= 0xFFFF); - -/* g_message("appended %u", size); */ - - * (guint16*) l = g_htons((guint16) size); - - return t; - - -fail: - p->size = size; - return NULL; -} - -gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p) { - g_assert(p); - - return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; -} - -guint avahi_dns_packet_space(AvahiDnsPacket *p) { - g_assert(p); - - g_assert(p->size <= p->max_size); - - return p->max_size - p->size; -} diff --git a/dns.h b/dns.h deleted file mode 100644 index 13d9278..0000000 --- a/dns.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef foodnshfoo -#define foodnshfoo - -#include - -#include "rr.h" - -#define AVAHI_DNS_PACKET_MAX_SIZE 9000 -#define AVAHI_DNS_PACKET_HEADER_SIZE 12 - -typedef struct _AvahiDnsPacket { - guint size, rindex, max_size; - GHashTable *name_table; /* for name compression */ -} AvahiDnsPacket; - - -#define AVAHI_DNS_PACKET_DATA(p) (((guint8*) p) + sizeof(AvahiDnsPacket)) - -AvahiDnsPacket* avahi_dns_packet_new(guint size); -AvahiDnsPacket* avahi_dns_packet_new_query(guint size); -AvahiDnsPacket* avahi_dns_packet_new_response(guint size); - -void avahi_dns_packet_free(AvahiDnsPacket *p); -void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v); -guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index); - -guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l); - -guint8 *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v); -guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v); -guint8 *avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name); -guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer, guint l); -guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response); -guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush); -guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s); - -gint avahi_dns_packet_is_query(AvahiDnsPacket *p); -gint avahi_dns_packet_check_valid(AvahiDnsPacket *p); - -gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v); -gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v); -gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l); -gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l); -AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response); -AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush); -gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l); - -gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p); - -gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length); - -gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p); -guint avahi_dns_packet_space(AvahiDnsPacket *p); - -#define AVAHI_DNS_FIELD_ID 0 -#define AVAHI_DNS_FIELD_FLAGS 1 -#define AVAHI_DNS_FIELD_QDCOUNT 2 -#define AVAHI_DNS_FIELD_ANCOUNT 3 -#define AVAHI_DNS_FIELD_NSCOUNT 4 -#define AVAHI_DNS_FIELD_ARCOUNT 5 - -#define AVAHI_DNS_FLAG_QR (1 << 15) -#define AVAHI_DNS_FLAG_OPCODE (15 << 11) -#define AVAHI_DNS_FLAG_RCODE (15) -#define AVAHI_DNS_FLAG_TC (1 << 9) - -#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ - (((guint16) !!qr << 15) | \ - ((guint16) (opcode & 15) << 11) | \ - ((guint16) !!aa << 10) | \ - ((guint16) !!tc << 9) | \ - ((guint16) !!rd << 8) | \ - ((guint16) !!ra << 7) | \ - ((guint16) !!ad << 5) | \ - ((guint16) !!cd << 4) | \ - ((guint16) (rd & 15))) - - -gchar *avahi_unescape_label(gchar *dest, guint size, const gchar **name); - -#endif - diff --git a/domain-test.c b/domain-test.c deleted file mode 100644 index f3deeac..0000000 --- a/domain-test.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "util.h" - -int main(int argc, char *argv[]) { - gchar *s; - - g_message("host name: %s", s = avahi_get_host_name()); - g_free(s); - - g_message("%s", s = avahi_normalize_name("foo.foo.")); - g_free(s); - - g_message("%s", s = avahi_normalize_name("foo.foo.")); - g_free(s); - - - g_message("%i", avahi_domain_equal("\\aaa bbb\\.cccc\\\\.dee.fff.", "aaa\\ bbb\\.cccc\\\\.dee.fff")); - - return 0; -} diff --git a/iface.c b/iface.c deleted file mode 100644 index 519a65b..0000000 --- a/iface.c +++ /dev/null @@ -1,562 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "iface.h" -#include "netlink.h" -#include "dns.h" -#include "socket.h" -#include "announce.h" - -static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove) { - g_assert(m); - g_assert(a); - - if (!avahi_interface_address_relevant(a) || remove) { - if (a->entry_group) { - avahi_entry_group_free(a->entry_group); - a->entry_group = NULL; - } - } else { - if (!a->entry_group) { - a->entry_group = avahi_entry_group_new(m->server, NULL, NULL); - avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address); - avahi_entry_group_commit(a->entry_group); - } - } -} - -static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, int remove) { - AvahiInterfaceAddress *a; - g_assert(m); - g_assert(i); - - for (a = i->addresses; a; a = a->address_next) - update_address_rr(m, a, remove); -} - -static void update_hw_interface_rr(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, int remove) { - AvahiInterface *i; - - g_assert(m); - g_assert(hw); - - for (i = hw->interfaces; i; i = i->by_hardware_next) - update_interface_rr(m, i, remove); -} - -static void free_address(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a) { - g_assert(m); - g_assert(a); - g_assert(a->interface); - - AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a); - - if (a->entry_group) - avahi_entry_group_free(a->entry_group); - - g_free(a); -} - -static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean send_goodbye) { - g_assert(m); - g_assert(i); - - g_message("removing interface %s.%i", i->hardware->name, i->protocol); - avahi_goodbye_interface(m->server, i, send_goodbye); - g_message("flushing..."); - avahi_packet_scheduler_flush_responses(i->scheduler); - g_message("done"); - - g_assert(!i->announcements); - - while (i->addresses) - free_address(m, i->addresses); - - avahi_packet_scheduler_free(i->scheduler); - avahi_cache_free(i->cache); - - AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i); - AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i); - - g_free(i); -} - -static void free_hw_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, gboolean send_goodbye) { - g_assert(m); - g_assert(hw); - - while (hw->interfaces) - free_interface(m, hw->interfaces, send_goodbye); - - AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, m->hw_interfaces, hw); - g_hash_table_remove(m->hash_table, &hw->index); - - g_free(hw->name); - g_free(hw); -} - -static AvahiInterfaceAddress* get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) { - AvahiInterfaceAddress *ia; - - g_assert(m); - g_assert(i); - g_assert(raddr); - - for (ia = i->addresses; ia; ia = ia->address_next) - if (avahi_address_cmp(&ia->address, raddr) == 0) - return ia; - - return NULL; -} - -static int netlink_list_items(AvahiNetlink *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 avahi_netlink_send(nl, n, ret_seq); -} - -static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, guchar protocol) { - AvahiInterface *i; - - g_assert(m); - g_assert(hw); - g_assert(protocol != AF_UNSPEC); - - i = g_new(AvahiInterface, 1); - i->monitor = m; - i->hardware = hw; - i->protocol = protocol; - i->announcing = FALSE; - - AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); - AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements); - - i->cache = avahi_cache_new(m->server, i); - i->scheduler = avahi_packet_scheduler_new(m->server, i); - - AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i); - AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i); -} - -static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i) { - gboolean b; - - g_assert(m); - g_assert(i); - - b = avahi_interface_relevant(i); - - if (b && !i->announcing) { - g_message("New relevant interface %s.%i", i->hardware->name, i->protocol); - - if (i->protocol == AF_INET) - avahi_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4); - if (i->protocol == AF_INET6) - avahi_mdns_mcast_join_ipv6 (i->hardware->index, m->server->fd_ipv6); - - i->announcing = TRUE; - avahi_announce_interface(m->server, i); - } else if (!b && i->announcing) { - g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol); - - avahi_goodbye_interface(m->server, i, FALSE); - - if (i->protocol == AF_INET) - avahi_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4); - if (i->protocol == AF_INET6) - avahi_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6); - - i->announcing = FALSE; - } -} - -static void check_hw_interface_relevant(AvahiInterfaceMonitor *m, AvahiHwInterface *hw) { - AvahiInterface *i; - - g_assert(m); - g_assert(hw); - - for (i = hw->interfaces; i; i = i->by_hardware_next) - check_interface_relevant(m, i); -} - -static void callback(AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata) { - AvahiInterfaceMonitor *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); - AvahiHwInterface *hw; - struct rtattr *a = NULL; - size_t l; - - if (ifinfomsg->ifi_family != AF_UNSPEC) - return; - - if (!(hw = g_hash_table_lookup(m->hash_table, &ifinfomsg->ifi_index))) { - hw = g_new(AvahiHwInterface, 1); - hw->monitor = m; - hw->name = NULL; - hw->flags = 0; - hw->mtu = 1500; - hw->index = ifinfomsg->ifi_index; - - AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces); - AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw); - - g_hash_table_insert(m->hash_table, &hw->index, hw); - - if (m->server->fd_ipv4 >= 0) - new_interface(m, hw, AF_INET); - if (m->server->fd_ipv6 >= 0) - new_interface(m, hw, AF_INET6); - } - - hw->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(hw->name); - hw->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a)); - break; - - case IFLA_MTU: - g_assert(RTA_PAYLOAD(a) == sizeof(unsigned int)); - hw->mtu = *((unsigned int*) RTA_DATA(a)); - break; - - default: - ; - } - - a = RTA_NEXT(a, l); - } - - update_hw_interface_rr(m, hw, FALSE); - check_hw_interface_relevant(m, hw); - - } else if (n->nlmsg_type == RTM_DELLINK) { - struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); - AvahiHwInterface *hw; - AvahiInterface *i; - - if (ifinfomsg->ifi_family != AF_UNSPEC) - return; - - if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index))) - return; - - update_hw_interface_rr(m, hw, TRUE); - free_hw_interface(m, hw, FALSE); - - } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { - - struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); - AvahiInterface *i; - struct rtattr *a = NULL; - size_t l; - AvahiAddress raddr; - int raddr_valid = 0; - - if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) - return; - - if (!(i = (AvahiInterface*) avahi_interface_monitor_get_interface(m, ifaddrmsg->ifa_index, ifaddrmsg->ifa_family))) - 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.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) { - AvahiInterfaceAddress *addr; - - if (!(addr = get_address(m, i, &raddr))) { - addr = g_new(AvahiInterfaceAddress, 1); - addr->monitor = m; - addr->address = raddr; - addr->interface = i; - addr->entry_group = NULL; - - AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, addr); - } - - addr->flags = ifaddrmsg->ifa_flags; - addr->scope = ifaddrmsg->ifa_scope; - - update_address_rr(m, addr, FALSE); - check_interface_relevant(m, i); - } else { - AvahiInterfaceAddress *addr; - - if (!(addr = get_address(m, i, &raddr))) - return; - - update_address_rr(m, addr, TRUE); - free_address(m, addr); - - check_interface_relevant(m, i); - } - - } 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; - g_message("Enumeration complete"); - } - - } 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)); - } -} - -AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) { - AvahiInterfaceMonitor *m = NULL; - - m = g_new0(AvahiInterfaceMonitor, 1); - m->server = s; - if (!(m->netlink = avahi_netlink_new(s->context, G_PRIORITY_DEFAULT-10, 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); - - AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces); - AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces); - - if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0) - goto fail; - - m->list = LIST_IFACE; - - return m; - -fail: - avahi_interface_monitor_free(m); - return NULL; -} - -void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { - g_assert(m); - - while (m->list != LIST_DONE) { - if (!avahi_netlink_work(m->netlink, TRUE)) - break; - } -} - -void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { - g_assert(m); - - while (m->hw_interfaces) - free_hw_interface(m, m->hw_interfaces, TRUE); - - g_assert(!m->interfaces); - - - if (m->netlink) - avahi_netlink_free(m->netlink); - - if (m->hash_table) - g_hash_table_destroy(m->hash_table); - - g_free(m); -} - - -AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol) { - AvahiHwInterface *hw; - AvahiInterface *i; - - g_assert(m); - g_assert(index > 0); - g_assert(protocol != AF_UNSPEC); - - if (!(hw = avahi_interface_monitor_get_hw_interface(m, index))) - return NULL; - - for (i = hw->interfaces; i; i = i->by_hardware_next) - if (i->protocol == protocol) - return i; - - return NULL; -} - -AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index) { - g_assert(m); - g_assert(index > 0); - - return g_hash_table_lookup(m->hash_table, &index); -} - - -void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { - g_assert(i); - g_assert(p); - - if (avahi_interface_relevant(i)) { - g_message("sending on '%s.%i'", i->hardware->name, i->protocol); - - if (i->protocol == AF_INET && i->monitor->server->fd_ipv4 >= 0) - avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p); - else if (i->protocol == AF_INET6 && i->monitor->server->fd_ipv6 >= 0) - avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p); - } -} - -void avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean immediately) { - g_assert(i); - g_assert(key); - - if (avahi_interface_relevant(i)) - avahi_packet_scheduler_post_query(i->scheduler, key, immediately); -} - - -void avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { - g_assert(i); - g_assert(record); - - if (avahi_interface_relevant(i)) - avahi_packet_scheduler_post_response(i->scheduler, a, record, flush_cache, immediately); -} - -void avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, gboolean immediately) { - g_assert(i); - g_assert(record); - - if (avahi_interface_relevant(i)) - avahi_packet_scheduler_post_probe(i->scheduler, record, immediately); -} - -void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f) { - AvahiInterface *i; - g_assert(m); - - for (i = m->interfaces; i; i = i->interface_next) { - if (avahi_interface_relevant(i)) { - fprintf(f, "\n;;; INTERFACE %s.%i ;;;\n", i->hardware->name, i->protocol); - avahi_cache_dump(i->cache, f); - } - } - fprintf(f, "\n"); -} - -gboolean avahi_interface_relevant(AvahiInterface *i) { - g_assert(i); - - return - (i->hardware->flags & IFF_UP) && - (i->hardware->flags & IFF_RUNNING) && - !(i->hardware->flags & IFF_LOOPBACK) && - (i->hardware->flags & IFF_MULTICAST) && - i->addresses; -} - -gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) { - g_assert(a); - - return a->scope == RT_SCOPE_UNIVERSE; -} - - -gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol) { - g_assert(i); - - if (index > 0 && index != i->hardware->index) - return FALSE; - - if (protocol != AF_UNSPEC && protocol != i->protocol) - return FALSE; - - return TRUE; -} - - -void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint interface, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata) { - g_assert(m); - g_assert(callback); - - if (interface > 0) { - if (protocol != AF_UNSPEC) { - AvahiInterface *i; - - if ((i = avahi_interface_monitor_get_interface(m, interface, protocol))) - callback(m, i, userdata); - - } else { - AvahiHwInterface *hw; - AvahiInterface *i; - - if ((hw = avahi_interface_monitor_get_hw_interface(m, interface))) - for (i = hw->interfaces; i; i = i->by_hardware_next) - if (avahi_interface_match(i, interface, protocol)) - callback(m, i, userdata); - } - - } else { - AvahiInterface *i; - - for (i = m->interfaces; i; i = i->interface_next) - if (avahi_interface_match(i, interface, protocol)) - callback(m, i, userdata); - } -} diff --git a/iface.h b/iface.h deleted file mode 100644 index cab4949..0000000 --- a/iface.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef fooifacehfoo -#define fooifacehfoo - -#include - -typedef struct _AvahiInterfaceMonitor AvahiInterfaceMonitor; -typedef struct _AvahiInterfaceAddress AvahiInterfaceAddress; -typedef struct _AvahiInterface AvahiInterface; -typedef struct _AvahiHwInterface AvahiHwInterface; - -#include "address.h" -#include "server.h" -#include "netlink.h" -#include "cache.h" -#include "llist.h" -#include "psched.h" -#include "dns.h" -#include "announce.h" - -struct _AvahiInterfaceMonitor { - AvahiServer *server; - AvahiNetlink *netlink; - GHashTable *hash_table; - - AVAHI_LLIST_HEAD(AvahiInterface, interfaces); - AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces); - - guint query_addr_seq, query_link_seq; - - enum { - LIST_IFACE, - LIST_ADDR, - LIST_DONE - } list; -}; - -struct _AvahiHwInterface { - AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware); - AvahiInterfaceMonitor *monitor; - - gchar *name; - gint index; - guint flags; - guint mtu; - - AVAHI_LLIST_HEAD(AvahiInterface, interfaces); -}; - -struct _AvahiInterface { - AVAHI_LLIST_FIELDS(AvahiInterface, interface); - AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware); - AvahiInterfaceMonitor *monitor; - - AvahiHwInterface *hardware; - guchar protocol; - gboolean announcing; - - AvahiCache *cache; - AvahiPacketScheduler *scheduler; - - AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses); - AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements); -}; - -struct _AvahiInterfaceAddress { - AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address); - AvahiInterfaceMonitor *monitor; - - guchar flags; - guchar scope; - AvahiAddress address; - - AvahiEntryGroup *entry_group; - AvahiInterface *interface; -}; - -AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server); -void avahi_interface_monitor_free(AvahiInterfaceMonitor *m); - -void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m); - -AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol); -AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index); - -void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); - -void avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, gboolean immediately); -void avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, gboolean immediately); -void avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately); - -void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f); - -gboolean avahi_interface_relevant(AvahiInterface *i); -gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a); - -gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol); - -typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata); - -void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint index, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata); - -#endif diff --git a/libavahi-core/Makefile b/libavahi-core/Makefile new file mode 100644 index 0000000..ff3977e --- /dev/null +++ b/libavahi-core/Makefile @@ -0,0 +1,28 @@ +#CC=gcc-2.95 +CFLAGS=-g -O1 -Wall -W -pipe $(shell pkg-config --cflags glib-2.0) -Wno-unused +LIBS=$(shell pkg-config --libs glib-2.0) + +all: strlst-test prioq-test domain-test dns-test flexmdns + +flexmdns: timeeventq.o main.o iface.o netlink.o server.o address.o util.o prioq.o cache.o rr.o dns.o socket.o psched.o announce.o subscribe.o strlst.o + $(CC) -o $@ $^ $(LIBS) + +#test-llist: test-llist.o +# $(CC) -o $@ $^ $(LIBS) + +prioq-test: prioq-test.o prioq.o + $(CC) -o $@ $^ $(LIBS) + +strlst-test: strlst-test.o strlst.o + $(CC) -o $@ $^ $(LIBS) + +domain-test: domain-test.o util.o + $(CC) -o $@ $^ $(LIBS) + +dns-test: dns-test.o util.o dns.o rr.o strlst.o + $(CC) -o $@ $^ $(LIBS) + +*.o: *.h + +clean: + rm -f *.o flexmdns prioq-test strlst-test domain-test dns-test diff --git a/libavahi-core/address.c b/libavahi-core/address.c new file mode 100644 index 0000000..e5f8866 --- /dev/null +++ b/libavahi-core/address.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +#include "address.h" + +guint avahi_address_get_size(const AvahiAddress *a) { + g_assert(a); + + if (a->family == AF_INET) + return 4; + else if (a->family == AF_INET6) + return 16; + + return 0; +} + +gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) { + g_assert(a); + g_assert(b); + + if (a->family != b->family) + return -1; + + return memcmp(a->data.data, b->data.data, avahi_address_get_size(a)); +} + +gchar *avahi_address_snprint(char *s, guint length, const AvahiAddress *a) { + g_assert(s); + g_assert(length); + g_assert(a); + return (gchar*) inet_ntop(a->family, a->data.data, s, length); +} + +gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a) { + guint32 n = ntohl(a->address); + g_assert(a); + + return g_strdup_printf("%u.%u.%u.%u.in-addr.arpa", n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24); +} + +static gchar *reverse_lookup_name_ipv6(const AvahiIPv6Address *a, const gchar *suffix) { + + 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.%s", + a->address[15] & 0xF, + a->address[15] >> 4, + a->address[14] & 0xF, + a->address[14] >> 4, + a->address[13] & 0xF, + a->address[13] >> 4, + a->address[12] & 0xF, + a->address[12] >> 4, + a->address[11] & 0xF, + a->address[11] >> 4, + a->address[10] & 0xF, + a->address[10] >> 4, + a->address[9] & 0xF, + a->address[9] >> 4, + a->address[8] & 0xF, + a->address[8] >> 4, + a->address[7] & 0xF, + a->address[7] >> 4, + a->address[6] & 0xF, + a->address[6] >> 4, + a->address[5] & 0xF, + a->address[5] >> 4, + a->address[4] & 0xF, + a->address[4] >> 4, + a->address[3] & 0xF, + a->address[3] >> 4, + a->address[2] & 0xF, + a->address[2] >> 4, + a->address[1] & 0xF, + a->address[1] >> 4, + a->address[0] & 0xF, + a->address[0] >> 4, + suffix); +} + +gchar *avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a) { + return reverse_lookup_name_ipv6(a, "ip6.arpa"); +} + +gchar *avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a) { + return reverse_lookup_name_ipv6(a, "ip6.int"); +} + +AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr) { + g_assert(ret_addr); + g_assert(s); + + if (inet_pton(family, s, ret_addr->data.data) < 0) + return NULL; + + ret_addr->family = family; + + return ret_addr; +} + +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) { + g_assert(sa); + g_assert(ret_addr); + + g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + ret_addr->family = sa->sa_family; + + if (sa->sa_family == AF_INET) + memcpy(&ret_addr->data.ipv4, &((struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4)); + else + memcpy(&ret_addr->data.ipv6, &((struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6)); + + return ret_addr; +} + +guint16 avahi_port_from_sockaddr(const struct sockaddr* sa) { + g_assert(sa); + + g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + if (sa->sa_family == AF_INET) + return ntohs(((struct sockaddr_in*) sa)->sin_port); + else + return ntohs(((struct sockaddr_in6*) sa)->sin6_port); +} diff --git a/libavahi-core/address.h b/libavahi-core/address.h new file mode 100644 index 0000000..2eea75d --- /dev/null +++ b/libavahi-core/address.h @@ -0,0 +1,40 @@ +#ifndef fooaddresshfoo +#define fooaddresshfoo + +#include + +#include + +typedef struct { + guint32 address; +} AvahiIPv4Address; + +typedef struct { + guint8 address[16]; +} AvahiIPv6Address; + +typedef struct { + guchar family; + + union { + AvahiIPv6Address ipv6; + AvahiIPv4Address ipv4; + guint8 data[0]; + } data; +} AvahiAddress; + +guint avahi_address_get_size(const AvahiAddress *a); +gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); + +gchar *avahi_address_snprint(char *ret_s, guint length, const AvahiAddress *a); + +AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr); + +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr); +guint16 avahi_port_from_sockaddr(const struct sockaddr* sa); + +gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a); +gchar* avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a); +gchar* avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a); + +#endif diff --git a/libavahi-core/announce.c b/libavahi-core/announce.c new file mode 100644 index 0000000..538ba2e --- /dev/null +++ b/libavahi-core/announce.c @@ -0,0 +1,397 @@ +#include "announce.h" +#include "util.h" + +#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250 +#define AVAHI_PROBE_JITTER_MSEC 250 +#define AVAHI_PROBE_INTERVAL_MSEC 250 + +static void remove_announcement(AvahiServer *s, AvahiAnnouncement *a) { + g_assert(s); + g_assert(a); + + if (a->time_event) + avahi_time_event_queue_remove(s->time_event_queue, a->time_event); + + AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_interface, a->interface->announcements, a); + AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_entry, a->entry->announcements, a); + + g_free(a); +} + +static void elapse_announce(AvahiTimeEvent *e, void *userdata); + +static void set_timeout(AvahiAnnouncement *a, const GTimeVal *tv) { + g_assert(a); + + if (!tv) { + if (a->time_event) { + avahi_time_event_queue_remove(a->server->time_event_queue, a->time_event); + a->time_event = NULL; + } + } else { + + if (a->time_event) + avahi_time_event_queue_update(a->server->time_event_queue, a->time_event, tv); + else + a->time_event = avahi_time_event_queue_add(a->server->time_event_queue, tv, elapse_announce, a); + } +} + +static void next_state(AvahiAnnouncement *a); + +void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately) { + AvahiEntry *e; + g_assert(g); + g_assert(!g->dead); + + /* Check whether all group members have been probed */ + + if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) + return; + + avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED); + + if (g->dead) + return; + + for (e = g->entries; e; e = e->entries_next) { + AvahiAnnouncement *a; + + for (a = e->announcements; a; a = a->by_entry_next) { + + if (a->state != AVAHI_WAITING) + continue; + + a->state = AVAHI_ANNOUNCING; + + if (immediately) { + /* Shortcut */ + + a->n_iteration = 1; + next_state(a); + } else { + GTimeVal tv; + a->n_iteration = 0; + avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + set_timeout(a, &tv); + } + } + } +} + +static void next_state(AvahiAnnouncement *a) { + g_assert(a); + +/* g_message("%i -- %u", a->state, a->n_iteration); */ + + if (a->state == AVAHI_WAITING) { + + g_assert(a->entry->group); + + avahi_entry_group_check_probed(a->entry->group, TRUE); + + } else if (a->state == AVAHI_PROBING) { + + if (a->n_iteration >= 4) { + /* Probing done */ + + gchar *t; + + g_message("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record)); + g_free(t); + + if (a->entry->group) { + g_assert(a->entry->group->n_probing); + a->entry->group->n_probing--; + } + + if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING) + a->state = AVAHI_WAITING; + else { + a->state = AVAHI_ANNOUNCING; + a->n_iteration = 1; + } + + set_timeout(a, NULL); + next_state(a); + } else { + GTimeVal tv; + + avahi_interface_post_probe(a->interface, a->entry->record, FALSE); + + avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0); + set_timeout(a, &tv); + + a->n_iteration++; + } + + } else if (a->state == AVAHI_ANNOUNCING) { + + avahi_interface_post_response(a->interface, NULL, a->entry->record, a->entry->flags & AVAHI_ENTRY_UNIQUE, FALSE); + + if (++a->n_iteration >= 4) { + gchar *t; + /* Announcing done */ + + g_message("Enough announcements for record [%s]", t = avahi_record_to_string(a->entry->record)); + g_free(t); + + a->state = AVAHI_ESTABLISHED; + + set_timeout(a, NULL); + } else { + GTimeVal tv; + avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + + if (a->n_iteration < 10) + a->sec_delay *= 2; + + set_timeout(a, &tv); + } + } +} + +static void elapse_announce(AvahiTimeEvent *e, void *userdata) { + g_assert(e); + + next_state(userdata); +} + +AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncement *a; + + g_assert(s); + g_assert(e); + g_assert(i); + + for (a = e->announcements; a; a = a->by_entry_next) + if (a->interface == i) + return a; + + return NULL; +} + +static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { + AvahiAnnouncement *a; + GTimeVal tv; + gchar *t; + + g_assert(s); + g_assert(i); + g_assert(e); + g_assert(!e->dead); + +/* g_message("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */ +/* g_free(t); */ + + if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_commited(e)) + return; + + /* We don't want duplicate announcements */ + if (avahi_get_announcement(s, e, i)) + return; + + a = g_new(AvahiAnnouncement, 1); + a->server = s; + a->interface = i; + a->entry = e; + + if ((e->flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_NOPROBE)) + a->state = AVAHI_PROBING; + else if (!(e->flags & AVAHI_ENTRY_NOANNOUNCE)) { + + if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED) + a->state = AVAHI_ANNOUNCING; + else + a->state = AVAHI_WAITING; + + } else + a->state = AVAHI_ESTABLISHED; + + + g_message("New announcement on interface %s.%i for entry [%s] state=%i", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record), a->state); + g_free(t); + + a->n_iteration = 1; + a->sec_delay = 1; + a->time_event = NULL; + + if (a->state == AVAHI_PROBING) + if (e->group) + e->group->n_probing++; + + AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_interface, i->announcements, a); + AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_entry, e->announcements, a); + + if (a->state == AVAHI_PROBING) { + avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC); + set_timeout(a, &tv); + } else if (a->state == AVAHI_ANNOUNCING) { + avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + set_timeout(a, &tv); + } +} + +void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { + AvahiEntry *e; + + g_assert(s); + g_assert(i); + + if (!i->announcing) + return; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + new_announcement(s, i, e); +} + +static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { + AvahiEntry *e = userdata; + + g_assert(m); + g_assert(i); + g_assert(e); + g_assert(!e->dead); + + new_announcement(m->server, i, e); +} + +void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) { + g_assert(s); + g_assert(e); + g_assert(!e->dead); + + avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e); +} + +void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g) { + AvahiEntry *e; + + g_assert(s); + g_assert(g); + + for (e = g->entries; e; e = e->by_group_next) + if (!e->dead) + avahi_announce_entry(s, e); +} + +gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncement *a; + + g_assert(s); + g_assert(e); + g_assert(i); + g_assert(!e->dead); + + if (!(a = avahi_get_announcement(s, e, i))) + return FALSE; + + return a->state == AVAHI_ANNOUNCING || a->state == AVAHI_ESTABLISHED; +} + +gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncement *a; + + g_assert(s); + g_assert(e); + g_assert(i); + g_assert(!e->dead); + + if (!(a = avahi_get_announcement(s, e, i))) + return FALSE; + + return a->state == AVAHI_PROBING || a->state == AVAHI_WAITING; +} + +static AvahiRecord *make_goodbye_record(AvahiRecord *r) { +/* gchar *t; */ + AvahiRecord *g; + + g_assert(r); + +/* g_message("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */ +/* g_free(t); */ + + g = avahi_record_copy(r); + g_assert(g->ref == 1); + g->ttl = 0; + + return g; +} + +static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { + AvahiEntry *e = userdata; + AvahiRecord *g; + + g_assert(m); + g_assert(i); + g_assert(e); + g_assert(!e->dead); + + if (!avahi_interface_match(i, e->interface, e->protocol)) + return; + + if (e->flags & AVAHI_ENTRY_NOANNOUNCE) + return; + + if (!avahi_entry_registered(m->server, e, i)) + return; + + g = make_goodbye_record(e->record); + avahi_interface_post_response(i, NULL, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); + avahi_record_unref(g); +} + +void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean goodbye) { + g_assert(s); + g_assert(i); + +/* g_message("goodbye interface: %s.%u", i->hardware->name, i->protocol); */ + + if (goodbye && avahi_interface_relevant(i)) { + AvahiEntry *e; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + send_goodbye_callback(s->monitor, i, e); + } + + while (i->announcements) + remove_announcement(s, i->announcements); + +/* g_message("goodbye interface done: %s.%u", i->hardware->name, i->protocol); */ + +} + +void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean goodbye) { + g_assert(s); + g_assert(e); + +/* g_message("goodbye entry: %p", e); */ + + if (goodbye && !e->dead) + avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e); + + while (e->announcements) + remove_announcement(s, e->announcements); + +/* g_message("goodbye entry done: %p", e); */ + +} + +void avahi_goodbye_all(AvahiServer *s, gboolean goodbye) { + AvahiEntry *e; + + g_assert(s); + +/* g_message("goodbye all"); */ + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + avahi_goodbye_entry(s, e, goodbye); + +/* g_message("goodbye all done"); */ + +} + diff --git a/libavahi-core/announce.h b/libavahi-core/announce.h new file mode 100644 index 0000000..cd96977 --- /dev/null +++ b/libavahi-core/announce.h @@ -0,0 +1,51 @@ +#ifndef fooannouncehfoo +#define fooannouncehfoo + +#include + +typedef struct _AvahiAnnouncement AvahiAnnouncement; + +#include "llist.h" +#include "iface.h" +#include "server.h" +#include "timeeventq.h" + +typedef enum { + AVAHI_PROBING, + AVAHI_WAITING, /* wait for other records in group */ + AVAHI_ANNOUNCING, + AVAHI_ESTABLISHED +} AvahiAnnouncementState; + +struct _AvahiAnnouncement { + AvahiServer *server; + AvahiInterface *interface; + AvahiEntry *entry; + + AvahiTimeEvent *time_event; + + AvahiAnnouncementState state; + guint n_iteration; + guint sec_delay; + + AVAHI_LLIST_FIELDS(AvahiAnnouncement, by_interface); + AVAHI_LLIST_FIELDS(AvahiAnnouncement, by_entry); +}; + +void avahi_announce_interface(AvahiServer *s, AvahiInterface *i); +void avahi_announce_entry(AvahiServer *s, AvahiEntry *e); +void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g); + +void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately); + +gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); +gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + +void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean send); +void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean send); + +void avahi_goodbye_all(AvahiServer *s, gboolean send); + +AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + +#endif diff --git a/libavahi-core/avahi.h b/libavahi-core/avahi.h new file mode 100644 index 0000000..1c59faf --- /dev/null +++ b/libavahi-core/avahi.h @@ -0,0 +1,143 @@ +#ifndef fooAvahihfoo +#define fooAvahihfoo + +#include +#include + +typedef struct _AvahiServer AvahiServer; +typedef struct _AvahiEntry AvahiEntry; +typedef struct _AvahiEntryGroup AvahiEntryGroup; + +#include "address.h" +#include "rr.h" + +typedef enum { + AVAHI_ENTRY_NULL = 0, + AVAHI_ENTRY_UNIQUE = 1, + AVAHI_ENTRY_NOPROBE = 2, + AVAHI_ENTRY_NOANNOUNCE = 4 +} AvahiEntryFlags; + +typedef enum { + AVAHI_ENTRY_GROUP_UNCOMMITED, + AVAHI_ENTRY_GROUP_REGISTERING, + AVAHI_ENTRY_GROUP_ESTABLISHED, + AVAHI_ENTRY_GROUP_COLLISION +} AvahiEntryGroupState; + +typedef void (*AvahiEntryGroupCallback) (AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata); + +AvahiServer *avahi_server_new(GMainContext *c); +void avahi_server_free(AvahiServer* s); + +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state); +void avahi_server_dump(AvahiServer *s, FILE *f); + +AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata); +void avahi_entry_group_free(AvahiEntryGroup *g); +void avahi_entry_group_commit(AvahiEntryGroup *g); +AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g); + +void avahi_server_add( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + AvahiRecord *r); + +void avahi_server_add_ptr( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + const gchar *dest); + +void avahi_server_add_address( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + AvahiAddress *a); + +void avahi_server_add_text( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + ... /* text records, terminated by NULL */); + +void avahi_server_add_text_va( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + va_list va); + +void avahi_server_add_text_strlst( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + AvahiStringList *strlst); + +void avahi_server_add_service( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + const gchar *type, + const gchar *name, + const gchar *domain, + const gchar *host, + guint16 port, + ... /* text records, terminated by NULL */); + +void avahi_server_add_service_va( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + const gchar *type, + const gchar *name, + const gchar *domain, + const gchar *host, + guint16 port, + va_list va); + +void avahi_server_add_service_strlst( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + const gchar *type, + const gchar *name, + const gchar *domain, + const gchar *host, + guint16 port, + AvahiStringList *strlst); + +typedef enum { + AVAHI_SUBSCRIPTION_NEW, + AVAHI_SUBSCRIPTION_REMOVE, + AVAHI_SUBSCRIPTION_CHANGE +} AvahiSubscriptionEvent; + +typedef struct _AvahiSubscription AvahiSubscription; + +typedef void (*AvahiSubscriptionCallback)(AvahiSubscription *s, AvahiRecord *record, gint interface, guchar protocol, AvahiSubscriptionEvent event, gpointer userdata); + +AvahiSubscription *avahi_subscription_new(AvahiServer *s, AvahiKey *key, gint interface, guchar protocol, AvahiSubscriptionCallback callback, gpointer userdata); +void avahi_subscription_free(AvahiSubscription *s); + +#endif diff --git a/libavahi-core/cache.c b/libavahi-core/cache.c new file mode 100644 index 0000000..c2d680e --- /dev/null +++ b/libavahi-core/cache.c @@ -0,0 +1,346 @@ +#include + +#include "util.h" +#include "cache.h" + +static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { + AvahiCacheEntry *t; + + g_assert(c); + g_assert(e); + +/* g_message("removing from cache: %p %p", c, e); */ + + /* Remove from hash table */ + t = g_hash_table_lookup(c->hash_table, e->record->key); + AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e); + if (t) + g_hash_table_replace(c->hash_table, t->record->key, t); + else + g_hash_table_remove(c->hash_table, e->record->key); + + /* Remove from linked list */ + AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e); + + if (e->time_event) + avahi_time_event_queue_remove(c->server->time_event_queue, e->time_event); + + avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_REMOVE); + + avahi_record_unref(e->record); + + g_free(e); +} + +AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) { + AvahiCache *c; + g_assert(server); + + c = g_new(AvahiCache, 1); + c->server = server; + c->interface = iface; + c->hash_table = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal); + + AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries); + + return c; +} + +void avahi_cache_free(AvahiCache *c) { + g_assert(c); + + while (c->entries) + remove_entry(c, c->entries); + + g_hash_table_destroy(c->hash_table); + + g_free(c); +} + +AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k) { + g_assert(c); + g_assert(k); + + g_assert(!avahi_key_is_pattern(k)); + + return g_hash_table_lookup(c->hash_table, k); +} + +gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata) { + gpointer ret; + + g_assert(c); + g_assert(pattern); + g_assert(cb); + + if (avahi_key_is_pattern(pattern)) { + AvahiCacheEntry *e, *n; + + for (e = c->entries; e; e = n) { + n = e->entry_next; + + if (avahi_key_pattern_match(pattern, e->record->key)) + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + + } else { + AvahiCacheEntry *e, *n; + + for (e = avahi_cache_lookup_key(c, pattern); e; e = n) { + n = e->by_key_next; + + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + } + + return NULL; +} + +static gpointer lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { + g_assert(c); + g_assert(pattern); + g_assert(e); + + if (avahi_record_equal_no_ttl(e->record, userdata)) + return e; + + return NULL; +} + +AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r) { + AvahiCacheEntry *e; + + g_assert(c); + g_assert(r); + + return avahi_cache_walk(c, r->key, lookup_record_callback, r); +} + +static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent); + +static void elapse_func(AvahiTimeEvent *t, void *userdata) { + AvahiCacheEntry *e = userdata; + + g_assert(t); + g_assert(e); + + if (e->state == AVAHI_CACHE_FINAL) { + remove_entry(e->cache, e); + g_message("Removing entry from cache due to expiration"); + } else { + guint percent = 0; + + switch (e->state) { + case AVAHI_CACHE_VALID: + e->state = AVAHI_CACHE_EXPIRY1; + percent = 85; + break; + + case AVAHI_CACHE_EXPIRY1: + e->state = AVAHI_CACHE_EXPIRY2; + percent = 90; + break; + case AVAHI_CACHE_EXPIRY2: + e->state = AVAHI_CACHE_EXPIRY3; + percent = 95; + break; + + case AVAHI_CACHE_EXPIRY3: + e->state = AVAHI_CACHE_FINAL; + percent = 100; + break; + + default: + ; + } + + g_assert(percent > 0); + + g_message("Requesting cache entry update at %i%%.", percent); + + /* Request a cache update, if we are subscribed to this entry */ + if (avahi_is_subscribed(e->cache->server, e->record->key)) + avahi_interface_post_query(e->cache->interface, e->record->key, TRUE); + + /* Check again later */ + next_expiry(e->cache, e, percent); + } +} + +static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) { + g_assert(c); + g_assert(e); + + if (e->time_event) + avahi_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry); + else + e->time_event = avahi_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e); +} + +static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent) { + gulong usec; + + g_assert(c); + g_assert(e); + g_assert(percent > 0 && percent <= 100); + + e->expiry = e->timestamp; + + usec = e->record->ttl * 10000; + + /* 2% jitter */ + usec = g_random_int_range(usec*percent, usec*(percent+2)); + + g_time_val_add(&e->expiry, usec); + update_time_event(c, e); +} + +void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a) { + AvahiCacheEntry *e, *t; + gchar *txt; + + g_assert(c); + g_assert(r && r->ref >= 1); + + g_message("cache update: %s", (txt = avahi_record_to_string(r))); + g_free(txt); + + if (r->ttl == 0) { + + /* This is a goodbye request */ + + if ((e = avahi_cache_lookup_record(c, r))) { + + e->state = AVAHI_CACHE_FINAL; + g_get_current_time(&e->timestamp); + e->expiry = e->timestamp; + g_time_val_add(&e->expiry, 1000000); /* 1s */ + update_time_event(c, e); + } + + } else { + + /* This is an update request */ + + if ((t = e = avahi_cache_lookup_key(c, r->key))) { + + if (unique) { + + /* For unique records, remove all entries but one */ + while (e->by_key_next) + remove_entry(c, e->by_key_next); + + } else { + + /* For non-unique record, look for exactly the same entry */ + for (; e; e = e->by_key_next) + if (avahi_record_equal_no_ttl(e->record, r)) + break; + } + } + + if (e) { + +/* g_message("found matching cache entry"); */ + + /* We are the first in the linked list so let's replace the hash table key with the new one */ + if (e->by_key_prev == NULL) + g_hash_table_replace(c->hash_table, r->key, e); + + /* Notify subscribers */ + if (!avahi_record_equal_no_ttl(e->record, r)) + avahi_subscription_notify(c->server, c->interface, r, AVAHI_SUBSCRIPTION_CHANGE); + + /* Update the record */ + avahi_record_unref(e->record); + e->record = avahi_record_ref(r); + + } else { + /* No entry found, therefore we create a new one */ + +/* g_message("couldn't find matching cache entry"); */ + + e = g_new(AvahiCacheEntry, 1); + e->cache = c; + e->time_event = NULL; + e->record = avahi_record_ref(r); + + /* Append to hash table */ + AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, t, e); + g_hash_table_replace(c->hash_table, e->record->key, t); + + /* Append to linked list */ + AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); + + /* Notify subscribers */ + avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_NEW); + } + + e->origin = *a; + g_get_current_time(&e->timestamp); + next_expiry(c, e, 80); + e->state = AVAHI_CACHE_VALID; + } +} + +static gpointer drop_key_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) { + g_assert(c); + g_assert(pattern); + g_assert(e); + + remove_entry(c, e); + return NULL; +} + +void avahi_cache_drop_key(AvahiCache *c, AvahiKey *k) { + g_assert(c); + g_assert(k); + + avahi_cache_walk(c, k, drop_key_callback, NULL); +} + +void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r) { + AvahiCacheEntry *e; + + g_assert(c); + g_assert(r); + + if ((e = avahi_cache_lookup_record(c, r))) + remove_entry(c, e); +} + +static void func(gpointer key, gpointer data, gpointer userdata) { + AvahiCacheEntry *e = data; + AvahiKey *k = key; + gchar *t; + + t = avahi_record_to_string(e->record); + fprintf((FILE*) userdata, "%s\n", t); + g_free(t); +} + +void avahi_cache_dump(AvahiCache *c, FILE *f) { + g_assert(c); + g_assert(f); + + fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n"); + g_hash_table_foreach(c->hash_table, func, f); +} + +gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { + GTimeVal now; + guint age; + + g_assert(c); + g_assert(e); + + g_get_current_time(&now); + + age = avahi_timeval_diff(&now, &e->timestamp)/1000000; + + g_message("age: %u, ttl/2: %u", age, e->record->ttl); + + return age >= e->record->ttl/2; +} diff --git a/libavahi-core/cache.h b/libavahi-core/cache.h new file mode 100644 index 0000000..de0ec9f --- /dev/null +++ b/libavahi-core/cache.h @@ -0,0 +1,66 @@ +#ifndef foocachehfoo +#define foocachehfoo + +#include + +struct _AvahiCache; +typedef struct _AvahiCache AvahiCache; + +#include "prioq.h" +#include "server.h" +#include "llist.h" +#include "timeeventq.h" + +typedef enum { + AVAHI_CACHE_VALID, + AVAHI_CACHE_EXPIRY1, + AVAHI_CACHE_EXPIRY2, + AVAHI_CACHE_EXPIRY3, + AVAHI_CACHE_FINAL +} AvahiCacheEntryState; + +typedef struct AvahiCacheEntry AvahiCacheEntry; + +struct AvahiCacheEntry { + AvahiCache *cache; + AvahiRecord *record; + GTimeVal timestamp; + GTimeVal expiry; + + AvahiAddress origin; + + AvahiCacheEntryState state; + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry); +}; + +struct _AvahiCache { + AvahiServer *server; + + AvahiInterface *interface; + + GHashTable *hash_table; + + AVAHI_LLIST_HEAD(AvahiCacheEntry, entries); +}; + +AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface); +void avahi_cache_free(AvahiCache *c); + +AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k); +AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r); + +void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a); + +void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r); + +void avahi_cache_dump(AvahiCache *c, FILE *f); + +typedef gpointer AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata); +gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata); + +gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e); + +#endif diff --git a/libavahi-core/dns-test.c b/libavahi-core/dns-test.c new file mode 100644 index 0000000..d8c7b53 --- /dev/null +++ b/libavahi-core/dns-test.c @@ -0,0 +1,35 @@ +#include "dns.h" +#include "util.h" + +int main(int argc, char *argv[]) { + gchar t[256], *a, *b, *c, *d; + AvahiDnsPacket *p; + + p = avahi_dns_packet_new(8000); + + avahi_dns_packet_append_name(p, a = "hello.hello.hello.de."); + avahi_dns_packet_append_name(p, b = "this is a test.hello.de."); + avahi_dns_packet_append_name(p, c = "this\\.is\\.a\\.test\\.with\\.dots.hello.de."); + avahi_dns_packet_append_name(p, d = "this\\\\is another\\ \\test.hello.de."); + + avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(a, t)); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(b, t)); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(c, t)); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(d, t)); + + avahi_dns_packet_free(p); + return 0; +} diff --git a/libavahi-core/dns.c b/libavahi-core/dns.c new file mode 100644 index 0000000..e485ed8 --- /dev/null +++ b/libavahi-core/dns.c @@ -0,0 +1,749 @@ +#include + +#include +#include +#include + +#include "dns.h" +#include "util.h" + +AvahiDnsPacket* avahi_dns_packet_new(guint max_size) { + AvahiDnsPacket *p; + + if (max_size <= 0) + max_size = AVAHI_DNS_PACKET_MAX_SIZE; + else if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) + max_size = AVAHI_DNS_PACKET_HEADER_SIZE; + + p = g_malloc(sizeof(AvahiDnsPacket) + max_size); + p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + p->max_size = max_size; + p->name_table = NULL; + + memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_query(guint max_size) { + AvahiDnsPacket *p; + + p = avahi_dns_packet_new(max_size); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_response(guint max_size) { + AvahiDnsPacket *p; + + p = avahi_dns_packet_new(max_size); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +void avahi_dns_packet_free(AvahiDnsPacket *p) { + g_assert(p); + + if (p->name_table) + g_hash_table_destroy(p->name_table); + + g_free(p); +} + +void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v) { + g_assert(p); + g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE); + + ((guint16*) AVAHI_DNS_PACKET_DATA(p))[index] = g_htons(v); +} + +guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index) { + g_assert(p); + g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE); + + return g_ntohs(((guint16*) AVAHI_DNS_PACKET_DATA(p))[index]); +} + +/* Read the first label from string *name, unescape "\" and write it to dest */ +gchar *avahi_unescape_label(gchar *dest, guint size, const gchar **name) { + guint i = 0; + gchar *d; + + g_assert(dest); + g_assert(size > 0); + g_assert(name); + g_assert(*name); + + d = dest; + + for (;;) { + if (i >= size) + return NULL; + + if (**name == '.') { + (*name)++; + break; + } + + if (**name == 0) + break; + + if (**name == '\\') { + (*name) ++; + + if (**name == 0) + break; + } + + *(d++) = *((*name) ++); + i++; + } + + g_assert(i < size); + + *d = 0; + + return dest; +} + +guint8* avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name) { + guint8 *d, *saved_ptr = NULL; + guint saved_size; + + g_assert(p); + g_assert(name); + + saved_size = p->size; + saved_ptr = avahi_dns_packet_extend(p, 0); + + while (*name) { + guint n; + guint8* prev; + const gchar *pname; + gchar label[64]; + + /* Check whether we can compress this name. */ + + if (p->name_table && (prev = g_hash_table_lookup(p->name_table, name))) { + guint index; + + g_assert(prev >= AVAHI_DNS_PACKET_DATA(p)); + index = (guint) (prev - AVAHI_DNS_PACKET_DATA(p)); + + g_assert(index < p->size); + + if (index < 0x4000) { + guint16 *t; + if (!(t = (guint16*) avahi_dns_packet_extend(p, sizeof(guint16)))) + return NULL; + + *t = g_htons((0xC000 | index)); + return saved_ptr; + } + } + + pname = name; + + if (!(avahi_unescape_label(label, sizeof(label), &name))) + goto fail; + + if (!(d = avahi_dns_packet_append_string(p, label))) + goto fail; + + if (!p->name_table) + p->name_table = g_hash_table_new_full((GHashFunc) avahi_domain_hash, (GEqualFunc) avahi_domain_equal, g_free, NULL); + + g_hash_table_insert(p->name_table, g_strdup(pname), d); + } + + if (!(d = avahi_dns_packet_extend(p, 1))) + goto fail; + + *d = 0; + + return saved_ptr; + +fail: + p->size = saved_size; + return NULL; +} + +guint8* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v) { + guint8 *d; + g_assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(guint16)))) + return NULL; + + *((guint16*) d) = g_htons(v); + return d; +} + +guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v) { + guint8 *d; + g_assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(guint32)))) + return NULL; + + *((guint32*) d) = g_htonl(v); + + return d; +} + +guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer b, guint l) { + guint8* d; + + g_assert(p); + g_assert(b); + g_assert(l); + + if (!(d = avahi_dns_packet_extend(p, l))) + return NULL; + + memcpy(d, b, l); + return d; +} + +guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s) { + guint8* d; + guint k; + + g_assert(p); + g_assert(s); + + if ((k = strlen(s)) >= 255) + k = 255; + + if (!(d = avahi_dns_packet_extend(p, k+1))) + return NULL; + + *d = (guint8) k; + memcpy(d+1, s, k); + + return d; +} + +guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l) { + guint8 *d; + + g_assert(p); + + if (p->size+l > p->max_size) + return NULL; + + d = AVAHI_DNS_PACKET_DATA(p) + p->size; + p->size += l; + + return d; +} + +gint avahi_dns_packet_check_valid(AvahiDnsPacket *p) { + guint16 flags; + g_assert(p); + + if (p->size < 12) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_OPCODE || flags & AVAHI_DNS_FLAG_RCODE) + return -1; + + return 0; +} + +gint avahi_dns_packet_is_query(AvahiDnsPacket *p) { + g_assert(p); + + return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); +} + +/* Read a label from a DNS packet, escape "\" and ".", append \0 */ +static gchar *escape_label(guint8* src, guint src_length, gchar **ret_name, guint *ret_name_length) { + gchar *r; + + g_assert(src); + g_assert(ret_name); + g_assert(*ret_name); + g_assert(ret_name_length); + g_assert(*ret_name_length > 0); + + r = *ret_name; + + while (src_length > 0) { + if (*src == '.' || *src == '\\') { + if (*ret_name_length < 3) + return NULL; + + *((*ret_name) ++) = '\\'; + (*ret_name_length) --; + } + + if (*ret_name_length < 2) + return NULL; + + *((*ret_name)++) = *src; + (*ret_name_length) --; + + src_length --; + src++; + } + + **ret_name = 0; + + return r; +} + +static gint consume_labels(AvahiDnsPacket *p, guint index, gchar *ret_name, guint l) { + gint ret = 0; + int compressed = 0; + int first_label = 1; + g_assert(p && ret_name && l); + + for (;;) { + guint8 n; + + if (index+1 > p->size) + return -1; + + n = AVAHI_DNS_PACKET_DATA(p)[index]; + + if (!n) { + index++; + if (!compressed) + ret++; + + if (l < 1) + return -1; + *ret_name = 0; + + return ret; + + } else if (n <= 63) { + /* Uncompressed label */ + index++; + if (!compressed) + ret++; + + if (index + n > p->size) + return -1; + + if ((guint) n + 1 > l) + return -1; + + if (!first_label) { + *(ret_name++) = '.'; + l--; + } else + first_label = 0; + + if (!(escape_label(AVAHI_DNS_PACKET_DATA(p) + index, n, &ret_name, &l))) + return -1; + + index += n; + + if (!compressed) + ret += n; + } else if ((n & 0xC0) == 0xC0) { + /* Compressed label */ + + if (index+2 > p->size) + return -1; + + index = ((guint) (AVAHI_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[index+1]; + + if (!compressed) + ret += 2; + + compressed = 1; + } else + return -1; + } +} + +gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l) { + gint r; + + if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) + return -1; + + p->rindex += r; + return 0; +} + +gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v) { + g_assert(p); + g_assert(ret_v); + + if (p->rindex + sizeof(guint16) > p->size) + return -1; + + *ret_v = g_ntohs(*((guint16*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex))); + p->rindex += sizeof(guint16); + + return 0; +} + +gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v) { + g_assert(p); + g_assert(ret_v); + + if (p->rindex + sizeof(guint32) > p->size) + return -1; + + *ret_v = g_ntohl(*((guint32*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex))); + p->rindex += sizeof(guint32); + + return 0; +} + +gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l) { + g_assert(p); + g_assert(ret_data); + g_assert(l > 0); + + if (p->rindex + l > p->size) + return -1; + + memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); + p->rindex += l; + + return 0; +} + +gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l) { + guint k; + + g_assert(p); + g_assert(ret_string); + g_assert(l > 0); + + if (p->rindex >= p->size) + return -1; + + k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; + + if (p->rindex+1+k > p->size) + return -1; + + if (l > k+1) + l = k+1; + + memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); + ret_string[l-1] = 0; + + + p->rindex += 1+k; + + return 0; + +} + +gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { + g_assert(p); + + if (p->rindex > p->size) + return NULL; + + return AVAHI_DNS_PACKET_DATA(p) + p->rindex; +} + +gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length) { + g_assert(p); + + if (p->rindex + length > p->size) + return -1; + + p->rindex += length; + return 0; +} + +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush) { + gchar name[257], buf[257]; + guint16 type, class; + guint32 ttl; + guint16 rdlength; + gconstpointer data; + AvahiRecord *r = NULL; + gconstpointer start; + + g_assert(p); + g_assert(ret_cache_flush); + +/* g_message("consume_record()"); */ + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0 || + avahi_dns_packet_consume_uint32(p, &ttl) < 0 || + avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || + p->rindex + rdlength > p->size) + goto fail; + +/* g_message("name = %s, rdlength = %u", name, rdlength); */ + + *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); + class &= ~AVAHI_DNS_CACHE_FLUSH; + + start = avahi_dns_packet_get_rptr(p); + + r = avahi_record_new_full(name, class, type); + + switch (type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + +/* g_message("ptr"); */ + + if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.ptr.name = g_strdup(buf); + break; + + + case AVAHI_DNS_TYPE_SRV: + +/* g_message("srv"); */ + + if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || + avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.srv.name = g_strdup(buf); + break; + + case AVAHI_DNS_TYPE_HINFO: + +/* g_message("hinfo"); */ + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.hinfo.cpu = g_strdup(buf); + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.hinfo.os = g_strdup(buf); + break; + + case AVAHI_DNS_TYPE_TXT: + +/* g_message("txt"); */ + + if (rdlength > 0) { + r->data.txt.string_list = avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength); + + if (avahi_dns_packet_skip(p, rdlength) < 0) + goto fail; + } else + r->data.txt.string_list = NULL; + + break; + + case AVAHI_DNS_TYPE_A: + +/* g_message("A"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) + goto fail; + + break; + + case AVAHI_DNS_TYPE_AAAA: + +/* g_message("aaaa"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) + goto fail; + + break; + + default: + +/* g_message("generic"); */ + + if (rdlength > 0) { + + r->data.generic.data = g_memdup(avahi_dns_packet_get_rptr(p), rdlength); + + if (avahi_dns_packet_skip(p, rdlength) < 0) + goto fail; + } + + break; + } + +/* g_message("%i == %u ?", (guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start, rdlength); */ + + /* Check if we read enough data */ + if ((guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start != rdlength) + goto fail; + + r->ttl = ttl; + + return r; + +fail: + if (r) + avahi_record_unref(r); + + return NULL; +} + +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response) { + gchar name[256]; + guint16 type, class; + + g_assert(p); + g_assert(ret_unicast_response); + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0) + return NULL; + + *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); + class &= ~AVAHI_DNS_UNICAST_RESPONSE; + + return avahi_key_new(name, class, type); +} + +guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response) { + guint8 *t; + guint size; + + g_assert(p); + g_assert(k); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, k->name)) || + !avahi_dns_packet_append_uint16(p, k->type) || + !avahi_dns_packet_append_uint16(p, k->class | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { + p->size = size; + return NULL; + } + + return t; +} + +guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush) { + guint8 *t, *l, *start; + guint size; + + g_assert(p); + g_assert(r); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || + !avahi_dns_packet_append_uint16(p, r->key->type) || + !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->class | AVAHI_DNS_CACHE_FLUSH) : (r->key->class &~ AVAHI_DNS_CACHE_FLUSH)) || + !avahi_dns_packet_append_uint32(p, r->ttl) || + !(l = avahi_dns_packet_append_uint16(p, 0))) + goto fail; + + start = avahi_dns_packet_extend(p, 0); + + switch (r->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME : + + if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) + goto fail; + + break; + + case AVAHI_DNS_TYPE_SRV: + + if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || + !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || + !avahi_dns_packet_append_uint16(p, r->data.srv.port) || + !avahi_dns_packet_append_name(p, r->data.srv.name)) + goto fail; + + break; + + case AVAHI_DNS_TYPE_HINFO: + if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || + !avahi_dns_packet_append_string(p, r->data.hinfo.os)) + goto fail; + + break; + + case AVAHI_DNS_TYPE_TXT: { + + guint8 *data; + guint size; + + size = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + +/* g_message("appending string: %u %p", size, r->data.txt.string_list); */ + + if (!(data = avahi_dns_packet_extend(p, size))) + goto fail; + + avahi_string_list_serialize(r->data.txt.string_list, data, size); + break; + } + + + case AVAHI_DNS_TYPE_A: + + if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) + goto fail; + + break; + + case AVAHI_DNS_TYPE_AAAA: + + if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) + goto fail; + + break; + + default: + + if (r->data.generic.size && + avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) + goto fail; + + break; + } + + + + + size = avahi_dns_packet_extend(p, 0) - start; + g_assert(size <= 0xFFFF); + +/* g_message("appended %u", size); */ + + * (guint16*) l = g_htons((guint16) size); + + return t; + + +fail: + p->size = size; + return NULL; +} + +gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p) { + g_assert(p); + + return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; +} + +guint avahi_dns_packet_space(AvahiDnsPacket *p) { + g_assert(p); + + g_assert(p->size <= p->max_size); + + return p->max_size - p->size; +} diff --git a/libavahi-core/dns.h b/libavahi-core/dns.h new file mode 100644 index 0000000..13d9278 --- /dev/null +++ b/libavahi-core/dns.h @@ -0,0 +1,82 @@ +#ifndef foodnshfoo +#define foodnshfoo + +#include + +#include "rr.h" + +#define AVAHI_DNS_PACKET_MAX_SIZE 9000 +#define AVAHI_DNS_PACKET_HEADER_SIZE 12 + +typedef struct _AvahiDnsPacket { + guint size, rindex, max_size; + GHashTable *name_table; /* for name compression */ +} AvahiDnsPacket; + + +#define AVAHI_DNS_PACKET_DATA(p) (((guint8*) p) + sizeof(AvahiDnsPacket)) + +AvahiDnsPacket* avahi_dns_packet_new(guint size); +AvahiDnsPacket* avahi_dns_packet_new_query(guint size); +AvahiDnsPacket* avahi_dns_packet_new_response(guint size); + +void avahi_dns_packet_free(AvahiDnsPacket *p); +void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v); +guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index); + +guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l); + +guint8 *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v); +guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v); +guint8 *avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name); +guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer, guint l); +guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response); +guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush); +guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s); + +gint avahi_dns_packet_is_query(AvahiDnsPacket *p); +gint avahi_dns_packet_check_valid(AvahiDnsPacket *p); + +gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v); +gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v); +gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l); +gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l); +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response); +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush); +gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l); + +gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p); + +gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length); + +gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p); +guint avahi_dns_packet_space(AvahiDnsPacket *p); + +#define AVAHI_DNS_FIELD_ID 0 +#define AVAHI_DNS_FIELD_FLAGS 1 +#define AVAHI_DNS_FIELD_QDCOUNT 2 +#define AVAHI_DNS_FIELD_ANCOUNT 3 +#define AVAHI_DNS_FIELD_NSCOUNT 4 +#define AVAHI_DNS_FIELD_ARCOUNT 5 + +#define AVAHI_DNS_FLAG_QR (1 << 15) +#define AVAHI_DNS_FLAG_OPCODE (15 << 11) +#define AVAHI_DNS_FLAG_RCODE (15) +#define AVAHI_DNS_FLAG_TC (1 << 9) + +#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ + (((guint16) !!qr << 15) | \ + ((guint16) (opcode & 15) << 11) | \ + ((guint16) !!aa << 10) | \ + ((guint16) !!tc << 9) | \ + ((guint16) !!rd << 8) | \ + ((guint16) !!ra << 7) | \ + ((guint16) !!ad << 5) | \ + ((guint16) !!cd << 4) | \ + ((guint16) (rd & 15))) + + +gchar *avahi_unescape_label(gchar *dest, guint size, const gchar **name); + +#endif + diff --git a/libavahi-core/domain-test.c b/libavahi-core/domain-test.c new file mode 100644 index 0000000..f3deeac --- /dev/null +++ b/libavahi-core/domain-test.c @@ -0,0 +1,19 @@ +#include "util.h" + +int main(int argc, char *argv[]) { + gchar *s; + + g_message("host name: %s", s = avahi_get_host_name()); + g_free(s); + + g_message("%s", s = avahi_normalize_name("foo.foo.")); + g_free(s); + + g_message("%s", s = avahi_normalize_name("foo.foo.")); + g_free(s); + + + g_message("%i", avahi_domain_equal("\\aaa bbb\\.cccc\\\\.dee.fff.", "aaa\\ bbb\\.cccc\\\\.dee.fff")); + + return 0; +} diff --git a/libavahi-core/iface.c b/libavahi-core/iface.c new file mode 100644 index 0000000..519a65b --- /dev/null +++ b/libavahi-core/iface.c @@ -0,0 +1,562 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "iface.h" +#include "netlink.h" +#include "dns.h" +#include "socket.h" +#include "announce.h" + +static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove) { + g_assert(m); + g_assert(a); + + if (!avahi_interface_address_relevant(a) || remove) { + if (a->entry_group) { + avahi_entry_group_free(a->entry_group); + a->entry_group = NULL; + } + } else { + if (!a->entry_group) { + a->entry_group = avahi_entry_group_new(m->server, NULL, NULL); + avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address); + avahi_entry_group_commit(a->entry_group); + } + } +} + +static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, int remove) { + AvahiInterfaceAddress *a; + g_assert(m); + g_assert(i); + + for (a = i->addresses; a; a = a->address_next) + update_address_rr(m, a, remove); +} + +static void update_hw_interface_rr(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, int remove) { + AvahiInterface *i; + + g_assert(m); + g_assert(hw); + + for (i = hw->interfaces; i; i = i->by_hardware_next) + update_interface_rr(m, i, remove); +} + +static void free_address(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a) { + g_assert(m); + g_assert(a); + g_assert(a->interface); + + AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a); + + if (a->entry_group) + avahi_entry_group_free(a->entry_group); + + g_free(a); +} + +static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean send_goodbye) { + g_assert(m); + g_assert(i); + + g_message("removing interface %s.%i", i->hardware->name, i->protocol); + avahi_goodbye_interface(m->server, i, send_goodbye); + g_message("flushing..."); + avahi_packet_scheduler_flush_responses(i->scheduler); + g_message("done"); + + g_assert(!i->announcements); + + while (i->addresses) + free_address(m, i->addresses); + + avahi_packet_scheduler_free(i->scheduler); + avahi_cache_free(i->cache); + + AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i); + AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i); + + g_free(i); +} + +static void free_hw_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, gboolean send_goodbye) { + g_assert(m); + g_assert(hw); + + while (hw->interfaces) + free_interface(m, hw->interfaces, send_goodbye); + + AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, m->hw_interfaces, hw); + g_hash_table_remove(m->hash_table, &hw->index); + + g_free(hw->name); + g_free(hw); +} + +static AvahiInterfaceAddress* get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) { + AvahiInterfaceAddress *ia; + + g_assert(m); + g_assert(i); + g_assert(raddr); + + for (ia = i->addresses; ia; ia = ia->address_next) + if (avahi_address_cmp(&ia->address, raddr) == 0) + return ia; + + return NULL; +} + +static int netlink_list_items(AvahiNetlink *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 avahi_netlink_send(nl, n, ret_seq); +} + +static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, guchar protocol) { + AvahiInterface *i; + + g_assert(m); + g_assert(hw); + g_assert(protocol != AF_UNSPEC); + + i = g_new(AvahiInterface, 1); + i->monitor = m; + i->hardware = hw; + i->protocol = protocol; + i->announcing = FALSE; + + AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); + AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements); + + i->cache = avahi_cache_new(m->server, i); + i->scheduler = avahi_packet_scheduler_new(m->server, i); + + AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i); + AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i); +} + +static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i) { + gboolean b; + + g_assert(m); + g_assert(i); + + b = avahi_interface_relevant(i); + + if (b && !i->announcing) { + g_message("New relevant interface %s.%i", i->hardware->name, i->protocol); + + if (i->protocol == AF_INET) + avahi_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4); + if (i->protocol == AF_INET6) + avahi_mdns_mcast_join_ipv6 (i->hardware->index, m->server->fd_ipv6); + + i->announcing = TRUE; + avahi_announce_interface(m->server, i); + } else if (!b && i->announcing) { + g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol); + + avahi_goodbye_interface(m->server, i, FALSE); + + if (i->protocol == AF_INET) + avahi_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4); + if (i->protocol == AF_INET6) + avahi_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6); + + i->announcing = FALSE; + } +} + +static void check_hw_interface_relevant(AvahiInterfaceMonitor *m, AvahiHwInterface *hw) { + AvahiInterface *i; + + g_assert(m); + g_assert(hw); + + for (i = hw->interfaces; i; i = i->by_hardware_next) + check_interface_relevant(m, i); +} + +static void callback(AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata) { + AvahiInterfaceMonitor *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); + AvahiHwInterface *hw; + struct rtattr *a = NULL; + size_t l; + + if (ifinfomsg->ifi_family != AF_UNSPEC) + return; + + if (!(hw = g_hash_table_lookup(m->hash_table, &ifinfomsg->ifi_index))) { + hw = g_new(AvahiHwInterface, 1); + hw->monitor = m; + hw->name = NULL; + hw->flags = 0; + hw->mtu = 1500; + hw->index = ifinfomsg->ifi_index; + + AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces); + AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw); + + g_hash_table_insert(m->hash_table, &hw->index, hw); + + if (m->server->fd_ipv4 >= 0) + new_interface(m, hw, AF_INET); + if (m->server->fd_ipv6 >= 0) + new_interface(m, hw, AF_INET6); + } + + hw->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(hw->name); + hw->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a)); + break; + + case IFLA_MTU: + g_assert(RTA_PAYLOAD(a) == sizeof(unsigned int)); + hw->mtu = *((unsigned int*) RTA_DATA(a)); + break; + + default: + ; + } + + a = RTA_NEXT(a, l); + } + + update_hw_interface_rr(m, hw, FALSE); + check_hw_interface_relevant(m, hw); + + } else if (n->nlmsg_type == RTM_DELLINK) { + struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); + AvahiHwInterface *hw; + AvahiInterface *i; + + if (ifinfomsg->ifi_family != AF_UNSPEC) + return; + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index))) + return; + + update_hw_interface_rr(m, hw, TRUE); + free_hw_interface(m, hw, FALSE); + + } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { + + struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); + AvahiInterface *i; + struct rtattr *a = NULL; + size_t l; + AvahiAddress raddr; + int raddr_valid = 0; + + if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) + return; + + if (!(i = (AvahiInterface*) avahi_interface_monitor_get_interface(m, ifaddrmsg->ifa_index, ifaddrmsg->ifa_family))) + 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.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) { + AvahiInterfaceAddress *addr; + + if (!(addr = get_address(m, i, &raddr))) { + addr = g_new(AvahiInterfaceAddress, 1); + addr->monitor = m; + addr->address = raddr; + addr->interface = i; + addr->entry_group = NULL; + + AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, addr); + } + + addr->flags = ifaddrmsg->ifa_flags; + addr->scope = ifaddrmsg->ifa_scope; + + update_address_rr(m, addr, FALSE); + check_interface_relevant(m, i); + } else { + AvahiInterfaceAddress *addr; + + if (!(addr = get_address(m, i, &raddr))) + return; + + update_address_rr(m, addr, TRUE); + free_address(m, addr); + + check_interface_relevant(m, i); + } + + } 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; + g_message("Enumeration complete"); + } + + } 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)); + } +} + +AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) { + AvahiInterfaceMonitor *m = NULL; + + m = g_new0(AvahiInterfaceMonitor, 1); + m->server = s; + if (!(m->netlink = avahi_netlink_new(s->context, G_PRIORITY_DEFAULT-10, 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); + + AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces); + AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces); + + if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0) + goto fail; + + m->list = LIST_IFACE; + + return m; + +fail: + avahi_interface_monitor_free(m); + return NULL; +} + +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { + g_assert(m); + + while (m->list != LIST_DONE) { + if (!avahi_netlink_work(m->netlink, TRUE)) + break; + } +} + +void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { + g_assert(m); + + while (m->hw_interfaces) + free_hw_interface(m, m->hw_interfaces, TRUE); + + g_assert(!m->interfaces); + + + if (m->netlink) + avahi_netlink_free(m->netlink); + + if (m->hash_table) + g_hash_table_destroy(m->hash_table); + + g_free(m); +} + + +AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol) { + AvahiHwInterface *hw; + AvahiInterface *i; + + g_assert(m); + g_assert(index > 0); + g_assert(protocol != AF_UNSPEC); + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, index))) + return NULL; + + for (i = hw->interfaces; i; i = i->by_hardware_next) + if (i->protocol == protocol) + return i; + + return NULL; +} + +AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index) { + g_assert(m); + g_assert(index > 0); + + return g_hash_table_lookup(m->hash_table, &index); +} + + +void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { + g_assert(i); + g_assert(p); + + if (avahi_interface_relevant(i)) { + g_message("sending on '%s.%i'", i->hardware->name, i->protocol); + + if (i->protocol == AF_INET && i->monitor->server->fd_ipv4 >= 0) + avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p); + else if (i->protocol == AF_INET6 && i->monitor->server->fd_ipv6 >= 0) + avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p); + } +} + +void avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean immediately) { + g_assert(i); + g_assert(key); + + if (avahi_interface_relevant(i)) + avahi_packet_scheduler_post_query(i->scheduler, key, immediately); +} + + +void avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { + g_assert(i); + g_assert(record); + + if (avahi_interface_relevant(i)) + avahi_packet_scheduler_post_response(i->scheduler, a, record, flush_cache, immediately); +} + +void avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, gboolean immediately) { + g_assert(i); + g_assert(record); + + if (avahi_interface_relevant(i)) + avahi_packet_scheduler_post_probe(i->scheduler, record, immediately); +} + +void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f) { + AvahiInterface *i; + g_assert(m); + + for (i = m->interfaces; i; i = i->interface_next) { + if (avahi_interface_relevant(i)) { + fprintf(f, "\n;;; INTERFACE %s.%i ;;;\n", i->hardware->name, i->protocol); + avahi_cache_dump(i->cache, f); + } + } + fprintf(f, "\n"); +} + +gboolean avahi_interface_relevant(AvahiInterface *i) { + g_assert(i); + + return + (i->hardware->flags & IFF_UP) && + (i->hardware->flags & IFF_RUNNING) && + !(i->hardware->flags & IFF_LOOPBACK) && + (i->hardware->flags & IFF_MULTICAST) && + i->addresses; +} + +gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) { + g_assert(a); + + return a->scope == RT_SCOPE_UNIVERSE; +} + + +gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol) { + g_assert(i); + + if (index > 0 && index != i->hardware->index) + return FALSE; + + if (protocol != AF_UNSPEC && protocol != i->protocol) + return FALSE; + + return TRUE; +} + + +void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint interface, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata) { + g_assert(m); + g_assert(callback); + + if (interface > 0) { + if (protocol != AF_UNSPEC) { + AvahiInterface *i; + + if ((i = avahi_interface_monitor_get_interface(m, interface, protocol))) + callback(m, i, userdata); + + } else { + AvahiHwInterface *hw; + AvahiInterface *i; + + if ((hw = avahi_interface_monitor_get_hw_interface(m, interface))) + for (i = hw->interfaces; i; i = i->by_hardware_next) + if (avahi_interface_match(i, interface, protocol)) + callback(m, i, userdata); + } + + } else { + AvahiInterface *i; + + for (i = m->interfaces; i; i = i->interface_next) + if (avahi_interface_match(i, interface, protocol)) + callback(m, i, userdata); + } +} diff --git a/libavahi-core/iface.h b/libavahi-core/iface.h new file mode 100644 index 0000000..cab4949 --- /dev/null +++ b/libavahi-core/iface.h @@ -0,0 +1,102 @@ +#ifndef fooifacehfoo +#define fooifacehfoo + +#include + +typedef struct _AvahiInterfaceMonitor AvahiInterfaceMonitor; +typedef struct _AvahiInterfaceAddress AvahiInterfaceAddress; +typedef struct _AvahiInterface AvahiInterface; +typedef struct _AvahiHwInterface AvahiHwInterface; + +#include "address.h" +#include "server.h" +#include "netlink.h" +#include "cache.h" +#include "llist.h" +#include "psched.h" +#include "dns.h" +#include "announce.h" + +struct _AvahiInterfaceMonitor { + AvahiServer *server; + AvahiNetlink *netlink; + GHashTable *hash_table; + + AVAHI_LLIST_HEAD(AvahiInterface, interfaces); + AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces); + + guint query_addr_seq, query_link_seq; + + enum { + LIST_IFACE, + LIST_ADDR, + LIST_DONE + } list; +}; + +struct _AvahiHwInterface { + AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware); + AvahiInterfaceMonitor *monitor; + + gchar *name; + gint index; + guint flags; + guint mtu; + + AVAHI_LLIST_HEAD(AvahiInterface, interfaces); +}; + +struct _AvahiInterface { + AVAHI_LLIST_FIELDS(AvahiInterface, interface); + AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware); + AvahiInterfaceMonitor *monitor; + + AvahiHwInterface *hardware; + guchar protocol; + gboolean announcing; + + AvahiCache *cache; + AvahiPacketScheduler *scheduler; + + AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses); + AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements); +}; + +struct _AvahiInterfaceAddress { + AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address); + AvahiInterfaceMonitor *monitor; + + guchar flags; + guchar scope; + AvahiAddress address; + + AvahiEntryGroup *entry_group; + AvahiInterface *interface; +}; + +AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server); +void avahi_interface_monitor_free(AvahiInterfaceMonitor *m); + +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m); + +AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol); +AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index); + +void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); + +void avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, gboolean immediately); +void avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, gboolean immediately); +void avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately); + +void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f); + +gboolean avahi_interface_relevant(AvahiInterface *i); +gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a); + +gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol); + +typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata); + +void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint index, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata); + +#endif diff --git a/libavahi-core/llist.h b/libavahi-core/llist.h new file mode 100644 index 0000000..16a16b6 --- /dev/null +++ b/libavahi-core/llist.h @@ -0,0 +1,50 @@ +#ifndef foollistfoo +#define foollistfoo + +#include + +/* Some macros for maintaining doubly linked lists */ + +/* The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define AVAHI_LLIST_HEAD(t,name) t *name + +/* The pointers in the linked list's items. Use this in the item structure */ +#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev + +/* Initialize the list's head */ +#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0) + +/* Initialize a list item */ +#define AVAHI_LLIST_INIT(t,name,item) do { \ + t *_item = (item); \ + g_assert(_item); \ + _item->name##_prev = _item->name##_next = NULL; \ + } while(0) + +/* Prepend an item to the list */ +#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + g_assert(_item); \ + if ((_item->name##_next = *_head)) \ + _item->name##_next->name##_prev = _item; \ + _item->name##_prev = NULL; \ + *_head = _item; \ + } while (0) + +/* Remove an item from the list */ +#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + g_assert(_item); \ + if (_item->name##_next) \ + _item->name##_next->name##_prev = _item->name##_prev; \ + if (_item->name##_prev) \ + _item->name##_prev->name##_next = _item->name##_next; \ + else {\ + g_assert(*_head == _item); \ + *_head = _item->name##_next; \ + } \ + _item->name##_next = _item->name##_prev = NULL; \ + } while(0) + +#endif diff --git a/libavahi-core/main.c b/libavahi-core/main.c new file mode 100644 index 0000000..9699329 --- /dev/null +++ b/libavahi-core/main.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#include "avahi.h" + +static gboolean quit_timeout(gpointer data) { + g_main_loop_quit(data); + return FALSE; +} + +static gboolean dump_timeout(gpointer data) { + AvahiServer *Avahi = data; + avahi_server_dump(Avahi, stdout); + return TRUE; +} + +static void subscription(AvahiSubscription *s, AvahiRecord *r, gint interface, guchar protocol, AvahiSubscriptionEvent event, gpointer userdata) { + gchar *t; + + g_assert(s); + g_assert(r); + g_assert(interface > 0); + g_assert(protocol != AF_UNSPEC); + + g_message("SUBSCRIPTION: record [%s] on %i.%i is %s", t = avahi_record_to_string(r), interface, protocol, + event == AVAHI_SUBSCRIPTION_NEW ? "new" : (event == AVAHI_SUBSCRIPTION_CHANGE ? "changed" : "removed")); + + g_free(t); +} + +static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) { + g_message("entry group state: %i", state); +} + +int main(int argc, char *argv[]) { + AvahiServer *avahi; + gchar *r; + GMainLoop *loop = NULL; + AvahiSubscription *s; + AvahiKey *k; + AvahiEntryGroup *g; + + avahi = avahi_server_new(NULL); + +/* g = avahi_entry_group_new(avahi, entry_group_callback, NULL); */ + +/* avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, NULL, "hallo", NULL); */ +/* avahi_server_add_service(avahi, g, 0, AF_UNSPEC, "_http._tcp", "gurke", NULL, NULL, 80, "foo", NULL); */ + +/* avahi_entry_group_commit(g); */ + + avahi_server_dump(avahi, stdout); + + +/* k = avahi_key_new("ecstasy.local.", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_ANY); */ +/* s = avahi_subscription_new(avahi, k, 0, AF_UNSPEC, subscription, NULL); */ +/* avahi_key_unref(k); */ + + loop = g_main_loop_new(NULL, FALSE); + + /* g_timeout_add(1000*20, dump_timeout, Avahi); */ +/* g_timeout_add(1000*30, quit_timeout, loop); */ + + g_main_loop_run(loop); + g_main_loop_unref(loop); + +/* avahi_subscription_free(s); */ + /* avahi_entry_group_free(g); */ + avahi_server_free(avahi); + + return 0; +} diff --git a/libavahi-core/netlink.c b/libavahi-core/netlink.c new file mode 100644 index 0000000..1289749 --- /dev/null +++ b/libavahi-core/netlink.c @@ -0,0 +1,162 @@ +#include +#include +#include + +#include "netlink.h" + +struct _AvahiNetlink { + GMainContext *context; + gint fd; + guint seq; + GPollFD poll_fd; + GSource *source; + void (*callback) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata); + gpointer userdata; +}; + +gboolean avahi_netlink_work(AvahiNetlink *nl, gboolean block) { + 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), block ? 0 : 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, (size_t) bytes)) { + g_warning("NETLINK: packet truncated"); + return FALSE; + } + + nl->callback(nl, p, nl->userdata); + } + } + + if (block) + break; + } + + 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) { + AvahiNetlink* nl; + g_assert(source); + + nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource))); + g_assert(nl); + + return nl->poll_fd.revents & (G_IO_IN|G_IO_HUP|G_IO_ERR); +} + +static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) { + AvahiNetlink* nl; + g_assert(source); + + nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource))); + g_assert(nl); + + return avahi_netlink_work(nl, FALSE); +} + +AvahiNetlink *avahi_netlink_new(GMainContext *context, gint priority, guint32 groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata), gpointer userdata) { + int fd; + struct sockaddr_nl addr; + AvahiNetlink *nl; + + static GSourceFuncs source_funcs = { + prepare_func, + check_func, + dispatch_func, + NULL, + NULL, + NULL + }; + + 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(AvahiNetlink, 1); + nl->context = context; + g_main_context_ref(context); + nl->fd = fd; + nl->seq = 0; + nl->callback = cb; + nl->userdata = userdata; + + nl->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiNetlink*)); + *((AvahiNetlink**) (((guint8*) nl->source) + sizeof(GSource))) = nl; + + g_source_set_priority(nl->source, priority); + + 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 avahi_netlink_free(AvahiNetlink *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 avahi_netlink_send(AvahiNetlink *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/libavahi-core/netlink.h b/libavahi-core/netlink.h new file mode 100644 index 0000000..8066aae --- /dev/null +++ b/libavahi-core/netlink.h @@ -0,0 +1,20 @@ +#ifndef foonetlinkhfoo +#define foonetlinkhfoo + +#include +#include +#include + +#include + +struct _AvahiNetlink; +typedef struct _AvahiNetlink AvahiNetlink; + +AvahiNetlink *avahi_netlink_new(GMainContext *c, gint priority, guint32 groups, void (*cb) (AvahiNetlink *n, struct nlmsghdr *m, gpointer userdata), gpointer userdata); +void avahi_netlink_free(AvahiNetlink *n); + +int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, guint *ret_seq); + +gboolean avahi_netlink_work(AvahiNetlink *n, gboolean block); + +#endif diff --git a/libavahi-core/prioq-test.c b/libavahi-core/prioq-test.c new file mode 100644 index 0000000..53a8383 --- /dev/null +++ b/libavahi-core/prioq-test.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "prioq.h" + +static gint compare_int(gconstpointer a, gconstpointer b) { + gint i = GPOINTER_TO_INT(a), j = GPOINTER_TO_INT(b); + + return i < j ? -1 : (i > j ? 1 : 0); +} + +static int compare_ptr(gconstpointer a, gconstpointer b) { + return a < b ? -1 : (a > b ? 1 : 0); +} + +static void rec(AvahiPrioQueueNode *n) { + if (!n) + return; + + if (n->left) + g_assert(n->left->parent == n); + + if (n->right) + g_assert(n->right->parent == n); + + if (n->parent) { + g_assert(n->parent->left == n || n->parent->right == n); + + if (n->parent->left == n) + g_assert(n->next == n->parent->right); + } + + if (!n->next) { + g_assert(n->queue->last == n); + + if (n->parent && n->parent->left == n) + g_assert(n->parent->right == NULL); + } + + + if (n->parent) { + int a = GPOINTER_TO_INT(n->parent->data), b = GPOINTER_TO_INT(n->data); + if (a > b) { + printf("%i <= %i: NO\n", a, b); + abort(); + } + } + + rec(n->left); + rec(n->right); +} + +int main(int argc, char *argv[]) { + AvahiPrioQueue *q, *q2; + gint i, prev; + + q = avahi_prio_queue_new(compare_int); + q2 = avahi_prio_queue_new(compare_ptr); + + srand(time(NULL)); + + for (i = 0; i < 10000; i++) + avahi_prio_queue_put(q2, avahi_prio_queue_put(q, GINT_TO_POINTER(random() & 0xFFFF))); + + while (q2->root) { + rec(q->root); + rec(q2->root); + + g_assert(q->n_nodes == q2->n_nodes); + + printf("%i\n", GPOINTER_TO_INT(((AvahiPrioQueueNode*)q2->root->data)->data)); + + avahi_prio_queue_remove(q, q2->root->data); + avahi_prio_queue_remove(q2, q2->root); + } + + +/* prev = 0; */ +/* while (q->root) { */ +/* gint v = GPOINTER_TO_INT(q->root->data); */ +/* rec(q->root); */ +/* printf("%i\n", v); */ +/* avahi_prio_queue_remove(q, q->root); */ +/* g_assert(v >= prev); */ +/* prev = v; */ +/* } */ + + avahi_prio_queue_free(q); + return 0; +} diff --git a/libavahi-core/prioq.c b/libavahi-core/prioq.c new file mode 100644 index 0000000..2eedf27 --- /dev/null +++ b/libavahi-core/prioq.c @@ -0,0 +1,359 @@ +#include "prioq.h" + +AvahiPrioQueue* avahi_prio_queue_new(gint (*compare) (gconstpointer a, gconstpointer b)) { + AvahiPrioQueue *q; + g_assert(compare); + + q = g_new(AvahiPrioQueue, 1); + q->root = q->last = NULL; + q->n_nodes = 0; + q->compare = compare; + return q; +} + +void avahi_prio_queue_free(AvahiPrioQueue *q) { + g_assert(q); + + while (q->last) + avahi_prio_queue_remove(q, q->last); + + g_assert(!q->n_nodes); + g_free(q); +} + +static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, guint x, guint y) { + guint r; + AvahiPrioQueueNode *n; + g_assert(q); + + n = q->root; + g_assert(n); + + for (r = 0; r < y; r++) { + g_assert(n); + + if ((x >> (y-r-1)) & 1) + n = n->right; + else + n = n->left; + } + + g_assert(n->x == x); + g_assert(n->y == y); + + return n; +} + +static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) { + AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn; + gint t; + g_assert(q); + g_assert(a); + g_assert(b); + g_assert(a != b); + + /* Swap positions */ + t = a->x; a->x = b->x; b->x = t; + t = a->y; a->y = b->y; b->y = t; + + if (a->parent == b) { + /* B is parent of A */ + + p = b->parent; + b->parent = a; + + if ((a->parent = p)) { + if (a->parent->left == b) + a->parent->left = a; + else + a->parent->right = a; + } else + q->root = a; + + if (b->left == a) { + if ((b->left = a->left)) + b->left->parent = b; + a->left = b; + + r = a->right; + if ((a->right = b->right)) + a->right->parent = a; + if ((b->right = r)) + b->right->parent = b; + + } else { + if ((b->right = a->right)) + b->right->parent = b; + a->right = b; + + l = a->left; + if ((a->left = b->left)) + a->left->parent = a; + if ((b->left = l)) + b->left->parent = b; + } + } else if (b->parent == a) { + /* A ist parent of B */ + + p = a->parent; + a->parent = b; + + if ((b->parent = p)) { + if (b->parent->left == a) + b->parent->left = b; + else + b->parent->right = b; + } else + q->root = b; + + if (a->left == b) { + if ((a->left = b->left)) + a->left->parent = a; + b->left = a; + + r = a->right; + if ((a->right = b->right)) + a->right->parent = a; + if ((b->right = r)) + b->right->parent = b; + } else { + if ((a->right = b->right)) + a->right->parent = a; + b->right = a; + + l = a->left; + if ((a->left = b->left)) + a->left->parent = a; + if ((b->left = l)) + b->left->parent = b; + } + } else { + AvahiPrioQueueNode *apl = NULL, *bpl = NULL; + + /* Swap parents */ + ap = a->parent; + bp = b->parent; + + if (ap) + apl = ap->left; + if (bp) + bpl = bp->left; + + if ((a->parent = bp)) { + if (bpl == b) + bp->left = a; + else + bp->right = a; + } else + q->root = a; + + if ((b->parent = ap)) { + if (apl == a) + ap->left = b; + else + ap->right = b; + } else + q->root = b; + + /* Swap children */ + l = a->left; + r = a->right; + + if ((a->left = b->left)) + a->left->parent = a; + + if ((b->left = l)) + b->left->parent = b; + + if ((a->right = b->right)) + a->right->parent = a; + + if ((b->right = r)) + b->right->parent = b; + } + + /* Swap siblings */ + ap = a->prev; an = a->next; + bp = b->prev; bn = b->next; + + if (a->next == b) { + /* A is predecessor of B */ + a->prev = b; + b->next = a; + + if ((a->next = bn)) + a->next->prev = a; + else + q->last = a; + + if ((b->prev = ap)) + b->prev->next = b; + + } else if (b->next == a) { + /* B is predecessor of A */ + a->next = b; + b->prev = a; + + if ((a->prev = bp)) + a->prev->next = a; + + if ((b->next = an)) + b->next->prev = b; + else + q->last = b; + + } else { + /* A is no neighbour of B */ + + if ((a->prev = bp)) + a->prev->next = a; + + if ((a->next = bn)) + a->next->prev = a; + else + q->last = a; + + if ((b->prev = ap)) + b->prev->next = b; + + if ((b->next = an)) + b->next->prev = b; + else + q->last = b; + } +} + +/* Move a node to the correct position */ +void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { + g_assert(q); + g_assert(n); + + /* Move up until the position is OK */ + while (n->parent && q->compare(n->parent->data, n->data) > 0) + exchange_nodes(q, n, n->parent); + + /* Move down until the position is OK */ + for (;;) { + AvahiPrioQueueNode *min; + + if (!(min = n->left)) { + /* No children */ + g_assert(!n->right); + break; + } + + if (n->right && q->compare(n->right->data, min->data) < 0) + min = n->right; + + /* min now contains the smaller one of our two children */ + + if (q->compare(n->data, min->data) <= 0) + /* Order OK */ + break; + + exchange_nodes(q, n, min); + } +} + +AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, gpointer data) { + AvahiPrioQueueNode *n; + g_assert(q); + + n = g_new(AvahiPrioQueueNode, 1); + n->queue = q; + n->data = data; + + if (q->last) { + g_assert(q->root); + g_assert(q->n_nodes); + + n->y = q->last->y; + n->x = q->last->x+1; + + if (n->x >= ((guint) 1 << n->y)) { + n->x = 0; + n->y++; + } + + q->last->next = n; + n->prev = q->last; + + g_assert(n->y > 0); + n->parent = get_node_at_xy(q, n->x/2, n->y-1); + + if (n->x & 1) + n->parent->right = n; + else + n->parent->left = n; + } else { + g_assert(!q->root); + g_assert(!q->n_nodes); + + n->y = n->x = 0; + q->root = n; + n->prev = n->parent = NULL; + } + + n->next = n->left = n->right = NULL; + q->last = n; + q->n_nodes++; + + avahi_prio_queue_shuffle(q, n); + + return n; +} + +void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { + g_assert(q); + g_assert(n); + + if (n != q->last) { + AvahiPrioQueueNode *replacement = q->last; + exchange_nodes(q, replacement, n); + avahi_prio_queue_remove(q, n); + avahi_prio_queue_shuffle(q, replacement); + return; + } + + g_assert(n == q->last); + g_assert(!n->next); + g_assert(!n->left); + g_assert(!n->right); + + q->last = n->prev; + + if (n->prev) { + n->prev->next = NULL; + g_assert(n->parent); + } else + g_assert(!n->parent); + + if (n->parent) { + g_assert(n->prev); + if (n->parent->left == n) { + if (n->parent->right != NULL) { + g_message("fuck"); + for (;;); + + } + + g_assert(n->parent->right == NULL); + n->parent->left = NULL; + } else { + g_assert(n->parent->right == n); + g_assert(n->parent->left != NULL); + n->parent->right = NULL; + } + } else { + g_assert(q->root == n); + g_assert(!n->prev); + g_assert(q->n_nodes == 1); + q->root = NULL; + } + + g_free(n); + + g_assert(q->n_nodes > 0); + q->n_nodes--; +} + diff --git a/libavahi-core/prioq.h b/libavahi-core/prioq.h new file mode 100644 index 0000000..6b5babb --- /dev/null +++ b/libavahi-core/prioq.h @@ -0,0 +1,35 @@ +#ifndef fooprioqhfoo +#define fooprioqhfoo + +#include + +struct _AvahiPrioQueue; +typedef struct _AvahiPrioQueue AvahiPrioQueue; + +struct _AvahiPrioQueueNode; +typedef struct _AvahiPrioQueueNode AvahiPrioQueueNode; + +struct _AvahiPrioQueue { + AvahiPrioQueueNode *root, *last; + + guint n_nodes; + gint (*compare) (gconstpointer a, gconstpointer b); +}; + +struct _AvahiPrioQueueNode { + AvahiPrioQueue *queue; + gpointer data; + guint x, y; + + AvahiPrioQueueNode *left, *right, *parent, *next, *prev; +}; + +AvahiPrioQueue* avahi_prio_queue_new(gint (*compare) (gconstpointer a, gconstpointer b)); +void avahi_prio_queue_free(AvahiPrioQueue *q); + +AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, gpointer data); +void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n); + +void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n); + +#endif diff --git a/libavahi-core/psched.c b/libavahi-core/psched.c new file mode 100644 index 0000000..faa2fa9 --- /dev/null +++ b/libavahi-core/psched.c @@ -0,0 +1,715 @@ +#include + +#include "util.h" +#include "psched.h" + +#define AVAHI_QUERY_HISTORY_MSEC 100 +#define AVAHI_QUERY_DEFER_MSEC 100 +#define AVAHI_RESPONSE_HISTORY_MSEC 700 +#define AVAHI_RESPONSE_DEFER_MSEC 20 +#define AVAHI_RESPONSE_JITTER_MSEC 100 +#define AVAHI_PROBE_DEFER_MSEC 70 + +AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i) { + AvahiPacketScheduler *s; + + g_assert(server); + g_assert(i); + + s = g_new(AvahiPacketScheduler, 1); + s->server = server; + s->interface = i; + + AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->query_jobs); + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->response_jobs); + AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers); + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->probe_jobs); + + return s; +} + +static void query_job_free(AvahiPacketScheduler *s, AvahiQueryJob *qj) { + g_assert(qj); + + if (qj->time_event) + avahi_time_event_queue_remove(qj->scheduler->server->time_event_queue, qj->time_event); + + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->query_jobs, qj); + + avahi_key_unref(qj->key); + g_free(qj); +} + +static void response_job_free(AvahiPacketScheduler *s, AvahiResponseJob *rj) { + g_assert(rj); + + if (rj->time_event) + avahi_time_event_queue_remove(rj->scheduler->server->time_event_queue, rj->time_event); + + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->response_jobs, rj); + + avahi_record_unref(rj->record); + g_free(rj); +} + +static void probe_job_free(AvahiPacketScheduler *s, AvahiProbeJob *pj) { + g_assert(pj); + + if (pj->time_event) + avahi_time_event_queue_remove(pj->scheduler->server->time_event_queue, pj->time_event); + + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->probe_jobs, pj); + + avahi_record_unref(pj->record); + g_free(pj); +} + +void avahi_packet_scheduler_free(AvahiPacketScheduler *s) { + AvahiQueryJob *qj; + AvahiResponseJob *rj; + AvahiProbeJob *pj; + AvahiTimeEvent *e; + + g_assert(s); + + g_assert(!s->known_answers); + + while ((qj = s->query_jobs)) + query_job_free(s, qj); + while ((rj = s->response_jobs)) + response_job_free(s, rj); + while ((pj = s->probe_jobs)) + probe_job_free(s, pj); + + g_free(s); +} + +static gpointer known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) { + AvahiPacketScheduler *s = userdata; + AvahiKnownAnswer *ka; + + g_assert(c); + g_assert(pattern); + g_assert(e); + g_assert(s); + + if (avahi_cache_entry_half_ttl(c, e)) + return NULL; + + ka = g_new0(AvahiKnownAnswer, 1); + ka->scheduler = s; + ka->record = avahi_record_ref(e->record); + + AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka); + return NULL; +} + +static guint8* packet_add_query_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) { + guint8 *d; + + g_assert(s); + g_assert(p); + g_assert(qj); + + if ((d = avahi_dns_packet_append_key(p, qj->key, FALSE))) { + GTimeVal tv; + + qj->done = 1; + + /* Drop query after some time from history */ + avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0); + avahi_time_event_queue_update(s->server->time_event_queue, qj->time_event, &tv); + + g_get_current_time(&qj->delivery); + + /* Add all matching known answers to the list */ + avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s); + } + + return d; +} + +static void append_known_answers_and_send(AvahiPacketScheduler *s, AvahiDnsPacket *p) { + AvahiKnownAnswer *ka; + guint n; + g_assert(s); + g_assert(p); + + n = 0; + + while ((ka = s->known_answers)) { + + while (!avahi_dns_packet_append_record(p, ka->record, FALSE)) { + + g_assert(!avahi_dns_packet_is_empty(p)); + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); + + p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48); + n = 0; + } + + AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka); + avahi_record_unref(ka->record); + g_free(ka); + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static void query_elapse(AvahiTimeEvent *e, gpointer data) { + AvahiQueryJob *qj = data; + AvahiPacketScheduler *s; + AvahiDnsPacket *p; + guint n; + guint8 *d; + + g_assert(qj); + s = qj->scheduler; + + if (qj->done) { + /* Lets remove it from the history */ + query_job_free(s, qj); + return; + } + + g_assert(!s->known_answers); + + p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48); + d = packet_add_query_job(s, p, qj); + g_assert(d); + n = 1; + + /* Try to fill up packet with more queries, if available */ + for (qj = s->query_jobs; qj; qj = qj->jobs_next) { + + if (qj->done) + continue; + + if (!packet_add_query_job(s, p, qj)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + /* Now add known answers */ + append_known_answers_and_send(s, p); +} + +AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + g_assert(s); + g_assert(key); + + qj = g_new(AvahiQueryJob, 1); + qj->scheduler = s; + qj->key = avahi_key_ref(key); + qj->done = FALSE; + qj->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->query_jobs, qj); + + return qj; +} + +void avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately) { + GTimeVal tv; + AvahiQueryJob *qj; + + g_assert(s); + g_assert(key); + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0); + + for (qj = s->query_jobs; qj; qj = qj->jobs_next) { + + if (avahi_key_equal(qj->key, key)) { + + glong d = avahi_timeval_diff(&tv, &qj->delivery); + + /* Duplicate questions suppression */ + if (d >= 0 && d <= AVAHI_QUERY_HISTORY_MSEC*1000) { + g_message("WARNING! DUPLICATE QUERY SUPPRESSION ACTIVE!"); + return; + } + + query_job_free(s, qj); + break; + } + + } + + qj = query_job_new(s, key); + qj->delivery = tv; + qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &qj->delivery, query_elapse, qj); +} + +static guint8* packet_add_response_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) { + guint8 *d; + + g_assert(s); + g_assert(p); + g_assert(rj); + + if ((d = avahi_dns_packet_append_record(p, rj->record, rj->flush_cache))) { + GTimeVal tv; + + rj->done = 1; + + /* Drop response after some time from history */ + avahi_elapse_time(&tv, AVAHI_RESPONSE_HISTORY_MSEC, 0); + avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv); + + g_get_current_time(&rj->delivery); + } + + return d; +} + +static void send_response_packet(AvahiPacketScheduler *s, AvahiResponseJob *rj) { + AvahiDnsPacket *p; + guint n; + + g_assert(s); + + p = avahi_dns_packet_new_response(s->interface->hardware->mtu - 200); + n = 0; + + /* If a job was specified, put it in the packet. */ + if (rj) { + guint8 *d; + d = packet_add_response_job(s, p, rj); + g_assert(d); + n++; + } + + /* Try to fill up packet with more responses, if available */ + for (rj = s->response_jobs; rj; rj = rj->jobs_next) { + + if (rj->done) + continue; + + if (!packet_add_response_job(s, p, rj)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static void response_elapse(AvahiTimeEvent *e, gpointer data) { + AvahiResponseJob *rj = data; + AvahiPacketScheduler *s; + + g_assert(rj); + s = rj->scheduler; + + if (rj->done) { + /* Lets remove it from the history */ + response_job_free(s, rj); + return; + } + + send_response_packet(s, rj); +} + +static AvahiResponseJob* look_for_response(AvahiPacketScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + g_assert(s); + g_assert(record); + + for (rj = s->response_jobs; rj; rj = rj->jobs_next) + if (avahi_record_equal_no_ttl(rj->record, record)) + return rj; + + return NULL; +} + +static AvahiResponseJob* response_job_new(AvahiPacketScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + g_assert(s); + g_assert(record); + + rj = g_new(AvahiResponseJob, 1); + rj->scheduler = s; + rj->record = avahi_record_ref(record); + rj->done = FALSE; + rj->time_event = NULL; + rj->address_valid = FALSE; + rj->flush_cache = FALSE; + + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->response_jobs, rj); + + return rj; +} + +void avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { + AvahiResponseJob *rj; + GTimeVal tv; + gchar *t; + + g_assert(s); + g_assert(record); + + g_assert(!avahi_key_is_pattern(record->key)); + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC); + + /* Don't send out duplicates */ + + if ((rj = look_for_response(s, record))) { + glong d; + + d = avahi_timeval_diff(&tv, &rj->delivery); + + /* If there's already a matching packet in our history or in + * the schedule, we do nothing. */ + if (!!record->ttl == !!rj->record->ttl && + d >= 0 && d <= AVAHI_RESPONSE_HISTORY_MSEC*1000) { + g_message("WARNING! DUPLICATE RESPONSE SUPPRESSION ACTIVE!"); + + /* This job is no longer specific to a single querier, so + * make sure it isn't suppressed by known answer + * suppresion */ + + if (rj->address_valid && (!a || avahi_address_cmp(a, &rj->address) != 0)) + rj->address_valid = FALSE; + + rj->flush_cache = flush_cache; + + return; + } + + /* Either one was a goodbye packet, but the other was not, so + * let's drop the older one. */ + response_job_free(s, rj); + } + +/* g_message("ACCEPTED NEW RESPONSE [%s]", t = avahi_record_to_string(record)); */ +/* g_free(t); */ + + /* Create a new job and schedule it */ + rj = response_job_new(s, record); + rj->flush_cache = flush_cache; + rj->delivery = tv; + rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &rj->delivery, response_elapse, rj); + + /* Store the address of the host this messages is intended to, so + that we can drop this job in case a truncated message with + known answer suppresion entries is recieved */ + + if ((rj->address_valid = !!a)) + rj->address = *a; +} + +void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key) { + GTimeVal tv; + AvahiQueryJob *qj; + + g_assert(s); + g_assert(key); + + /* This function is called whenever an incoming query was + * receieved. We drop all scheduled queries which match here. The + * keyword is "DUPLICATE QUESTION SUPPRESION". */ + + for (qj = s->query_jobs; qj; qj = qj->jobs_next) + if (avahi_key_equal(qj->key, key)) { + + if (qj->done) + return; + + goto mark_done; + } + + + /* No matching job was found. Add the query to the history */ + qj = query_job_new(s, key); + +mark_done: + qj->done = TRUE; + + /* Drop the query after some time */ + avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0); + qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, query_elapse, qj); + + g_get_current_time(&qj->delivery); +} + +void response_job_set_elapse_time(AvahiPacketScheduler *s, AvahiResponseJob *rj, guint msec, guint jitter) { + GTimeVal tv; + + g_assert(s); + g_assert(rj); + + avahi_elapse_time(&tv, msec, jitter); + + if (rj->time_event) + avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv); + else + rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, response_elapse, rj); + +} + +void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + g_assert(s); + g_assert(record); + + /* This function is called whenever an incoming response was + * receieved. We drop all scheduled responses which match + * here. The keyword is "DUPLICATE ANSWER SUPPRESION". */ + + for (rj = s->response_jobs; rj; rj = rj->jobs_next) + if (avahi_record_equal_no_ttl(rj->record, record)) { + + if (rj->done) { + + if (!!record->ttl == !!rj->record->ttl) { + /* An entry like this is already in our history, + * so let's get out of here! */ + + return; + + } else { + /* Either one was a goodbye packet but other was + * none. We remove the history entry, and add a + * new one */ + + response_job_free(s, rj); + break; + } + + } else { + + if (!!record->ttl == !!rj->record->ttl) { + + /* The incoming packet matches our scheduled + * record, so let's mark that one as done */ + + goto mark_done; + + } else { + + /* Either one was a goodbye packet but other was + * none. We ignore the incoming packet. */ + + return; + } + } + } + + /* No matching job was found. Add the query to the history */ + rj = response_job_new(s, record); + +mark_done: + rj->done = TRUE; + + /* Drop response after 500ms from history */ + response_job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); + + g_get_current_time(&rj->delivery); +} + +void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *a) { + AvahiResponseJob *rj; + + g_assert(s); + g_assert(record); + g_assert(a); + + for (rj = s->response_jobs; rj; rj = rj->jobs_next) { + + g_assert(record->ttl > 0); + g_assert(rj->record->ttl/2); + + if (avahi_record_equal_no_ttl(rj->record, record)) + if (rj->address_valid) + if (avahi_address_cmp(&rj->address, a)) + if (record->ttl >= rj->record->ttl/2) { + + /* Let's suppress it */ + + response_job_free(s, rj); + break; + } + } +} + +void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s) { + AvahiResponseJob *rj; + + g_assert(s); + + /* Send all scheduled responses, ignoring the scheduled time */ + + for (rj = s->response_jobs; rj; rj = rj->jobs_next) + if (!rj->done) + send_response_packet(s, rj); +} + +static AvahiProbeJob* probe_job_new(AvahiPacketScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + g_assert(s); + g_assert(record); + + pj = g_new(AvahiProbeJob, 1); + pj->scheduler = s; + pj->record = avahi_record_ref(record); + pj->time_event = NULL; + pj->chosen = FALSE; + + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->probe_jobs, pj); + + return pj; +} + +static guint8* packet_add_probe_query(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) { + guint size; + guint8 *ret; + AvahiKey *k; + + g_assert(s); + g_assert(p); + g_assert(pj); + + g_assert(!pj->chosen); + + /* Estimate the size for this record */ + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record); + + /* Too large */ + if (size > avahi_dns_packet_space(p)) + return NULL; + + /* Create the probe query */ + k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY); + ret = avahi_dns_packet_append_key(p, k, FALSE); + g_assert(ret); + + /* Mark this job for addition to the packet */ + pj->chosen = TRUE; + + /* Scan for more jobs whith matching key pattern */ + for (pj = s->probe_jobs; pj; pj = pj->jobs_next) { + if (pj->chosen) + continue; + + /* Does the record match the probe? */ + if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name)) + continue; + + /* This job wouldn't fit in */ + if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p)) + break; + + /* Mark this job for addition to the packet */ + pj->chosen = TRUE; + } + + avahi_key_unref(k); + + return ret; +} + +static void probe_elapse(AvahiTimeEvent *e, gpointer data) { + AvahiProbeJob *pj = data, *next; + AvahiPacketScheduler *s; + AvahiDnsPacket *p; + guint n; + guint8 *d; + + g_assert(pj); + s = pj->scheduler; + + p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48); + + /* Add the import probe */ + if (!packet_add_probe_query(s, p, pj)) { + g_warning("Record too large! ---"); + avahi_dns_packet_free(p); + return; + } + + n = 1; + + /* Try to fill up packet with more probes, if available */ + for (pj = s->probe_jobs; pj; pj = pj->jobs_next) { + + if (pj->chosen) + continue; + + if (!packet_add_probe_query(s, p, pj)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + n = 0; + + /* Now add the chosen records to the authorative section */ + for (pj = s->probe_jobs; pj; pj = next) { + + next = pj->jobs_next; + + if (!pj->chosen) + continue; + + if (!avahi_dns_packet_append_record(p, pj->record, TRUE)) { + g_warning("Bad probe size estimate!"); + + /* Unmark all following jobs */ + for (; pj; pj = pj->jobs_next) + pj->chosen = FALSE; + + break; + } + + probe_job_free(s, pj); + + n ++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); + + /* Send it now */ + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +void avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately) { + AvahiProbeJob *pj; + GTimeVal tv; + + g_assert(s); + g_assert(record); + g_assert(!avahi_key_is_pattern(record->key)); + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0); + + /* Create a new job and schedule it */ + pj = probe_job_new(s, record); + pj->delivery = tv; + pj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &pj->delivery, probe_elapse, pj); +} diff --git a/libavahi-core/psched.h b/libavahi-core/psched.h new file mode 100644 index 0000000..8f5e77c --- /dev/null +++ b/libavahi-core/psched.h @@ -0,0 +1,78 @@ +#ifndef foopschedhfoo +#define foopschedhfoo + +typedef struct _AvahiQueryJob AvahiQueryJob; +typedef struct _AvahiResponseJob AvahiResponseJob; +typedef struct _AvahiPacketScheduler AvahiPacketScheduler; +typedef struct _AvahiKnownAnswer AvahiKnownAnswer; +typedef struct _AvahiProbeJob AvahiProbeJob; + +#include "timeeventq.h" +#include "rr.h" +#include "llist.h" +#include "iface.h" + +struct _AvahiQueryJob { + AvahiPacketScheduler *scheduler; + AvahiTimeEvent *time_event; + AvahiKey *key; + gboolean done; + GTimeVal delivery; + AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs); +}; + +struct _AvahiResponseJob { + AvahiPacketScheduler *scheduler; + AvahiTimeEvent *time_event; + AvahiRecord *record; + AvahiAddress address; + gboolean address_valid; + gboolean done; + GTimeVal delivery; + gboolean flush_cache; + AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs); +}; + +struct _AvahiKnownAnswer { + AvahiPacketScheduler *scheduler; + AvahiRecord *record; + + AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer); +}; + +struct _AvahiProbeJob { + AvahiPacketScheduler *scheduler; + AvahiTimeEvent *time_event; + AvahiRecord *record; + + gboolean chosen; /* Use for packet assembling */ + GTimeVal delivery; + + AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs); +}; + +struct _AvahiPacketScheduler { + AvahiServer *server; + + AvahiInterface *interface; + + AVAHI_LLIST_HEAD(AvahiQueryJob, query_jobs); + AVAHI_LLIST_HEAD(AvahiResponseJob, response_jobs); + AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers); + AVAHI_LLIST_HEAD(AvahiProbeJob, probe_jobs); +}; + +AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i); +void avahi_packet_scheduler_free(AvahiPacketScheduler *s); + +void avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately); +void avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately); +void avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately); + +void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key); +void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record); +void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *a); + +void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s); + +#endif diff --git a/libavahi-core/rr.c b/libavahi-core/rr.c new file mode 100644 index 0000000..f4102dd --- /dev/null +++ b/libavahi-core/rr.c @@ -0,0 +1,556 @@ +#include +#include +#include +#include +#include + +#include "util.h" +#include "rr.h" +#include "dns.h" + +AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type) { + AvahiKey *k; + g_assert(name); + + k = g_new(AvahiKey, 1); + k->ref = 1; + k->name = avahi_normalize_name(name); + k->class = class; + k->type = type; + +/* g_message("%p %% ref=1", k); */ + + return k; +} + +AvahiKey *avahi_key_ref(AvahiKey *k) { + g_assert(k); + g_assert(k->ref >= 1); + + k->ref++; + +/* g_message("%p ++ ref=%i", k, k->ref); */ + + return k; +} + +void avahi_key_unref(AvahiKey *k) { + g_assert(k); + g_assert(k->ref >= 1); + +/* g_message("%p -- ref=%i", k, k->ref-1); */ + + if ((--k->ref) <= 0) { + g_free(k->name); + g_free(k); + } +} + +AvahiRecord *avahi_record_new(AvahiKey *k) { + AvahiRecord *r; + + g_assert(k); + + r = g_new(AvahiRecord, 1); + r->ref = 1; + r->key = avahi_key_ref(k); + + memset(&r->data, 0, sizeof(r->data)); + + r->ttl = AVAHI_DEFAULT_TTL; + + return r; +} + +AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type) { + AvahiRecord *r; + AvahiKey *k; + + g_assert(name); + + k = avahi_key_new(name, class, type); + r = avahi_record_new(k); + avahi_key_unref(k); + + return r; +} + +AvahiRecord *avahi_record_ref(AvahiRecord *r) { + g_assert(r); + g_assert(r->ref >= 1); + + r->ref++; + return r; +} + +void avahi_record_unref(AvahiRecord *r) { + g_assert(r); + g_assert(r->ref >= 1); + + if ((--r->ref) <= 0) { + switch (r->key->type) { + + case AVAHI_DNS_TYPE_SRV: + g_free(r->data.srv.name); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + g_free(r->data.ptr.name); + break; + + case AVAHI_DNS_TYPE_HINFO: + g_free(r->data.hinfo.cpu); + g_free(r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_TXT: + avahi_string_list_free(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + break; + + default: + g_free(r->data.generic.data); + } + + avahi_key_unref(r->key); + g_free(r); + } +} + +const gchar *avahi_dns_class_to_string(guint16 class) { + if (class & AVAHI_DNS_CACHE_FLUSH) + return "FLUSH"; + + if (class == AVAHI_DNS_CLASS_IN) + return "IN"; + + return NULL; +} + +const gchar *avahi_dns_type_to_string(guint16 type) { + switch (type) { + case AVAHI_DNS_TYPE_CNAME: + return "CNAME"; + case AVAHI_DNS_TYPE_A: + return "A"; + case AVAHI_DNS_TYPE_AAAA: + return "AAAA"; + case AVAHI_DNS_TYPE_PTR: + return "PTR"; + case AVAHI_DNS_TYPE_HINFO: + return "HINFO"; + case AVAHI_DNS_TYPE_TXT: + return "TXT"; + case AVAHI_DNS_TYPE_SRV: + return "SRV"; + case AVAHI_DNS_TYPE_ANY: + return "ANY"; + default: + return NULL; + } +} + + +gchar *avahi_key_to_string(const AvahiKey *k) { + return g_strdup_printf("%s\t%s\t%s", + k->name, + avahi_dns_class_to_string(k->class), + avahi_dns_type_to_string(k->type)); +} + +gchar *avahi_record_to_string(const AvahiRecord *r) { + gchar *p, *s; + char buf[257], *t = NULL, *d = NULL; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_A: + inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_AAAA: + inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME : + + t = r->data.ptr.name; + break; + + case AVAHI_DNS_TYPE_TXT: + t = d = avahi_string_list_to_string(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_HINFO: + + snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_SRV: + + snprintf(t = buf, sizeof(buf), "%u %u %u %s", + r->data.srv.priority, + r->data.srv.weight, + r->data.srv.port, + r->data.srv.name); + + break; + } + + p = avahi_key_to_string(r->key); + s = g_strdup_printf("%s %s ; ttl=%u", p, t ? t : "", r->ttl); + g_free(p); + g_free(d); + + return s; +} + +gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return TRUE; + +/* g_message("equal: %p %p", a, b); */ + + return avahi_domain_equal(a->name, b->name) && + a->type == b->type && + a->class == b->class; +} + +gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { + g_assert(pattern); + g_assert(k); + +/* g_message("equal: %p %p", a, b); */ + + g_assert(!avahi_key_is_pattern(k)); + + if (pattern == k) + return TRUE; + + return avahi_domain_equal(pattern->name, k->name) && + (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && + pattern->class == k->class; +} + +gboolean avahi_key_is_pattern(const AvahiKey *k) { + g_assert(k); + + return k->type == AVAHI_DNS_TYPE_ANY; +} + + +guint avahi_key_hash(const AvahiKey *k) { + g_assert(k); + + return avahi_domain_hash(k->name) + k->type + k->class; +} + +static gboolean rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { + g_assert(a); + g_assert(b); + g_assert(a->key->type == b->key->type); + +/* t = avahi_record_to_string(a); */ +/* g_message("comparing %s", t); */ +/* g_free(t); */ + +/* t = avahi_record_to_string(b); */ +/* g_message("and %s", t); */ +/* g_free(t); */ + + + switch (a->key->type) { + case AVAHI_DNS_TYPE_SRV: + return + a->data.srv.priority == b->data.srv.priority && + a->data.srv.weight == b->data.srv.weight && + a->data.srv.port == b->data.srv.port && + avahi_domain_equal(a->data.srv.name, b->data.srv.name); + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_HINFO: + return + !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) && + !strcmp(a->data.hinfo.os, b->data.hinfo.os); + + case AVAHI_DNS_TYPE_TXT: + return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; + + default: + return a->data.generic.size == b->data.generic.size && + (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); + } + +} + +gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return TRUE; + + return + avahi_key_equal(a->key, b->key) && + rdata_equal(a, b); +} + + +AvahiRecord *avahi_record_copy(AvahiRecord *r) { + AvahiRecord *copy; + + copy = g_new(AvahiRecord, 1); + copy->ref = 1; + copy->key = avahi_key_ref(r->key); + copy->ttl = r->ttl; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + copy->data.ptr.name = g_strdup(r->data.ptr.name); + break; + + case AVAHI_DNS_TYPE_SRV: + copy->data.srv.priority = r->data.srv.priority; + copy->data.srv.weight = r->data.srv.weight; + copy->data.srv.port = r->data.srv.port; + copy->data.srv.name = g_strdup(r->data.srv.name); + break; + + case AVAHI_DNS_TYPE_HINFO: + copy->data.hinfo.os = g_strdup(r->data.hinfo.os); + copy->data.hinfo.cpu = g_strdup(r->data.hinfo.cpu); + break; + + case AVAHI_DNS_TYPE_TXT: + copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + copy->data.a.address = r->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + copy->data.aaaa.address = r->data.aaaa.address; + break; + + default: + copy->data.generic.data = g_memdup(r->data.generic.data, r->data.generic.size); + copy->data.generic.size = r->data.generic.size; + break; + + } + + return copy; +} + + +guint avahi_key_get_estimate_size(AvahiKey *k) { + g_assert(k); + + return strlen(k->name)+1+4; +} + +guint avahi_record_get_estimate_size(AvahiRecord *r) { + guint n; + g_assert(r); + + n = avahi_key_get_estimate_size(r->key) + 4 + 2; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + n += strlen(r->data.ptr.name) + 1; + break; + + case AVAHI_DNS_TYPE_SRV: + n += 6 + strlen(r->data.srv.name) + 1; + break; + + case AVAHI_DNS_TYPE_HINFO: + n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; + break; + + case AVAHI_DNS_TYPE_TXT: + n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + break; + + case AVAHI_DNS_TYPE_A: + n += sizeof(AvahiIPv4Address); + break; + + case AVAHI_DNS_TYPE_AAAA: + n += sizeof(AvahiIPv6Address); + break; + + default: + n += r->data.generic.size; + } + + return n; +} + +static gint lexicographical_memcmp(gconstpointer a, size_t al, gconstpointer b, size_t bl) { + size_t c; + gint ret; + + g_assert(a); + g_assert(b); + + c = al < bl ? al : bl; + if ((ret = memcmp(a, b, c)) != 0) + return ret; + + if (al == bl) + return 0; + else + return al == c ? 1 : -1; +} + +static gint uint16_cmp(guint16 a, guint16 b) { + return a == b ? 0 : (a < b ? a : b); +} + +static gint lexicographical_domain_cmp(const gchar *a, const gchar *b) { + g_assert(a); + g_assert(b); + + + for (;;) { + gchar t1[64]; + gchar t2[64]; + size_t al, bl; + gint r; + + if (!a && !b) + return 0; + + if (a && !b) + return 1; + + if (b && !a) + return -1; + + avahi_unescape_label(t1, sizeof(t1), &a); + avahi_unescape_label(t2, sizeof(t2), &b); + + al = strlen(t1); + bl = strlen(t2); + + if (al != bl) + return al < bl ? -1 : 1; + + if ((r = strcmp(t1, t2)) != 0) + return r; + } +} + +gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return 0; + +/* gchar *t; */ + +/* g_message("comparing [%s]", t = avahi_record_to_string(a)); */ +/* g_free(t); */ + +/* g_message("and [%s]", t = avahi_record_to_string(b)); */ +/* g_free(t); */ + + if (a->key->class < b->key->class) + return -1; + else if (a->key->class > b->key->class) + return 1; + + if (a->key->type < b->key->type) + return -1; + else if (a->key->type > b->key->type) + return 1; + + switch (a->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + return lexicographical_domain_cmp(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_SRV: { + gint r; + if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && + (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && + (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) + r = lexicographical_domain_cmp(a->data.srv.name, b->data.srv.name); + + return r; + } + + case AVAHI_DNS_TYPE_HINFO: { + size_t al = strlen(a->data.hinfo.cpu), bl = strlen(b->data.hinfo.cpu); + gint r; + + if (al != bl) + return al < bl ? -1 : 1; + + if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) != 0) + return r; + + al = strlen(a->data.hinfo.os), bl = strlen(b->data.hinfo.os); + + if (al != bl) + return al < bl ? -1 : 1; + + if ((r = strcmp(a->data.hinfo.os, b->data.hinfo.os)) != 0) + return r; + + return 0; + + } + + case AVAHI_DNS_TYPE_TXT: { + + guint8 *ma, *mb; + guint asize, bsize; + gint r; + + ma = g_new(guint8, asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0)); + mb = g_new(guint8, bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0)); + avahi_string_list_serialize(a->data.txt.string_list, ma, asize); + avahi_string_list_serialize(a->data.txt.string_list, mb, bsize); + + r = lexicographical_memcmp(ma, asize, mb, bsize); + g_free(ma); + g_free(mb); + + return r; + } + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); + + default: + return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, + b->data.generic.data, b->data.generic.size); + } + +} diff --git a/libavahi-core/rr.h b/libavahi-core/rr.h new file mode 100644 index 0000000..6f1a8cb --- /dev/null +++ b/libavahi-core/rr.h @@ -0,0 +1,117 @@ +#ifndef foorrhfoo +#define foorrhfoo + +#include + +#include "strlst.h" +#include "address.h" + +enum { + AVAHI_DNS_TYPE_A = 0x01, + AVAHI_DNS_TYPE_NS = 0x02, + AVAHI_DNS_TYPE_CNAME = 0x05, + AVAHI_DNS_TYPE_SOA = 0x06, + AVAHI_DNS_TYPE_PTR = 0x0C, + AVAHI_DNS_TYPE_HINFO = 0x0D, + AVAHI_DNS_TYPE_MX = 0x0F, + AVAHI_DNS_TYPE_TXT = 0x10, + AVAHI_DNS_TYPE_AAAA = 0x1C, + AVAHI_DNS_TYPE_SRV = 0x21, + AVAHI_DNS_TYPE_ANY = 0xFF +}; + +enum { + AVAHI_DNS_CLASS_IN = 0x01, + AVAHI_DNS_CACHE_FLUSH = 0x8000, + AVAHI_DNS_UNICAST_RESPONSE = 0x8000 +}; + +#define AVAHI_DEFAULT_TTL (120*60) + +typedef struct { + guint ref; + gchar *name; + guint16 class; + guint16 type; +} AvahiKey; + +typedef struct { + guint ref; + AvahiKey *key; + + guint32 ttl; + + union { + struct { + gpointer data; + guint16 size; + } generic; + + struct { + guint16 priority; + guint16 weight; + guint16 port; + gchar *name; + } srv; + + struct { + gchar *name; + } ptr; /* and cname */ + + struct { + gchar *cpu; + gchar *os; + } hinfo; + + struct { + AvahiStringList *string_list; + } txt; + + struct { + AvahiIPv4Address address; + } a; + + struct { + AvahiIPv6Address address; + } aaaa; + + } data; + +} AvahiRecord; + +AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type); +AvahiKey *avahi_key_ref(AvahiKey *k); +void avahi_key_unref(AvahiKey *k); + +gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b); /* Treat AVAHI_DNS_CLASS_ANY like any other type */ +gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); /* If pattern.type is AVAHI_DNS_CLASS_ANY, k.type is ignored */ + +gboolean avahi_key_is_pattern(const AvahiKey *k); + +guint avahi_key_hash(const AvahiKey *k); + +AvahiRecord *avahi_record_new(AvahiKey *k); +AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type); +AvahiRecord *avahi_record_ref(AvahiRecord *r); +void avahi_record_unref(AvahiRecord *r); + +const gchar *avahi_dns_class_to_string(guint16 class); +const gchar *avahi_dns_type_to_string(guint16 type); + +gchar *avahi_key_to_string(const AvahiKey *k); /* g_free() the result! */ +gchar *avahi_record_to_string(const AvahiRecord *r); /* g_free() the result! */ + +gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b); + +AvahiRecord *avahi_record_copy(AvahiRecord *r); + +/* returns a maximum estimate for the space that is needed to store + * this key in a DNS packet */ +guint avahi_key_get_estimate_size(AvahiKey *k); + +/* ditto */ +guint avahi_record_get_estimate_size(AvahiRecord *r); + +gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); + +#endif diff --git a/libavahi-core/server.c b/libavahi-core/server.c new file mode 100644 index 0000000..b17b142 --- /dev/null +++ b/libavahi-core/server.c @@ -0,0 +1,1014 @@ +#include +#include +#include +#include +#include + +#include "server.h" +#include "util.h" +#include "iface.h" +#include "socket.h" +#include "subscribe.h" + +static void free_entry(AvahiServer*s, AvahiEntry *e) { + AvahiEntry *t; + + g_assert(s); + g_assert(e); + + avahi_goodbye_entry(s, e, TRUE); + + /* Remove from linked list */ + AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e); + + /* Remove from hash table indexed by name */ + t = g_hash_table_lookup(s->entries_by_key, e->record->key); + AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); + if (t) + g_hash_table_replace(s->entries_by_key, t->record->key, t); + else + g_hash_table_remove(s->entries_by_key, e->record->key); + + /* Remove from associated group */ + if (e->group) + AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); + + avahi_record_unref(e->record); + g_free(e); +} + +static void free_group(AvahiServer *s, AvahiEntryGroup *g) { + g_assert(s); + g_assert(g); + + while (g->entries) + free_entry(s, g->entries); + + AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g); + g_free(g); +} + +static void cleanup_dead(AvahiServer *s) { + AvahiEntryGroup *g, *ng; + AvahiEntry *e, *ne; + g_assert(s); + + + if (s->need_group_cleanup) { + for (g = s->groups; g; g = ng) { + ng = g->groups_next; + + if (g->dead) + free_group(s, g); + } + + s->need_group_cleanup = FALSE; + } + + if (s->need_entry_cleanup) { + for (e = s->entries; e; e = ne) { + ne = e->entries_next; + + if (e->dead) + free_entry(s, e); + } + + s->need_entry_cleanup = FALSE; + } +} + +static void handle_query_key(AvahiServer *s, AvahiKey *k, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean unicast_response) { + AvahiEntry *e; + gchar *txt; + + g_assert(s); + g_assert(k); + g_assert(i); + g_assert(a); + + g_message("Handling query: %s", txt = avahi_key_to_string(k)); + g_free(txt); + + avahi_packet_scheduler_incoming_query(i->scheduler, k); + + if (k->type == AVAHI_DNS_TYPE_ANY) { + + /* Handle ANY query */ + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i)) + avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, FALSE); + } else { + + /* Handle all other queries */ + + for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next) + if (!e->dead && avahi_entry_registered(s, e, i)) + avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, FALSE); + } +} + +static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { + g_assert(s); + g_assert(e); + + + if (e->group) { + AvahiEntry *k; + + for (k = e->group->entries; k; k = k->by_group_next) { + avahi_goodbye_entry(s, k, FALSE); + k->dead = TRUE; + } + + avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION); + } else { + avahi_goodbye_entry(s, e, FALSE); + e->dead = TRUE; + } + + s->need_entry_cleanup = TRUE; +} + +static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { + AvahiEntry *e, *n; + gchar *t; + + g_assert(s); + g_assert(record); + g_assert(i); + + t = avahi_record_to_string(record); + +/* g_message("PROBE: [%s]", t); */ + + for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) { + n = e->by_key_next; + + if (e->dead || avahi_record_equal_no_ttl(record, e->record)) + continue; + + if (avahi_entry_registering(s, e, i)) { + gint cmp; + + if ((cmp = avahi_record_lexicographical_compare(record, e->record)) > 0) { + withdraw_entry(s, e); + g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t); + } else if (cmp < 0) + g_message("Recieved conflicting probe [%s]. Local host won.", t); + + } + } + + g_free(t); +} + +static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) { + guint n; + + g_assert(s); + g_assert(p); + g_assert(i); + g_assert(a); + + /* Handle the questions */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) { + AvahiKey *key; + gboolean unicast_response = FALSE; + + if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { + g_warning("Packet too short (1)"); + return; + } + + handle_query_key(s, key, i, a, port, legacy_unicast, unicast_response); + avahi_key_unref(key); + } + + /* Known Answer Suppression */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { + AvahiRecord *record; + gboolean unique = FALSE; + + if (!(record = avahi_dns_packet_consume_record(p, &unique))) { + g_warning("Packet too short (2)"); + return; + } + + avahi_packet_scheduler_incoming_known_answer(i->scheduler, record, a); + avahi_record_unref(record); + } + + /* Probe record */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) { + AvahiRecord *record; + gboolean unique = FALSE; + + if (!(record = avahi_dns_packet_consume_record(p, &unique))) { + g_warning("Packet too short (3)"); + return; + } + + if (record->key->type != AVAHI_DNS_TYPE_ANY) + incoming_probe(s, record, i); + + avahi_record_unref(record); + } +} + +static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) { + gboolean valid = TRUE; + AvahiEntry *e, *n; + gchar *t; + + g_assert(s); + g_assert(i); + g_assert(record); + + t = avahi_record_to_string(record); + +/* g_message("CHECKING FOR CONFLICT: [%s]", t); */ + + for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) { + n = e->by_key_next; + + if (e->dead) + continue; + + if (avahi_entry_registered(s, e, i)) { + + gboolean equal = avahi_record_equal_no_ttl(record, e->record); + + /* Check whether there is a unique record conflict */ + if (!equal && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) { + gint cmp; + + /* The lexicographically later data wins. */ + if ((cmp = avahi_record_lexicographical_compare(record, e->record)) > 0) { + g_message("Recieved conflicting record [%s]. Local host lost. Withdrawing.", t); + withdraw_entry(s, e); + } else if (cmp < 0) { + /* Tell the other host that our entry is lexicographically later */ + + g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t); + + valid = FALSE; + avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); + } + + /* Check wheter there is a TTL conflict */ + } else if (equal && record->ttl <= e->record->ttl/2) { + /* Correct the TTL */ + valid = FALSE; + avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); + g_message("Recieved record with bad TTL [%s]. Refreshing.", t); + } + + } else if (avahi_entry_registering(s, e, i)) { + + if (!avahi_record_equal_no_ttl(record, e->record) && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) { + + /* We are currently registering a matching record, but + * someone else already claimed it, so let's + * withdraw */ + + g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t); + withdraw_entry(s, e); + } + } + } + + g_free(t); + + return valid; +} + +static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) { + guint n; + + g_assert(s); + g_assert(p); + g_assert(i); + g_assert(a); + + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) { + AvahiRecord *record; + gboolean cache_flush = FALSE; + gchar *txt; + + if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) { + g_warning("Packet too short (4)"); + return; + } + + if (record->key->type != AVAHI_DNS_TYPE_ANY) { + + g_message("Handling response: %s", txt = avahi_record_to_string(record)); + g_free(txt); + + if (handle_conflict(s, i, record, cache_flush, a)) { + avahi_cache_update(i->cache, record, cache_flush, a); + avahi_packet_scheduler_incoming_response(i->scheduler, record); + } + } + + avahi_record_unref(record); + } +} + +static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) { + AvahiInterface *i; + AvahiAddress a; + guint16 port; + + g_assert(s); + g_assert(p); + g_assert(sa); + g_assert(iface > 0); + + if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) { + g_warning("Recieved packet from invalid interface."); + return; + } + + g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); + + if (sa->sa_family == AF_INET6) { + static const guint8 ipv4_in_ipv6[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF }; + + /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ + + if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0) + return; + } + + if (avahi_dns_packet_check_valid(p) < 0) { + g_warning("Recieved invalid packet."); + return; + } + + port = avahi_port_from_sockaddr(sa); + avahi_address_from_sockaddr(sa, &a); + + if (avahi_dns_packet_is_query(p)) { + gboolean legacy_unicast = FALSE; + + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) == 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) { + g_warning("Invalid query packet."); + return; + } + + if (port != AVAHI_MDNS_PORT) { + /* Legacy Unicast */ + + if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) { + g_warning("Invalid legacy unicast query packet."); + return; + } + + legacy_unicast = TRUE; + } + + handle_query(s, p, i, &a, port, legacy_unicast); + + g_message("Handled query"); + } else { + + if (port != AVAHI_MDNS_PORT) { + g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol); + return; + } + + if (ttl != 255) { + g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol); + if (!s->ignore_bad_ttl) + return; + } + + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) { + g_warning("Invalid response packet."); + return; + } + + handle_response(s, p, i, &a); + g_message("Handled response"); + } +} + +static void work(AvahiServer *s) { + struct sockaddr_in6 sa6; + struct sockaddr_in sa; + AvahiDnsPacket *p; + gint iface = -1; + guint8 ttl; + + g_assert(s); + + if (s->pollfd_ipv4.revents & G_IO_IN) { + if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) { + dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl); + avahi_dns_packet_free(p); + } + } + + if (s->pollfd_ipv6.revents & G_IO_IN) { + if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) { + dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl); + avahi_dns_packet_free(p); + } + } +} + +static gboolean prepare_func(GSource *source, gint *timeout) { + g_assert(source); + g_assert(timeout); + + *timeout = -1; + return FALSE; +} + +static gboolean check_func(GSource *source) { + AvahiServer* s; + g_assert(source); + + s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource))); + g_assert(s); + + return (s->pollfd_ipv4.revents | s->pollfd_ipv6.revents) & (G_IO_IN | G_IO_HUP | G_IO_ERR); +} + +static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) { + AvahiServer* s; + g_assert(source); + + s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource))); + g_assert(s); + + work(s); + cleanup_dead(s); + + return TRUE; +} + +static void add_default_entries(AvahiServer *s) { + gint length = 0; + struct utsname utsname; + gchar *hinfo; + AvahiAddress a; + AvahiRecord *r; + + g_assert(s); + + /* Fill in HINFO rr */ + r = avahi_record_new_full(s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO); + uname(&utsname); + r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine)); + r->data.hinfo.os = g_strdup(g_strup(utsname.sysname)); + avahi_server_add(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r); + avahi_record_unref(r); + + /* Add localhost entries */ + avahi_address_parse("127.0.0.1", AF_INET, &a); + avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE|AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a); + + avahi_address_parse("::1", AF_INET6, &a); + avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE|AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a); +} + +AvahiServer *avahi_server_new(GMainContext *c) { + gchar *hn, *e; + AvahiServer *s; + + static GSourceFuncs source_funcs = { + prepare_func, + check_func, + dispatch_func, + NULL, + NULL, + NULL + }; + + s = g_new(AvahiServer, 1); + + s->ignore_bad_ttl = FALSE; + s->need_entry_cleanup = s->need_group_cleanup = FALSE; + + s->fd_ipv4 = avahi_open_socket_ipv4(); + s->fd_ipv6 = avahi_open_socket_ipv6(); + + if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) { + g_critical("Failed to create IP sockets.\n"); + g_free(s); + return NULL; + } + + if (s->fd_ipv4 < 0) + g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode"); + else if (s->fd_ipv6 < 0) + g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode"); + + if (c) + g_main_context_ref(s->context = c); + else + s->context = g_main_context_default(); + + AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries); + s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal); + AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups); + + AVAHI_LLIST_HEAD_INIT(AvahiSubscription, s->subscriptions); + s->subscription_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal); + + /* Get host name */ + hn = avahi_get_host_name(); + hn[strcspn(hn, ".")] = 0; + + s->hostname = g_strdup_printf("%s.local.", hn); + g_free(hn); + + s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */ + s->monitor = avahi_interface_monitor_new(s); + avahi_interface_monitor_sync(s->monitor); + add_default_entries(s); + + /* Prepare IO source registration */ + s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*)); + *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s; + + memset(&s->pollfd_ipv4, 0, sizeof(s->pollfd_ipv4)); + s->pollfd_ipv4.fd = s->fd_ipv4; + s->pollfd_ipv4.events = G_IO_IN|G_IO_ERR|G_IO_HUP; + g_source_add_poll(s->source, &s->pollfd_ipv4); + + memset(&s->pollfd_ipv6, 0, sizeof(s->pollfd_ipv6)); + s->pollfd_ipv6.fd = s->fd_ipv6; + s->pollfd_ipv6.events = G_IO_IN|G_IO_ERR|G_IO_HUP; + g_source_add_poll(s->source, &s->pollfd_ipv6); + + g_source_attach(s->source, s->context); + + return s; +} + +void avahi_server_free(AvahiServer* s) { + g_assert(s); + + while(s->entries) + free_entry(s, s->entries); + + avahi_interface_monitor_free(s->monitor); + + while (s->groups) + free_group(s, s->groups); + + while (s->subscriptions) + avahi_subscription_free(s->subscriptions); + g_hash_table_destroy(s->subscription_hashtable); + + g_hash_table_destroy(s->entries_by_key); + + avahi_time_event_queue_free(s->time_event_queue); + + if (s->fd_ipv4 >= 0) + close(s->fd_ipv4); + if (s->fd_ipv6 >= 0) + close(s->fd_ipv6); + + g_free(s->hostname); + + g_source_destroy(s->source); + g_source_unref(s->source); + g_main_context_unref(s->context); + + g_free(s); +} + +void avahi_server_add( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + AvahiRecord *r) { + + AvahiEntry *e, *t; + g_assert(s); + g_assert(r); + + g_assert(r->key->type != AVAHI_DNS_TYPE_ANY); + + e = g_new(AvahiEntry, 1); + e->server = s; + e->record = avahi_record_ref(r); + e->group = g; + e->interface = interface; + e->protocol = protocol; + e->flags = flags; + e->dead = FALSE; + + AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements); + + AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); + + /* Insert into hash table indexed by name */ + t = g_hash_table_lookup(s->entries_by_key, e->record->key); + AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); + g_hash_table_replace(s->entries_by_key, e->record->key, t); + + /* Insert into group list */ + if (g) + AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); + + avahi_announce_entry(s, e); +} +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) { + AvahiEntry **e = (AvahiEntry**) state; + g_assert(s); + g_assert(e); + + if (!*e) + *e = g ? g->entries : s->entries; + + while (*e && (*e)->dead) + *e = g ? (*e)->by_group_next : (*e)->entries_next; + + if (!*e) + return NULL; + + return avahi_record_ref((*e)->record); +} + +void avahi_server_dump(AvahiServer *s, FILE *f) { + AvahiEntry *e; + g_assert(s); + g_assert(f); + + fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n"); + + for (e = s->entries; e; e = e->entries_next) { + gchar *t; + + if (e->dead) + continue; + + t = avahi_record_to_string(e->record); + fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol); + g_free(t); + } + + avahi_dump_caches(s->monitor, f); +} + +void avahi_server_add_ptr( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + const gchar *dest) { + + AvahiRecord *r; + + g_assert(dest); + + r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); + r->data.ptr.name = avahi_normalize_name(dest); + avahi_server_add(s, g, interface, protocol, flags, r); + avahi_record_unref(r); +} + +void avahi_server_add_address( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + AvahiAddress *a) { + + gchar *n = NULL; + g_assert(s); + g_assert(a); + + name = name ? (n = avahi_normalize_name(name)) : s->hostname; + + if (a->family == AF_INET) { + gchar *reverse; + AvahiRecord *r; + + r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); + r->data.a.address = a->data.ipv4; + avahi_server_add(s, g, interface, protocol, flags, r); + avahi_record_unref(r); + + reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4); + g_assert(reverse); + avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name); + g_free(reverse); + + } else { + gchar *reverse; + AvahiRecord *r; + + r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); + r->data.aaaa.address = a->data.ipv6; + avahi_server_add(s, g, interface, protocol, flags, r); + avahi_record_unref(r); + + reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6); + g_assert(reverse); + avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name); + g_free(reverse); + + reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6); + g_assert(reverse); + avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name); + g_free(reverse); + } + + g_free(n); +} + +void avahi_server_add_text_strlst( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + AvahiStringList *strlst) { + + AvahiRecord *r; + + g_assert(s); + + r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); + r->data.txt.string_list = strlst; + avahi_server_add(s, g, interface, protocol, flags, r); + avahi_record_unref(r); +} + +void avahi_server_add_text_va( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + va_list va) { + + g_assert(s); + + avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va)); +} + +void avahi_server_add_text( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + AvahiEntryFlags flags, + const gchar *name, + ...) { + + va_list va; + + g_assert(s); + + va_start(va, name); + avahi_server_add_text_va(s, g, interface, protocol, flags, name, va); + va_end(va); +} + +static void escape_service_name(gchar *d, guint size, const gchar *s) { + g_assert(d); + g_assert(size); + g_assert(s); + + while (*s && size >= 2) { + if (*s == '.' || *s == '\\') { + if (size < 3) + break; + + *(d++) = '\\'; + size--; + } + + *(d++) = *(s++); + size--; + } + + g_assert(size > 0); + *(d++) = 0; +} + +void avahi_server_add_service_strlst( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + const gchar *type, + const gchar *name, + const gchar *domain, + const gchar *host, + guint16 port, + AvahiStringList *strlst) { + + gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256]; + AvahiRecord *r; + + g_assert(s); + g_assert(type); + g_assert(name); + + escape_service_name(ename, sizeof(ename), name); + + if (domain) { + while (domain[0] == '.') + domain++; + } else + domain = "local"; + + if (!host) + host = s->hostname; + + snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain); + snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain); + + avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name); + + r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); + r->data.srv.priority = 0; + r->data.srv.weight = 0; + r->data.srv.port = port; + r->data.srv.name = avahi_normalize_name(host); + avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r); + avahi_record_unref(r); + + avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst); + + snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain); + avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name); +} + +void avahi_server_add_service_va( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + const gchar *type, + const gchar *name, + const gchar *domain, + const gchar *host, + guint16 port, + va_list va){ + + g_assert(s); + g_assert(type); + g_assert(name); + + avahi_server_add_service(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va)); +} + +void avahi_server_add_service( + AvahiServer *s, + AvahiEntryGroup *g, + gint interface, + guchar protocol, + const gchar *type, + const gchar *name, + const gchar *domain, + const gchar *host, + guint16 port, + ... ){ + + va_list va; + + g_assert(s); + g_assert(type); + g_assert(name); + + va_start(va, port); + avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va); + va_end(va); +} + +static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { + AvahiKey *k = userdata; + + g_assert(m); + g_assert(i); + g_assert(k); + + avahi_interface_post_query(i, k, FALSE); +} + +void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) { + g_assert(s); + g_assert(key); + + avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key); +} + +struct tmpdata { + AvahiRecord *record; + gboolean flush_cache; +}; + +static void post_response_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { + struct tmpdata *tmpdata = userdata; + + g_assert(m); + g_assert(i); + g_assert(tmpdata); + + avahi_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE); +} + +void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache) { + struct tmpdata tmpdata; + + g_assert(s); + g_assert(record); + + tmpdata.record = record; + tmpdata.flush_cache = flush_cache; + + avahi_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata); +} + +void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) { + g_assert(g); + + g->state = state; + + if (g->callback) { + g->callback(g->server, g, state, g->userdata); + return; + } +} + +AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) { + AvahiEntryGroup *g; + + g_assert(s); + + g = g_new(AvahiEntryGroup, 1); + g->server = s; + g->callback = callback; + g->userdata = userdata; + g->dead = FALSE; + g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; + g->n_probing = 0; + AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); + + AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g); + return g; +} + +void avahi_entry_group_free(AvahiEntryGroup *g) { + g_assert(g); + g_assert(g->server); + + g->dead = TRUE; + g->server->need_group_cleanup = TRUE; +} + +void avahi_entry_group_commit(AvahiEntryGroup *g) { + AvahiEntry *e; + + g_assert(g); + g_assert(!g->dead); + + if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED) + return; + + avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); + avahi_announce_group(g->server, g); + avahi_entry_group_check_probed(g, FALSE); +} + +gboolean avahi_entry_commited(AvahiEntry *e) { + g_assert(e); + g_assert(!e->dead); + + return !e->group || + e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || + e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; +} + +AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) { + g_assert(g); + g_assert(!g->dead); + + return g->state; +} diff --git a/libavahi-core/server.h b/libavahi-core/server.h new file mode 100644 index 0000000..9f7ef51 --- /dev/null +++ b/libavahi-core/server.h @@ -0,0 +1,79 @@ +#ifndef fooAvahiserverhfoo +#define fooAvahiserverhfoo + +#include "avahi.h" +#include "iface.h" +#include "prioq.h" +#include "llist.h" +#include "timeeventq.h" +#include "announce.h" +#include "subscribe.h" + +struct _AvahiEntry { + AvahiServer *server; + AvahiEntryGroup *group; + + gboolean dead; + + AvahiEntryFlags flags; + AvahiRecord *record; + gint interface; + guchar protocol; + + AVAHI_LLIST_FIELDS(AvahiEntry, entries); + AVAHI_LLIST_FIELDS(AvahiEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiEntry, by_group); + + AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements); +}; + +struct _AvahiEntryGroup { + AvahiServer *server; + gboolean dead; + + AvahiEntryGroupState state; + gpointer userdata; + AvahiEntryGroupCallback callback; + + guint n_probing; + + AVAHI_LLIST_FIELDS(AvahiEntryGroup, groups); + AVAHI_LLIST_HEAD(AvahiEntry, entries); +}; + +struct _AvahiServer { + GMainContext *context; + AvahiInterfaceMonitor *monitor; + + AVAHI_LLIST_HEAD(AvahiEntry, entries); + GHashTable *entries_by_key; + + AVAHI_LLIST_HEAD(AvahiEntryGroup, groups); + + AVAHI_LLIST_HEAD(AvahiSubscription, subscriptions); + GHashTable *subscription_hashtable; + + gboolean need_entry_cleanup, need_group_cleanup; + + AvahiTimeEventQueue *time_event_queue; + + gchar *hostname; + + gint fd_ipv4, fd_ipv6; + + GPollFD pollfd_ipv4, pollfd_ipv6; + GSource *source; + + gboolean ignore_bad_ttl; +}; + +gboolean avahi_server_entry_match_interface(AvahiEntry *e, AvahiInterface *i); + +void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key); +void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache); + +void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state); + +gboolean avahi_entry_commited(AvahiEntry *e); + +#endif diff --git a/libavahi-core/socket.c b/libavahi-core/socket.c new file mode 100644 index 0000000..1ef8131 --- /dev/null +++ b/libavahi-core/socket.c @@ -0,0 +1,488 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" +#include "util.h" +#include "socket.h" + +static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { + g_assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in)); + + ret_sa->sin_family = AF_INET; + ret_sa->sin_port = htons(AVAHI_MDNS_PORT); + inet_pton(AF_INET, "224.0.0.251", &ret_sa->sin_addr); +} + +static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { + + g_assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in6)); + + ret_sa->sin6_family = AF_INET6; + ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); + inet_pton(AF_INET6, "ff02::fb", &ret_sa->sin6_addr); +} + +int avahi_mdns_mcast_join_ipv4 (int index, int fd) +{ + struct ip_mreqn mreq; + struct sockaddr_in sa; + + mdns_mcast_group_ipv4 (&sa); + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = sa.sin_addr; + mreq.imr_ifindex = index; + + if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + g_warning("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +int avahi_mdns_mcast_join_ipv6 (int index, int fd) +{ + struct ipv6_mreq mreq6; + struct sockaddr_in6 sa6; + + mdns_mcast_group_ipv6 (&sa6); + + memset(&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_multiaddr = sa6.sin6_addr; + mreq6.ipv6mr_interface = index; + + if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + g_warning("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +int avahi_mdns_mcast_leave_ipv4 (int index, int fd) +{ + struct ip_mreqn mreq; + struct sockaddr_in sa; + + mdns_mcast_group_ipv4 (&sa); + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = sa.sin_addr; + mreq.imr_ifindex = index; + + if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + g_warning("IP_DROP_MEMBERSHIP failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +int avahi_mdns_mcast_leave_ipv6 (int index, int fd) +{ + struct ipv6_mreq mreq6; + struct sockaddr_in6 sa6; + + mdns_mcast_group_ipv6 (&sa6); + + memset(&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_multiaddr = sa6.sin6_addr; + mreq6.ipv6mr_interface = index; + + if (setsockopt(fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + g_warning("IPV6_DROP_MEMBERSHIP failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +gint avahi_open_socket_ipv4(void) { + struct sockaddr_in sa, local; + int fd = -1, ttl, yes; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + g_warning("socket() failed: %s\n", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { + g_warning("IP_MULTICAST_TTL failed: %s\n", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { + g_warning("IP_TTL failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + g_warning("SO_REUSEADDR failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + g_warning("IP_MULTICAST_LOOP failed: %s\n", strerror(errno)); + goto fail; + } + + + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(AVAHI_MDNS_PORT); + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { + g_warning("bind() failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { + g_warning("IP_RECVTTL failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { + g_warning("IP_PKTINFO failed: %s\n", strerror(errno)); + goto fail; + } + + if (avahi_set_cloexec(fd) < 0) { + g_warning("FD_CLOEXEC failed: %s\n", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + g_warning("O_NONBLOCK failed: %s\n", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +gint avahi_open_socket_ipv6(void) { + struct sockaddr_in6 sa, local; + int fd = -1, ttl, yes; + + mdns_mcast_group_ipv6(&sa); + + if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + g_warning("socket() failed: %s\n", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + g_warning("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + g_warning("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + g_warning("SO_REUSEADDR failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { + g_warning("IPV6_V6ONLY failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + g_warning("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin6_family = AF_INET6; + local.sin6_port = htons(AVAHI_MDNS_PORT); + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { + g_warning("bind() failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { + g_warning("IPV6_HOPLIMIT failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { + g_warning("IPV6_PKTINFO failed: %s\n", strerror(errno)); + goto fail; + } + + if (avahi_set_cloexec(fd) < 0) { + g_warning("FD_CLOEXEC failed: %s\n", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + g_warning("O_NONBLOCK failed: %s\n", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) { + g_assert(fd >= 0); + g_assert(msg); + + for (;;) { + + if (sendmsg(fd, msg, flags) >= 0) + break; + + if (errno != EAGAIN) { + g_message("sendmsg() failed: %s\n", strerror(errno)); + return -1; + } + + if (avahi_wait_for_write(fd) < 0) + return -1; + } + + return 0; +} + +gint avahi_send_dns_packet_ipv4(gint fd, gint interface, AvahiDnsPacket *p) { + struct sockaddr_in sa; + struct msghdr msg; + struct iovec io; + struct cmsghdr *cmsg; + struct in_pktinfo *pkti; + uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)]; + int i, n; + + g_assert(fd >= 0); + g_assert(p); + g_assert(avahi_dns_packet_check_valid(p) >= 0); + + mdns_mcast_group_ipv4(&sa); + + memset(&io, 0, sizeof(io)); + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->size; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = (struct cmsghdr*) cmsg_data; + cmsg->cmsg_len = sizeof(cmsg_data); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); + pkti->ipi_ifindex = interface; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_data; + msg.msg_controllen = sizeof(cmsg_data); + msg.msg_flags = 0; + + return sendmsg_loop(fd, &msg, MSG_DONTROUTE); +} + +gint avahi_send_dns_packet_ipv6(gint fd, gint interface, AvahiDnsPacket *p) { + struct sockaddr_in6 sa; + struct msghdr msg; + struct iovec io; + struct cmsghdr *cmsg; + struct in6_pktinfo *pkti; + uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)]; + int i, n; + + g_assert(fd >= 0); + g_assert(p); + g_assert(avahi_dns_packet_check_valid(p) >= 0); + + mdns_mcast_group_ipv6(&sa); + + memset(&io, 0, sizeof(io)); + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->size; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = (struct cmsghdr*) cmsg_data; + cmsg->cmsg_len = sizeof(cmsg_data); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); + pkti->ipi6_ifindex = interface; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_data; + msg.msg_controllen = sizeof(cmsg_data); + msg.msg_flags = 0; + + return sendmsg_loop(fd, &msg, MSG_DONTROUTE); +} + +AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) { + AvahiDnsPacket *p= NULL; + struct msghdr msg; + struct iovec io; + uint8_t aux[64]; + ssize_t l; + struct cmsghdr *cmsg; + gboolean found_ttl = FALSE, found_iface = FALSE; + + g_assert(fd >= 0); + g_assert(ret_sa); + g_assert(ret_iface); + g_assert(ret_ttl); + + p = avahi_dns_packet_new(0); + + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->max_size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = ret_sa; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + if ((l = recvmsg(fd, &msg, 0)) < 0) + goto fail; + + p->size = (size_t) l; + + *ret_ttl = 0; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) { + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) { + *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg); + found_ttl = TRUE; + } + + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { + *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex; + found_iface = TRUE; + } + } + + g_assert(found_iface); + g_assert(found_ttl); + + return p; + +fail: + if (p) + avahi_dns_packet_free(p); + + return NULL; +} + +AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) { + AvahiDnsPacket *p = NULL; + struct msghdr msg; + struct iovec io; + uint8_t aux[64]; + ssize_t l; + struct cmsghdr *cmsg; + gboolean found_ttl = FALSE, found_iface = FALSE; + + g_assert(fd >= 0); + g_assert(ret_sa); + g_assert(ret_iface); + g_assert(ret_ttl); + + p = avahi_dns_packet_new(0); + + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->max_size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = ret_sa; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + if ((l = recvmsg(fd, &msg, 0)) < 0) + goto fail; + + p->size = (size_t) l; + + *ret_ttl = 0; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) { + *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg); + found_ttl = TRUE; + } + + if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex; + found_iface = TRUE; + } + } + + g_assert(found_iface); + g_assert(found_ttl); + + return p; + +fail: + if (p) + avahi_dns_packet_free(p); + + return NULL; +} + diff --git a/libavahi-core/socket.h b/libavahi-core/socket.h new file mode 100644 index 0000000..2baa122 --- /dev/null +++ b/libavahi-core/socket.h @@ -0,0 +1,27 @@ +#ifndef foosockethfoo +#define foosockethfoo + +#include + +#include "dns.h" + +#define AVAHI_MDNS_PORT 5353 + + + +gint avahi_open_socket_ipv4(void); +gint avahi_open_socket_ipv6(void); + +gint avahi_send_dns_packet_ipv4(gint fd, gint iface, AvahiDnsPacket *p); +gint avahi_send_dns_packet_ipv6(gint fd, gint iface, AvahiDnsPacket *p); + +AvahiDnsPacket *avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in*ret_sa, gint *ret_iface, guint8 *ret_ttl); +AvahiDnsPacket *avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6*ret_sa, gint *ret_iface, guint8 *ret_ttl); + +int avahi_mdns_mcast_join_ipv4(int index, int fd); +int avahi_mdns_mcast_join_ipv6(int index, int fd); + +int avahi_mdns_mcast_leave_ipv4(int index, int fd); +int avahi_mdns_mcast_leave_ipv6(int index, int fd); + +#endif diff --git a/libavahi-core/strlst-test.c b/libavahi-core/strlst-test.c new file mode 100644 index 0000000..5e28ce2 --- /dev/null +++ b/libavahi-core/strlst-test.c @@ -0,0 +1,58 @@ +#include +#include + +#include "strlst.h" + +int main(int argc, char *argv[]) { + gchar *t; + guint8 data[1024]; + AvahiStringList *a = NULL, *b; + guint size, n; + + a = avahi_string_list_add(a, "start"); + a = avahi_string_list_add(a, "foo"); + a = avahi_string_list_add(a, "bar"); + a = avahi_string_list_add(a, "quux"); + a = avahi_string_list_add_arbitrary(a, "null\0null", 9); + a = avahi_string_list_add(a, "end"); + + t = avahi_string_list_to_string(a); + printf("--%s--\n", t); + g_free(t); + + size = avahi_string_list_serialize(a, data, sizeof(data)); + + printf("%u\n", size); + + for (t = (gchar*) data, n = 0; n < size; n++, t++) { + if (*t <= 32) + printf("(%u)", *t); + else + printf("%c", *t); + } + + printf("\n"); + + b = avahi_string_list_parse(data, size); + + g_assert(avahi_string_list_equal(a, b)); + + t = avahi_string_list_to_string(b); + printf("--%s--\n", t); + g_free(t); + + avahi_string_list_free(b); + + b = avahi_string_list_copy(a); + + g_assert(avahi_string_list_equal(a, b)); + + t = avahi_string_list_to_string(b); + printf("--%s--\n", t); + g_free(t); + + avahi_string_list_free(a); + avahi_string_list_free(b); + + return 0; +} diff --git a/libavahi-core/strlst.c b/libavahi-core/strlst.c new file mode 100644 index 0000000..f2d76e4 --- /dev/null +++ b/libavahi-core/strlst.c @@ -0,0 +1,218 @@ +#include +#include + +#include "strlst.h" + +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8*text, guint size) { + AvahiStringList *n; + + g_assert(text); + + n = g_malloc(sizeof(AvahiStringList) + size); + n->next = l; + memcpy(n->text, text, n->size = size); + + return n; +} + +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text) { + g_assert(text); + + return avahi_string_list_add_arbitrary(l, (const guint8*) text, strlen(text)); +} + +AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size) { + AvahiStringList *r = NULL; + const guint8 *c; + g_assert(data); + + c = data; + for (;;) { + guint k; + + if (size < 1) + break; + + k = *(c++); + r = avahi_string_list_add_arbitrary(r, c, k); + c += k; + + size -= 1 + k; + } + + return r; +} + +void avahi_string_list_free(AvahiStringList *l) { + AvahiStringList *n; + + while (l) { + n = l->next; + g_free(l); + l = n; + } +} + +static AvahiStringList* string_list_reverse(AvahiStringList *l) { + AvahiStringList *r = NULL, *n; + + while (l) { + n = l->next; + l->next = r; + r = l; + l = n; + } + + return r; +} + +gchar* avahi_string_list_to_string(AvahiStringList *l) { + AvahiStringList *n; + guint s = 0; + gchar *t, *e; + + l = string_list_reverse(l); + + for (n = l; n; n = n->next) { + if (n != l) + s ++; + + s += n->size+3; + } + + t = e = g_new(gchar, s); + + for (n = l; n; n = n->next) { + if (n != l) + *(e++) = ' '; + + *(e++) = '"'; + strncpy(e, n->text, n->size); + e[n->size] = 0; + e = strchr(e, 0); + *(e++) = '"'; + } + + l = string_list_reverse(l); + + *e = 0; + + return t; +} + +guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size) { + guint used = 0; + + if (data) { + guint8 *c; + AvahiStringList *n; + + g_assert(data); + + l = string_list_reverse(l); + c = data; + + for (n = l; n; n = n->next) { + guint k; + if (size < 1) + break; + + k = n->size; + if (k > 255) + k = 255; + + if (k > size-1) + k = size-1; + + *(c++) = k; + memcpy(c, n->text, k); + c += k; + + used += 1+ k; + } + + l = string_list_reverse(l); + } else { + AvahiStringList *n; + + for (n = l; n; n = n->next) { + guint k; + + k = n->size; + if (k > 255) + k = 255; + + used += 1+k; + } + } + + return used; +} + +gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b) { + + for (;;) { + if (!a && !b) + return TRUE; + + if (!a || !b) + return FALSE; + + if (a->size != b->size) + return FALSE; + + if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0) + return FALSE; + + a = a->next; + b = b->next; + } +} + +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) { + va_list va; + + va_start(va, r); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + + return r; +} + +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) { + const gchar *txt; + + while ((txt = va_arg(va, const gchar*))) + r = avahi_string_list_add(r, txt); + + return r; +} + + +AvahiStringList *avahi_string_list_new(const gchar *txt, ...) { + va_list va; + AvahiStringList *r = NULL; + + if (txt) { + r = avahi_string_list_add(r, txt); + + va_start(va, txt); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + } + + return r; +} + +AvahiStringList *avahi_string_list_new_va(va_list va) { + return avahi_string_list_add_many_va(NULL, va); +} + +AvahiStringList *avahi_string_list_copy(AvahiStringList *l) { + AvahiStringList *r = NULL; + + for (; l; l = l->next) + r = avahi_string_list_add_arbitrary(r, l->text, l->size); + + return string_list_reverse(r); +} diff --git a/libavahi-core/strlst.h b/libavahi-core/strlst.h new file mode 100644 index 0000000..22cdc88 --- /dev/null +++ b/libavahi-core/strlst.h @@ -0,0 +1,34 @@ +#ifndef footxtlisthfoo +#define footxtlisthfoo + +#include + +typedef struct _AvahiStringList AvahiStringList; + +struct _AvahiStringList { + AvahiStringList *next; + guint size; + guint8 text[1]; +}; + +AvahiStringList *avahi_string_list_new(const gchar *txt, ...); +AvahiStringList *avahi_string_list_new_va(va_list va); + +void avahi_string_list_free(AvahiStringList *l); + +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text); +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8 *text, guint size); +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...); +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); + +gchar* avahi_string_list_to_string(AvahiStringList *l); + +guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size); +AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size); + +gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b); + +AvahiStringList *avahi_string_list_copy(AvahiStringList *l); + +#endif + diff --git a/libavahi-core/subscribe.c b/libavahi-core/subscribe.c new file mode 100644 index 0000000..902e966 --- /dev/null +++ b/libavahi-core/subscribe.c @@ -0,0 +1,134 @@ +#include "subscribe.h" +#include "util.h" + +static void elapse(AvahiTimeEvent *e, void *userdata) { + AvahiSubscription *s = userdata; + GTimeVal tv; + gchar *t; + + g_assert(s); + + avahi_server_post_query(s->server, s->interface, s->protocol, s->key); + + if (s->n_query++ <= 8) + s->sec_delay *= 2; + + g_message("%i. Continuous querying for %s", s->n_query, t = avahi_key_to_string(s->key)); + g_free(t); + + avahi_elapse_time(&tv, s->sec_delay*1000, 0); + avahi_time_event_queue_update(s->server->time_event_queue, s->time_event, &tv); +} + +struct cbdata { + AvahiSubscription *subscription; + AvahiInterface *interface; +}; + +static gpointer scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) { + struct cbdata *cbdata = userdata; + + g_assert(c); + g_assert(pattern); + g_assert(e); + g_assert(cbdata); + + cbdata->subscription->callback( + cbdata->subscription, + e->record, + cbdata->interface->hardware->index, + cbdata->interface->protocol, + AVAHI_SUBSCRIPTION_NEW, + cbdata->subscription->userdata); + + return NULL; +} + +static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { + AvahiSubscription *s = userdata; + struct cbdata cbdata = { s, i }; + + g_assert(m); + g_assert(i); + g_assert(s); + + avahi_cache_walk(i->cache, s->key, scan_cache_callback, &cbdata); +} + +AvahiSubscription *avahi_subscription_new(AvahiServer *server, AvahiKey *key, gint interface, guchar protocol, AvahiSubscriptionCallback callback, gpointer userdata) { + AvahiSubscription *s, *t; + GTimeVal tv; + + g_assert(server); + g_assert(key); + g_assert(callback); + + g_assert(!avahi_key_is_pattern(key)); + + s = g_new(AvahiSubscription, 1); + s->server = server; + s->key = avahi_key_ref(key); + s->interface = interface; + s->protocol = protocol; + s->callback = callback; + s->userdata = userdata; + s->n_query = 1; + s->sec_delay = 1; + + avahi_server_post_query(s->server, s->interface, s->protocol, s->key); + + avahi_elapse_time(&tv, s->sec_delay*1000, 0); + s->time_event = avahi_time_event_queue_add(server->time_event_queue, &tv, elapse, s); + + AVAHI_LLIST_PREPEND(AvahiSubscription, subscriptions, server->subscriptions, s); + + /* Add the new entry to the subscription hash table */ + t = g_hash_table_lookup(server->subscription_hashtable, key); + AVAHI_LLIST_PREPEND(AvahiSubscription, by_key, t, s); + g_hash_table_replace(server->subscription_hashtable, key, t); + + /* Scan the caches */ + avahi_interface_monitor_walk(s->server->monitor, s->interface, s->protocol, scan_interface_callback, s); + + return s; +} + +void avahi_subscription_free(AvahiSubscription *s) { + AvahiSubscription *t; + + g_assert(s); + + AVAHI_LLIST_REMOVE(AvahiSubscription, subscriptions, s->server->subscriptions, s); + + t = g_hash_table_lookup(s->server->subscription_hashtable, s->key); + AVAHI_LLIST_REMOVE(AvahiSubscription, by_key, t, s); + if (t) + g_hash_table_replace(s->server->subscription_hashtable, t->key, t); + else + g_hash_table_remove(s->server->subscription_hashtable, s->key); + + avahi_time_event_queue_remove(s->server->time_event_queue, s->time_event); + avahi_key_unref(s->key); + + + g_free(s); +} + +void avahi_subscription_notify(AvahiServer *server, AvahiInterface *i, AvahiRecord *record, AvahiSubscriptionEvent event) { + AvahiSubscription *s; + AvahiKey *pattern; + + g_assert(server); + g_assert(record); + + for (s = g_hash_table_lookup(server->subscription_hashtable, record->key); s; s = s->by_key_next) + if (avahi_interface_match(i, s->interface, s->protocol)) + s->callback(s, record, i->hardware->index, i->protocol, event, s->userdata); +} + +gboolean avahi_is_subscribed(AvahiServer *server, AvahiKey *k) { + g_assert(server); + g_assert(k); + + return !!g_hash_table_lookup(server->subscription_hashtable, k); +} diff --git a/libavahi-core/subscribe.h b/libavahi-core/subscribe.h new file mode 100644 index 0000000..2cb1528 --- /dev/null +++ b/libavahi-core/subscribe.h @@ -0,0 +1,31 @@ +#ifndef foosubscribehfoo +#define foosubscribehfoo + +#include "llist.h" +#include "avahi.h" +#include "subscribe.h" +#include "timeeventq.h" +#include "server.h" + +struct _AvahiSubscription { + AvahiServer *server; + AvahiKey *key; + gint interface; + guchar protocol; + gint n_query; + guint sec_delay; + + AvahiTimeEvent *time_event; + + AvahiSubscriptionCallback callback; + gpointer userdata; + + AVAHI_LLIST_FIELDS(AvahiSubscription, subscriptions); + AVAHI_LLIST_FIELDS(AvahiSubscription, by_key); +}; + +void avahi_subscription_notify(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, AvahiSubscriptionEvent event); + +gboolean avahi_is_subscribed(AvahiServer *s, AvahiKey *k); + +#endif diff --git a/libavahi-core/timeeventq.c b/libavahi-core/timeeventq.c new file mode 100644 index 0000000..fc84157 --- /dev/null +++ b/libavahi-core/timeeventq.c @@ -0,0 +1,160 @@ +#include "timeeventq.h" +#include "util.h" + +static gint compare(gconstpointer _a, gconstpointer _b) { + const AvahiTimeEvent *a = _a, *b = _b; + + return avahi_timeval_compare(&a->expiry, &b->expiry); +} + +static gboolean prepare_func(GSource *source, gint *timeout) { + AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source; + AvahiTimeEvent *e; + GTimeVal now; + + g_assert(source); + g_assert(timeout); + + if (!q->prioq->root) { + *timeout = -1; + return FALSE; + } + + e = q->prioq->root->data; + g_assert(e); + + g_source_get_current_time(source, &now); + + if (avahi_timeval_compare(&now, &e->expiry) >= 0) { + *timeout = -1; + return TRUE; + } + + *timeout = (gint) (avahi_timeval_diff(&e->expiry, &now)/1000); + + return FALSE; +} + +static gboolean check_func(GSource *source) { + AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source; + AvahiTimeEvent *e; + GTimeVal now; + + g_assert(source); + + if (!q->prioq->root) + return FALSE; + + e = q->prioq->root->data; + g_assert(e); + + g_source_get_current_time(source, &now); + + return avahi_timeval_compare(&now, &e->expiry) >= 0; +} + +static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) { + AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source; + GTimeVal now; + + g_assert(source); + + g_source_get_current_time(source, &now); + + while (q->prioq->root) { + AvahiTimeEvent *e = q->prioq->root->data; + + if (avahi_timeval_compare(&now, &e->expiry) < 0) + break; + + g_assert(e->callback); + e->callback(e, e->userdata); + } + + return TRUE; +} + +AvahiTimeEventQueue* avahi_time_event_queue_new(GMainContext *context, gint priority) { + AvahiTimeEventQueue *q; + + static GSourceFuncs source_funcs = { + prepare_func, + check_func, + dispatch_func, + NULL, + NULL, + NULL + }; + + q = (AvahiTimeEventQueue*) g_source_new(&source_funcs, sizeof(AvahiTimeEventQueue)); + q->prioq = avahi_prio_queue_new(compare); + + g_source_set_priority((GSource*) q, priority); + + g_source_attach(&q->source, context); + + return q; +} + +void avahi_time_event_queue_free(AvahiTimeEventQueue *q) { + g_assert(q); + + while (q->prioq->root) + avahi_time_event_queue_remove(q, q->prioq->root->data); + avahi_prio_queue_free(q->prioq); + + g_source_destroy(&q->source); + g_source_unref(&q->source); +} + +AvahiTimeEvent* avahi_time_event_queue_add(AvahiTimeEventQueue *q, const GTimeVal *timeval, void (*callback)(AvahiTimeEvent *e, void *userdata), void *userdata) { + AvahiTimeEvent *e; + + g_assert(q); + g_assert(timeval); + g_assert(callback); + g_assert(userdata); + + e = g_new(AvahiTimeEvent, 1); + e->queue = q; + e->expiry = *timeval; + e->callback = callback; + e->userdata = userdata; + + e->node = avahi_prio_queue_put(q->prioq, e); + + return e; +} + +void avahi_time_event_queue_remove(AvahiTimeEventQueue *q, AvahiTimeEvent *e) { + g_assert(q); + g_assert(e); + g_assert(e->queue == q); + + avahi_prio_queue_remove(q->prioq, e->node); + g_free(e); +} + +void avahi_time_event_queue_update(AvahiTimeEventQueue *q, AvahiTimeEvent *e, const GTimeVal *timeval) { + g_assert(q); + g_assert(e); + g_assert(e->queue == q); + + e->expiry = *timeval; + + avahi_prio_queue_shuffle(q->prioq, e->node); +} + +AvahiTimeEvent* avahi_time_event_queue_root(AvahiTimeEventQueue *q) { + g_assert(q); + + return q->prioq->root ? q->prioq->root->data : NULL; +} + +AvahiTimeEvent* avahi_time_event_next(AvahiTimeEvent *e) { + g_assert(e); + + return e->node->next->data; +} + + diff --git a/libavahi-core/timeeventq.h b/libavahi-core/timeeventq.h new file mode 100644 index 0000000..69ff860 --- /dev/null +++ b/libavahi-core/timeeventq.h @@ -0,0 +1,36 @@ +#ifndef footimeeventqhfoo +#define footimeeventqhfoo + +typedef struct _AvahiTimeEventQueue AvahiTimeEventQueue; +typedef struct _AvahiTimeEvent AvahiTimeEvent; + +#include "prioq.h" + +struct _AvahiTimeEvent { + AvahiTimeEventQueue *queue; + AvahiPrioQueueNode *node; + GTimeVal expiry; + void (*callback)(AvahiTimeEvent *e, void *userdata); + void *userdata; +}; + +struct _AvahiTimeEventQueue { + GSource source; + AvahiPrioQueue *prioq; +}; + +AvahiTimeEventQueue* avahi_time_event_queue_new(GMainContext *context, gint priority); +void avahi_time_event_queue_free(AvahiTimeEventQueue *q); + +AvahiTimeEvent* avahi_time_event_queue_add(AvahiTimeEventQueue *q, const GTimeVal *timeval, void (*callback)(AvahiTimeEvent *e, void *userdata), void *userdata); +void avahi_time_event_queue_remove(AvahiTimeEventQueue *q, AvahiTimeEvent *e); + +void avahi_time_event_queue_update(AvahiTimeEventQueue *q, AvahiTimeEvent *e, const GTimeVal *timeval); + +AvahiTimeEvent* avahi_time_event_queue_root(AvahiTimeEventQueue *q); +AvahiTimeEvent* avahi_time_event_next(AvahiTimeEvent *e); + + + + +#endif diff --git a/libavahi-core/util.c b/libavahi-core/util.c new file mode 100644 index 0000000..270a48f --- /dev/null +++ b/libavahi-core/util.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" + +gchar *avahi_get_host_name(void) { +#ifdef HOST_NAME_MAX + char t[HOST_NAME_MAX]; +#else + char t[256]; +#endif + gethostname(t, sizeof(t)); + t[sizeof(t)-1] = 0; + return avahi_normalize_name(t); +} + +gchar *avahi_normalize_name(const 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); +} + +gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) { + g_assert(a); + g_assert(b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b) { + g_assert(a); + g_assert(b); + + if (avahi_timeval_compare(a, b) < 0) + return avahi_timeval_diff(b, a); + + return ((glong) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec; +} + + +gint avahi_set_cloexec(gint fd) { + gint n; + + g_assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFD)) < 0) + return -1; + + if (n & FD_CLOEXEC) + return 0; + + return fcntl(fd, F_SETFD, n|FD_CLOEXEC); +} + +gint avahi_set_nonblock(gint fd) { + gint n; + + g_assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +gint avahi_wait_for_write(gint fd) { + fd_set fds; + gint r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) { + g_message("select() failed: %s", strerror(errno)); + + return -1; + } + + g_assert(r > 0); + + return 0; +} + +GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) { + g_assert(tv); + + g_get_current_time(tv); + + if (msec) + g_time_val_add(tv, msec*1000); + + if (jitter) + g_time_val_add(tv, g_random_int_range(0, jitter) * 1000); + + return tv; +} + +gint avahi_age(const GTimeVal *a) { + GTimeVal now; + + g_assert(a); + + g_get_current_time(&now); + + return avahi_timeval_diff(&now, a); +} + +gboolean avahi_domain_cmp(const gchar *a, const gchar *b) { + int escaped_a = 0, escaped_b = 0; + g_assert(a); + g_assert(b); + + for (;;) { + /* Check for escape characters "\" */ + if ((escaped_a = *a == '\\')) + a ++; + + if ((escaped_b = *b == '\\')) + b++; + + /* Check for string end */ + if (*a == 0 && *b == 0) + return 0; + + if (*a == 0 && !escaped_b && *b == '.' && *(b+1) == 0) + return 0; + + if (!escaped_a && *a == '.' && *(a+1) == 0 && *b == 0) + return 0; + + /* Compare characters */ + if (escaped_a == escaped_b && *a != *b) + return *a < *b ? -1 : 1; + + /* Next characters */ + a++; + b++; + + } +} + +gboolean avahi_domain_equal(const gchar *a, const gchar *b) { + return avahi_domain_cmp(a, b) == 0; +} + +guint avahi_domain_hash(const gchar *p) { + char t[256]; + strncpy(t, p, sizeof(t)-1); + t[sizeof(t)-1] = 0; + + return g_int_hash(t); +} + +void avahi_hexdump(gconstpointer p, guint size) { + const guint8 *c = p; + g_assert(p); + + printf("Dumping %u bytes from %p:\n", size, p); + + while (size > 0) { + guint i; + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%02x ", c[i]); + else + printf(" "); + } + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.'); + else + printf(" "); + } + + printf("\n"); + + c += 16; + + if (size <= 16) + break; + + size -= 16; + } +} diff --git a/libavahi-core/util.h b/libavahi-core/util.h new file mode 100644 index 0000000..7755ee2 --- /dev/null +++ b/libavahi-core/util.h @@ -0,0 +1,26 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +#include + +gchar *avahi_normalize_name(const gchar *s); /* g_free() the result! */ +gchar *avahi_get_host_name(void); /* g_free() the result! */ + +gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b); +glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b); + +gint avahi_set_cloexec(gint fd); +gint avahi_set_nonblock(gint fd); +gint avahi_wait_for_write(gint fd); + +GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter); + +gint avahi_age(const GTimeVal *a); + +guint avahi_domain_hash(const gchar *p); +gboolean avahi_domain_cmp(const gchar *a, const gchar *b); +gboolean avahi_domain_equal(const gchar *a, const gchar *b); + +void avahi_hexdump(gconstpointer p, guint size); + +#endif diff --git a/llist.h b/llist.h deleted file mode 100644 index 16a16b6..0000000 --- a/llist.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef foollistfoo -#define foollistfoo - -#include - -/* Some macros for maintaining doubly linked lists */ - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define AVAHI_LLIST_HEAD(t,name) t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev - -/* Initialize the list's head */ -#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0) - -/* Initialize a list item */ -#define AVAHI_LLIST_INIT(t,name,item) do { \ - t *_item = (item); \ - g_assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while(0) - -/* Prepend an item to the list */ -#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \ - t **_head = &(head), *_item = (item); \ - g_assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_head = _item; \ - } while (0) - -/* Remove an item from the list */ -#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \ - t **_head = &(head), *_item = (item); \ - g_assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else {\ - g_assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while(0) - -#endif diff --git a/main.c b/main.c deleted file mode 100644 index 9699329..0000000 --- a/main.c +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include -#include - -#include "avahi.h" - -static gboolean quit_timeout(gpointer data) { - g_main_loop_quit(data); - return FALSE; -} - -static gboolean dump_timeout(gpointer data) { - AvahiServer *Avahi = data; - avahi_server_dump(Avahi, stdout); - return TRUE; -} - -static void subscription(AvahiSubscription *s, AvahiRecord *r, gint interface, guchar protocol, AvahiSubscriptionEvent event, gpointer userdata) { - gchar *t; - - g_assert(s); - g_assert(r); - g_assert(interface > 0); - g_assert(protocol != AF_UNSPEC); - - g_message("SUBSCRIPTION: record [%s] on %i.%i is %s", t = avahi_record_to_string(r), interface, protocol, - event == AVAHI_SUBSCRIPTION_NEW ? "new" : (event == AVAHI_SUBSCRIPTION_CHANGE ? "changed" : "removed")); - - g_free(t); -} - -static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) { - g_message("entry group state: %i", state); -} - -int main(int argc, char *argv[]) { - AvahiServer *avahi; - gchar *r; - GMainLoop *loop = NULL; - AvahiSubscription *s; - AvahiKey *k; - AvahiEntryGroup *g; - - avahi = avahi_server_new(NULL); - -/* g = avahi_entry_group_new(avahi, entry_group_callback, NULL); */ - -/* avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, NULL, "hallo", NULL); */ -/* avahi_server_add_service(avahi, g, 0, AF_UNSPEC, "_http._tcp", "gurke", NULL, NULL, 80, "foo", NULL); */ - -/* avahi_entry_group_commit(g); */ - - avahi_server_dump(avahi, stdout); - - -/* k = avahi_key_new("ecstasy.local.", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_ANY); */ -/* s = avahi_subscription_new(avahi, k, 0, AF_UNSPEC, subscription, NULL); */ -/* avahi_key_unref(k); */ - - loop = g_main_loop_new(NULL, FALSE); - - /* g_timeout_add(1000*20, dump_timeout, Avahi); */ -/* g_timeout_add(1000*30, quit_timeout, loop); */ - - g_main_loop_run(loop); - g_main_loop_unref(loop); - -/* avahi_subscription_free(s); */ - /* avahi_entry_group_free(g); */ - avahi_server_free(avahi); - - return 0; -} diff --git a/netlink.c b/netlink.c deleted file mode 100644 index 1289749..0000000 --- a/netlink.c +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include - -#include "netlink.h" - -struct _AvahiNetlink { - GMainContext *context; - gint fd; - guint seq; - GPollFD poll_fd; - GSource *source; - void (*callback) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata); - gpointer userdata; -}; - -gboolean avahi_netlink_work(AvahiNetlink *nl, gboolean block) { - 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), block ? 0 : 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, (size_t) bytes)) { - g_warning("NETLINK: packet truncated"); - return FALSE; - } - - nl->callback(nl, p, nl->userdata); - } - } - - if (block) - break; - } - - 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) { - AvahiNetlink* nl; - g_assert(source); - - nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource))); - g_assert(nl); - - return nl->poll_fd.revents & (G_IO_IN|G_IO_HUP|G_IO_ERR); -} - -static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) { - AvahiNetlink* nl; - g_assert(source); - - nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource))); - g_assert(nl); - - return avahi_netlink_work(nl, FALSE); -} - -AvahiNetlink *avahi_netlink_new(GMainContext *context, gint priority, guint32 groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata), gpointer userdata) { - int fd; - struct sockaddr_nl addr; - AvahiNetlink *nl; - - static GSourceFuncs source_funcs = { - prepare_func, - check_func, - dispatch_func, - NULL, - NULL, - NULL - }; - - 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(AvahiNetlink, 1); - nl->context = context; - g_main_context_ref(context); - nl->fd = fd; - nl->seq = 0; - nl->callback = cb; - nl->userdata = userdata; - - nl->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiNetlink*)); - *((AvahiNetlink**) (((guint8*) nl->source) + sizeof(GSource))) = nl; - - g_source_set_priority(nl->source, priority); - - 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 avahi_netlink_free(AvahiNetlink *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 avahi_netlink_send(AvahiNetlink *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 deleted file mode 100644 index 8066aae..0000000 --- a/netlink.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef foonetlinkhfoo -#define foonetlinkhfoo - -#include -#include -#include - -#include - -struct _AvahiNetlink; -typedef struct _AvahiNetlink AvahiNetlink; - -AvahiNetlink *avahi_netlink_new(GMainContext *c, gint priority, guint32 groups, void (*cb) (AvahiNetlink *n, struct nlmsghdr *m, gpointer userdata), gpointer userdata); -void avahi_netlink_free(AvahiNetlink *n); - -int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, guint *ret_seq); - -gboolean avahi_netlink_work(AvahiNetlink *n, gboolean block); - -#endif diff --git a/prioq-test.c b/prioq-test.c deleted file mode 100644 index 53a8383..0000000 --- a/prioq-test.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#include "prioq.h" - -static gint compare_int(gconstpointer a, gconstpointer b) { - gint i = GPOINTER_TO_INT(a), j = GPOINTER_TO_INT(b); - - return i < j ? -1 : (i > j ? 1 : 0); -} - -static int compare_ptr(gconstpointer a, gconstpointer b) { - return a < b ? -1 : (a > b ? 1 : 0); -} - -static void rec(AvahiPrioQueueNode *n) { - if (!n) - return; - - if (n->left) - g_assert(n->left->parent == n); - - if (n->right) - g_assert(n->right->parent == n); - - if (n->parent) { - g_assert(n->parent->left == n || n->parent->right == n); - - if (n->parent->left == n) - g_assert(n->next == n->parent->right); - } - - if (!n->next) { - g_assert(n->queue->last == n); - - if (n->parent && n->parent->left == n) - g_assert(n->parent->right == NULL); - } - - - if (n->parent) { - int a = GPOINTER_TO_INT(n->parent->data), b = GPOINTER_TO_INT(n->data); - if (a > b) { - printf("%i <= %i: NO\n", a, b); - abort(); - } - } - - rec(n->left); - rec(n->right); -} - -int main(int argc, char *argv[]) { - AvahiPrioQueue *q, *q2; - gint i, prev; - - q = avahi_prio_queue_new(compare_int); - q2 = avahi_prio_queue_new(compare_ptr); - - srand(time(NULL)); - - for (i = 0; i < 10000; i++) - avahi_prio_queue_put(q2, avahi_prio_queue_put(q, GINT_TO_POINTER(random() & 0xFFFF))); - - while (q2->root) { - rec(q->root); - rec(q2->root); - - g_assert(q->n_nodes == q2->n_nodes); - - printf("%i\n", GPOINTER_TO_INT(((AvahiPrioQueueNode*)q2->root->data)->data)); - - avahi_prio_queue_remove(q, q2->root->data); - avahi_prio_queue_remove(q2, q2->root); - } - - -/* prev = 0; */ -/* while (q->root) { */ -/* gint v = GPOINTER_TO_INT(q->root->data); */ -/* rec(q->root); */ -/* printf("%i\n", v); */ -/* avahi_prio_queue_remove(q, q->root); */ -/* g_assert(v >= prev); */ -/* prev = v; */ -/* } */ - - avahi_prio_queue_free(q); - return 0; -} diff --git a/prioq.c b/prioq.c deleted file mode 100644 index 2eedf27..0000000 --- a/prioq.c +++ /dev/null @@ -1,359 +0,0 @@ -#include "prioq.h" - -AvahiPrioQueue* avahi_prio_queue_new(gint (*compare) (gconstpointer a, gconstpointer b)) { - AvahiPrioQueue *q; - g_assert(compare); - - q = g_new(AvahiPrioQueue, 1); - q->root = q->last = NULL; - q->n_nodes = 0; - q->compare = compare; - return q; -} - -void avahi_prio_queue_free(AvahiPrioQueue *q) { - g_assert(q); - - while (q->last) - avahi_prio_queue_remove(q, q->last); - - g_assert(!q->n_nodes); - g_free(q); -} - -static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, guint x, guint y) { - guint r; - AvahiPrioQueueNode *n; - g_assert(q); - - n = q->root; - g_assert(n); - - for (r = 0; r < y; r++) { - g_assert(n); - - if ((x >> (y-r-1)) & 1) - n = n->right; - else - n = n->left; - } - - g_assert(n->x == x); - g_assert(n->y == y); - - return n; -} - -static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) { - AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn; - gint t; - g_assert(q); - g_assert(a); - g_assert(b); - g_assert(a != b); - - /* Swap positions */ - t = a->x; a->x = b->x; b->x = t; - t = a->y; a->y = b->y; b->y = t; - - if (a->parent == b) { - /* B is parent of A */ - - p = b->parent; - b->parent = a; - - if ((a->parent = p)) { - if (a->parent->left == b) - a->parent->left = a; - else - a->parent->right = a; - } else - q->root = a; - - if (b->left == a) { - if ((b->left = a->left)) - b->left->parent = b; - a->left = b; - - r = a->right; - if ((a->right = b->right)) - a->right->parent = a; - if ((b->right = r)) - b->right->parent = b; - - } else { - if ((b->right = a->right)) - b->right->parent = b; - a->right = b; - - l = a->left; - if ((a->left = b->left)) - a->left->parent = a; - if ((b->left = l)) - b->left->parent = b; - } - } else if (b->parent == a) { - /* A ist parent of B */ - - p = a->parent; - a->parent = b; - - if ((b->parent = p)) { - if (b->parent->left == a) - b->parent->left = b; - else - b->parent->right = b; - } else - q->root = b; - - if (a->left == b) { - if ((a->left = b->left)) - a->left->parent = a; - b->left = a; - - r = a->right; - if ((a->right = b->right)) - a->right->parent = a; - if ((b->right = r)) - b->right->parent = b; - } else { - if ((a->right = b->right)) - a->right->parent = a; - b->right = a; - - l = a->left; - if ((a->left = b->left)) - a->left->parent = a; - if ((b->left = l)) - b->left->parent = b; - } - } else { - AvahiPrioQueueNode *apl = NULL, *bpl = NULL; - - /* Swap parents */ - ap = a->parent; - bp = b->parent; - - if (ap) - apl = ap->left; - if (bp) - bpl = bp->left; - - if ((a->parent = bp)) { - if (bpl == b) - bp->left = a; - else - bp->right = a; - } else - q->root = a; - - if ((b->parent = ap)) { - if (apl == a) - ap->left = b; - else - ap->right = b; - } else - q->root = b; - - /* Swap children */ - l = a->left; - r = a->right; - - if ((a->left = b->left)) - a->left->parent = a; - - if ((b->left = l)) - b->left->parent = b; - - if ((a->right = b->right)) - a->right->parent = a; - - if ((b->right = r)) - b->right->parent = b; - } - - /* Swap siblings */ - ap = a->prev; an = a->next; - bp = b->prev; bn = b->next; - - if (a->next == b) { - /* A is predecessor of B */ - a->prev = b; - b->next = a; - - if ((a->next = bn)) - a->next->prev = a; - else - q->last = a; - - if ((b->prev = ap)) - b->prev->next = b; - - } else if (b->next == a) { - /* B is predecessor of A */ - a->next = b; - b->prev = a; - - if ((a->prev = bp)) - a->prev->next = a; - - if ((b->next = an)) - b->next->prev = b; - else - q->last = b; - - } else { - /* A is no neighbour of B */ - - if ((a->prev = bp)) - a->prev->next = a; - - if ((a->next = bn)) - a->next->prev = a; - else - q->last = a; - - if ((b->prev = ap)) - b->prev->next = b; - - if ((b->next = an)) - b->next->prev = b; - else - q->last = b; - } -} - -/* Move a node to the correct position */ -void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { - g_assert(q); - g_assert(n); - - /* Move up until the position is OK */ - while (n->parent && q->compare(n->parent->data, n->data) > 0) - exchange_nodes(q, n, n->parent); - - /* Move down until the position is OK */ - for (;;) { - AvahiPrioQueueNode *min; - - if (!(min = n->left)) { - /* No children */ - g_assert(!n->right); - break; - } - - if (n->right && q->compare(n->right->data, min->data) < 0) - min = n->right; - - /* min now contains the smaller one of our two children */ - - if (q->compare(n->data, min->data) <= 0) - /* Order OK */ - break; - - exchange_nodes(q, n, min); - } -} - -AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, gpointer data) { - AvahiPrioQueueNode *n; - g_assert(q); - - n = g_new(AvahiPrioQueueNode, 1); - n->queue = q; - n->data = data; - - if (q->last) { - g_assert(q->root); - g_assert(q->n_nodes); - - n->y = q->last->y; - n->x = q->last->x+1; - - if (n->x >= ((guint) 1 << n->y)) { - n->x = 0; - n->y++; - } - - q->last->next = n; - n->prev = q->last; - - g_assert(n->y > 0); - n->parent = get_node_at_xy(q, n->x/2, n->y-1); - - if (n->x & 1) - n->parent->right = n; - else - n->parent->left = n; - } else { - g_assert(!q->root); - g_assert(!q->n_nodes); - - n->y = n->x = 0; - q->root = n; - n->prev = n->parent = NULL; - } - - n->next = n->left = n->right = NULL; - q->last = n; - q->n_nodes++; - - avahi_prio_queue_shuffle(q, n); - - return n; -} - -void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { - g_assert(q); - g_assert(n); - - if (n != q->last) { - AvahiPrioQueueNode *replacement = q->last; - exchange_nodes(q, replacement, n); - avahi_prio_queue_remove(q, n); - avahi_prio_queue_shuffle(q, replacement); - return; - } - - g_assert(n == q->last); - g_assert(!n->next); - g_assert(!n->left); - g_assert(!n->right); - - q->last = n->prev; - - if (n->prev) { - n->prev->next = NULL; - g_assert(n->parent); - } else - g_assert(!n->parent); - - if (n->parent) { - g_assert(n->prev); - if (n->parent->left == n) { - if (n->parent->right != NULL) { - g_message("fuck"); - for (;;); - - } - - g_assert(n->parent->right == NULL); - n->parent->left = NULL; - } else { - g_assert(n->parent->right == n); - g_assert(n->parent->left != NULL); - n->parent->right = NULL; - } - } else { - g_assert(q->root == n); - g_assert(!n->prev); - g_assert(q->n_nodes == 1); - q->root = NULL; - } - - g_free(n); - - g_assert(q->n_nodes > 0); - q->n_nodes--; -} - diff --git a/prioq.h b/prioq.h deleted file mode 100644 index 6b5babb..0000000 --- a/prioq.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef fooprioqhfoo -#define fooprioqhfoo - -#include - -struct _AvahiPrioQueue; -typedef struct _AvahiPrioQueue AvahiPrioQueue; - -struct _AvahiPrioQueueNode; -typedef struct _AvahiPrioQueueNode AvahiPrioQueueNode; - -struct _AvahiPrioQueue { - AvahiPrioQueueNode *root, *last; - - guint n_nodes; - gint (*compare) (gconstpointer a, gconstpointer b); -}; - -struct _AvahiPrioQueueNode { - AvahiPrioQueue *queue; - gpointer data; - guint x, y; - - AvahiPrioQueueNode *left, *right, *parent, *next, *prev; -}; - -AvahiPrioQueue* avahi_prio_queue_new(gint (*compare) (gconstpointer a, gconstpointer b)); -void avahi_prio_queue_free(AvahiPrioQueue *q); - -AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, gpointer data); -void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n); - -void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n); - -#endif diff --git a/psched.c b/psched.c deleted file mode 100644 index faa2fa9..0000000 --- a/psched.c +++ /dev/null @@ -1,715 +0,0 @@ -#include - -#include "util.h" -#include "psched.h" - -#define AVAHI_QUERY_HISTORY_MSEC 100 -#define AVAHI_QUERY_DEFER_MSEC 100 -#define AVAHI_RESPONSE_HISTORY_MSEC 700 -#define AVAHI_RESPONSE_DEFER_MSEC 20 -#define AVAHI_RESPONSE_JITTER_MSEC 100 -#define AVAHI_PROBE_DEFER_MSEC 70 - -AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i) { - AvahiPacketScheduler *s; - - g_assert(server); - g_assert(i); - - s = g_new(AvahiPacketScheduler, 1); - s->server = server; - s->interface = i; - - AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->query_jobs); - AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->response_jobs); - AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers); - AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->probe_jobs); - - return s; -} - -static void query_job_free(AvahiPacketScheduler *s, AvahiQueryJob *qj) { - g_assert(qj); - - if (qj->time_event) - avahi_time_event_queue_remove(qj->scheduler->server->time_event_queue, qj->time_event); - - AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->query_jobs, qj); - - avahi_key_unref(qj->key); - g_free(qj); -} - -static void response_job_free(AvahiPacketScheduler *s, AvahiResponseJob *rj) { - g_assert(rj); - - if (rj->time_event) - avahi_time_event_queue_remove(rj->scheduler->server->time_event_queue, rj->time_event); - - AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->response_jobs, rj); - - avahi_record_unref(rj->record); - g_free(rj); -} - -static void probe_job_free(AvahiPacketScheduler *s, AvahiProbeJob *pj) { - g_assert(pj); - - if (pj->time_event) - avahi_time_event_queue_remove(pj->scheduler->server->time_event_queue, pj->time_event); - - AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->probe_jobs, pj); - - avahi_record_unref(pj->record); - g_free(pj); -} - -void avahi_packet_scheduler_free(AvahiPacketScheduler *s) { - AvahiQueryJob *qj; - AvahiResponseJob *rj; - AvahiProbeJob *pj; - AvahiTimeEvent *e; - - g_assert(s); - - g_assert(!s->known_answers); - - while ((qj = s->query_jobs)) - query_job_free(s, qj); - while ((rj = s->response_jobs)) - response_job_free(s, rj); - while ((pj = s->probe_jobs)) - probe_job_free(s, pj); - - g_free(s); -} - -static gpointer known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) { - AvahiPacketScheduler *s = userdata; - AvahiKnownAnswer *ka; - - g_assert(c); - g_assert(pattern); - g_assert(e); - g_assert(s); - - if (avahi_cache_entry_half_ttl(c, e)) - return NULL; - - ka = g_new0(AvahiKnownAnswer, 1); - ka->scheduler = s; - ka->record = avahi_record_ref(e->record); - - AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka); - return NULL; -} - -static guint8* packet_add_query_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) { - guint8 *d; - - g_assert(s); - g_assert(p); - g_assert(qj); - - if ((d = avahi_dns_packet_append_key(p, qj->key, FALSE))) { - GTimeVal tv; - - qj->done = 1; - - /* Drop query after some time from history */ - avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0); - avahi_time_event_queue_update(s->server->time_event_queue, qj->time_event, &tv); - - g_get_current_time(&qj->delivery); - - /* Add all matching known answers to the list */ - avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s); - } - - return d; -} - -static void append_known_answers_and_send(AvahiPacketScheduler *s, AvahiDnsPacket *p) { - AvahiKnownAnswer *ka; - guint n; - g_assert(s); - g_assert(p); - - n = 0; - - while ((ka = s->known_answers)) { - - while (!avahi_dns_packet_append_record(p, ka->record, FALSE)) { - - g_assert(!avahi_dns_packet_is_empty(p)); - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC); - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); - - p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48); - n = 0; - } - - AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka); - avahi_record_unref(ka->record); - g_free(ka); - - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); -} - -static void query_elapse(AvahiTimeEvent *e, gpointer data) { - AvahiQueryJob *qj = data; - AvahiPacketScheduler *s; - AvahiDnsPacket *p; - guint n; - guint8 *d; - - g_assert(qj); - s = qj->scheduler; - - if (qj->done) { - /* Lets remove it from the history */ - query_job_free(s, qj); - return; - } - - g_assert(!s->known_answers); - - p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48); - d = packet_add_query_job(s, p, qj); - g_assert(d); - n = 1; - - /* Try to fill up packet with more queries, if available */ - for (qj = s->query_jobs; qj; qj = qj->jobs_next) { - - if (qj->done) - continue; - - if (!packet_add_query_job(s, p, qj)) - break; - - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); - - /* Now add known answers */ - append_known_answers_and_send(s, p); -} - -AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) { - AvahiQueryJob *qj; - - g_assert(s); - g_assert(key); - - qj = g_new(AvahiQueryJob, 1); - qj->scheduler = s; - qj->key = avahi_key_ref(key); - qj->done = FALSE; - qj->time_event = NULL; - - AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->query_jobs, qj); - - return qj; -} - -void avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately) { - GTimeVal tv; - AvahiQueryJob *qj; - - g_assert(s); - g_assert(key); - - avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0); - - for (qj = s->query_jobs; qj; qj = qj->jobs_next) { - - if (avahi_key_equal(qj->key, key)) { - - glong d = avahi_timeval_diff(&tv, &qj->delivery); - - /* Duplicate questions suppression */ - if (d >= 0 && d <= AVAHI_QUERY_HISTORY_MSEC*1000) { - g_message("WARNING! DUPLICATE QUERY SUPPRESSION ACTIVE!"); - return; - } - - query_job_free(s, qj); - break; - } - - } - - qj = query_job_new(s, key); - qj->delivery = tv; - qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &qj->delivery, query_elapse, qj); -} - -static guint8* packet_add_response_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) { - guint8 *d; - - g_assert(s); - g_assert(p); - g_assert(rj); - - if ((d = avahi_dns_packet_append_record(p, rj->record, rj->flush_cache))) { - GTimeVal tv; - - rj->done = 1; - - /* Drop response after some time from history */ - avahi_elapse_time(&tv, AVAHI_RESPONSE_HISTORY_MSEC, 0); - avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv); - - g_get_current_time(&rj->delivery); - } - - return d; -} - -static void send_response_packet(AvahiPacketScheduler *s, AvahiResponseJob *rj) { - AvahiDnsPacket *p; - guint n; - - g_assert(s); - - p = avahi_dns_packet_new_response(s->interface->hardware->mtu - 200); - n = 0; - - /* If a job was specified, put it in the packet. */ - if (rj) { - guint8 *d; - d = packet_add_response_job(s, p, rj); - g_assert(d); - n++; - } - - /* Try to fill up packet with more responses, if available */ - for (rj = s->response_jobs; rj; rj = rj->jobs_next) { - - if (rj->done) - continue; - - if (!packet_add_response_job(s, p, rj)) - break; - - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); -} - -static void response_elapse(AvahiTimeEvent *e, gpointer data) { - AvahiResponseJob *rj = data; - AvahiPacketScheduler *s; - - g_assert(rj); - s = rj->scheduler; - - if (rj->done) { - /* Lets remove it from the history */ - response_job_free(s, rj); - return; - } - - send_response_packet(s, rj); -} - -static AvahiResponseJob* look_for_response(AvahiPacketScheduler *s, AvahiRecord *record) { - AvahiResponseJob *rj; - - g_assert(s); - g_assert(record); - - for (rj = s->response_jobs; rj; rj = rj->jobs_next) - if (avahi_record_equal_no_ttl(rj->record, record)) - return rj; - - return NULL; -} - -static AvahiResponseJob* response_job_new(AvahiPacketScheduler *s, AvahiRecord *record) { - AvahiResponseJob *rj; - - g_assert(s); - g_assert(record); - - rj = g_new(AvahiResponseJob, 1); - rj->scheduler = s; - rj->record = avahi_record_ref(record); - rj->done = FALSE; - rj->time_event = NULL; - rj->address_valid = FALSE; - rj->flush_cache = FALSE; - - AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->response_jobs, rj); - - return rj; -} - -void avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { - AvahiResponseJob *rj; - GTimeVal tv; - gchar *t; - - g_assert(s); - g_assert(record); - - g_assert(!avahi_key_is_pattern(record->key)); - - avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC); - - /* Don't send out duplicates */ - - if ((rj = look_for_response(s, record))) { - glong d; - - d = avahi_timeval_diff(&tv, &rj->delivery); - - /* If there's already a matching packet in our history or in - * the schedule, we do nothing. */ - if (!!record->ttl == !!rj->record->ttl && - d >= 0 && d <= AVAHI_RESPONSE_HISTORY_MSEC*1000) { - g_message("WARNING! DUPLICATE RESPONSE SUPPRESSION ACTIVE!"); - - /* This job is no longer specific to a single querier, so - * make sure it isn't suppressed by known answer - * suppresion */ - - if (rj->address_valid && (!a || avahi_address_cmp(a, &rj->address) != 0)) - rj->address_valid = FALSE; - - rj->flush_cache = flush_cache; - - return; - } - - /* Either one was a goodbye packet, but the other was not, so - * let's drop the older one. */ - response_job_free(s, rj); - } - -/* g_message("ACCEPTED NEW RESPONSE [%s]", t = avahi_record_to_string(record)); */ -/* g_free(t); */ - - /* Create a new job and schedule it */ - rj = response_job_new(s, record); - rj->flush_cache = flush_cache; - rj->delivery = tv; - rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &rj->delivery, response_elapse, rj); - - /* Store the address of the host this messages is intended to, so - that we can drop this job in case a truncated message with - known answer suppresion entries is recieved */ - - if ((rj->address_valid = !!a)) - rj->address = *a; -} - -void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key) { - GTimeVal tv; - AvahiQueryJob *qj; - - g_assert(s); - g_assert(key); - - /* This function is called whenever an incoming query was - * receieved. We drop all scheduled queries which match here. The - * keyword is "DUPLICATE QUESTION SUPPRESION". */ - - for (qj = s->query_jobs; qj; qj = qj->jobs_next) - if (avahi_key_equal(qj->key, key)) { - - if (qj->done) - return; - - goto mark_done; - } - - - /* No matching job was found. Add the query to the history */ - qj = query_job_new(s, key); - -mark_done: - qj->done = TRUE; - - /* Drop the query after some time */ - avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0); - qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, query_elapse, qj); - - g_get_current_time(&qj->delivery); -} - -void response_job_set_elapse_time(AvahiPacketScheduler *s, AvahiResponseJob *rj, guint msec, guint jitter) { - GTimeVal tv; - - g_assert(s); - g_assert(rj); - - avahi_elapse_time(&tv, msec, jitter); - - if (rj->time_event) - avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv); - else - rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, response_elapse, rj); - -} - -void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record) { - AvahiResponseJob *rj; - - g_assert(s); - g_assert(record); - - /* This function is called whenever an incoming response was - * receieved. We drop all scheduled responses which match - * here. The keyword is "DUPLICATE ANSWER SUPPRESION". */ - - for (rj = s->response_jobs; rj; rj = rj->jobs_next) - if (avahi_record_equal_no_ttl(rj->record, record)) { - - if (rj->done) { - - if (!!record->ttl == !!rj->record->ttl) { - /* An entry like this is already in our history, - * so let's get out of here! */ - - return; - - } else { - /* Either one was a goodbye packet but other was - * none. We remove the history entry, and add a - * new one */ - - response_job_free(s, rj); - break; - } - - } else { - - if (!!record->ttl == !!rj->record->ttl) { - - /* The incoming packet matches our scheduled - * record, so let's mark that one as done */ - - goto mark_done; - - } else { - - /* Either one was a goodbye packet but other was - * none. We ignore the incoming packet. */ - - return; - } - } - } - - /* No matching job was found. Add the query to the history */ - rj = response_job_new(s, record); - -mark_done: - rj->done = TRUE; - - /* Drop response after 500ms from history */ - response_job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); - - g_get_current_time(&rj->delivery); -} - -void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *a) { - AvahiResponseJob *rj; - - g_assert(s); - g_assert(record); - g_assert(a); - - for (rj = s->response_jobs; rj; rj = rj->jobs_next) { - - g_assert(record->ttl > 0); - g_assert(rj->record->ttl/2); - - if (avahi_record_equal_no_ttl(rj->record, record)) - if (rj->address_valid) - if (avahi_address_cmp(&rj->address, a)) - if (record->ttl >= rj->record->ttl/2) { - - /* Let's suppress it */ - - response_job_free(s, rj); - break; - } - } -} - -void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s) { - AvahiResponseJob *rj; - - g_assert(s); - - /* Send all scheduled responses, ignoring the scheduled time */ - - for (rj = s->response_jobs; rj; rj = rj->jobs_next) - if (!rj->done) - send_response_packet(s, rj); -} - -static AvahiProbeJob* probe_job_new(AvahiPacketScheduler *s, AvahiRecord *record) { - AvahiProbeJob *pj; - - g_assert(s); - g_assert(record); - - pj = g_new(AvahiProbeJob, 1); - pj->scheduler = s; - pj->record = avahi_record_ref(record); - pj->time_event = NULL; - pj->chosen = FALSE; - - AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->probe_jobs, pj); - - return pj; -} - -static guint8* packet_add_probe_query(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) { - guint size; - guint8 *ret; - AvahiKey *k; - - g_assert(s); - g_assert(p); - g_assert(pj); - - g_assert(!pj->chosen); - - /* Estimate the size for this record */ - size = - avahi_key_get_estimate_size(pj->record->key) + - avahi_record_get_estimate_size(pj->record); - - /* Too large */ - if (size > avahi_dns_packet_space(p)) - return NULL; - - /* Create the probe query */ - k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY); - ret = avahi_dns_packet_append_key(p, k, FALSE); - g_assert(ret); - - /* Mark this job for addition to the packet */ - pj->chosen = TRUE; - - /* Scan for more jobs whith matching key pattern */ - for (pj = s->probe_jobs; pj; pj = pj->jobs_next) { - if (pj->chosen) - continue; - - /* Does the record match the probe? */ - if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name)) - continue; - - /* This job wouldn't fit in */ - if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p)) - break; - - /* Mark this job for addition to the packet */ - pj->chosen = TRUE; - } - - avahi_key_unref(k); - - return ret; -} - -static void probe_elapse(AvahiTimeEvent *e, gpointer data) { - AvahiProbeJob *pj = data, *next; - AvahiPacketScheduler *s; - AvahiDnsPacket *p; - guint n; - guint8 *d; - - g_assert(pj); - s = pj->scheduler; - - p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48); - - /* Add the import probe */ - if (!packet_add_probe_query(s, p, pj)) { - g_warning("Record too large! ---"); - avahi_dns_packet_free(p); - return; - } - - n = 1; - - /* Try to fill up packet with more probes, if available */ - for (pj = s->probe_jobs; pj; pj = pj->jobs_next) { - - if (pj->chosen) - continue; - - if (!packet_add_probe_query(s, p, pj)) - break; - - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); - - n = 0; - - /* Now add the chosen records to the authorative section */ - for (pj = s->probe_jobs; pj; pj = next) { - - next = pj->jobs_next; - - if (!pj->chosen) - continue; - - if (!avahi_dns_packet_append_record(p, pj->record, TRUE)) { - g_warning("Bad probe size estimate!"); - - /* Unmark all following jobs */ - for (; pj; pj = pj->jobs_next) - pj->chosen = FALSE; - - break; - } - - probe_job_free(s, pj); - - n ++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); - - /* Send it now */ - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); -} - -void avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately) { - AvahiProbeJob *pj; - GTimeVal tv; - - g_assert(s); - g_assert(record); - g_assert(!avahi_key_is_pattern(record->key)); - - avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0); - - /* Create a new job and schedule it */ - pj = probe_job_new(s, record); - pj->delivery = tv; - pj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &pj->delivery, probe_elapse, pj); -} diff --git a/psched.h b/psched.h deleted file mode 100644 index 8f5e77c..0000000 --- a/psched.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef foopschedhfoo -#define foopschedhfoo - -typedef struct _AvahiQueryJob AvahiQueryJob; -typedef struct _AvahiResponseJob AvahiResponseJob; -typedef struct _AvahiPacketScheduler AvahiPacketScheduler; -typedef struct _AvahiKnownAnswer AvahiKnownAnswer; -typedef struct _AvahiProbeJob AvahiProbeJob; - -#include "timeeventq.h" -#include "rr.h" -#include "llist.h" -#include "iface.h" - -struct _AvahiQueryJob { - AvahiPacketScheduler *scheduler; - AvahiTimeEvent *time_event; - AvahiKey *key; - gboolean done; - GTimeVal delivery; - AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs); -}; - -struct _AvahiResponseJob { - AvahiPacketScheduler *scheduler; - AvahiTimeEvent *time_event; - AvahiRecord *record; - AvahiAddress address; - gboolean address_valid; - gboolean done; - GTimeVal delivery; - gboolean flush_cache; - AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs); -}; - -struct _AvahiKnownAnswer { - AvahiPacketScheduler *scheduler; - AvahiRecord *record; - - AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer); -}; - -struct _AvahiProbeJob { - AvahiPacketScheduler *scheduler; - AvahiTimeEvent *time_event; - AvahiRecord *record; - - gboolean chosen; /* Use for packet assembling */ - GTimeVal delivery; - - AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs); -}; - -struct _AvahiPacketScheduler { - AvahiServer *server; - - AvahiInterface *interface; - - AVAHI_LLIST_HEAD(AvahiQueryJob, query_jobs); - AVAHI_LLIST_HEAD(AvahiResponseJob, response_jobs); - AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers); - AVAHI_LLIST_HEAD(AvahiProbeJob, probe_jobs); -}; - -AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i); -void avahi_packet_scheduler_free(AvahiPacketScheduler *s); - -void avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately); -void avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately); -void avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately); - -void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key); -void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record); -void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *a); - -void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s); - -#endif diff --git a/rr.c b/rr.c deleted file mode 100644 index f4102dd..0000000 --- a/rr.c +++ /dev/null @@ -1,556 +0,0 @@ -#include -#include -#include -#include -#include - -#include "util.h" -#include "rr.h" -#include "dns.h" - -AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type) { - AvahiKey *k; - g_assert(name); - - k = g_new(AvahiKey, 1); - k->ref = 1; - k->name = avahi_normalize_name(name); - k->class = class; - k->type = type; - -/* g_message("%p %% ref=1", k); */ - - return k; -} - -AvahiKey *avahi_key_ref(AvahiKey *k) { - g_assert(k); - g_assert(k->ref >= 1); - - k->ref++; - -/* g_message("%p ++ ref=%i", k, k->ref); */ - - return k; -} - -void avahi_key_unref(AvahiKey *k) { - g_assert(k); - g_assert(k->ref >= 1); - -/* g_message("%p -- ref=%i", k, k->ref-1); */ - - if ((--k->ref) <= 0) { - g_free(k->name); - g_free(k); - } -} - -AvahiRecord *avahi_record_new(AvahiKey *k) { - AvahiRecord *r; - - g_assert(k); - - r = g_new(AvahiRecord, 1); - r->ref = 1; - r->key = avahi_key_ref(k); - - memset(&r->data, 0, sizeof(r->data)); - - r->ttl = AVAHI_DEFAULT_TTL; - - return r; -} - -AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type) { - AvahiRecord *r; - AvahiKey *k; - - g_assert(name); - - k = avahi_key_new(name, class, type); - r = avahi_record_new(k); - avahi_key_unref(k); - - return r; -} - -AvahiRecord *avahi_record_ref(AvahiRecord *r) { - g_assert(r); - g_assert(r->ref >= 1); - - r->ref++; - return r; -} - -void avahi_record_unref(AvahiRecord *r) { - g_assert(r); - g_assert(r->ref >= 1); - - if ((--r->ref) <= 0) { - switch (r->key->type) { - - case AVAHI_DNS_TYPE_SRV: - g_free(r->data.srv.name); - break; - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - g_free(r->data.ptr.name); - break; - - case AVAHI_DNS_TYPE_HINFO: - g_free(r->data.hinfo.cpu); - g_free(r->data.hinfo.os); - break; - - case AVAHI_DNS_TYPE_TXT: - avahi_string_list_free(r->data.txt.string_list); - break; - - case AVAHI_DNS_TYPE_A: - case AVAHI_DNS_TYPE_AAAA: - break; - - default: - g_free(r->data.generic.data); - } - - avahi_key_unref(r->key); - g_free(r); - } -} - -const gchar *avahi_dns_class_to_string(guint16 class) { - if (class & AVAHI_DNS_CACHE_FLUSH) - return "FLUSH"; - - if (class == AVAHI_DNS_CLASS_IN) - return "IN"; - - return NULL; -} - -const gchar *avahi_dns_type_to_string(guint16 type) { - switch (type) { - case AVAHI_DNS_TYPE_CNAME: - return "CNAME"; - case AVAHI_DNS_TYPE_A: - return "A"; - case AVAHI_DNS_TYPE_AAAA: - return "AAAA"; - case AVAHI_DNS_TYPE_PTR: - return "PTR"; - case AVAHI_DNS_TYPE_HINFO: - return "HINFO"; - case AVAHI_DNS_TYPE_TXT: - return "TXT"; - case AVAHI_DNS_TYPE_SRV: - return "SRV"; - case AVAHI_DNS_TYPE_ANY: - return "ANY"; - default: - return NULL; - } -} - - -gchar *avahi_key_to_string(const AvahiKey *k) { - return g_strdup_printf("%s\t%s\t%s", - k->name, - avahi_dns_class_to_string(k->class), - avahi_dns_type_to_string(k->type)); -} - -gchar *avahi_record_to_string(const AvahiRecord *r) { - gchar *p, *s; - char buf[257], *t = NULL, *d = NULL; - - switch (r->key->type) { - case AVAHI_DNS_TYPE_A: - inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); - break; - - case AVAHI_DNS_TYPE_AAAA: - inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); - break; - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME : - - t = r->data.ptr.name; - break; - - case AVAHI_DNS_TYPE_TXT: - t = d = avahi_string_list_to_string(r->data.txt.string_list); - break; - - case AVAHI_DNS_TYPE_HINFO: - - snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); - break; - - case AVAHI_DNS_TYPE_SRV: - - snprintf(t = buf, sizeof(buf), "%u %u %u %s", - r->data.srv.priority, - r->data.srv.weight, - r->data.srv.port, - r->data.srv.name); - - break; - } - - p = avahi_key_to_string(r->key); - s = g_strdup_printf("%s %s ; ttl=%u", p, t ? t : "", r->ttl); - g_free(p); - g_free(d); - - return s; -} - -gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { - g_assert(a); - g_assert(b); - - if (a == b) - return TRUE; - -/* g_message("equal: %p %p", a, b); */ - - return avahi_domain_equal(a->name, b->name) && - a->type == b->type && - a->class == b->class; -} - -gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { - g_assert(pattern); - g_assert(k); - -/* g_message("equal: %p %p", a, b); */ - - g_assert(!avahi_key_is_pattern(k)); - - if (pattern == k) - return TRUE; - - return avahi_domain_equal(pattern->name, k->name) && - (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && - pattern->class == k->class; -} - -gboolean avahi_key_is_pattern(const AvahiKey *k) { - g_assert(k); - - return k->type == AVAHI_DNS_TYPE_ANY; -} - - -guint avahi_key_hash(const AvahiKey *k) { - g_assert(k); - - return avahi_domain_hash(k->name) + k->type + k->class; -} - -static gboolean rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { - g_assert(a); - g_assert(b); - g_assert(a->key->type == b->key->type); - -/* t = avahi_record_to_string(a); */ -/* g_message("comparing %s", t); */ -/* g_free(t); */ - -/* t = avahi_record_to_string(b); */ -/* g_message("and %s", t); */ -/* g_free(t); */ - - - switch (a->key->type) { - case AVAHI_DNS_TYPE_SRV: - return - a->data.srv.priority == b->data.srv.priority && - a->data.srv.weight == b->data.srv.weight && - a->data.srv.port == b->data.srv.port && - avahi_domain_equal(a->data.srv.name, b->data.srv.name); - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); - - case AVAHI_DNS_TYPE_HINFO: - return - !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) && - !strcmp(a->data.hinfo.os, b->data.hinfo.os); - - case AVAHI_DNS_TYPE_TXT: - return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); - - case AVAHI_DNS_TYPE_A: - return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; - - case AVAHI_DNS_TYPE_AAAA: - return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; - - default: - return a->data.generic.size == b->data.generic.size && - (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); - } - -} - -gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { - g_assert(a); - g_assert(b); - - if (a == b) - return TRUE; - - return - avahi_key_equal(a->key, b->key) && - rdata_equal(a, b); -} - - -AvahiRecord *avahi_record_copy(AvahiRecord *r) { - AvahiRecord *copy; - - copy = g_new(AvahiRecord, 1); - copy->ref = 1; - copy->key = avahi_key_ref(r->key); - copy->ttl = r->ttl; - - switch (r->key->type) { - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - copy->data.ptr.name = g_strdup(r->data.ptr.name); - break; - - case AVAHI_DNS_TYPE_SRV: - copy->data.srv.priority = r->data.srv.priority; - copy->data.srv.weight = r->data.srv.weight; - copy->data.srv.port = r->data.srv.port; - copy->data.srv.name = g_strdup(r->data.srv.name); - break; - - case AVAHI_DNS_TYPE_HINFO: - copy->data.hinfo.os = g_strdup(r->data.hinfo.os); - copy->data.hinfo.cpu = g_strdup(r->data.hinfo.cpu); - break; - - case AVAHI_DNS_TYPE_TXT: - copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); - break; - - case AVAHI_DNS_TYPE_A: - copy->data.a.address = r->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - copy->data.aaaa.address = r->data.aaaa.address; - break; - - default: - copy->data.generic.data = g_memdup(r->data.generic.data, r->data.generic.size); - copy->data.generic.size = r->data.generic.size; - break; - - } - - return copy; -} - - -guint avahi_key_get_estimate_size(AvahiKey *k) { - g_assert(k); - - return strlen(k->name)+1+4; -} - -guint avahi_record_get_estimate_size(AvahiRecord *r) { - guint n; - g_assert(r); - - n = avahi_key_get_estimate_size(r->key) + 4 + 2; - - switch (r->key->type) { - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - n += strlen(r->data.ptr.name) + 1; - break; - - case AVAHI_DNS_TYPE_SRV: - n += 6 + strlen(r->data.srv.name) + 1; - break; - - case AVAHI_DNS_TYPE_HINFO: - n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; - break; - - case AVAHI_DNS_TYPE_TXT: - n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); - break; - - case AVAHI_DNS_TYPE_A: - n += sizeof(AvahiIPv4Address); - break; - - case AVAHI_DNS_TYPE_AAAA: - n += sizeof(AvahiIPv6Address); - break; - - default: - n += r->data.generic.size; - } - - return n; -} - -static gint lexicographical_memcmp(gconstpointer a, size_t al, gconstpointer b, size_t bl) { - size_t c; - gint ret; - - g_assert(a); - g_assert(b); - - c = al < bl ? al : bl; - if ((ret = memcmp(a, b, c)) != 0) - return ret; - - if (al == bl) - return 0; - else - return al == c ? 1 : -1; -} - -static gint uint16_cmp(guint16 a, guint16 b) { - return a == b ? 0 : (a < b ? a : b); -} - -static gint lexicographical_domain_cmp(const gchar *a, const gchar *b) { - g_assert(a); - g_assert(b); - - - for (;;) { - gchar t1[64]; - gchar t2[64]; - size_t al, bl; - gint r; - - if (!a && !b) - return 0; - - if (a && !b) - return 1; - - if (b && !a) - return -1; - - avahi_unescape_label(t1, sizeof(t1), &a); - avahi_unescape_label(t2, sizeof(t2), &b); - - al = strlen(t1); - bl = strlen(t2); - - if (al != bl) - return al < bl ? -1 : 1; - - if ((r = strcmp(t1, t2)) != 0) - return r; - } -} - -gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { - g_assert(a); - g_assert(b); - - if (a == b) - return 0; - -/* gchar *t; */ - -/* g_message("comparing [%s]", t = avahi_record_to_string(a)); */ -/* g_free(t); */ - -/* g_message("and [%s]", t = avahi_record_to_string(b)); */ -/* g_free(t); */ - - if (a->key->class < b->key->class) - return -1; - else if (a->key->class > b->key->class) - return 1; - - if (a->key->type < b->key->type) - return -1; - else if (a->key->type > b->key->type) - return 1; - - switch (a->key->type) { - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - return lexicographical_domain_cmp(a->data.ptr.name, b->data.ptr.name); - - case AVAHI_DNS_TYPE_SRV: { - gint r; - if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && - (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && - (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) - r = lexicographical_domain_cmp(a->data.srv.name, b->data.srv.name); - - return r; - } - - case AVAHI_DNS_TYPE_HINFO: { - size_t al = strlen(a->data.hinfo.cpu), bl = strlen(b->data.hinfo.cpu); - gint r; - - if (al != bl) - return al < bl ? -1 : 1; - - if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) != 0) - return r; - - al = strlen(a->data.hinfo.os), bl = strlen(b->data.hinfo.os); - - if (al != bl) - return al < bl ? -1 : 1; - - if ((r = strcmp(a->data.hinfo.os, b->data.hinfo.os)) != 0) - return r; - - return 0; - - } - - case AVAHI_DNS_TYPE_TXT: { - - guint8 *ma, *mb; - guint asize, bsize; - gint r; - - ma = g_new(guint8, asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0)); - mb = g_new(guint8, bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0)); - avahi_string_list_serialize(a->data.txt.string_list, ma, asize); - avahi_string_list_serialize(a->data.txt.string_list, mb, bsize); - - r = lexicographical_memcmp(ma, asize, mb, bsize); - g_free(ma); - g_free(mb); - - return r; - } - - case AVAHI_DNS_TYPE_A: - return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); - - case AVAHI_DNS_TYPE_AAAA: - return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); - - default: - return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, - b->data.generic.data, b->data.generic.size); - } - -} diff --git a/rr.h b/rr.h deleted file mode 100644 index 6f1a8cb..0000000 --- a/rr.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef foorrhfoo -#define foorrhfoo - -#include - -#include "strlst.h" -#include "address.h" - -enum { - AVAHI_DNS_TYPE_A = 0x01, - AVAHI_DNS_TYPE_NS = 0x02, - AVAHI_DNS_TYPE_CNAME = 0x05, - AVAHI_DNS_TYPE_SOA = 0x06, - AVAHI_DNS_TYPE_PTR = 0x0C, - AVAHI_DNS_TYPE_HINFO = 0x0D, - AVAHI_DNS_TYPE_MX = 0x0F, - AVAHI_DNS_TYPE_TXT = 0x10, - AVAHI_DNS_TYPE_AAAA = 0x1C, - AVAHI_DNS_TYPE_SRV = 0x21, - AVAHI_DNS_TYPE_ANY = 0xFF -}; - -enum { - AVAHI_DNS_CLASS_IN = 0x01, - AVAHI_DNS_CACHE_FLUSH = 0x8000, - AVAHI_DNS_UNICAST_RESPONSE = 0x8000 -}; - -#define AVAHI_DEFAULT_TTL (120*60) - -typedef struct { - guint ref; - gchar *name; - guint16 class; - guint16 type; -} AvahiKey; - -typedef struct { - guint ref; - AvahiKey *key; - - guint32 ttl; - - union { - struct { - gpointer data; - guint16 size; - } generic; - - struct { - guint16 priority; - guint16 weight; - guint16 port; - gchar *name; - } srv; - - struct { - gchar *name; - } ptr; /* and cname */ - - struct { - gchar *cpu; - gchar *os; - } hinfo; - - struct { - AvahiStringList *string_list; - } txt; - - struct { - AvahiIPv4Address address; - } a; - - struct { - AvahiIPv6Address address; - } aaaa; - - } data; - -} AvahiRecord; - -AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type); -AvahiKey *avahi_key_ref(AvahiKey *k); -void avahi_key_unref(AvahiKey *k); - -gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b); /* Treat AVAHI_DNS_CLASS_ANY like any other type */ -gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); /* If pattern.type is AVAHI_DNS_CLASS_ANY, k.type is ignored */ - -gboolean avahi_key_is_pattern(const AvahiKey *k); - -guint avahi_key_hash(const AvahiKey *k); - -AvahiRecord *avahi_record_new(AvahiKey *k); -AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type); -AvahiRecord *avahi_record_ref(AvahiRecord *r); -void avahi_record_unref(AvahiRecord *r); - -const gchar *avahi_dns_class_to_string(guint16 class); -const gchar *avahi_dns_type_to_string(guint16 type); - -gchar *avahi_key_to_string(const AvahiKey *k); /* g_free() the result! */ -gchar *avahi_record_to_string(const AvahiRecord *r); /* g_free() the result! */ - -gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b); - -AvahiRecord *avahi_record_copy(AvahiRecord *r); - -/* returns a maximum estimate for the space that is needed to store - * this key in a DNS packet */ -guint avahi_key_get_estimate_size(AvahiKey *k); - -/* ditto */ -guint avahi_record_get_estimate_size(AvahiRecord *r); - -gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); - -#endif diff --git a/server.c b/server.c deleted file mode 100644 index b17b142..0000000 --- a/server.c +++ /dev/null @@ -1,1014 +0,0 @@ -#include -#include -#include -#include -#include - -#include "server.h" -#include "util.h" -#include "iface.h" -#include "socket.h" -#include "subscribe.h" - -static void free_entry(AvahiServer*s, AvahiEntry *e) { - AvahiEntry *t; - - g_assert(s); - g_assert(e); - - avahi_goodbye_entry(s, e, TRUE); - - /* Remove from linked list */ - AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e); - - /* Remove from hash table indexed by name */ - t = g_hash_table_lookup(s->entries_by_key, e->record->key); - AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); - if (t) - g_hash_table_replace(s->entries_by_key, t->record->key, t); - else - g_hash_table_remove(s->entries_by_key, e->record->key); - - /* Remove from associated group */ - if (e->group) - AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); - - avahi_record_unref(e->record); - g_free(e); -} - -static void free_group(AvahiServer *s, AvahiEntryGroup *g) { - g_assert(s); - g_assert(g); - - while (g->entries) - free_entry(s, g->entries); - - AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g); - g_free(g); -} - -static void cleanup_dead(AvahiServer *s) { - AvahiEntryGroup *g, *ng; - AvahiEntry *e, *ne; - g_assert(s); - - - if (s->need_group_cleanup) { - for (g = s->groups; g; g = ng) { - ng = g->groups_next; - - if (g->dead) - free_group(s, g); - } - - s->need_group_cleanup = FALSE; - } - - if (s->need_entry_cleanup) { - for (e = s->entries; e; e = ne) { - ne = e->entries_next; - - if (e->dead) - free_entry(s, e); - } - - s->need_entry_cleanup = FALSE; - } -} - -static void handle_query_key(AvahiServer *s, AvahiKey *k, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean unicast_response) { - AvahiEntry *e; - gchar *txt; - - g_assert(s); - g_assert(k); - g_assert(i); - g_assert(a); - - g_message("Handling query: %s", txt = avahi_key_to_string(k)); - g_free(txt); - - avahi_packet_scheduler_incoming_query(i->scheduler, k); - - if (k->type == AVAHI_DNS_TYPE_ANY) { - - /* Handle ANY query */ - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i)) - avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, FALSE); - } else { - - /* Handle all other queries */ - - for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next) - if (!e->dead && avahi_entry_registered(s, e, i)) - avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, FALSE); - } -} - -static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { - g_assert(s); - g_assert(e); - - - if (e->group) { - AvahiEntry *k; - - for (k = e->group->entries; k; k = k->by_group_next) { - avahi_goodbye_entry(s, k, FALSE); - k->dead = TRUE; - } - - avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION); - } else { - avahi_goodbye_entry(s, e, FALSE); - e->dead = TRUE; - } - - s->need_entry_cleanup = TRUE; -} - -static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { - AvahiEntry *e, *n; - gchar *t; - - g_assert(s); - g_assert(record); - g_assert(i); - - t = avahi_record_to_string(record); - -/* g_message("PROBE: [%s]", t); */ - - for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) { - n = e->by_key_next; - - if (e->dead || avahi_record_equal_no_ttl(record, e->record)) - continue; - - if (avahi_entry_registering(s, e, i)) { - gint cmp; - - if ((cmp = avahi_record_lexicographical_compare(record, e->record)) > 0) { - withdraw_entry(s, e); - g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t); - } else if (cmp < 0) - g_message("Recieved conflicting probe [%s]. Local host won.", t); - - } - } - - g_free(t); -} - -static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) { - guint n; - - g_assert(s); - g_assert(p); - g_assert(i); - g_assert(a); - - /* Handle the questions */ - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) { - AvahiKey *key; - gboolean unicast_response = FALSE; - - if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { - g_warning("Packet too short (1)"); - return; - } - - handle_query_key(s, key, i, a, port, legacy_unicast, unicast_response); - avahi_key_unref(key); - } - - /* Known Answer Suppression */ - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { - AvahiRecord *record; - gboolean unique = FALSE; - - if (!(record = avahi_dns_packet_consume_record(p, &unique))) { - g_warning("Packet too short (2)"); - return; - } - - avahi_packet_scheduler_incoming_known_answer(i->scheduler, record, a); - avahi_record_unref(record); - } - - /* Probe record */ - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) { - AvahiRecord *record; - gboolean unique = FALSE; - - if (!(record = avahi_dns_packet_consume_record(p, &unique))) { - g_warning("Packet too short (3)"); - return; - } - - if (record->key->type != AVAHI_DNS_TYPE_ANY) - incoming_probe(s, record, i); - - avahi_record_unref(record); - } -} - -static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) { - gboolean valid = TRUE; - AvahiEntry *e, *n; - gchar *t; - - g_assert(s); - g_assert(i); - g_assert(record); - - t = avahi_record_to_string(record); - -/* g_message("CHECKING FOR CONFLICT: [%s]", t); */ - - for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) { - n = e->by_key_next; - - if (e->dead) - continue; - - if (avahi_entry_registered(s, e, i)) { - - gboolean equal = avahi_record_equal_no_ttl(record, e->record); - - /* Check whether there is a unique record conflict */ - if (!equal && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) { - gint cmp; - - /* The lexicographically later data wins. */ - if ((cmp = avahi_record_lexicographical_compare(record, e->record)) > 0) { - g_message("Recieved conflicting record [%s]. Local host lost. Withdrawing.", t); - withdraw_entry(s, e); - } else if (cmp < 0) { - /* Tell the other host that our entry is lexicographically later */ - - g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t); - - valid = FALSE; - avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); - } - - /* Check wheter there is a TTL conflict */ - } else if (equal && record->ttl <= e->record->ttl/2) { - /* Correct the TTL */ - valid = FALSE; - avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); - g_message("Recieved record with bad TTL [%s]. Refreshing.", t); - } - - } else if (avahi_entry_registering(s, e, i)) { - - if (!avahi_record_equal_no_ttl(record, e->record) && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) { - - /* We are currently registering a matching record, but - * someone else already claimed it, so let's - * withdraw */ - - g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t); - withdraw_entry(s, e); - } - } - } - - g_free(t); - - return valid; -} - -static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) { - guint n; - - g_assert(s); - g_assert(p); - g_assert(i); - g_assert(a); - - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) { - AvahiRecord *record; - gboolean cache_flush = FALSE; - gchar *txt; - - if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) { - g_warning("Packet too short (4)"); - return; - } - - if (record->key->type != AVAHI_DNS_TYPE_ANY) { - - g_message("Handling response: %s", txt = avahi_record_to_string(record)); - g_free(txt); - - if (handle_conflict(s, i, record, cache_flush, a)) { - avahi_cache_update(i->cache, record, cache_flush, a); - avahi_packet_scheduler_incoming_response(i->scheduler, record); - } - } - - avahi_record_unref(record); - } -} - -static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) { - AvahiInterface *i; - AvahiAddress a; - guint16 port; - - g_assert(s); - g_assert(p); - g_assert(sa); - g_assert(iface > 0); - - if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) { - g_warning("Recieved packet from invalid interface."); - return; - } - - g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); - - if (sa->sa_family == AF_INET6) { - static const guint8 ipv4_in_ipv6[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF }; - - /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ - - if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0) - return; - } - - if (avahi_dns_packet_check_valid(p) < 0) { - g_warning("Recieved invalid packet."); - return; - } - - port = avahi_port_from_sockaddr(sa); - avahi_address_from_sockaddr(sa, &a); - - if (avahi_dns_packet_is_query(p)) { - gboolean legacy_unicast = FALSE; - - if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) == 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) { - g_warning("Invalid query packet."); - return; - } - - if (port != AVAHI_MDNS_PORT) { - /* Legacy Unicast */ - - if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) { - g_warning("Invalid legacy unicast query packet."); - return; - } - - legacy_unicast = TRUE; - } - - handle_query(s, p, i, &a, port, legacy_unicast); - - g_message("Handled query"); - } else { - - if (port != AVAHI_MDNS_PORT) { - g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol); - return; - } - - if (ttl != 255) { - g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol); - if (!s->ignore_bad_ttl) - return; - } - - if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) { - g_warning("Invalid response packet."); - return; - } - - handle_response(s, p, i, &a); - g_message("Handled response"); - } -} - -static void work(AvahiServer *s) { - struct sockaddr_in6 sa6; - struct sockaddr_in sa; - AvahiDnsPacket *p; - gint iface = -1; - guint8 ttl; - - g_assert(s); - - if (s->pollfd_ipv4.revents & G_IO_IN) { - if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) { - dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl); - avahi_dns_packet_free(p); - } - } - - if (s->pollfd_ipv6.revents & G_IO_IN) { - if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) { - dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl); - avahi_dns_packet_free(p); - } - } -} - -static gboolean prepare_func(GSource *source, gint *timeout) { - g_assert(source); - g_assert(timeout); - - *timeout = -1; - return FALSE; -} - -static gboolean check_func(GSource *source) { - AvahiServer* s; - g_assert(source); - - s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource))); - g_assert(s); - - return (s->pollfd_ipv4.revents | s->pollfd_ipv6.revents) & (G_IO_IN | G_IO_HUP | G_IO_ERR); -} - -static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) { - AvahiServer* s; - g_assert(source); - - s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource))); - g_assert(s); - - work(s); - cleanup_dead(s); - - return TRUE; -} - -static void add_default_entries(AvahiServer *s) { - gint length = 0; - struct utsname utsname; - gchar *hinfo; - AvahiAddress a; - AvahiRecord *r; - - g_assert(s); - - /* Fill in HINFO rr */ - r = avahi_record_new_full(s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO); - uname(&utsname); - r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine)); - r->data.hinfo.os = g_strdup(g_strup(utsname.sysname)); - avahi_server_add(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r); - avahi_record_unref(r); - - /* Add localhost entries */ - avahi_address_parse("127.0.0.1", AF_INET, &a); - avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE|AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a); - - avahi_address_parse("::1", AF_INET6, &a); - avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE|AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a); -} - -AvahiServer *avahi_server_new(GMainContext *c) { - gchar *hn, *e; - AvahiServer *s; - - static GSourceFuncs source_funcs = { - prepare_func, - check_func, - dispatch_func, - NULL, - NULL, - NULL - }; - - s = g_new(AvahiServer, 1); - - s->ignore_bad_ttl = FALSE; - s->need_entry_cleanup = s->need_group_cleanup = FALSE; - - s->fd_ipv4 = avahi_open_socket_ipv4(); - s->fd_ipv6 = avahi_open_socket_ipv6(); - - if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) { - g_critical("Failed to create IP sockets.\n"); - g_free(s); - return NULL; - } - - if (s->fd_ipv4 < 0) - g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode"); - else if (s->fd_ipv6 < 0) - g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode"); - - if (c) - g_main_context_ref(s->context = c); - else - s->context = g_main_context_default(); - - AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries); - s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal); - AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups); - - AVAHI_LLIST_HEAD_INIT(AvahiSubscription, s->subscriptions); - s->subscription_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal); - - /* Get host name */ - hn = avahi_get_host_name(); - hn[strcspn(hn, ".")] = 0; - - s->hostname = g_strdup_printf("%s.local.", hn); - g_free(hn); - - s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */ - s->monitor = avahi_interface_monitor_new(s); - avahi_interface_monitor_sync(s->monitor); - add_default_entries(s); - - /* Prepare IO source registration */ - s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*)); - *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s; - - memset(&s->pollfd_ipv4, 0, sizeof(s->pollfd_ipv4)); - s->pollfd_ipv4.fd = s->fd_ipv4; - s->pollfd_ipv4.events = G_IO_IN|G_IO_ERR|G_IO_HUP; - g_source_add_poll(s->source, &s->pollfd_ipv4); - - memset(&s->pollfd_ipv6, 0, sizeof(s->pollfd_ipv6)); - s->pollfd_ipv6.fd = s->fd_ipv6; - s->pollfd_ipv6.events = G_IO_IN|G_IO_ERR|G_IO_HUP; - g_source_add_poll(s->source, &s->pollfd_ipv6); - - g_source_attach(s->source, s->context); - - return s; -} - -void avahi_server_free(AvahiServer* s) { - g_assert(s); - - while(s->entries) - free_entry(s, s->entries); - - avahi_interface_monitor_free(s->monitor); - - while (s->groups) - free_group(s, s->groups); - - while (s->subscriptions) - avahi_subscription_free(s->subscriptions); - g_hash_table_destroy(s->subscription_hashtable); - - g_hash_table_destroy(s->entries_by_key); - - avahi_time_event_queue_free(s->time_event_queue); - - if (s->fd_ipv4 >= 0) - close(s->fd_ipv4); - if (s->fd_ipv6 >= 0) - close(s->fd_ipv6); - - g_free(s->hostname); - - g_source_destroy(s->source); - g_source_unref(s->source); - g_main_context_unref(s->context); - - g_free(s); -} - -void avahi_server_add( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - AvahiRecord *r) { - - AvahiEntry *e, *t; - g_assert(s); - g_assert(r); - - g_assert(r->key->type != AVAHI_DNS_TYPE_ANY); - - e = g_new(AvahiEntry, 1); - e->server = s; - e->record = avahi_record_ref(r); - e->group = g; - e->interface = interface; - e->protocol = protocol; - e->flags = flags; - e->dead = FALSE; - - AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements); - - AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); - - /* Insert into hash table indexed by name */ - t = g_hash_table_lookup(s->entries_by_key, e->record->key); - AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); - g_hash_table_replace(s->entries_by_key, e->record->key, t); - - /* Insert into group list */ - if (g) - AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); - - avahi_announce_entry(s, e); -} -const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) { - AvahiEntry **e = (AvahiEntry**) state; - g_assert(s); - g_assert(e); - - if (!*e) - *e = g ? g->entries : s->entries; - - while (*e && (*e)->dead) - *e = g ? (*e)->by_group_next : (*e)->entries_next; - - if (!*e) - return NULL; - - return avahi_record_ref((*e)->record); -} - -void avahi_server_dump(AvahiServer *s, FILE *f) { - AvahiEntry *e; - g_assert(s); - g_assert(f); - - fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n"); - - for (e = s->entries; e; e = e->entries_next) { - gchar *t; - - if (e->dead) - continue; - - t = avahi_record_to_string(e->record); - fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol); - g_free(t); - } - - avahi_dump_caches(s->monitor, f); -} - -void avahi_server_add_ptr( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - const gchar *dest) { - - AvahiRecord *r; - - g_assert(dest); - - r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); - r->data.ptr.name = avahi_normalize_name(dest); - avahi_server_add(s, g, interface, protocol, flags, r); - avahi_record_unref(r); -} - -void avahi_server_add_address( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - AvahiAddress *a) { - - gchar *n = NULL; - g_assert(s); - g_assert(a); - - name = name ? (n = avahi_normalize_name(name)) : s->hostname; - - if (a->family == AF_INET) { - gchar *reverse; - AvahiRecord *r; - - r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); - r->data.a.address = a->data.ipv4; - avahi_server_add(s, g, interface, protocol, flags, r); - avahi_record_unref(r); - - reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4); - g_assert(reverse); - avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name); - g_free(reverse); - - } else { - gchar *reverse; - AvahiRecord *r; - - r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); - r->data.aaaa.address = a->data.ipv6; - avahi_server_add(s, g, interface, protocol, flags, r); - avahi_record_unref(r); - - reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6); - g_assert(reverse); - avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name); - g_free(reverse); - - reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6); - g_assert(reverse); - avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name); - g_free(reverse); - } - - g_free(n); -} - -void avahi_server_add_text_strlst( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - AvahiStringList *strlst) { - - AvahiRecord *r; - - g_assert(s); - - r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); - r->data.txt.string_list = strlst; - avahi_server_add(s, g, interface, protocol, flags, r); - avahi_record_unref(r); -} - -void avahi_server_add_text_va( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - va_list va) { - - g_assert(s); - - avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va)); -} - -void avahi_server_add_text( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - AvahiEntryFlags flags, - const gchar *name, - ...) { - - va_list va; - - g_assert(s); - - va_start(va, name); - avahi_server_add_text_va(s, g, interface, protocol, flags, name, va); - va_end(va); -} - -static void escape_service_name(gchar *d, guint size, const gchar *s) { - g_assert(d); - g_assert(size); - g_assert(s); - - while (*s && size >= 2) { - if (*s == '.' || *s == '\\') { - if (size < 3) - break; - - *(d++) = '\\'; - size--; - } - - *(d++) = *(s++); - size--; - } - - g_assert(size > 0); - *(d++) = 0; -} - -void avahi_server_add_service_strlst( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - const gchar *type, - const gchar *name, - const gchar *domain, - const gchar *host, - guint16 port, - AvahiStringList *strlst) { - - gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256]; - AvahiRecord *r; - - g_assert(s); - g_assert(type); - g_assert(name); - - escape_service_name(ename, sizeof(ename), name); - - if (domain) { - while (domain[0] == '.') - domain++; - } else - domain = "local"; - - if (!host) - host = s->hostname; - - snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain); - snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain); - - avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name); - - r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); - r->data.srv.priority = 0; - r->data.srv.weight = 0; - r->data.srv.port = port; - r->data.srv.name = avahi_normalize_name(host); - avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r); - avahi_record_unref(r); - - avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst); - - snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain); - avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name); -} - -void avahi_server_add_service_va( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - const gchar *type, - const gchar *name, - const gchar *domain, - const gchar *host, - guint16 port, - va_list va){ - - g_assert(s); - g_assert(type); - g_assert(name); - - avahi_server_add_service(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va)); -} - -void avahi_server_add_service( - AvahiServer *s, - AvahiEntryGroup *g, - gint interface, - guchar protocol, - const gchar *type, - const gchar *name, - const gchar *domain, - const gchar *host, - guint16 port, - ... ){ - - va_list va; - - g_assert(s); - g_assert(type); - g_assert(name); - - va_start(va, port); - avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va); - va_end(va); -} - -static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { - AvahiKey *k = userdata; - - g_assert(m); - g_assert(i); - g_assert(k); - - avahi_interface_post_query(i, k, FALSE); -} - -void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) { - g_assert(s); - g_assert(key); - - avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key); -} - -struct tmpdata { - AvahiRecord *record; - gboolean flush_cache; -}; - -static void post_response_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { - struct tmpdata *tmpdata = userdata; - - g_assert(m); - g_assert(i); - g_assert(tmpdata); - - avahi_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE); -} - -void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache) { - struct tmpdata tmpdata; - - g_assert(s); - g_assert(record); - - tmpdata.record = record; - tmpdata.flush_cache = flush_cache; - - avahi_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata); -} - -void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) { - g_assert(g); - - g->state = state; - - if (g->callback) { - g->callback(g->server, g, state, g->userdata); - return; - } -} - -AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) { - AvahiEntryGroup *g; - - g_assert(s); - - g = g_new(AvahiEntryGroup, 1); - g->server = s; - g->callback = callback; - g->userdata = userdata; - g->dead = FALSE; - g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; - g->n_probing = 0; - AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); - - AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g); - return g; -} - -void avahi_entry_group_free(AvahiEntryGroup *g) { - g_assert(g); - g_assert(g->server); - - g->dead = TRUE; - g->server->need_group_cleanup = TRUE; -} - -void avahi_entry_group_commit(AvahiEntryGroup *g) { - AvahiEntry *e; - - g_assert(g); - g_assert(!g->dead); - - if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED) - return; - - avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); - avahi_announce_group(g->server, g); - avahi_entry_group_check_probed(g, FALSE); -} - -gboolean avahi_entry_commited(AvahiEntry *e) { - g_assert(e); - g_assert(!e->dead); - - return !e->group || - e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || - e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; -} - -AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) { - g_assert(g); - g_assert(!g->dead); - - return g->state; -} diff --git a/server.h b/server.h deleted file mode 100644 index 9f7ef51..0000000 --- a/server.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef fooAvahiserverhfoo -#define fooAvahiserverhfoo - -#include "avahi.h" -#include "iface.h" -#include "prioq.h" -#include "llist.h" -#include "timeeventq.h" -#include "announce.h" -#include "subscribe.h" - -struct _AvahiEntry { - AvahiServer *server; - AvahiEntryGroup *group; - - gboolean dead; - - AvahiEntryFlags flags; - AvahiRecord *record; - gint interface; - guchar protocol; - - AVAHI_LLIST_FIELDS(AvahiEntry, entries); - AVAHI_LLIST_FIELDS(AvahiEntry, by_key); - AVAHI_LLIST_FIELDS(AvahiEntry, by_group); - - AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements); -}; - -struct _AvahiEntryGroup { - AvahiServer *server; - gboolean dead; - - AvahiEntryGroupState state; - gpointer userdata; - AvahiEntryGroupCallback callback; - - guint n_probing; - - AVAHI_LLIST_FIELDS(AvahiEntryGroup, groups); - AVAHI_LLIST_HEAD(AvahiEntry, entries); -}; - -struct _AvahiServer { - GMainContext *context; - AvahiInterfaceMonitor *monitor; - - AVAHI_LLIST_HEAD(AvahiEntry, entries); - GHashTable *entries_by_key; - - AVAHI_LLIST_HEAD(AvahiEntryGroup, groups); - - AVAHI_LLIST_HEAD(AvahiSubscription, subscriptions); - GHashTable *subscription_hashtable; - - gboolean need_entry_cleanup, need_group_cleanup; - - AvahiTimeEventQueue *time_event_queue; - - gchar *hostname; - - gint fd_ipv4, fd_ipv6; - - GPollFD pollfd_ipv4, pollfd_ipv6; - GSource *source; - - gboolean ignore_bad_ttl; -}; - -gboolean avahi_server_entry_match_interface(AvahiEntry *e, AvahiInterface *i); - -void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key); -void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache); - -void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state); - -gboolean avahi_entry_commited(AvahiEntry *e); - -#endif diff --git a/socket.c b/socket.c deleted file mode 100644 index 1ef8131..0000000 --- a/socket.c +++ /dev/null @@ -1,488 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dns.h" -#include "util.h" -#include "socket.h" - -static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { - g_assert(ret_sa); - - memset(ret_sa, 0, sizeof(struct sockaddr_in)); - - ret_sa->sin_family = AF_INET; - ret_sa->sin_port = htons(AVAHI_MDNS_PORT); - inet_pton(AF_INET, "224.0.0.251", &ret_sa->sin_addr); -} - -static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { - - g_assert(ret_sa); - - memset(ret_sa, 0, sizeof(struct sockaddr_in6)); - - ret_sa->sin6_family = AF_INET6; - ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); - inet_pton(AF_INET6, "ff02::fb", &ret_sa->sin6_addr); -} - -int avahi_mdns_mcast_join_ipv4 (int index, int fd) -{ - struct ip_mreqn mreq; - struct sockaddr_in sa; - - mdns_mcast_group_ipv4 (&sa); - - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr = sa.sin_addr; - mreq.imr_ifindex = index; - - if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - g_warning("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -int avahi_mdns_mcast_join_ipv6 (int index, int fd) -{ - struct ipv6_mreq mreq6; - struct sockaddr_in6 sa6; - - mdns_mcast_group_ipv6 (&sa6); - - memset(&mreq6, 0, sizeof(mreq6)); - mreq6.ipv6mr_multiaddr = sa6.sin6_addr; - mreq6.ipv6mr_interface = index; - - if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { - g_warning("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -int avahi_mdns_mcast_leave_ipv4 (int index, int fd) -{ - struct ip_mreqn mreq; - struct sockaddr_in sa; - - mdns_mcast_group_ipv4 (&sa); - - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr = sa.sin_addr; - mreq.imr_ifindex = index; - - if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - g_warning("IP_DROP_MEMBERSHIP failed: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -int avahi_mdns_mcast_leave_ipv6 (int index, int fd) -{ - struct ipv6_mreq mreq6; - struct sockaddr_in6 sa6; - - mdns_mcast_group_ipv6 (&sa6); - - memset(&mreq6, 0, sizeof(mreq6)); - mreq6.ipv6mr_multiaddr = sa6.sin6_addr; - mreq6.ipv6mr_interface = index; - - if (setsockopt(fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { - g_warning("IPV6_DROP_MEMBERSHIP failed: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -gint avahi_open_socket_ipv4(void) { - struct sockaddr_in sa, local; - int fd = -1, ttl, yes; - - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - g_warning("socket() failed: %s\n", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { - g_warning("IP_MULTICAST_TTL failed: %s\n", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { - g_warning("IP_TTL failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { - g_warning("SO_REUSEADDR failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { - g_warning("IP_MULTICAST_LOOP failed: %s\n", strerror(errno)); - goto fail; - } - - - memset(&local, 0, sizeof(local)); - local.sin_family = AF_INET; - local.sin_port = htons(AVAHI_MDNS_PORT); - - if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { - g_warning("bind() failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { - g_warning("IP_RECVTTL failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { - g_warning("IP_PKTINFO failed: %s\n", strerror(errno)); - goto fail; - } - - if (avahi_set_cloexec(fd) < 0) { - g_warning("FD_CLOEXEC failed: %s\n", strerror(errno)); - goto fail; - } - - if (avahi_set_nonblock(fd) < 0) { - g_warning("O_NONBLOCK failed: %s\n", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} - -gint avahi_open_socket_ipv6(void) { - struct sockaddr_in6 sa, local; - int fd = -1, ttl, yes; - - mdns_mcast_group_ipv6(&sa); - - if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - g_warning("socket() failed: %s\n", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { - g_warning("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { - g_warning("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { - g_warning("SO_REUSEADDR failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { - g_warning("IPV6_V6ONLY failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { - g_warning("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno)); - goto fail; - } - - memset(&local, 0, sizeof(local)); - local.sin6_family = AF_INET6; - local.sin6_port = htons(AVAHI_MDNS_PORT); - - if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { - g_warning("bind() failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { - g_warning("IPV6_HOPLIMIT failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { - g_warning("IPV6_PKTINFO failed: %s\n", strerror(errno)); - goto fail; - } - - if (avahi_set_cloexec(fd) < 0) { - g_warning("FD_CLOEXEC failed: %s\n", strerror(errno)); - goto fail; - } - - if (avahi_set_nonblock(fd) < 0) { - g_warning("O_NONBLOCK failed: %s\n", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} - -static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) { - g_assert(fd >= 0); - g_assert(msg); - - for (;;) { - - if (sendmsg(fd, msg, flags) >= 0) - break; - - if (errno != EAGAIN) { - g_message("sendmsg() failed: %s\n", strerror(errno)); - return -1; - } - - if (avahi_wait_for_write(fd) < 0) - return -1; - } - - return 0; -} - -gint avahi_send_dns_packet_ipv4(gint fd, gint interface, AvahiDnsPacket *p) { - struct sockaddr_in sa; - struct msghdr msg; - struct iovec io; - struct cmsghdr *cmsg; - struct in_pktinfo *pkti; - uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)]; - int i, n; - - g_assert(fd >= 0); - g_assert(p); - g_assert(avahi_dns_packet_check_valid(p) >= 0); - - mdns_mcast_group_ipv4(&sa); - - memset(&io, 0, sizeof(io)); - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->size; - - memset(cmsg_data, 0, sizeof(cmsg_data)); - cmsg = (struct cmsghdr*) cmsg_data; - cmsg->cmsg_len = sizeof(cmsg_data); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - - pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); - pkti->ipi_ifindex = interface; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = cmsg_data; - msg.msg_controllen = sizeof(cmsg_data); - msg.msg_flags = 0; - - return sendmsg_loop(fd, &msg, MSG_DONTROUTE); -} - -gint avahi_send_dns_packet_ipv6(gint fd, gint interface, AvahiDnsPacket *p) { - struct sockaddr_in6 sa; - struct msghdr msg; - struct iovec io; - struct cmsghdr *cmsg; - struct in6_pktinfo *pkti; - uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)]; - int i, n; - - g_assert(fd >= 0); - g_assert(p); - g_assert(avahi_dns_packet_check_valid(p) >= 0); - - mdns_mcast_group_ipv6(&sa); - - memset(&io, 0, sizeof(io)); - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->size; - - memset(cmsg_data, 0, sizeof(cmsg_data)); - cmsg = (struct cmsghdr*) cmsg_data; - cmsg->cmsg_len = sizeof(cmsg_data); - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - - pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); - pkti->ipi6_ifindex = interface; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = cmsg_data; - msg.msg_controllen = sizeof(cmsg_data); - msg.msg_flags = 0; - - return sendmsg_loop(fd, &msg, MSG_DONTROUTE); -} - -AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) { - AvahiDnsPacket *p= NULL; - struct msghdr msg; - struct iovec io; - uint8_t aux[64]; - ssize_t l; - struct cmsghdr *cmsg; - gboolean found_ttl = FALSE, found_iface = FALSE; - - g_assert(fd >= 0); - g_assert(ret_sa); - g_assert(ret_iface); - g_assert(ret_ttl); - - p = avahi_dns_packet_new(0); - - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->max_size; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = ret_sa; - msg.msg_namelen = sizeof(struct sockaddr_in); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = aux; - msg.msg_controllen = sizeof(aux); - msg.msg_flags = 0; - - if ((l = recvmsg(fd, &msg, 0)) < 0) - goto fail; - - p->size = (size_t) l; - - *ret_ttl = 0; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) { - if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) { - *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg); - found_ttl = TRUE; - } - - if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { - *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex; - found_iface = TRUE; - } - } - - g_assert(found_iface); - g_assert(found_ttl); - - return p; - -fail: - if (p) - avahi_dns_packet_free(p); - - return NULL; -} - -AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) { - AvahiDnsPacket *p = NULL; - struct msghdr msg; - struct iovec io; - uint8_t aux[64]; - ssize_t l; - struct cmsghdr *cmsg; - gboolean found_ttl = FALSE, found_iface = FALSE; - - g_assert(fd >= 0); - g_assert(ret_sa); - g_assert(ret_iface); - g_assert(ret_ttl); - - p = avahi_dns_packet_new(0); - - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->max_size; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = ret_sa; - msg.msg_namelen = sizeof(struct sockaddr_in6); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = aux; - msg.msg_controllen = sizeof(aux); - msg.msg_flags = 0; - - if ((l = recvmsg(fd, &msg, 0)) < 0) - goto fail; - - p->size = (size_t) l; - - *ret_ttl = 0; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) { - *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg); - found_ttl = TRUE; - } - - if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { - *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex; - found_iface = TRUE; - } - } - - g_assert(found_iface); - g_assert(found_ttl); - - return p; - -fail: - if (p) - avahi_dns_packet_free(p); - - return NULL; -} - diff --git a/socket.h b/socket.h deleted file mode 100644 index 2baa122..0000000 --- a/socket.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef foosockethfoo -#define foosockethfoo - -#include - -#include "dns.h" - -#define AVAHI_MDNS_PORT 5353 - - - -gint avahi_open_socket_ipv4(void); -gint avahi_open_socket_ipv6(void); - -gint avahi_send_dns_packet_ipv4(gint fd, gint iface, AvahiDnsPacket *p); -gint avahi_send_dns_packet_ipv6(gint fd, gint iface, AvahiDnsPacket *p); - -AvahiDnsPacket *avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in*ret_sa, gint *ret_iface, guint8 *ret_ttl); -AvahiDnsPacket *avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6*ret_sa, gint *ret_iface, guint8 *ret_ttl); - -int avahi_mdns_mcast_join_ipv4(int index, int fd); -int avahi_mdns_mcast_join_ipv6(int index, int fd); - -int avahi_mdns_mcast_leave_ipv4(int index, int fd); -int avahi_mdns_mcast_leave_ipv6(int index, int fd); - -#endif diff --git a/strlst-test.c b/strlst-test.c deleted file mode 100644 index 5e28ce2..0000000 --- a/strlst-test.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include - -#include "strlst.h" - -int main(int argc, char *argv[]) { - gchar *t; - guint8 data[1024]; - AvahiStringList *a = NULL, *b; - guint size, n; - - a = avahi_string_list_add(a, "start"); - a = avahi_string_list_add(a, "foo"); - a = avahi_string_list_add(a, "bar"); - a = avahi_string_list_add(a, "quux"); - a = avahi_string_list_add_arbitrary(a, "null\0null", 9); - a = avahi_string_list_add(a, "end"); - - t = avahi_string_list_to_string(a); - printf("--%s--\n", t); - g_free(t); - - size = avahi_string_list_serialize(a, data, sizeof(data)); - - printf("%u\n", size); - - for (t = (gchar*) data, n = 0; n < size; n++, t++) { - if (*t <= 32) - printf("(%u)", *t); - else - printf("%c", *t); - } - - printf("\n"); - - b = avahi_string_list_parse(data, size); - - g_assert(avahi_string_list_equal(a, b)); - - t = avahi_string_list_to_string(b); - printf("--%s--\n", t); - g_free(t); - - avahi_string_list_free(b); - - b = avahi_string_list_copy(a); - - g_assert(avahi_string_list_equal(a, b)); - - t = avahi_string_list_to_string(b); - printf("--%s--\n", t); - g_free(t); - - avahi_string_list_free(a); - avahi_string_list_free(b); - - return 0; -} diff --git a/strlst.c b/strlst.c deleted file mode 100644 index f2d76e4..0000000 --- a/strlst.c +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include - -#include "strlst.h" - -AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8*text, guint size) { - AvahiStringList *n; - - g_assert(text); - - n = g_malloc(sizeof(AvahiStringList) + size); - n->next = l; - memcpy(n->text, text, n->size = size); - - return n; -} - -AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text) { - g_assert(text); - - return avahi_string_list_add_arbitrary(l, (const guint8*) text, strlen(text)); -} - -AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size) { - AvahiStringList *r = NULL; - const guint8 *c; - g_assert(data); - - c = data; - for (;;) { - guint k; - - if (size < 1) - break; - - k = *(c++); - r = avahi_string_list_add_arbitrary(r, c, k); - c += k; - - size -= 1 + k; - } - - return r; -} - -void avahi_string_list_free(AvahiStringList *l) { - AvahiStringList *n; - - while (l) { - n = l->next; - g_free(l); - l = n; - } -} - -static AvahiStringList* string_list_reverse(AvahiStringList *l) { - AvahiStringList *r = NULL, *n; - - while (l) { - n = l->next; - l->next = r; - r = l; - l = n; - } - - return r; -} - -gchar* avahi_string_list_to_string(AvahiStringList *l) { - AvahiStringList *n; - guint s = 0; - gchar *t, *e; - - l = string_list_reverse(l); - - for (n = l; n; n = n->next) { - if (n != l) - s ++; - - s += n->size+3; - } - - t = e = g_new(gchar, s); - - for (n = l; n; n = n->next) { - if (n != l) - *(e++) = ' '; - - *(e++) = '"'; - strncpy(e, n->text, n->size); - e[n->size] = 0; - e = strchr(e, 0); - *(e++) = '"'; - } - - l = string_list_reverse(l); - - *e = 0; - - return t; -} - -guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size) { - guint used = 0; - - if (data) { - guint8 *c; - AvahiStringList *n; - - g_assert(data); - - l = string_list_reverse(l); - c = data; - - for (n = l; n; n = n->next) { - guint k; - if (size < 1) - break; - - k = n->size; - if (k > 255) - k = 255; - - if (k > size-1) - k = size-1; - - *(c++) = k; - memcpy(c, n->text, k); - c += k; - - used += 1+ k; - } - - l = string_list_reverse(l); - } else { - AvahiStringList *n; - - for (n = l; n; n = n->next) { - guint k; - - k = n->size; - if (k > 255) - k = 255; - - used += 1+k; - } - } - - return used; -} - -gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b) { - - for (;;) { - if (!a && !b) - return TRUE; - - if (!a || !b) - return FALSE; - - if (a->size != b->size) - return FALSE; - - if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0) - return FALSE; - - a = a->next; - b = b->next; - } -} - -AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) { - va_list va; - - va_start(va, r); - r = avahi_string_list_add_many_va(r, va); - va_end(va); - - return r; -} - -AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) { - const gchar *txt; - - while ((txt = va_arg(va, const gchar*))) - r = avahi_string_list_add(r, txt); - - return r; -} - - -AvahiStringList *avahi_string_list_new(const gchar *txt, ...) { - va_list va; - AvahiStringList *r = NULL; - - if (txt) { - r = avahi_string_list_add(r, txt); - - va_start(va, txt); - r = avahi_string_list_add_many_va(r, va); - va_end(va); - } - - return r; -} - -AvahiStringList *avahi_string_list_new_va(va_list va) { - return avahi_string_list_add_many_va(NULL, va); -} - -AvahiStringList *avahi_string_list_copy(AvahiStringList *l) { - AvahiStringList *r = NULL; - - for (; l; l = l->next) - r = avahi_string_list_add_arbitrary(r, l->text, l->size); - - return string_list_reverse(r); -} diff --git a/strlst.h b/strlst.h deleted file mode 100644 index 22cdc88..0000000 --- a/strlst.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef footxtlisthfoo -#define footxtlisthfoo - -#include - -typedef struct _AvahiStringList AvahiStringList; - -struct _AvahiStringList { - AvahiStringList *next; - guint size; - guint8 text[1]; -}; - -AvahiStringList *avahi_string_list_new(const gchar *txt, ...); -AvahiStringList *avahi_string_list_new_va(va_list va); - -void avahi_string_list_free(AvahiStringList *l); - -AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text); -AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8 *text, guint size); -AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...); -AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); - -gchar* avahi_string_list_to_string(AvahiStringList *l); - -guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size); -AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size); - -gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b); - -AvahiStringList *avahi_string_list_copy(AvahiStringList *l); - -#endif - diff --git a/subscribe.c b/subscribe.c deleted file mode 100644 index 902e966..0000000 --- a/subscribe.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "subscribe.h" -#include "util.h" - -static void elapse(AvahiTimeEvent *e, void *userdata) { - AvahiSubscription *s = userdata; - GTimeVal tv; - gchar *t; - - g_assert(s); - - avahi_server_post_query(s->server, s->interface, s->protocol, s->key); - - if (s->n_query++ <= 8) - s->sec_delay *= 2; - - g_message("%i. Continuous querying for %s", s->n_query, t = avahi_key_to_string(s->key)); - g_free(t); - - avahi_elapse_time(&tv, s->sec_delay*1000, 0); - avahi_time_event_queue_update(s->server->time_event_queue, s->time_event, &tv); -} - -struct cbdata { - AvahiSubscription *subscription; - AvahiInterface *interface; -}; - -static gpointer scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) { - struct cbdata *cbdata = userdata; - - g_assert(c); - g_assert(pattern); - g_assert(e); - g_assert(cbdata); - - cbdata->subscription->callback( - cbdata->subscription, - e->record, - cbdata->interface->hardware->index, - cbdata->interface->protocol, - AVAHI_SUBSCRIPTION_NEW, - cbdata->subscription->userdata); - - return NULL; -} - -static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) { - AvahiSubscription *s = userdata; - struct cbdata cbdata = { s, i }; - - g_assert(m); - g_assert(i); - g_assert(s); - - avahi_cache_walk(i->cache, s->key, scan_cache_callback, &cbdata); -} - -AvahiSubscription *avahi_subscription_new(AvahiServer *server, AvahiKey *key, gint interface, guchar protocol, AvahiSubscriptionCallback callback, gpointer userdata) { - AvahiSubscription *s, *t; - GTimeVal tv; - - g_assert(server); - g_assert(key); - g_assert(callback); - - g_assert(!avahi_key_is_pattern(key)); - - s = g_new(AvahiSubscription, 1); - s->server = server; - s->key = avahi_key_ref(key); - s->interface = interface; - s->protocol = protocol; - s->callback = callback; - s->userdata = userdata; - s->n_query = 1; - s->sec_delay = 1; - - avahi_server_post_query(s->server, s->interface, s->protocol, s->key); - - avahi_elapse_time(&tv, s->sec_delay*1000, 0); - s->time_event = avahi_time_event_queue_add(server->time_event_queue, &tv, elapse, s); - - AVAHI_LLIST_PREPEND(AvahiSubscription, subscriptions, server->subscriptions, s); - - /* Add the new entry to the subscription hash table */ - t = g_hash_table_lookup(server->subscription_hashtable, key); - AVAHI_LLIST_PREPEND(AvahiSubscription, by_key, t, s); - g_hash_table_replace(server->subscription_hashtable, key, t); - - /* Scan the caches */ - avahi_interface_monitor_walk(s->server->monitor, s->interface, s->protocol, scan_interface_callback, s); - - return s; -} - -void avahi_subscription_free(AvahiSubscription *s) { - AvahiSubscription *t; - - g_assert(s); - - AVAHI_LLIST_REMOVE(AvahiSubscription, subscriptions, s->server->subscriptions, s); - - t = g_hash_table_lookup(s->server->subscription_hashtable, s->key); - AVAHI_LLIST_REMOVE(AvahiSubscription, by_key, t, s); - if (t) - g_hash_table_replace(s->server->subscription_hashtable, t->key, t); - else - g_hash_table_remove(s->server->subscription_hashtable, s->key); - - avahi_time_event_queue_remove(s->server->time_event_queue, s->time_event); - avahi_key_unref(s->key); - - - g_free(s); -} - -void avahi_subscription_notify(AvahiServer *server, AvahiInterface *i, AvahiRecord *record, AvahiSubscriptionEvent event) { - AvahiSubscription *s; - AvahiKey *pattern; - - g_assert(server); - g_assert(record); - - for (s = g_hash_table_lookup(server->subscription_hashtable, record->key); s; s = s->by_key_next) - if (avahi_interface_match(i, s->interface, s->protocol)) - s->callback(s, record, i->hardware->index, i->protocol, event, s->userdata); -} - -gboolean avahi_is_subscribed(AvahiServer *server, AvahiKey *k) { - g_assert(server); - g_assert(k); - - return !!g_hash_table_lookup(server->subscription_hashtable, k); -} diff --git a/subscribe.h b/subscribe.h deleted file mode 100644 index 2cb1528..0000000 --- a/subscribe.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef foosubscribehfoo -#define foosubscribehfoo - -#include "llist.h" -#include "avahi.h" -#include "subscribe.h" -#include "timeeventq.h" -#include "server.h" - -struct _AvahiSubscription { - AvahiServer *server; - AvahiKey *key; - gint interface; - guchar protocol; - gint n_query; - guint sec_delay; - - AvahiTimeEvent *time_event; - - AvahiSubscriptionCallback callback; - gpointer userdata; - - AVAHI_LLIST_FIELDS(AvahiSubscription, subscriptions); - AVAHI_LLIST_FIELDS(AvahiSubscription, by_key); -}; - -void avahi_subscription_notify(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, AvahiSubscriptionEvent event); - -gboolean avahi_is_subscribed(AvahiServer *s, AvahiKey *k); - -#endif diff --git a/timeeventq.c b/timeeventq.c deleted file mode 100644 index fc84157..0000000 --- a/timeeventq.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "timeeventq.h" -#include "util.h" - -static gint compare(gconstpointer _a, gconstpointer _b) { - const AvahiTimeEvent *a = _a, *b = _b; - - return avahi_timeval_compare(&a->expiry, &b->expiry); -} - -static gboolean prepare_func(GSource *source, gint *timeout) { - AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source; - AvahiTimeEvent *e; - GTimeVal now; - - g_assert(source); - g_assert(timeout); - - if (!q->prioq->root) { - *timeout = -1; - return FALSE; - } - - e = q->prioq->root->data; - g_assert(e); - - g_source_get_current_time(source, &now); - - if (avahi_timeval_compare(&now, &e->expiry) >= 0) { - *timeout = -1; - return TRUE; - } - - *timeout = (gint) (avahi_timeval_diff(&e->expiry, &now)/1000); - - return FALSE; -} - -static gboolean check_func(GSource *source) { - AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source; - AvahiTimeEvent *e; - GTimeVal now; - - g_assert(source); - - if (!q->prioq->root) - return FALSE; - - e = q->prioq->root->data; - g_assert(e); - - g_source_get_current_time(source, &now); - - return avahi_timeval_compare(&now, &e->expiry) >= 0; -} - -static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) { - AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source; - GTimeVal now; - - g_assert(source); - - g_source_get_current_time(source, &now); - - while (q->prioq->root) { - AvahiTimeEvent *e = q->prioq->root->data; - - if (avahi_timeval_compare(&now, &e->expiry) < 0) - break; - - g_assert(e->callback); - e->callback(e, e->userdata); - } - - return TRUE; -} - -AvahiTimeEventQueue* avahi_time_event_queue_new(GMainContext *context, gint priority) { - AvahiTimeEventQueue *q; - - static GSourceFuncs source_funcs = { - prepare_func, - check_func, - dispatch_func, - NULL, - NULL, - NULL - }; - - q = (AvahiTimeEventQueue*) g_source_new(&source_funcs, sizeof(AvahiTimeEventQueue)); - q->prioq = avahi_prio_queue_new(compare); - - g_source_set_priority((GSource*) q, priority); - - g_source_attach(&q->source, context); - - return q; -} - -void avahi_time_event_queue_free(AvahiTimeEventQueue *q) { - g_assert(q); - - while (q->prioq->root) - avahi_time_event_queue_remove(q, q->prioq->root->data); - avahi_prio_queue_free(q->prioq); - - g_source_destroy(&q->source); - g_source_unref(&q->source); -} - -AvahiTimeEvent* avahi_time_event_queue_add(AvahiTimeEventQueue *q, const GTimeVal *timeval, void (*callback)(AvahiTimeEvent *e, void *userdata), void *userdata) { - AvahiTimeEvent *e; - - g_assert(q); - g_assert(timeval); - g_assert(callback); - g_assert(userdata); - - e = g_new(AvahiTimeEvent, 1); - e->queue = q; - e->expiry = *timeval; - e->callback = callback; - e->userdata = userdata; - - e->node = avahi_prio_queue_put(q->prioq, e); - - return e; -} - -void avahi_time_event_queue_remove(AvahiTimeEventQueue *q, AvahiTimeEvent *e) { - g_assert(q); - g_assert(e); - g_assert(e->queue == q); - - avahi_prio_queue_remove(q->prioq, e->node); - g_free(e); -} - -void avahi_time_event_queue_update(AvahiTimeEventQueue *q, AvahiTimeEvent *e, const GTimeVal *timeval) { - g_assert(q); - g_assert(e); - g_assert(e->queue == q); - - e->expiry = *timeval; - - avahi_prio_queue_shuffle(q->prioq, e->node); -} - -AvahiTimeEvent* avahi_time_event_queue_root(AvahiTimeEventQueue *q) { - g_assert(q); - - return q->prioq->root ? q->prioq->root->data : NULL; -} - -AvahiTimeEvent* avahi_time_event_next(AvahiTimeEvent *e) { - g_assert(e); - - return e->node->next->data; -} - - diff --git a/timeeventq.h b/timeeventq.h deleted file mode 100644 index 69ff860..0000000 --- a/timeeventq.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef footimeeventqhfoo -#define footimeeventqhfoo - -typedef struct _AvahiTimeEventQueue AvahiTimeEventQueue; -typedef struct _AvahiTimeEvent AvahiTimeEvent; - -#include "prioq.h" - -struct _AvahiTimeEvent { - AvahiTimeEventQueue *queue; - AvahiPrioQueueNode *node; - GTimeVal expiry; - void (*callback)(AvahiTimeEvent *e, void *userdata); - void *userdata; -}; - -struct _AvahiTimeEventQueue { - GSource source; - AvahiPrioQueue *prioq; -}; - -AvahiTimeEventQueue* avahi_time_event_queue_new(GMainContext *context, gint priority); -void avahi_time_event_queue_free(AvahiTimeEventQueue *q); - -AvahiTimeEvent* avahi_time_event_queue_add(AvahiTimeEventQueue *q, const GTimeVal *timeval, void (*callback)(AvahiTimeEvent *e, void *userdata), void *userdata); -void avahi_time_event_queue_remove(AvahiTimeEventQueue *q, AvahiTimeEvent *e); - -void avahi_time_event_queue_update(AvahiTimeEventQueue *q, AvahiTimeEvent *e, const GTimeVal *timeval); - -AvahiTimeEvent* avahi_time_event_queue_root(AvahiTimeEventQueue *q); -AvahiTimeEvent* avahi_time_event_next(AvahiTimeEvent *e); - - - - -#endif diff --git a/util.c b/util.c deleted file mode 100644 index 270a48f..0000000 --- a/util.c +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "util.h" - -gchar *avahi_get_host_name(void) { -#ifdef HOST_NAME_MAX - char t[HOST_NAME_MAX]; -#else - char t[256]; -#endif - gethostname(t, sizeof(t)); - t[sizeof(t)-1] = 0; - return avahi_normalize_name(t); -} - -gchar *avahi_normalize_name(const 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); -} - -gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) { - g_assert(a); - g_assert(b); - - if (a->tv_sec < b->tv_sec) - return -1; - - if (a->tv_sec > b->tv_sec) - return 1; - - if (a->tv_usec < b->tv_usec) - return -1; - - if (a->tv_usec > b->tv_usec) - return 1; - - return 0; -} - -glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b) { - g_assert(a); - g_assert(b); - - if (avahi_timeval_compare(a, b) < 0) - return avahi_timeval_diff(b, a); - - return ((glong) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec; -} - - -gint avahi_set_cloexec(gint fd) { - gint n; - - g_assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFD)) < 0) - return -1; - - if (n & FD_CLOEXEC) - return 0; - - return fcntl(fd, F_SETFD, n|FD_CLOEXEC); -} - -gint avahi_set_nonblock(gint fd) { - gint n; - - g_assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFL)) < 0) - return -1; - - if (n & O_NONBLOCK) - return 0; - - return fcntl(fd, F_SETFL, n|O_NONBLOCK); -} - -gint avahi_wait_for_write(gint fd) { - fd_set fds; - gint r; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) { - g_message("select() failed: %s", strerror(errno)); - - return -1; - } - - g_assert(r > 0); - - return 0; -} - -GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) { - g_assert(tv); - - g_get_current_time(tv); - - if (msec) - g_time_val_add(tv, msec*1000); - - if (jitter) - g_time_val_add(tv, g_random_int_range(0, jitter) * 1000); - - return tv; -} - -gint avahi_age(const GTimeVal *a) { - GTimeVal now; - - g_assert(a); - - g_get_current_time(&now); - - return avahi_timeval_diff(&now, a); -} - -gboolean avahi_domain_cmp(const gchar *a, const gchar *b) { - int escaped_a = 0, escaped_b = 0; - g_assert(a); - g_assert(b); - - for (;;) { - /* Check for escape characters "\" */ - if ((escaped_a = *a == '\\')) - a ++; - - if ((escaped_b = *b == '\\')) - b++; - - /* Check for string end */ - if (*a == 0 && *b == 0) - return 0; - - if (*a == 0 && !escaped_b && *b == '.' && *(b+1) == 0) - return 0; - - if (!escaped_a && *a == '.' && *(a+1) == 0 && *b == 0) - return 0; - - /* Compare characters */ - if (escaped_a == escaped_b && *a != *b) - return *a < *b ? -1 : 1; - - /* Next characters */ - a++; - b++; - - } -} - -gboolean avahi_domain_equal(const gchar *a, const gchar *b) { - return avahi_domain_cmp(a, b) == 0; -} - -guint avahi_domain_hash(const gchar *p) { - char t[256]; - strncpy(t, p, sizeof(t)-1); - t[sizeof(t)-1] = 0; - - return g_int_hash(t); -} - -void avahi_hexdump(gconstpointer p, guint size) { - const guint8 *c = p; - g_assert(p); - - printf("Dumping %u bytes from %p:\n", size, p); - - while (size > 0) { - guint i; - - for (i = 0; i < 16; i++) { - if (i < size) - printf("%02x ", c[i]); - else - printf(" "); - } - - for (i = 0; i < 16; i++) { - if (i < size) - printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.'); - else - printf(" "); - } - - printf("\n"); - - c += 16; - - if (size <= 16) - break; - - size -= 16; - } -} diff --git a/util.h b/util.h deleted file mode 100644 index 7755ee2..0000000 --- a/util.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef fooutilhfoo -#define fooutilhfoo - -#include - -gchar *avahi_normalize_name(const gchar *s); /* g_free() the result! */ -gchar *avahi_get_host_name(void); /* g_free() the result! */ - -gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b); -glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b); - -gint avahi_set_cloexec(gint fd); -gint avahi_set_nonblock(gint fd); -gint avahi_wait_for_write(gint fd); - -GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter); - -gint avahi_age(const GTimeVal *a); - -guint avahi_domain_hash(const gchar *p); -gboolean avahi_domain_cmp(const gchar *a, const gchar *b); -gboolean avahi_domain_equal(const gchar *a, const gchar *b); - -void avahi_hexdump(gconstpointer p, guint size); - -#endif -- cgit