+ int e;
+
+ if (sc && (e = valid_server_config(sc)) < 0) {
+ if (error)
+ *error = e;
+ return NULL;
+ }
+
+ if (!(s = avahi_new(AvahiServer, 1))) {
+ if (error)
+ *error = AVAHI_ERR_NO_MEMORY;
+
+ return NULL;
+ }
+
+ s->poll_api = poll_api;
+
+ if (sc)
+ avahi_server_config_copy(&s->config, sc);
+ else
+ avahi_server_config_init(&s->config);
+
+ if ((e = setup_sockets(s)) < 0) {
+ if (error)
+ *error = e;
+
+ avahi_server_config_free(&s->config);
+ avahi_free(s);
+
+ return NULL;
+ }
+
+
+ s->n_host_rr_pending = 0;
+ s->need_entry_cleanup = 0;
+ s->need_group_cleanup = 0;
+ s->need_browser_cleanup = 0;
+
+ s->time_event_queue = avahi_time_event_queue_new(poll_api);
+
+ s->callback = callback;
+ s->userdata = userdata;
+
+ s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
+ AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
+ AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
+
+ s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
+ AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
+
+ s->legacy_unicast_reflect_slots = NULL;
+ s->legacy_unicast_reflect_id = 0;
+
+ if (s->config.enable_wide_area) {
+ s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
+ avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
+ } else
+ s->wide_area_lookup_engine = NULL;
+
+ s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
+
+ do {
+ s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
+ } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
+
+ /* Get host name */
+ s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
+ s->host_name[strcspn(s->host_name, ".")] = 0;
+ s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
+ s->host_name_fqdn = NULL;
+ update_fqdn(s);
+
+ s->record_list = avahi_record_list_new();
+
+ s->state = AVAHI_SERVER_INVALID;
+
+ s->monitor = avahi_interface_monitor_new(s);
+ avahi_interface_monitor_sync(s->monitor);
+
+ register_localhost(s);
+
+ s->hinfo_entry_group = NULL;
+ s->browse_domain_entry_group = NULL;
+ register_stuff(s);
+
+ s->error = AVAHI_OK;
+
+ return s;
+}
+
+void avahi_server_free(AvahiServer* s) {
+ assert(s);
+
+ /* Remove all browsers */
+
+ while (s->dns_server_browsers)
+ avahi_s_dns_server_browser_free(s->dns_server_browsers);
+ while (s->host_name_resolvers)
+ avahi_s_host_name_resolver_free(s->host_name_resolvers);
+ while (s->address_resolvers)
+ avahi_s_address_resolver_free(s->address_resolvers);
+ while (s->domain_browsers)
+ avahi_s_domain_browser_free(s->domain_browsers);
+ while (s->service_type_browsers)
+ avahi_s_service_type_browser_free(s->service_type_browsers);
+ while (s->service_browsers)
+ avahi_s_service_browser_free(s->service_browsers);
+ while (s->service_resolvers)
+ avahi_s_service_resolver_free(s->service_resolvers);
+ while (s->record_browsers)
+ avahi_s_record_browser_destroy(s->record_browsers);
+
+ /* Remove all locally rgeistered stuff */
+
+ while(s->entries)
+ free_entry(s, s->entries);
+
+ avahi_interface_monitor_free(s->monitor);
+
+ while (s->groups)
+ free_group(s, s->groups);
+
+ free_slots(s);
+
+ avahi_hashmap_free(s->entries_by_key);
+ avahi_record_list_free(s->record_list);
+ avahi_hashmap_free(s->record_browser_hashmap);
+
+ if (s->wide_area_lookup_engine)
+ avahi_wide_area_engine_free(s->wide_area_lookup_engine);
+ avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
+
+ avahi_time_event_queue_free(s->time_event_queue);
+
+ /* Free watches */
+
+ if (s->watch_ipv4)
+ s->poll_api->watch_free(s->watch_ipv4);
+ if (s->watch_ipv6)
+ s->poll_api->watch_free(s->watch_ipv6);
+
+ if (s->watch_legacy_unicast_ipv4)
+ s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
+ if (s->watch_legacy_unicast_ipv6)
+ s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
+
+ /* Free sockets */
+
+ if (s->fd_ipv4 >= 0)
+ close(s->fd_ipv4);
+ if (s->fd_ipv6 >= 0)
+ close(s->fd_ipv6);
+
+ if (s->fd_legacy_unicast_ipv4 >= 0)
+ close(s->fd_legacy_unicast_ipv4);
+ if (s->fd_legacy_unicast_ipv6 >= 0)
+ close(s->fd_legacy_unicast_ipv6);
+
+ /* Free other stuff */
+
+ avahi_free(s->host_name);
+ avahi_free(s->domain_name);
+ avahi_free(s->host_name_fqdn);
+
+ avahi_server_config_free(&s->config);
+
+ avahi_free(s);
+}
+
+static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(r);
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
+ if (e->dead)
+ continue;
+
+ if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
+ continue;
+
+ if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
+ continue;
+
+ if ((interface <= 0 ||
+ e->interface <= 0 ||
+ e->interface == interface) &&
+ (protocol == AVAHI_PROTO_UNSPEC ||
+ e->protocol == AVAHI_PROTO_UNSPEC ||
+ e->protocol == protocol))
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int avahi_server_add(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ AvahiRecord *r) {
+
+ AvahiEntry *e, *t;
+
+ assert(s);
+ assert(r);
+
+ if (!AVAHI_IF_VALID(interface))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
+
+ if (!AVAHI_PROTO_VALID(protocol))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
+
+ if (!AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_UNIQUE|AVAHI_PUBLISH_ALLOW_MULTIPLE|AVAHI_PUBLISH_IS_PROXY))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
+
+ if (!avahi_is_valid_domain_name(r->key->name))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (r->ttl == 0)
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
+
+ if (avahi_key_is_pattern(r->key))
+ return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
+
+ if (!avahi_record_is_valid(r))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
+
+ if (check_record_conflict(s, interface, protocol, r, flags) < 0)
+ return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
+
+ if (!(e = avahi_new(AvahiEntry, 1)))
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ e->server = s;
+ e->record = avahi_record_ref(r);
+ e->group = g;
+ e->interface = interface;
+ e->protocol = protocol;
+ e->flags = flags;
+ e->dead = 0;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
+
+ AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
+
+ /* Insert into hash table indexed by name */
+ t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
+ AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
+ avahi_hashmap_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);
+
+ return 0;
+}
+
+const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
+ AvahiEntry **e = (AvahiEntry**) state;
+ assert(s);
+ 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);
+}
+
+int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(callback);
+
+ callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
+
+ for (e = s->entries; e; e = e->entries_next) {
+ char *t;
+ char ln[256];
+
+ if (e->dead)
+ continue;
+
+ if (!(t = avahi_record_to_string(e->record)))
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
+ avahi_free(t);
+
+ callback(ln, userdata);
+ }
+
+ avahi_dump_caches(s->monitor, callback, userdata);
+
+ if (s->wide_area_lookup_engine)
+ avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
+ return AVAHI_OK;
+}
+
+int avahi_server_add_ptr(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ const char *dest) {
+
+ AvahiRecord *r;
+ int ret;
+
+ assert(s);
+ assert(dest);
+
+ if ((name && !avahi_is_valid_domain_name(name)) || !avahi_is_valid_domain_name(dest))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (!name)
+ name = s->host_name_fqdn;
+
+ if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ r->data.ptr.name = avahi_normalize_name_strdup(dest);
+ ret = avahi_server_add(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+ return ret;
+}
+
+int avahi_server_add_address(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ AvahiAddress *a) {
+
+ char *n = NULL;
+ int ret = AVAHI_OK;
+
+ assert(s);
+ assert(a);
+
+ if (!AVAHI_IF_VALID(interface))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
+
+ if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(a->proto))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
+
+ if (!AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_REVERSE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
+
+ if (name && !avahi_is_valid_domain_name(name))
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (!name)
+ name = s->host_name_fqdn;
+ else {
+ if (!(n = avahi_normalize_name_strdup(name)))
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ name = n;
+ }
+
+ if (a->proto == AVAHI_PROTO_INET) {
+ AvahiRecord *r;
+
+ if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ r->data.a.address = a->data.ipv4;
+ ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
+ avahi_record_unref(r);
+
+ if (ret < 0)
+ goto fail;
+
+ if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
+ char *reverse;
+
+ if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
+ avahi_free(reverse);
+ }
+
+ } else {
+ AvahiRecord *r;
+
+ assert(a->proto == AVAHI_PROTO_INET6);
+
+ if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ r->data.aaaa.address = a->data.ipv6;
+ ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
+ avahi_record_unref(r);
+
+ if (ret < 0)
+ goto fail;
+
+ if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
+ char *reverse;
+
+ if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
+ avahi_free(reverse);
+
+ if (ret < 0)
+ goto fail;
+
+ if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
+ avahi_free(reverse);
+ }
+ }
+
+fail:
+
+ avahi_free(n);
+
+ return ret;
+}
+
+static int server_add_txt_strlst_nocopy(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ AvahiStringList *strlst) {
+
+ AvahiRecord *r;
+ int ret;
+
+ assert(s);
+
+ if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
+ avahi_string_list_free(strlst);
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ }
+
+ r->data.txt.string_list = strlst;
+ ret = avahi_server_add(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+
+ return ret;
+}
+
+int avahi_server_add_txt_strlst(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ AvahiStringList *strlst) {
+
+ assert(s);
+
+ return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
+}
+
+int avahi_server_add_txt_va(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ va_list va) {
+
+ assert(s);
+
+ return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
+}
+
+int avahi_server_add_txt(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ ...) {
+
+ va_list va;
+ int ret;
+
+ assert(s);
+
+ va_start(va, name);
+ ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
+ va_end(va);
+
+ return ret;
+}
+
+static AvahiStringList *add_magic_cookie(
+ AvahiServer *s,
+ AvahiStringList *strlst) {
+
+ assert(s);
+
+ if (!s->config.add_service_cookie)
+ return strlst;
+
+ if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
+ /* This string list already contains a magic cookie */
+ return strlst;
+
+ return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
+}
+
+static int server_add_service_strlst_nocopy(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ AvahiStringList *strlst) {
+
+ char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
+ AvahiRecord *r = NULL;
+ int ret = AVAHI_OK;
+
+ assert(s);
+ assert(type);
+ assert(name);
+
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE|AVAHI_PUBLISH_IS_PROXY), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_domain_name(host), AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (!domain)
+ domain = s->domain_name;
+
+ if (!host)
+ host = s->host_name_fqdn;
+
+ if (!(h = avahi_normalize_name_strdup(host))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
+ (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
+ (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
+ avahi_server_set_errno(s, ret);
+ goto fail;
+ }
+
+ /* Add service enumeration PTR record */
+
+ if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
+ goto fail;
+
+ /* Add SRV record */