+++ /dev/null
-#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
+++ /dev/null
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <string.h>
-
-#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);
-}
+++ /dev/null
-#ifndef fooaddresshfoo
-#define fooaddresshfoo
-
-#include <glib.h>
-
-#include <sys/socket.h>
-
-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
+++ /dev/null
-#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"); */
-
-}
-
+++ /dev/null
-#ifndef fooannouncehfoo
-#define fooannouncehfoo
-
-#include <glib.h>
-
-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
+++ /dev/null
-#ifndef fooAvahihfoo
-#define fooAvahihfoo
-
-#include <stdio.h>
-#include <glib.h>
-
-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
+++ /dev/null
-#include <string.h>
-
-#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;
-}
+++ /dev/null
-#ifndef foocachehfoo
-#define foocachehfoo
-
-#include <glib.h>
-
-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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#include <netinet/in.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#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;
-}
+++ /dev/null
-#ifndef foodnshfoo
-#define foodnshfoo
-
-#include <glib.h>
-
-#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
-
+++ /dev/null
-#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;
-}
+++ /dev/null
-#include <string.h>
-#include <sys/socket.h>
-#include <asm/types.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <errno.h>
-#include <net/if.h>
-
-#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);
- }
-}
+++ /dev/null
-#ifndef fooifacehfoo
-#define fooifacehfoo
-
-#include <glib.h>
-
-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
--- /dev/null
+#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
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#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);
+}
--- /dev/null
+#ifndef fooaddresshfoo
+#define fooaddresshfoo
+
+#include <glib.h>
+
+#include <sys/socket.h>
+
+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
--- /dev/null
+#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"); */
+
+}
+
--- /dev/null
+#ifndef fooannouncehfoo
+#define fooannouncehfoo
+
+#include <glib.h>
+
+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
--- /dev/null
+#ifndef fooAvahihfoo
+#define fooAvahihfoo
+
+#include <stdio.h>
+#include <glib.h>
+
+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
--- /dev/null
+#include <string.h>
+
+#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;
+}
--- /dev/null
+#ifndef foocachehfoo
+#define foocachehfoo
+
+#include <glib.h>
+
+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
--- /dev/null
+#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;
+}
--- /dev/null
+#include <netinet/in.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#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;
+}
--- /dev/null
+#ifndef foodnshfoo
+#define foodnshfoo
+
+#include <glib.h>
+
+#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
+
--- /dev/null
+#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;
+}
--- /dev/null
+#include <string.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <errno.h>
+#include <net/if.h>
+
+#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);
+ }
+}
--- /dev/null
+#ifndef fooifacehfoo
+#define fooifacehfoo
+
+#include <glib.h>
+
+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
--- /dev/null
+#ifndef foollistfoo
+#define foollistfoo
+
+#include <glib.h>
+
+/* 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
--- /dev/null
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#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;
+}
--- /dev/null
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#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;
+}
--- /dev/null
+#ifndef foonetlinkhfoo
+#define foonetlinkhfoo
+
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include <glib.h>
+
+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
--- /dev/null
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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;
+}
--- /dev/null
+#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--;
+}
+
--- /dev/null
+#ifndef fooprioqhfoo
+#define fooprioqhfoo
+
+#include <glib.h>
+
+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
--- /dev/null
+#include <string.h>
+
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#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 : "<unparsable>", 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);
+ }
+
+}
--- /dev/null
+#ifndef foorrhfoo
+#define foorrhfoo
+
+#include <glib.h>
+
+#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
--- /dev/null
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#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;
+}
+
--- /dev/null
+#ifndef foosockethfoo
+#define foosockethfoo
+
+#include <netinet/in.h>
+
+#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
--- /dev/null
+#include <glib.h>
+#include <stdio.h>
+
+#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;
+}
--- /dev/null
+#include <string.h>
+#include <stdarg.h>
+
+#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);
+}
--- /dev/null
+#ifndef footxtlisthfoo
+#define footxtlisthfoo
+
+#include <glib.h>
+
+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
+
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#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;
+ }
+}
--- /dev/null
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+#include <glib.h>
+
+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
+++ /dev/null
-#ifndef foollistfoo
-#define foollistfoo
-
-#include <glib.h>
-
-/* 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
+++ /dev/null
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-
-#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;
-}
+++ /dev/null
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#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;
-}
+++ /dev/null
-#ifndef foonetlinkhfoo
-#define foonetlinkhfoo
-
-#include <sys/socket.h>
-#include <asm/types.h>
-#include <linux/netlink.h>
-
-#include <glib.h>
-
-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
+++ /dev/null
-#include <time.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#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;
-}
+++ /dev/null
-#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--;
-}
-
+++ /dev/null
-#ifndef fooprioqhfoo
-#define fooprioqhfoo
-
-#include <glib.h>
-
-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
+++ /dev/null
-#include <string.h>
-
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
-#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 : "<unparsable>", 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);
- }
-
-}
+++ /dev/null
-#ifndef foorrhfoo
-#define foorrhfoo
-
-#include <glib.h>
-
-#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
+++ /dev/null
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <sys/utsname.h>
-#include <unistd.h>
-
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#include <inttypes.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/time.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
-
-#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;
-}
-
+++ /dev/null
-#ifndef foosockethfoo
-#define foosockethfoo
-
-#include <netinet/in.h>
-
-#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
+++ /dev/null
-#include <glib.h>
-#include <stdio.h>
-
-#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;
-}
+++ /dev/null
-#include <string.h>
-#include <stdarg.h>
-
-#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);
-}
+++ /dev/null
-#ifndef footxtlisthfoo
-#define footxtlisthfoo
-
-#include <glib.h>
-
-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
-
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
-
+++ /dev/null
-#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
+++ /dev/null
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-
-#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;
- }
-}
+++ /dev/null
-#ifndef fooutilhfoo
-#define fooutilhfoo
-
-#include <glib.h>
-
-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