+#define RESOLV_CONF "/etc/resolv.conf"
+#define BROWSE_DOMAINS_MAX 16
+
+static AvahiSEntryGroup *dns_servers_entry_group = NULL;
+static AvahiSEntryGroup *resolv_conf_entry_group = NULL;
+
+static char **resolv_conf_name_servers = NULL;
+static char **resolv_conf_search_domains = NULL;
+
+static DaemonConfig config;
+
+static int has_prefix(const char *s, const char *prefix) {
+ size_t l;
+
+ l = strlen(prefix);
+
+ return strlen(s) >= l && strncmp(s, prefix, l) == 0;
+}
+
+static int load_resolv_conf(void) {
+ int ret = -1;
+ FILE *f;
+ int i = 0, j = 0;
+
+ avahi_strfreev(resolv_conf_name_servers);
+ resolv_conf_name_servers = NULL;
+
+ avahi_strfreev(resolv_conf_search_domains);
+ resolv_conf_search_domains = NULL;
+
+#ifdef ENABLE_CHROOT
+ f = avahi_chroot_helper_get_file(RESOLV_CONF);
+#else
+ f = fopen(RESOLV_CONF, "r");
+#endif
+
+ if (!f) {
+ avahi_log_warn("Failed to open "RESOLV_CONF": %s", strerror(errno));
+ goto finish;
+ }
+
+ resolv_conf_name_servers = avahi_new0(char*, AVAHI_WIDE_AREA_SERVERS_MAX+1);
+ resolv_conf_search_domains = avahi_new0(char*, BROWSE_DOMAINS_MAX+1);
+
+ while (!feof(f)) {
+ char ln[128];
+ char *p;
+
+ if (!(fgets(ln, sizeof(ln), f)))
+ break;
+
+ ln[strcspn(ln, "\r\n#")] = 0;
+ p = ln + strspn(ln, "\t ");
+
+ if ((has_prefix(p, "nameserver ") || has_prefix(p, "nameserver\t")) && i < AVAHI_WIDE_AREA_SERVERS_MAX) {
+ p += 10;
+ p += strspn(p, "\t ");
+ p[strcspn(p, "\t ")] = 0;
+ resolv_conf_name_servers[i++] = avahi_strdup(p);
+ }
+
+ if ((has_prefix(p, "search ") || has_prefix(p, "search\t") ||
+ has_prefix(p, "domain ") || has_prefix(p, "domain\t"))) {
+
+ p += 6;
+
+ while (j < BROWSE_DOMAINS_MAX) {
+ size_t k;
+
+ p += strspn(p, "\t ");
+ k = strcspn(p, "\t ");
+
+ if (k > 0) {
+ resolv_conf_search_domains[j++] = avahi_strndup(p, k);
+ p += k;
+ }
+
+ if (!*p)
+ break;
+ }
+ }
+ }
+
+ ret = 0;
+
+finish:
+
+ if (ret != 0) {
+ avahi_strfreev(resolv_conf_name_servers);
+ resolv_conf_name_servers = NULL;
+
+ avahi_strfreev(resolv_conf_search_domains);
+ resolv_conf_search_domains = NULL;
+ }
+
+ if (f)
+ fclose(f);
+
+ return ret;
+}
+
+static AvahiSEntryGroup* add_dns_servers(AvahiServer *s, AvahiSEntryGroup* g, char **l) {
+ char **p;
+
+ assert(s);
+ assert(l);
+
+ if (!g)
+ g = avahi_s_entry_group_new(s, NULL, NULL);
+
+ assert(avahi_s_entry_group_is_empty(g));
+
+ for (p = l; *p; p++) {
+ AvahiAddress a;
+
+ if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a))
+ avahi_log_warn("Failed to parse address '%s', ignoring.", *p);
+ else
+ if (avahi_server_add_dns_server_address(s, g, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, &a, 53) < 0) {
+ avahi_s_entry_group_free(g);
+ avahi_log_error("Failed to add DNS server address: %s", avahi_strerror(avahi_server_errno(s)));
+ return NULL;
+ }
+ }
+
+ avahi_s_entry_group_commit(g);
+
+ return g;
+}
+
+static void remove_dns_server_entry_groups(void) {
+
+ if (resolv_conf_entry_group)
+ avahi_s_entry_group_reset(resolv_conf_entry_group);
+
+ if (dns_servers_entry_group)
+ avahi_s_entry_group_reset(dns_servers_entry_group);
+}
+
+static void update_wide_area_servers(void) {
+ AvahiAddress a[AVAHI_WIDE_AREA_SERVERS_MAX];
+ unsigned n = 0;
+ char **p;
+
+ if (!resolv_conf_name_servers) {
+ avahi_server_set_wide_area_servers(avahi_server, NULL, 0);
+ return;
+ }
+
+ for (p = resolv_conf_name_servers; *p && n < AVAHI_WIDE_AREA_SERVERS_MAX; p++) {
+ if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a[n]))
+ avahi_log_warn("Failed to parse address '%s', ignoring.", *p);
+ else
+ n++;
+ }
+
+ avahi_server_set_wide_area_servers(avahi_server, a, n);
+}
+
+static AvahiStringList *filter_duplicate_domains(AvahiStringList *l) {
+ AvahiStringList *e, *n, *p;
+
+ if (!l)
+ return l;
+
+ for (p = l, e = l->next; e; e = n) {
+ n = e->next;
+
+ if (avahi_domain_equal((char*) e->text, (char*) l->text)) {
+ p->next = e->next;
+ avahi_free(e);
+ } else
+ p = e;
+ }
+
+ l->next = filter_duplicate_domains(l->next);
+ return l;
+}
+
+static void update_browse_domains(void) {
+ AvahiStringList *l;
+ int n;
+ char **p;
+
+ if (!resolv_conf_search_domains) {
+ avahi_server_set_browse_domains(avahi_server, NULL);
+ return;
+ }
+
+ l = avahi_string_list_copy(config.server_config.browse_domains);
+
+ for (p = resolv_conf_search_domains, n = 0; *p && n < BROWSE_DOMAINS_MAX; p++, n++) {
+ if (!avahi_is_valid_domain_name(*p))
+ avahi_log_warn("'%s' is no valid domain name, ignoring.", *p);
+ else
+ l = avahi_string_list_add(l, *p);
+ }
+
+ l = filter_duplicate_domains(l);
+
+ avahi_server_set_browse_domains(avahi_server, l);
+ avahi_string_list_free(l);
+}
+
+static void server_callback(AvahiServer *s, AvahiServerState state, void *userdata) {
+ DaemonConfig *c = userdata;
+
+ assert(s);
+ assert(c);
+
+ /* This function is possibly called before the global variable
+ * avahi_server has been set, therefore we do it explicitly */
+
+ avahi_server = s;
+
+#ifdef HAVE_DBUS
+ if (c->enable_dbus && state != AVAHI_SERVER_INVALID && state != AVAHI_SERVER_FAILURE)
+ dbus_protocol_server_state_changed(state);
+#endif
+
+ switch (state) {
+ case AVAHI_SERVER_RUNNING:
+ avahi_log_info("Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s));
+
+ avahi_set_proc_title(argv0, "%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s));
+
+ static_service_add_to_server();
+ static_hosts_add_to_server();
+
+ remove_dns_server_entry_groups();