4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
40 #define AVAHI_HOST_RR_HOLDOFF_MSEC 2000
42 static void free_entry(AvahiServer*s, AvahiEntry *e) {
48 avahi_goodbye_entry(s, e, TRUE);
50 /* Remove from linked list */
51 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
53 /* Remove from hash table indexed by name */
54 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
55 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
57 g_hash_table_replace(s->entries_by_key, t->record->key, t);
59 g_hash_table_remove(s->entries_by_key, e->record->key);
61 /* Remove from associated group */
63 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
65 avahi_record_unref(e->record);
69 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
74 free_entry(s, g->entries);
76 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
80 static void cleanup_dead(AvahiServer *s) {
81 AvahiEntryGroup *g, *ng;
86 if (s->need_group_cleanup) {
87 for (g = s->groups; g; g = ng) {
94 s->need_group_cleanup = FALSE;
97 if (s->need_entry_cleanup) {
98 for (e = s->entries; e; e = ne) {
105 s->need_entry_cleanup = FALSE;
108 if (s->need_browser_cleanup)
109 avahi_browser_cleanup(s);
112 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
121 g_assert(type != AVAHI_DNS_TYPE_ANY);
123 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
125 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
126 if (!e->dead && avahi_entry_registered(s, e, i))
127 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
132 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
138 if (r->key->class == AVAHI_DNS_CLASS_IN) {
139 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
140 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
141 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
142 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
143 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
144 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
149 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
154 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
157 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
165 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
168 if (avahi_key_is_pattern(k)) {
170 /* Handle ANY query */
172 for (e = s->entries; e; e = e->entries_next)
173 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
174 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
178 /* Handle all other queries */
180 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
181 if (!e->dead && avahi_entry_registered(s, e, i))
182 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
186 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
193 for (k = e->group->entries; k; k = k->by_group_next) {
194 avahi_goodbye_entry(s, k, FALSE);
198 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
200 avahi_goodbye_entry(s, e, FALSE);
204 s->need_entry_cleanup = TRUE;
207 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
213 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
214 withdraw_entry(s, e);
217 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
220 gboolean ours = FALSE, won = FALSE, lost = FALSE;
226 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
233 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
238 if (avahi_entry_probing(s, e, i)) {
247 t = avahi_record_to_string(record);
252 avahi_log_debug("xxx Recieved conflicting probe [%s]. Local host won.", t);
254 avahi_log_debug("yyy Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
255 withdraw_rrset(s, record->key);
262 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
263 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
264 AvahiEntry *e, *n, *conflicting_entry = NULL;
271 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
273 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
276 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
279 /* Either our entry or the other is intended to be unique, so let's check */
281 if (avahi_record_equal_no_ttl(e->record, record)) {
282 ours = TRUE; /* We have an identical record, so this is no conflict */
284 /* Check wheter there is a TTL conflict */
285 if (record->ttl <= e->record->ttl/2 &&
286 avahi_entry_registered(s, e, i)) {
289 t = avahi_record_to_string(record);
291 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
292 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
298 /* There's no need to check the other entries of this RRset */
303 if (avahi_entry_registered(s, e, i)) {
305 /* A conflict => we have to return to probe mode */
307 conflicting_entry = e;
309 } else if (avahi_entry_probing(s, e, i)) {
311 /* We are currently registering a matching record, but
312 * someone else already claimed it, so let's
315 withdraw_immediately = TRUE;
320 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
322 if (!ours && conflict) {
327 t = avahi_record_to_string(record);
329 if (withdraw_immediately) {
330 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
331 withdraw_rrset(s, record->key);
333 g_assert(conflicting_entry);
334 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
335 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
337 /* Local unique records are returned to probin
338 * state. Local shared records are reannounced. */
347 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
348 gboolean *unicast_response = userdata;
352 g_assert(unicast_response);
354 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
357 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
361 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
364 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
368 g_assert(!legacy_unicast || (a && port > 0 && p));
370 if (legacy_unicast) {
371 AvahiDnsPacket *reply;
374 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
376 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
378 append_aux_records_to_list(s, i, r, FALSE);
380 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
381 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
383 gchar *t = avahi_record_to_string(r);
384 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
388 avahi_record_unref(r);
391 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
392 avahi_interface_send_packet_unicast(i, reply, a, port);
394 avahi_dns_packet_free(reply);
397 gboolean unicast_response, flush_cache, auxiliary;
398 AvahiDnsPacket *reply = NULL;
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
406 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
408 if (!avahi_interface_post_response(i, r, flush_cache, a, !tc && flush_cache && !auxiliary) && unicast_response) {
410 append_aux_records_to_list(s, i, r, unicast_response);
412 /* Due to some reasons the record has not been scheduled.
413 * The client requested an unicast response in that
414 * case. Therefore we prepare such a response */
420 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
423 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
425 /* Appending this record succeeded, so incremeant
426 * the specific header field, and return to the caller */
428 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
433 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
436 /* The record is too large for one packet, so create a larger packet */
438 avahi_dns_packet_free(reply);
439 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
440 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
441 size = AVAHI_DNS_PACKET_MAX_SIZE;
442 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
444 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
445 avahi_dns_packet_free(reply);
447 gchar *t = avahi_record_to_string(r);
448 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
452 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
455 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
456 avahi_interface_send_packet_unicast(i, reply, a, port);
457 avahi_dns_packet_free(reply);
462 avahi_record_unref(r);
466 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
467 avahi_interface_send_packet_unicast(i, reply, a, port);
468 avahi_dns_packet_free(reply);
472 avahi_record_list_flush(s->record_list);
476 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
483 if (!s->config.enable_reflector)
486 for (j = s->monitor->interfaces; j; j = j->interface_next)
487 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
488 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
491 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
492 AvahiServer *s = userdata;
499 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
503 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
510 if (!s->config.enable_reflector)
513 for (j = s->monitor->interfaces; j; j = j->interface_next)
514 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
515 /* Post the query to other networks */
516 avahi_interface_post_query(j, k, TRUE);
518 /* Reply from caches of other network. This is needed to
519 * "work around" known answer suppression. */
521 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
525 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
532 if (!s->config.enable_reflector)
535 for (j = s->monitor->interfaces; j; j = j->interface_next)
536 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
537 avahi_interface_post_probe(j, r, TRUE);
540 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
548 /* avahi_log_debug("query"); */
550 g_assert(avahi_record_list_empty(s->record_list));
552 /* Handle the questions */
553 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
555 gboolean unicast_response = FALSE;
557 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
558 avahi_log_warn("Packet too short (1)");
563 reflect_query(s, i, key);
565 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
566 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
567 /* Allow our own queries to be suppressed by incoming
568 * queries only when they do not include known answers */
569 avahi_query_scheduler_incoming(i->query_scheduler, key);
571 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
572 avahi_key_unref(key);
575 /* Known Answer Suppression */
576 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
578 gboolean unique = FALSE;
580 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
581 avahi_log_warn("Packet too short (2)");
585 if (handle_conflict(s, i, record, unique, a)) {
586 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
587 avahi_record_list_drop(s->record_list, record);
590 avahi_record_unref(record);
594 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
596 gboolean unique = FALSE;
598 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
599 avahi_log_warn("Packet too short (3)");
603 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
604 reflect_probe(s, i, record);
605 incoming_probe(s, record, i);
608 avahi_record_unref(record);
611 if (!avahi_record_list_empty(s->record_list))
612 avahi_server_generate_response(s, i, p, a, port, legacy_unicast);
617 avahi_record_list_flush(s->record_list);
621 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
629 /* avahi_log_debug("response"); */
631 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
632 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
634 gboolean cache_flush = FALSE;
637 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
638 avahi_log_warn("Packet too short (4)");
642 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
644 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
647 if (handle_conflict(s, i, record, cache_flush, a)) {
648 reflect_response(s, i, record, cache_flush);
649 avahi_cache_update(i->cache, record, cache_flush, a);
650 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
654 avahi_record_unref(record);
658 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
659 guint n, index = (guint) -1;
660 AvahiLegacyUnicastReflectSlot *slot;
664 if (!s->legacy_unicast_reflect_slots)
665 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
667 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
668 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
670 if (!s->legacy_unicast_reflect_slots[index])
674 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
677 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
678 slot->id = s->legacy_unicast_reflect_id++;
683 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
689 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
691 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
693 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
696 s->legacy_unicast_reflect_slots[index] = NULL;
699 static void free_slots(AvahiServer *s) {
703 if (!s->legacy_unicast_reflect_slots)
706 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
707 if (s->legacy_unicast_reflect_slots[index])
708 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
710 g_free(s->legacy_unicast_reflect_slots);
711 s->legacy_unicast_reflect_slots = NULL;
714 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
719 if (!s->legacy_unicast_reflect_slots)
722 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
724 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
727 return s->legacy_unicast_reflect_slots[index];
730 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
731 AvahiLegacyUnicastReflectSlot *slot = userdata;
735 g_assert(slot->time_event == e);
737 deallocate_slot(slot->server, slot);
740 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
741 AvahiLegacyUnicastReflectSlot *slot;
749 g_assert(i->protocol == a->family);
751 if (!s->config.enable_reflector)
754 /* avahi_log_debug("legacy unicast reflectr"); */
756 /* Reflecting legacy unicast queries is a little more complicated
757 than reflecting normal queries, since we must route the
758 responses back to the right client. Therefore we must store
759 some information for finding the right client contact data for
760 response packets. In contrast to normal queries legacy
761 unicast query and response packets are reflected untouched and
762 are not reassembled into larger packets */
764 if (!(slot = allocate_slot(s))) {
765 /* No slot available, we drop this legacy unicast query */
766 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
770 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
773 slot->interface = i->hardware->index;
775 avahi_elapse_time(&slot->elapse_time, 2000, 0);
776 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
778 /* Patch the packet with our new locally generatedt id */
779 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
781 for (j = s->monitor->interfaces; j; j = j->interface_next)
782 if (avahi_interface_relevant(j) &&
784 (s->config.reflect_ipv || j->protocol == i->protocol)) {
786 if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
787 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
788 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
789 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
793 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
796 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
801 if (!s->config.enable_reflector)
804 avahi_address_from_sockaddr(sa, &a);
806 if (!avahi_address_is_local(s->monitor, &a))
809 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
810 struct sockaddr_in lsa;
811 socklen_t l = sizeof(lsa);
813 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
814 avahi_log_warn("getsockname(): %s", strerror(errno));
816 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
820 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
821 struct sockaddr_in6 lsa;
822 socklen_t l = sizeof(lsa);
824 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
825 avahi_log_warn("getsockname(): %s", strerror(errno));
827 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
833 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
843 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
844 !avahi_interface_relevant(i)) {
845 avahi_log_warn("Recieved packet from invalid interface.");
849 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
851 port = avahi_port_from_sockaddr(sa);
852 avahi_address_from_sockaddr(sa, &a);
854 if (avahi_address_is_ipv4_in_ipv6(&a))
855 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
858 if (originates_from_local_legacy_unicast_socket(s, sa))
859 /* This originates from our local reflector, so let's ignore it */
862 if (avahi_dns_packet_check_valid(p) < 0) {
863 avahi_log_warn("Recieved invalid packet.");
867 if (avahi_dns_packet_is_query(p)) {
868 gboolean legacy_unicast = FALSE;
870 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
871 avahi_log_warn("Invalid query packet.");
875 if (port != AVAHI_MDNS_PORT) {
878 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
879 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
880 avahi_log_warn("Invalid legacy unicast query packet.");
884 legacy_unicast = TRUE;
888 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
890 handle_query_packet(s, p, i, &a, port, legacy_unicast);
892 /* avahi_log_debug("Handled query"); */
895 if (port != AVAHI_MDNS_PORT) {
896 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
901 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
902 if (s->config.check_response_ttl)
906 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
907 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
908 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
909 avahi_log_warn("Invalid response packet.");
913 handle_response_packet(s, p, i, &a);
914 /* avahi_log_debug("Handled response"); */
918 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
919 AvahiInterface *i, *j;
922 AvahiLegacyUnicastReflectSlot *slot;
929 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
930 !avahi_interface_relevant(i)) {
931 avahi_log_warn("Recieved packet from invalid interface.");
935 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
937 port = avahi_port_from_sockaddr(sa);
938 avahi_address_from_sockaddr(sa, &a);
940 if (avahi_address_is_ipv4_in_ipv6(&a))
941 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
944 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
945 avahi_log_warn("Recieved invalid packet.");
949 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
950 avahi_log_warn("Recieved legacy unicast response with unknown id");
954 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
955 !avahi_interface_relevant(j))
958 /* Patch the original ID into this response */
959 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
961 /* Forward the response to the correct client */
962 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
964 /* Undo changes to packet */
965 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
968 static void work(AvahiServer *s) {
969 struct sockaddr_in6 sa6;
970 struct sockaddr_in sa;
977 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
978 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
979 dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
980 avahi_dns_packet_free(p);
984 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
985 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
986 dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
987 avahi_dns_packet_free(p);
991 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
992 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
993 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
994 avahi_dns_packet_free(p);
998 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
999 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
1000 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1001 avahi_dns_packet_free(p);
1006 static gboolean prepare_func(GSource *source, gint *timeout) {
1014 static gboolean check_func(GSource *source) {
1016 gushort revents = 0;
1020 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1023 if (s->fd_ipv4 >= 0)
1024 revents |= s->pollfd_ipv4.revents;
1025 if (s->fd_ipv6 >= 0)
1026 revents |= s->pollfd_ipv6.revents;
1027 if (s->fd_legacy_unicast_ipv4 >= 0)
1028 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1029 if (s->fd_legacy_unicast_ipv6 >= 0)
1030 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1032 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1035 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1039 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1048 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1051 if (s->state == state)
1057 s->callback(s, state, s->userdata);
1060 static void withdraw_host_rrs(AvahiServer *s) {
1063 if (s->hinfo_entry_group) {
1064 avahi_entry_group_free(s->hinfo_entry_group);
1065 s->hinfo_entry_group = NULL;
1068 if (s->browse_domain_entry_group) {
1069 avahi_entry_group_free(s->browse_domain_entry_group);
1070 s->browse_domain_entry_group = NULL;
1073 avahi_update_host_rrs(s->monitor, TRUE);
1074 s->n_host_rr_pending = 0;
1077 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1080 g_assert(s->n_host_rr_pending > 0);
1082 if (--s->n_host_rr_pending == 0)
1083 server_set_state(s, AVAHI_SERVER_RUNNING);
1086 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1089 s->n_host_rr_pending ++;
1092 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1096 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1097 s->state == AVAHI_SERVER_REGISTERING)
1098 avahi_server_increase_host_rr_pending(s);
1099 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1100 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1101 withdraw_host_rrs(s);
1102 server_set_state(s, AVAHI_SERVER_COLLISION);
1103 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1104 s->state == AVAHI_SERVER_REGISTERING)
1105 avahi_server_decrease_host_rr_pending(s);
1108 static void register_hinfo(AvahiServer *s) {
1109 struct utsname utsname;
1114 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1117 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1119 /* Fill in HINFO rr */
1120 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1122 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1123 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1124 avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1125 avahi_record_unref(r);
1127 avahi_entry_group_commit(s->hinfo_entry_group);
1130 static void register_localhost(AvahiServer *s) {
1134 /* Add localhost entries */
1135 avahi_address_parse("127.0.0.1", AF_INET, &a);
1136 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1138 avahi_address_parse("::1", AF_INET6, &a);
1139 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1142 static void register_browse_domain(AvahiServer *s) {
1145 if (!s->config.publish_domain || s->browse_domain_entry_group)
1148 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1149 avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1150 avahi_entry_group_commit(s->browse_domain_entry_group);
1153 static void register_stuff(AvahiServer *s) {
1156 server_set_state(s, AVAHI_SERVER_REGISTERING);
1158 register_browse_domain(s);
1159 avahi_update_host_rrs(s->monitor, FALSE);
1161 if (s->n_host_rr_pending == 0)
1162 server_set_state(s, AVAHI_SERVER_RUNNING);
1165 static void update_fqdn(AvahiServer *s) {
1168 g_assert(s->host_name);
1169 g_assert(s->domain_name);
1171 g_free(s->host_name_fqdn);
1172 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1175 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1176 AvahiServer *s = userdata;
1181 g_assert(e == s->register_time_event);
1182 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1183 s->register_time_event = NULL;
1185 if (s->state == AVAHI_SERVER_SLEEPING)
1189 static void delayed_register_stuff(AvahiServer *s) {
1194 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1196 if (s->register_time_event)
1197 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1199 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1202 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1204 g_assert(host_name);
1206 server_set_state(s, AVAHI_SERVER_SLEEPING);
1207 withdraw_host_rrs(s);
1209 g_free(s->host_name);
1210 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1211 s->host_name[strcspn(s->host_name, ".")] = 0;
1214 delayed_register_stuff(s);
1218 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1220 g_assert(domain_name);
1222 server_set_state(s, AVAHI_SERVER_SLEEPING);
1223 withdraw_host_rrs(s);
1225 g_free(s->domain_name);
1226 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1229 delayed_register_stuff(s);
1234 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1239 memset(pollfd, 0, sizeof(GPollFD));
1241 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1242 g_source_add_poll(s->source, pollfd);
1245 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1248 static GSourceFuncs source_funcs = {
1257 s = g_new(AvahiServer, 1);
1258 s->n_host_rr_pending = 0;
1259 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1262 avahi_server_config_copy(&s->config, sc);
1264 avahi_server_config_init(&s->config);
1266 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1267 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1269 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1270 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1271 avahi_server_config_free(&s->config);
1276 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1277 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1278 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1279 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1281 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1282 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1284 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1286 /* Prepare IO source registration */
1287 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1288 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1290 if (s->fd_ipv4 >= 0)
1291 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1292 if (s->fd_ipv6 >= 0)
1293 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1294 if (s->fd_legacy_unicast_ipv4 >= 0)
1295 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1296 if (s->fd_legacy_unicast_ipv6 >= 0)
1297 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1299 g_source_attach(s->source, s->context);
1301 s->callback = callback;
1302 s->userdata = userdata;
1304 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1305 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1306 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1308 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1309 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1310 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1311 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1312 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1313 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1314 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1315 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1317 s->legacy_unicast_reflect_slots = NULL;
1318 s->legacy_unicast_reflect_id = 0;
1321 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1322 s->host_name[strcspn(s->host_name, ".")] = 0;
1323 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1324 s->host_name_fqdn = NULL;
1327 s->record_list = avahi_record_list_new();
1329 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1330 s->register_time_event = NULL;
1332 s->state = AVAHI_SERVER_INVALID;
1334 s->monitor = avahi_interface_monitor_new(s);
1335 avahi_interface_monitor_sync(s->monitor);
1337 register_localhost(s);
1339 s->hinfo_entry_group = NULL;
1340 s->browse_domain_entry_group = NULL;
1346 void avahi_server_free(AvahiServer* s) {
1350 free_entry(s, s->entries);
1352 avahi_interface_monitor_free(s->monitor);
1355 free_group(s, s->groups);
1359 while (s->host_name_resolvers)
1360 avahi_host_name_resolver_free(s->host_name_resolvers);
1361 while (s->address_resolvers)
1362 avahi_address_resolver_free(s->address_resolvers);
1363 while (s->domain_browsers)
1364 avahi_domain_browser_free(s->domain_browsers);
1365 while (s->service_type_browsers)
1366 avahi_service_type_browser_free(s->service_type_browsers);
1367 while (s->service_browsers)
1368 avahi_service_browser_free(s->service_browsers);
1369 while (s->service_resolvers)
1370 avahi_service_resolver_free(s->service_resolvers);
1371 while (s->record_browsers)
1372 avahi_record_browser_destroy(s->record_browsers);
1373 g_hash_table_destroy(s->record_browser_hashtable);
1375 g_hash_table_destroy(s->entries_by_key);
1377 if (s->register_time_event)
1378 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1379 avahi_time_event_queue_free(s->time_event_queue);
1381 avahi_record_list_free(s->record_list);
1383 if (s->fd_ipv4 >= 0)
1385 if (s->fd_ipv6 >= 0)
1387 if (s->fd_legacy_unicast_ipv4 >= 0)
1388 close(s->fd_legacy_unicast_ipv4);
1389 if (s->fd_legacy_unicast_ipv6 >= 0)
1390 close(s->fd_legacy_unicast_ipv6);
1392 g_free(s->host_name);
1393 g_free(s->domain_name);
1394 g_free(s->host_name_fqdn);
1396 g_source_destroy(s->source);
1397 g_source_unref(s->source);
1398 g_main_context_unref(s->context);
1400 avahi_server_config_free(&s->config);
1405 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1411 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1415 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1418 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1421 if (interface <= 0 ||
1422 e->interface <= 0 ||
1423 e->interface == interface ||
1424 protocol == AF_UNSPEC ||
1425 e->protocol == AF_UNSPEC ||
1426 e->protocol == protocol)
1435 gint avahi_server_add(
1440 AvahiEntryFlags flags,
1447 g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1449 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1452 e = g_new(AvahiEntry, 1);
1454 e->record = avahi_record_ref(r);
1456 e->interface = interface;
1457 e->protocol = protocol;
1461 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1463 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1465 /* Insert into hash table indexed by name */
1466 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1467 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1468 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1470 /* Insert into group list */
1472 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1474 avahi_announce_entry(s, e);
1479 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1480 AvahiEntry **e = (AvahiEntry**) state;
1485 *e = g ? g->entries : s->entries;
1487 while (*e && (*e)->dead)
1488 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1493 return avahi_record_ref((*e)->record);
1496 void avahi_server_dump(AvahiServer *s, FILE *f) {
1501 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1503 for (e = s->entries; e; e = e->entries_next) {
1509 t = avahi_record_to_string(e->record);
1510 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1514 avahi_dump_caches(s->monitor, f);
1517 gint avahi_server_add_ptr(
1522 AvahiEntryFlags flags,
1524 const gchar *dest) {
1531 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1532 r->data.ptr.name = avahi_normalize_name(dest);
1533 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1534 avahi_record_unref(r);
1538 gint avahi_server_add_address(
1543 AvahiEntryFlags flags,
1552 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1554 if (a->family == AF_INET) {
1558 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1559 r->data.a.address = a->data.ipv4;
1560 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1561 avahi_record_unref(r);
1563 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1564 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1571 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1572 r->data.aaaa.address = a->data.ipv6;
1573 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1574 avahi_record_unref(r);
1576 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1577 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1580 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1581 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1590 gint avahi_server_add_text_strlst(
1595 AvahiEntryFlags flags,
1597 AvahiStringList *strlst) {
1604 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1605 r->data.txt.string_list = strlst;
1606 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1607 avahi_record_unref(r);
1612 gint avahi_server_add_text_va(
1617 AvahiEntryFlags flags,
1623 return avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1626 gint avahi_server_add_text(
1631 AvahiEntryFlags flags,
1641 ret = avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1647 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1652 while (*s && size >= 2) {
1653 if (*s == '.' || *s == '\\') {
1669 gint avahi_server_add_service_strlst(
1676 const gchar *domain,
1679 AvahiStringList *strlst) {
1681 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1689 escape_service_name(ename, sizeof(ename), name);
1692 while (domain[0] == '.')
1695 domain = s->domain_name;
1698 host = s->host_name_fqdn;
1700 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1701 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1703 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1705 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1706 r->data.srv.priority = 0;
1707 r->data.srv.weight = 0;
1708 r->data.srv.port = port;
1709 r->data.srv.name = avahi_normalize_name(host);
1710 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1711 avahi_record_unref(r);
1713 ret |= avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1715 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1716 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1721 gint avahi_server_add_service_va(
1728 const gchar *domain,
1737 return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1740 gint avahi_server_add_service(
1747 const gchar *domain,
1760 ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1765 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1766 AvahiKey *k = userdata;
1772 avahi_interface_post_query(i, k, FALSE);
1775 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1779 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1782 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1785 if (g->state == state)
1791 g->callback(g->server, g, state, g->userdata);
1796 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1801 g = g_new(AvahiEntryGroup, 1);
1803 g->callback = callback;
1804 g->userdata = userdata;
1806 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1808 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1810 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1814 void avahi_entry_group_free(AvahiEntryGroup *g) {
1818 g_assert(g->server);
1820 for (e = g->entries; e; e = e->by_group_next) {
1821 avahi_goodbye_entry(g->server, e, TRUE);
1827 g->server->need_group_cleanup = TRUE;
1828 g->server->need_entry_cleanup = TRUE;
1831 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1835 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1838 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1839 avahi_announce_group(g->server, g);
1840 avahi_entry_group_check_probed(g, FALSE);
1845 gboolean avahi_entry_commited(AvahiEntry *e) {
1850 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1851 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1854 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1861 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1864 g->userdata = userdata;
1867 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1873 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1876 return s->domain_name;
1879 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1882 return s->host_name;
1885 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1888 return s->host_name_fqdn;
1891 gpointer avahi_server_get_data(AvahiServer *s) {
1897 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1900 s->userdata = userdata;
1903 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1909 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1912 memset(c, 0, sizeof(AvahiServerConfig));
1915 c->host_name = NULL;
1916 c->domain_name = NULL;
1917 c->check_response_ttl = TRUE;
1918 c->publish_hinfo = TRUE;
1919 c->publish_addresses = TRUE;
1920 c->publish_workstation = TRUE;
1921 c->publish_domain = TRUE;
1922 c->use_iff_running = FALSE;
1923 c->enable_reflector = FALSE;
1924 c->reflect_ipv = FALSE;
1929 void avahi_server_config_free(AvahiServerConfig *c) {
1932 g_free(c->host_name);
1933 g_free(c->domain_name);
1936 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1942 ret->host_name = g_strdup(c->host_name);
1943 ret->domain_name = g_strdup(c->domain_name);