if (a->n_iteration >= 4) {
/* Probing done */
- gchar *t;
+/* gchar *t; */
/* g_message("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record)); */
/* g_free(t); */
static void create_entries(gboolean new_name);
static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) {
- g_message("=======> entry group state: %i", state);
+ g_message("entry group state: %i", state);
if (state == AVAHI_ENTRY_GROUP_COLLISION) {
remove_entries();
create_entries(TRUE);
+ g_message("Service name conflict, retrying with <%s>", service_name);
+ } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+ g_message("Service established under name <%s>", service_name);
}
}
static void server_callback(AvahiServer *s, AvahiServerState state, gpointer userdata) {
- g_message("=======> server state: %i", state);
- if (state == AVAHI_SERVER_RUNNING)
+ g_message("server state: %i", state);
+
+ if (state == AVAHI_SERVER_RUNNING) {
+ g_message("Server startup complete. Host name is <%s>", avahi_server_get_host_name_fqdn(s));
create_entries(FALSE);
- else if (state == AVAHI_SERVER_COLLISION) {
+ } else if (state == AVAHI_SERVER_COLLISION) {
gchar *n;
remove_entries();
n = avahi_alternative_host_name(avahi_server_get_host_name(s));
+
+ g_message("Host name conflict, retrying with <%s>", n);
avahi_server_set_host_name(s, n);
g_free(n);
}
if (!service_name)
service_name = g_strdup("Test Service");
else if (new_name) {
- gchar *n = avahi_alternative_service_name(avahi_server_get_host_name(server));
+ gchar *n = avahi_alternative_service_name(service_name);
g_free(service_name);
service_name = n;
}
}
int main(int argc, char *argv[]) {
GMainLoop *loop = NULL;
-/* AvahiSubscription *s; */
-/* AvahiKey *k; */
-
- server = avahi_server_new(NULL, NULL, server_callback, NULL);
+ AvahiSubscription *s;
+ AvahiKey *k;
+ AvahiServerConfig config;
+
+ avahi_server_config_init(&config);
+ config.host_name = g_strdup("test");
+ server = avahi_server_new(NULL, &config, server_callback, NULL);
+ avahi_server_config_free(&config);
-/* k = avahi_key_new("HALLO", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); */
-/* s = avahi_subscription_new(avahi, k, 0, AF_UNSPEC, subscription, NULL); */
-/* avahi_key_unref(k); */
+ k = avahi_key_new("_http._tcp.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
+ s = avahi_subscription_new(server, k, 0, AF_UNSPEC, subscription, NULL);
+ avahi_key_unref(k);
loop = g_main_loop_new(NULL, FALSE);
- g_timeout_add(1000*5, dump_timeout, server);
+/* g_timeout_add(1000*5, dump_timeout, server); */
/* g_timeout_add(1000*30, quit_timeout, loop); */
g_main_loop_run(loop);
g_main_loop_unref(loop);
-/* avahi_subscription_free(s); */
+ avahi_subscription_free(s);
if (group)
avahi_entry_group_free(group);
if (e->state == AVAHI_CACHE_FINAL) {
remove_entry(e->cache, e);
- g_message("Removing entry from cache due to expiration");
+/* g_message("Removing entry from cache due to expiration"); */
} else {
guint percent = 0;
/* Request a cache update, if we are subscribed to this entry */
if (avahi_is_subscribed(e->cache->server, e->record->key)) {
- g_message("Requesting cache entry update at %i%%.", percent);
+/* g_message("Requesting cache entry update at %i%%.", percent); */
avahi_interface_post_query(e->cache->interface, e->record->key, TRUE);
}
AVAHI_SERVER_INVALID = -1, /**< Invalid state (initial) */
AVAHI_SERVER_REGISTERING = 0, /**< Host RRs are being registered */
AVAHI_SERVER_RUNNING, /**< All host RRs have been established */
- AVAHI_SERVER_COLLISION /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */
+ AVAHI_SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */
+ AVAHI_SERVER_SLEEPING /**< The host or domain name has changed and the server waits for old entries to be expired */
} AvahiServerState;
/** Flags for server entries */
gboolean register_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */
gboolean register_addresses; /**< Register A, AAAA and PTR records for all local IP addresses */
gboolean check_response_ttl; /**< If enabled the server ignores all incoming responses with IP TTL != 255 */
+ gboolean announce_domain; /**< Announce the local domain for browsing */
+ gboolean use_iff_running; /**< Require IFF_RUNNING on local network interfaces. This is the official way to check for link beat. Unfortunately this doesn't work with all drivers. So bettere leave this off. */
} AvahiServerConfig;
/** Allocate a new mDNS responder object. */
g_assert(m);
g_assert(a);
-
if (avahi_interface_address_relevant(a) &&
!remove &&
m->server->config.register_addresses &&
(m->server->state == AVAHI_SERVER_RUNNING ||
m->server->state == AVAHI_SERVER_REGISTERING)) {
-
+
if (!a->entry_group) {
a->entry_group = avahi_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address);
static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean remove) {
AvahiInterfaceAddress *a;
+
g_assert(m);
g_assert(i);
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_flags = NLM_F_ROOT/*|NLM_F_MATCH*/|NLM_F_REQUEST;
n->nlmsg_pid = 0;
gen = NLMSG_DATA(n);
b = avahi_interface_relevant(i);
if (b && !i->announcing) {
- g_message("New relevant interface %s.%i", i->hardware->name, i->protocol);
+ g_message("New relevant interface %s.%i (#%i)", i->hardware->name, i->protocol, i->hardware->index);
if (i->protocol == AF_INET)
- avahi_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4);
+ 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);
+ avahi_mdns_mcast_join_ipv6(i->hardware->index, m->server->fd_ipv6);
i->announcing = TRUE;
avahi_announce_interface(m->server, i);
g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol);
if (i->protocol == AF_INET)
- avahi_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4);
+ 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);
+ avahi_mdns_mcast_leave_ipv6(i->hardware->index, m->server->fd_ipv6);
avahi_goodbye_interface(m->server, i, FALSE);
avahi_response_scheduler_clear(i->response_scheduler);
struct rtattr *a = NULL;
size_t l;
AvahiAddress raddr;
- int raddr_valid = 0;
+ gboolean raddr_valid = FALSE;
if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
return;
raddr.family = ifaddrmsg->ifa_family;
- l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+ l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
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) ||
return;
memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
- raddr_valid = 1;
+ raddr_valid = TRUE;
break;
a = RTA_NEXT(a, l);
}
-
if (!raddr_valid)
return;
}
gboolean avahi_interface_relevant(AvahiInterface *i) {
+ AvahiInterfaceAddress *a;
+ gboolean relevant_address;
+
g_assert(i);
+ relevant_address = FALSE;
+
+ for (a = i->addresses; a; a = a->address_next)
+ if (avahi_interface_address_relevant(a)) {
+ relevant_address = TRUE;
+ break;
+ }
+
+/* g_message("%p. iface-relevant: %i %i %i %i %i %i", i, relevant_address, */
+/* (i->hardware->flags & IFF_UP), */
+/* (i->hardware->flags & IFF_RUNNING), */
+/* !(i->hardware->flags & IFF_LOOPBACK), */
+/* (i->hardware->flags & IFF_MULTICAST), */
+/* !(i->hardware->flags & IFF_POINTOPOINT)); */
+
return
(i->hardware->flags & IFF_UP) &&
- (i->hardware->flags & IFF_RUNNING) &&
+ (!i->monitor->server->config.use_iff_running || (i->hardware->flags & IFF_RUNNING)) &&
!(i->hardware->flags & IFF_LOOPBACK) &&
(i->hardware->flags & IFF_MULTICAST) &&
- i->addresses;
+ !(i->hardware->flags & IFF_POINTOPOINT) &&
+ relevant_address;
}
gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) {
#include "socket.h"
#include "subscribe.h"
+#define AVAHI_HOST_RR_HOLDOFF_MSEC 1000
+
static void free_entry(AvahiServer*s, AvahiEntry *e) {
AvahiEntry *t;
gboolean unicast_response, flush_cache, auxiliary;
AvahiDnsPacket *reply = NULL;
AvahiRecord *r;
+
+ /* In case the query packet was truncated never respond
+ immediately, because known answer suppression records might be
+ contained in later packets */
+ gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
-
- if (!avahi_interface_post_response(i, r, flush_cache, a, flush_cache && !auxiliary) && unicast_response) {
+ if (!avahi_interface_post_response(i, r, flush_cache, a, !tc && flush_cache && !auxiliary) && unicast_response) {
append_aux_records_to_list(s, i, r, unicast_response);
avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
AvahiRecord *record;
gboolean cache_flush = FALSE;
- gchar *txt;
+/* gchar *txt; */
if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
g_warning("Packet too short (4)");
struct sockaddr_in6 sa6;
struct sockaddr_in sa;
AvahiDnsPacket *p;
- gint iface = -1;
+ gint iface = 0;
guint8 ttl;
g_assert(s);
s->hinfo_entry_group = NULL;
}
+ if (s->browse_domain_entry_group) {
+ avahi_entry_group_free(s->browse_domain_entry_group);
+ s->browse_domain_entry_group = NULL;
+ }
+
avahi_update_host_rrs(s->monitor, TRUE);
s->n_host_rr_pending = 0;
}
if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
s->state == AVAHI_SERVER_REGISTERING)
avahi_server_increase_host_rr_pending(s);
- else if (state == AVAHI_ENTRY_GROUP_COLLISION) {
+ else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
+ (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
withdraw_host_rrs(s);
server_set_state(s, AVAHI_SERVER_COLLISION);
} else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
}
+static void register_browse_domain(AvahiServer *s) {
+ g_assert(s);
+
+ if (!s->config.announce_domain || s->browse_domain_entry_group)
+ return;
+
+ s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
+ avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
+ avahi_entry_group_commit(s->browse_domain_entry_group);
+}
+
static void register_stuff(AvahiServer *s) {
g_assert(s);
server_set_state(s, AVAHI_SERVER_REGISTERING);
register_hinfo(s);
+ register_browse_domain(s);
avahi_update_host_rrs(s->monitor, FALSE);
if (s->n_host_rr_pending == 0)
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);
+}
+
void 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);
- register_stuff(s);
+ delayed_register_stuff(s);
}
void 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);
- register_stuff(s);
+ delayed_register_stuff(s);
}
AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
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;
s->monitor = avahi_interface_monitor_new(s);
register_localhost(s);
s->hinfo_entry_group = NULL;
+ s->browse_domain_entry_group = NULL;
register_stuff(s);
return s;
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);
while (domain[0] == '.')
domain++;
} else
- domain = "local";
+ domain = s->domain_name;
if (!host)
host = s->host_name_fqdn;
}
void avahi_entry_group_free(AvahiEntryGroup *g) {
+ AvahiEntry *e;
+
g_assert(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;
+ }
+
g->dead = TRUE;
+
g->server->need_group_cleanup = TRUE;
+ g->server->need_entry_cleanup = TRUE;
}
void avahi_entry_group_commit(AvahiEntryGroup *g) {
c->host_name = NULL;
c->domain_name = NULL;
c->check_response_ttl = TRUE;
-
+ c->announce_domain = TRUE;
+ c->use_iff_running = FALSE;
+
return c;
}
void avahi_server_config_free(AvahiServerConfig *c) {
g_assert(c);
- g_assert(c->host_name);
- g_assert(c->domain_name);
- g_free(c);
+ g_free(c->host_name);
+ g_free(c->domain_name);
}
AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
gpointer userdata;
AvahiEntryGroup *hinfo_entry_group;
+ AvahiEntryGroup *browse_domain_entry_group;
guint n_host_rr_pending;
+
+ AvahiTimeEvent *register_time_event;
/* Used for assembling responses */
AvahiRecordList *record_list;
AvahiDnsPacket *p= NULL;
struct msghdr msg;
struct iovec io;
- uint8_t aux[64];
+ uint8_t aux[1024];
ssize_t l;
struct cmsghdr *cmsg;
gboolean found_ttl = FALSE, found_iface = FALSE;
msg.msg_controllen = sizeof(aux);
msg.msg_flags = 0;
- if ((l = recvmsg(fd, &msg, 0)) < 0)
+ if ((l = recvmsg(fd, &msg, 0)) < 0) {
+ g_warning("recvmsg(): %s\n", strerror(errno));
goto fail;
+ }
+ if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
+ /* Linux 2.4 behaves very strangely sometimes! */
+ goto fail;
+ }
+
+ g_assert(!(msg.msg_flags & MSG_CTRUNC));
+ g_assert(!(msg.msg_flags & MSG_TRUNC));
p->size = (size_t) l;
*ret_ttl = 0;
+
+/* avahi_hexdump(msg.msg_control, msg.msg_controllen); */
- 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;
- }
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+/* avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
+
+ if (cmsg->cmsg_level == SOL_IP) {
- if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) {
- *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
- found_iface = TRUE;
+ if (cmsg->cmsg_type == IP_TTL) {
+ *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
+ found_ttl = TRUE;
+ } else if (cmsg->cmsg_type == IP_PKTINFO) {
+ *ret_iface = (gint) ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
+ found_iface = TRUE;
+ }
}
}
+/* g_message("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
+
g_assert(found_iface);
g_assert(found_ttl);
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);
+ *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
found_ttl = TRUE;
}
static void elapse(AvahiTimeEvent *e, void *userdata) {
AvahiSubscription *s = userdata;
GTimeVal tv;
- gchar *t;
+/* gchar *t; */
g_assert(s);
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);
+/* 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);