+ /* Patch the original ID into this response */
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
+
+ /* Forward the response to the correct client */
+ avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
+
+ /* Undo changes to packet */
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
+}
+
+static void cleanup_dead(AvahiServer *s) {
+ assert(s);
+
+ avahi_cleanup_dead_entries(s);
+ avahi_browser_cleanup(s);
+}
+
+static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
+ AvahiServer *s = userdata;
+ AvahiAddress dest, src;
+ AvahiDnsPacket *p = NULL;
+ AvahiIfIndex iface;
+ uint16_t port;
+ uint8_t ttl;
+
+ assert(w);
+ assert(fd >= 0);
+ assert(events & AVAHI_WATCH_IN);
+
+ if (fd == s->fd_ipv4) {
+ dest.proto = src.proto = AVAHI_PROTO_INET;
+ p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
+ } else {
+ assert(fd == s->fd_ipv6);
+ dest.proto = src.proto = AVAHI_PROTO_INET6;
+ p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
+ }
+
+ if (p) {
+ if (iface == AVAHI_IF_UNSPEC)
+ iface = avahi_find_interface_for_address(s->monitor, &dest);
+
+ if (iface != AVAHI_IF_UNSPEC)
+ dispatch_packet(s, p, &src, port, &dest, iface, ttl);
+ else
+ avahi_log_error("Incoming packet recieved on address that isn't local.");
+
+ avahi_dns_packet_free(p);
+
+ cleanup_dead(s);
+ }
+}
+
+static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
+ AvahiServer *s = userdata;
+ AvahiDnsPacket *p = NULL;
+
+ assert(w);
+ assert(fd >= 0);
+ assert(events & AVAHI_WATCH_IN);
+
+ if (fd == s->fd_legacy_unicast_ipv4)
+ p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
+ else {
+ assert(fd == s->fd_legacy_unicast_ipv6);
+ p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
+ }
+
+ if (p) {
+ dispatch_legacy_unicast_packet(s, p);
+ avahi_dns_packet_free(p);
+
+ cleanup_dead(s);
+ }
+}
+
+static void server_set_state(AvahiServer *s, AvahiServerState state) {
+ assert(s);
+
+ if (s->state == state)
+ return;
+
+ s->state = state;
+
+ avahi_interface_monitor_update_rrs(s->monitor, 0);
+
+ if (s->callback)
+ s->callback(s, state, s->userdata);
+}
+
+static void withdraw_host_rrs(AvahiServer *s) {
+ assert(s);
+
+ if (s->hinfo_entry_group)
+ avahi_s_entry_group_reset(s->hinfo_entry_group);
+
+ if (s->browse_domain_entry_group)
+ avahi_s_entry_group_reset(s->browse_domain_entry_group);
+
+ avahi_interface_monitor_update_rrs(s->monitor, 1);
+ s->n_host_rr_pending = 0;
+}
+
+void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
+ assert(s);
+
+ assert(s->n_host_rr_pending > 0);
+
+ if (--s->n_host_rr_pending == 0)
+ server_set_state(s, AVAHI_SERVER_RUNNING);
+}
+
+void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
+ assert(s);
+ assert(g);
+
+ if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
+ s->state == AVAHI_SERVER_REGISTERING)
+ s->n_host_rr_pending ++;
+
+ 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 &&
+ s->state == AVAHI_SERVER_REGISTERING)
+ avahi_server_decrease_host_rr_pending(s);
+}
+
+static void register_hinfo(AvahiServer *s) {
+ struct utsname utsname;
+ AvahiRecord *r;
+
+ assert(s);
+
+ if (!s->config.publish_hinfo)
+ return;
+
+ if (s->hinfo_entry_group)
+ assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
+ else
+ s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
+
+ if (!s->hinfo_entry_group) {
+ avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
+ return;
+ }
+
+ /* Fill in HINFO rr */
+ if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+
+ if (uname(&utsname) < 0)
+ avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
+ else {
+
+ r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
+ r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
+
+ avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
+
+ if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
+ avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
+ return;
+ }
+ }
+
+ avahi_record_unref(r);
+ }
+
+ if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
+ avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
+
+}
+
+static void register_localhost(AvahiServer *s) {
+ AvahiAddress a;
+ assert(s);
+
+ /* Add localhost entries */
+ avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
+ avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
+
+ avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
+ avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
+}
+
+static void register_browse_domain(AvahiServer *s) {
+ assert(s);
+
+ if (!s->config.publish_domain)
+ return;
+
+ if (avahi_domain_equal(s->domain_name, "local"))
+ return;
+
+ if (s->browse_domain_entry_group)
+ assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
+ else
+ s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
+
+ if (!s->browse_domain_entry_group) {
+ avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
+ return;
+ }
+
+ if (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) < 0) {
+ avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
+ return;
+ }
+
+ if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
+ avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));