#include <sys/utsname.h>
#include <unistd.h>
#include <errno.h>
+#include <stdio.h>
#include "server.h"
#include "util.h"
#include "browse.h"
#include "log.h"
-#define AVAHI_HOST_RR_HOLDOFF_MSEC 2000
+#define AVAHI_RR_HOLDOFF_MSEC 1000
+#define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
+#define AVAHI_RR_RATE_LIMIT_COUNT 15
static void free_entry(AvahiServer*s, AvahiEntry *e) {
AvahiEntry *t;
while (g->entries)
free_entry(s, g->entries);
+ if (g->register_time_event)
+ avahi_time_event_queue_remove(s->time_event_queue, g->register_time_event);
+
AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
g_free(g);
}
AvahiEntry *k;
for (k = e->group->entries; k; k = k->by_group_next) {
- avahi_goodbye_entry(s, k, FALSE);
- k->dead = TRUE;
+ if (!k->dead) {
+ avahi_goodbye_entry(s, k, FALSE);
+ k->dead = TRUE;
+ }
}
-
+
+ e->group->n_probing = 0;
+
avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
} else {
avahi_goodbye_entry(s, e, FALSE);
}
static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
- guint n, index = (guint) -1;
+ guint n, idx = (guint) -1;
AvahiLegacyUnicastReflectSlot *slot;
g_assert(s);
s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
- index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
+ idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
- if (!s->legacy_unicast_reflect_slots[index])
+ if (!s->legacy_unicast_reflect_slots[idx])
break;
}
- if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
+ if (idx == (guint) -1 || s->legacy_unicast_reflect_slots[idx])
return NULL;
- slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
+ slot = s->legacy_unicast_reflect_slots[idx] = g_new(AvahiLegacyUnicastReflectSlot, 1);
slot->id = s->legacy_unicast_reflect_id++;
slot->server = s;
return slot;
}
static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
- guint index;
+ guint idx;
g_assert(s);
g_assert(slot);
- index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
+ idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
- g_assert(s->legacy_unicast_reflect_slots[index] == slot);
+ g_assert(s->legacy_unicast_reflect_slots[idx] == slot);
avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
g_free(slot);
- s->legacy_unicast_reflect_slots[index] = NULL;
+ s->legacy_unicast_reflect_slots[idx] = NULL;
}
static void free_slots(AvahiServer *s) {
- guint index;
+ guint idx;
g_assert(s);
if (!s->legacy_unicast_reflect_slots)
return;
- for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
- if (s->legacy_unicast_reflect_slots[index])
- deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
+ for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
+ if (s->legacy_unicast_reflect_slots[idx])
+ deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
g_free(s->legacy_unicast_reflect_slots);
s->legacy_unicast_reflect_slots = NULL;
}
static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
- guint index;
+ guint idx;
g_assert(s);
if (!s->legacy_unicast_reflect_slots)
return NULL;
- index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
+ idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
- if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
+ if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
return NULL;
- return s->legacy_unicast_reflect_slots[index];
+ return s->legacy_unicast_reflect_slots[idx];
}
static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
if (!s->config.enable_reflector)
return;
-/* avahi_log_debug("legacy unicast reflectr"); */
+/* avahi_log_debug("legacy unicast reflector"); */
/* Reflecting legacy unicast queries is a little more complicated
than reflecting normal queries, since we must route the
if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
avahi_log_warn("getsockname(): %s", strerror(errno));
else
- return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
+ return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
}
if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
avahi_log_warn("getsockname(): %s", strerror(errno));
else
- return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
+ return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
}
return FALSE;
static void withdraw_host_rrs(AvahiServer *s) {
g_assert(s);
- if (s->hinfo_entry_group) {
- avahi_entry_group_free(s->hinfo_entry_group);
- s->hinfo_entry_group = NULL;
- }
+ if (s->hinfo_entry_group)
+ avahi_entry_group_reset(s->hinfo_entry_group);
- if (s->browse_domain_entry_group) {
- avahi_entry_group_free(s->browse_domain_entry_group);
- s->browse_domain_entry_group = NULL;
- }
+ if (s->browse_domain_entry_group)
+ avahi_entry_group_reset(s->browse_domain_entry_group);
avahi_update_host_rrs(s->monitor, TRUE);
s->n_host_rr_pending = 0;
g_assert(s);
- if (!s->config.publish_hinfo || s->hinfo_entry_group)
+ if (!s->config.publish_hinfo)
return;
-
- s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
+
+ if (s->hinfo_entry_group)
+ g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
+ else
+ s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
/* Fill in HINFO rr */
r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
static void register_browse_domain(AvahiServer *s) {
g_assert(s);
- if (!s->config.publish_domain || s->browse_domain_entry_group)
+ if (!s->config.publish_domain)
return;
- s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
+ if (s->browse_domain_entry_group)
+ g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
+ else
+ s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
+
avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name);
avahi_entry_group_commit(s->browse_domain_entry_group);
}
s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
}
-static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
- AvahiServer *s = userdata;
-
- g_assert(e);
- g_assert(s);
-
- g_assert(e == s->register_time_event);
- avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
- s->register_time_event = NULL;
-
- if (s->state == AVAHI_SERVER_SLEEPING)
- register_stuff(s);
-}
-
-static void delayed_register_stuff(AvahiServer *s) {
- GTimeVal tv;
-
- g_assert(s);
-
- avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
-
- if (s->register_time_event)
- avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
- else
- s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
-}
-
gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
g_assert(s);
g_assert(host_name);
- server_set_state(s, AVAHI_SERVER_SLEEPING);
withdraw_host_rrs(s);
g_free(s->host_name);
s->host_name[strcspn(s->host_name, ".")] = 0;
update_fqdn(s);
- delayed_register_stuff(s);
- return 0;
+ register_stuff(s);
+ return AVAHI_OK;
}
gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
g_assert(s);
g_assert(domain_name);
- server_set_state(s, AVAHI_SERVER_SLEEPING);
withdraw_host_rrs(s);
g_free(s->domain_name);
s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
update_fqdn(s);
- delayed_register_stuff(s);
- return 0;
+ register_stuff(s);
+ return AVAHI_OK;
}
-
static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
g_assert(s);
g_assert(pollfd);
s->record_list = avahi_record_list_new();
s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
- s->register_time_event = NULL;
s->state = AVAHI_SERVER_INVALID;
g_hash_table_destroy(s->entries_by_key);
- if (s->register_time_event)
- avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
avahi_time_event_queue_free(s->time_event_queue);
avahi_record_list_free(s->record_list);
g_free(s);
}
-static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
+static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
AvahiEntry *e;
g_assert(s);
gint avahi_server_add(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
AvahiEntryFlags flags,
AvahiRecord *r) {
continue;
t = avahi_record_to_string(e->record);
- snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
+ g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
g_free(t);
callback(ln, userdata);
gint avahi_server_add_ptr(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
AvahiEntryFlags flags,
guint32 ttl,
const gchar *name,
gint avahi_server_add_address(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
AvahiEntryFlags flags,
const gchar *name,
AvahiAddress *a) {
return ret;
}
-gint avahi_server_add_txt_strlst(
+static gint server_add_txt_strlst_nocopy(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
AvahiEntryFlags flags,
guint32 ttl,
const gchar *name,
return ret;
}
+gint avahi_server_add_txt_strlst(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiEntryFlags flags,
+ guint32 ttl,
+ const gchar *name,
+ AvahiStringList *strlst) {
+
+ return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
+}
+
gint avahi_server_add_txt_va(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
AvahiEntryFlags flags,
guint32 ttl,
const gchar *name,
va_list va) {
-
+
g_assert(s);
- return avahi_server_add_txt_strlst(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
+ return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
}
gint avahi_server_add_txt(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
AvahiEntryFlags flags,
guint32 ttl,
const gchar *name,
*(d++) = 0;
}
-gint avahi_server_add_service_strlst(
+static gint server_add_service_strlst_nocopy(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
const gchar *name,
const gchar *type,
const gchar *domain,
d = avahi_normalize_name(domain);
t = avahi_normalize_name(type);
- snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
- snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
+ g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
+ g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
avahi_record_unref(r);
- ret |= avahi_server_add_txt_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
+ ret |= server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
- snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
+ g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
g_free(d);
return ret;
}
+gint avahi_server_add_service_strlst(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const gchar *name,
+ const gchar *type,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ AvahiStringList *strlst) {
+
+ return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
+}
+
gint avahi_server_add_service_va(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
const gchar *name,
const gchar *type,
const gchar *domain,
g_assert(type);
g_assert(name);
- return avahi_server_add_service_strlst(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
+ return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
}
gint avahi_server_add_service(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
const gchar *name,
const gchar *type,
const gchar *domain,
gint avahi_server_add_dns_server_address(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
const gchar *domain,
AvahiDNSServerType type,
const AvahiAddress *address,
gint avahi_server_add_dns_server_name(
AvahiServer *s,
AvahiEntryGroup *g,
- gint interface,
- guchar protocol,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
const gchar *domain,
AvahiDNSServerType type,
const gchar *name,
domain = s->domain_name;
d = avahi_normalize_name(domain);
- snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
+ g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
g_free(d);
r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
avahi_interface_post_query(i, k, FALSE);
}
-void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
+void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
g_assert(s);
g_assert(key);
g->dead = FALSE;
g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
g->n_probing = 0;
+ g->n_register_try = 0;
+ g->register_time_event = NULL;
+ g->register_time.tv_sec = 0;
+ g->register_time.tv_usec = 0;
AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
g_assert(g->server);
for (e = g->entries; e; e = e->by_group_next) {
- avahi_goodbye_entry(g->server, e, TRUE);
- e->dead = TRUE;
+ if (!e->dead) {
+ avahi_goodbye_entry(g->server, e, TRUE);
+ e->dead = TRUE;
+ }
+ }
+
+ if (g->register_time_event) {
+ avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
+ g->register_time_event = NULL;
}
g->dead = TRUE;
g->server->need_entry_cleanup = TRUE;
}
-gint avahi_entry_group_commit(AvahiEntryGroup *g) {
+static void entry_group_commit_real(AvahiEntryGroup *g) {
g_assert(g);
- g_assert(!g->dead);
-
- if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
- return -1;
avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
avahi_announce_group(g->server, g);
avahi_entry_group_check_probed(g, FALSE);
- return 0;
+ g_get_current_time(&g->register_time);
+}
+
+static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
+ AvahiEntryGroup *g = userdata;
+ g_assert(g);
+
+ avahi_log_debug("Holdoff passed, waking up and going on.");
+
+ avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
+ g->register_time_event = NULL;
+
+ /* Holdoff time passed, so let's start probing */
+ entry_group_commit_real(g);
+}
+
+gint avahi_entry_group_commit(AvahiEntryGroup *g) {
+ GTimeVal now;
+
+ g_assert(g);
+ g_assert(!g->dead);
+
+ if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
+ return AVAHI_ERR_BAD_STATE;
+
+ g->n_register_try++;
+
+ avahi_timeval_add(&g->register_time,
+ 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
+ AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
+ AVAHI_RR_HOLDOFF_MSEC));
+
+ g_get_current_time(&now);
+
+ if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
+ /* Holdoff time passed, so let's start probing */
+ avahi_log_debug("Holdoff passed, directly going on.");
+
+ entry_group_commit_real(g);
+ } else {
+ avahi_log_debug("Holdoff not passed, sleeping.");
+
+ /* Holdoff time has not yet passed, so let's wait */
+ avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_SLEEPING);
+
+ g_assert(!g->register_time_event);
+ g->register_time_event = avahi_time_event_queue_add(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
+ }
+
+ return AVAHI_OK;
+}
+
+void avahi_entry_group_reset(AvahiEntryGroup *g) {
+ AvahiEntry *e;
+ g_assert(g);
+
+ if (g->register_time_event) {
+ avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
+ g->register_time_event = NULL;
+ }
+
+ for (e = g->entries; e; e = e->by_group_next) {
+ if (!e->dead) {
+ avahi_goodbye_entry(g->server, e, TRUE);
+ e->dead = TRUE;
+ }
+ }
+
+ g->server->need_entry_cleanup = TRUE;
+ g->n_probing = 0;
+
+ avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
}
gboolean avahi_entry_commited(AvahiEntry *e) {
return g->userdata;
}
+gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
+ AvahiEntry *e;
+ g_assert(g);
+
+ /* Look for an entry that is not dead */
+ for (e = g->entries; e; e = e->by_group_next)
+ if (!e->dead)
+ return FALSE;
+
+ return TRUE;
+}
+
const gchar* avahi_server_get_domain_name(AvahiServer *s) {
g_assert(s);