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);
620 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
628 /* avahi_log_debug("response"); */
630 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
631 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
633 gboolean cache_flush = FALSE;
636 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
637 avahi_log_warn("Packet too short (4)");
641 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
643 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
646 if (handle_conflict(s, i, record, cache_flush, a)) {
647 reflect_response(s, i, record, cache_flush);
648 avahi_cache_update(i->cache, record, cache_flush, a);
649 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
653 avahi_record_unref(record);
656 /* If the incoming response contained a conflicting record, some
657 records have been scheduling for sending. We need to flush them
659 if (!avahi_record_list_empty(s->record_list))
660 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE);
663 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
664 guint n, index = (guint) -1;
665 AvahiLegacyUnicastReflectSlot *slot;
669 if (!s->legacy_unicast_reflect_slots)
670 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
672 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
673 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
675 if (!s->legacy_unicast_reflect_slots[index])
679 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
682 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
683 slot->id = s->legacy_unicast_reflect_id++;
688 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
694 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
696 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
698 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
701 s->legacy_unicast_reflect_slots[index] = NULL;
704 static void free_slots(AvahiServer *s) {
708 if (!s->legacy_unicast_reflect_slots)
711 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
712 if (s->legacy_unicast_reflect_slots[index])
713 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
715 g_free(s->legacy_unicast_reflect_slots);
716 s->legacy_unicast_reflect_slots = NULL;
719 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
724 if (!s->legacy_unicast_reflect_slots)
727 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
729 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
732 return s->legacy_unicast_reflect_slots[index];
735 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
736 AvahiLegacyUnicastReflectSlot *slot = userdata;
740 g_assert(slot->time_event == e);
742 deallocate_slot(slot->server, slot);
745 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
746 AvahiLegacyUnicastReflectSlot *slot;
754 g_assert(i->protocol == a->family);
756 if (!s->config.enable_reflector)
759 /* avahi_log_debug("legacy unicast reflectr"); */
761 /* Reflecting legacy unicast queries is a little more complicated
762 than reflecting normal queries, since we must route the
763 responses back to the right client. Therefore we must store
764 some information for finding the right client contact data for
765 response packets. In contrast to normal queries legacy
766 unicast query and response packets are reflected untouched and
767 are not reassembled into larger packets */
769 if (!(slot = allocate_slot(s))) {
770 /* No slot available, we drop this legacy unicast query */
771 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
775 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
778 slot->interface = i->hardware->index;
780 avahi_elapse_time(&slot->elapse_time, 2000, 0);
781 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
783 /* Patch the packet with our new locally generatedt id */
784 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
786 for (j = s->monitor->interfaces; j; j = j->interface_next)
787 if (avahi_interface_relevant(j) &&
789 (s->config.reflect_ipv || j->protocol == i->protocol)) {
791 if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
792 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
793 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
794 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
798 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
801 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
806 if (!s->config.enable_reflector)
809 avahi_address_from_sockaddr(sa, &a);
811 if (!avahi_address_is_local(s->monitor, &a))
814 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
815 struct sockaddr_in lsa;
816 socklen_t l = sizeof(lsa);
818 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
819 avahi_log_warn("getsockname(): %s", strerror(errno));
821 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
825 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
826 struct sockaddr_in6 lsa;
827 socklen_t l = sizeof(lsa);
829 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
830 avahi_log_warn("getsockname(): %s", strerror(errno));
832 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
838 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
848 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
849 !avahi_interface_relevant(i)) {
850 avahi_log_warn("Recieved packet from invalid interface.");
854 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
856 port = avahi_port_from_sockaddr(sa);
857 avahi_address_from_sockaddr(sa, &a);
859 if (avahi_address_is_ipv4_in_ipv6(&a))
860 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
863 if (originates_from_local_legacy_unicast_socket(s, sa))
864 /* This originates from our local reflector, so let's ignore it */
867 if (avahi_dns_packet_check_valid(p) < 0) {
868 avahi_log_warn("Recieved invalid packet.");
872 if (avahi_dns_packet_is_query(p)) {
873 gboolean legacy_unicast = FALSE;
875 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
876 avahi_log_warn("Invalid query packet.");
880 if (port != AVAHI_MDNS_PORT) {
883 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
884 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
885 avahi_log_warn("Invalid legacy unicast query packet.");
889 legacy_unicast = TRUE;
893 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
895 handle_query_packet(s, p, i, &a, port, legacy_unicast);
897 /* avahi_log_debug("Handled query"); */
900 if (port != AVAHI_MDNS_PORT) {
901 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
906 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
907 if (s->config.check_response_ttl)
911 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
912 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
913 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
914 avahi_log_warn("Invalid response packet.");
918 handle_response_packet(s, p, i, &a);
919 /* avahi_log_debug("Handled response"); */
923 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
924 AvahiInterface *i, *j;
927 AvahiLegacyUnicastReflectSlot *slot;
934 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
935 !avahi_interface_relevant(i)) {
936 avahi_log_warn("Recieved packet from invalid interface.");
940 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
942 port = avahi_port_from_sockaddr(sa);
943 avahi_address_from_sockaddr(sa, &a);
945 if (avahi_address_is_ipv4_in_ipv6(&a))
946 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
949 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
950 avahi_log_warn("Recieved invalid packet.");
954 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
955 avahi_log_warn("Recieved legacy unicast response with unknown id");
959 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
960 !avahi_interface_relevant(j))
963 /* Patch the original ID into this response */
964 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
966 /* Forward the response to the correct client */
967 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
969 /* Undo changes to packet */
970 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
973 static void work(AvahiServer *s) {
974 struct sockaddr_in6 sa6;
975 struct sockaddr_in sa;
982 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
983 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
984 dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
985 avahi_dns_packet_free(p);
989 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
990 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
991 dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
992 avahi_dns_packet_free(p);
996 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
997 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
998 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
999 avahi_dns_packet_free(p);
1003 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1004 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
1005 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1006 avahi_dns_packet_free(p);
1011 static gboolean prepare_func(GSource *source, gint *timeout) {
1019 static gboolean check_func(GSource *source) {
1021 gushort revents = 0;
1025 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1028 if (s->fd_ipv4 >= 0)
1029 revents |= s->pollfd_ipv4.revents;
1030 if (s->fd_ipv6 >= 0)
1031 revents |= s->pollfd_ipv6.revents;
1032 if (s->fd_legacy_unicast_ipv4 >= 0)
1033 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1034 if (s->fd_legacy_unicast_ipv6 >= 0)
1035 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1037 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1040 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1044 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1053 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1056 if (s->state == state)
1062 s->callback(s, state, s->userdata);
1065 static void withdraw_host_rrs(AvahiServer *s) {
1068 if (s->hinfo_entry_group) {
1069 avahi_entry_group_free(s->hinfo_entry_group);
1070 s->hinfo_entry_group = NULL;
1073 if (s->browse_domain_entry_group) {
1074 avahi_entry_group_free(s->browse_domain_entry_group);
1075 s->browse_domain_entry_group = NULL;
1078 avahi_update_host_rrs(s->monitor, TRUE);
1079 s->n_host_rr_pending = 0;
1082 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1085 g_assert(s->n_host_rr_pending > 0);
1087 if (--s->n_host_rr_pending == 0)
1088 server_set_state(s, AVAHI_SERVER_RUNNING);
1091 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1094 s->n_host_rr_pending ++;
1097 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1101 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1102 s->state == AVAHI_SERVER_REGISTERING)
1103 avahi_server_increase_host_rr_pending(s);
1104 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1105 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1106 withdraw_host_rrs(s);
1107 server_set_state(s, AVAHI_SERVER_COLLISION);
1108 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1109 s->state == AVAHI_SERVER_REGISTERING)
1110 avahi_server_decrease_host_rr_pending(s);
1113 static void register_hinfo(AvahiServer *s) {
1114 struct utsname utsname;
1119 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1122 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1124 /* Fill in HINFO rr */
1125 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1127 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1128 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1129 avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1130 avahi_record_unref(r);
1132 avahi_entry_group_commit(s->hinfo_entry_group);
1135 static void register_localhost(AvahiServer *s) {
1139 /* Add localhost entries */
1140 avahi_address_parse("127.0.0.1", AF_INET, &a);
1141 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1143 avahi_address_parse("::1", AF_INET6, &a);
1144 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1147 static void register_browse_domain(AvahiServer *s) {
1150 if (!s->config.publish_domain || s->browse_domain_entry_group)
1153 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1154 avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1155 avahi_entry_group_commit(s->browse_domain_entry_group);
1158 static void register_stuff(AvahiServer *s) {
1161 server_set_state(s, AVAHI_SERVER_REGISTERING);
1163 register_browse_domain(s);
1164 avahi_update_host_rrs(s->monitor, FALSE);
1166 if (s->n_host_rr_pending == 0)
1167 server_set_state(s, AVAHI_SERVER_RUNNING);
1170 static void update_fqdn(AvahiServer *s) {
1173 g_assert(s->host_name);
1174 g_assert(s->domain_name);
1176 g_free(s->host_name_fqdn);
1177 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1180 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1181 AvahiServer *s = userdata;
1186 g_assert(e == s->register_time_event);
1187 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1188 s->register_time_event = NULL;
1190 if (s->state == AVAHI_SERVER_SLEEPING)
1194 static void delayed_register_stuff(AvahiServer *s) {
1199 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1201 if (s->register_time_event)
1202 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1204 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1207 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1209 g_assert(host_name);
1211 server_set_state(s, AVAHI_SERVER_SLEEPING);
1212 withdraw_host_rrs(s);
1214 g_free(s->host_name);
1215 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1216 s->host_name[strcspn(s->host_name, ".")] = 0;
1219 delayed_register_stuff(s);
1223 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1225 g_assert(domain_name);
1227 server_set_state(s, AVAHI_SERVER_SLEEPING);
1228 withdraw_host_rrs(s);
1230 g_free(s->domain_name);
1231 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1234 delayed_register_stuff(s);
1239 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1244 memset(pollfd, 0, sizeof(GPollFD));
1246 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1247 g_source_add_poll(s->source, pollfd);
1250 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1253 static GSourceFuncs source_funcs = {
1262 s = g_new(AvahiServer, 1);
1263 s->n_host_rr_pending = 0;
1264 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1267 avahi_server_config_copy(&s->config, sc);
1269 avahi_server_config_init(&s->config);
1271 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1272 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1274 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1275 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1276 avahi_server_config_free(&s->config);
1281 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1282 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1283 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1284 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1286 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1287 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1289 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1291 /* Prepare IO source registration */
1292 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1293 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1295 if (s->fd_ipv4 >= 0)
1296 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1297 if (s->fd_ipv6 >= 0)
1298 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1299 if (s->fd_legacy_unicast_ipv4 >= 0)
1300 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1301 if (s->fd_legacy_unicast_ipv6 >= 0)
1302 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1304 g_source_attach(s->source, s->context);
1306 s->callback = callback;
1307 s->userdata = userdata;
1309 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1310 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1311 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1313 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1314 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1315 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1316 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1317 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1318 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1319 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1320 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1322 s->legacy_unicast_reflect_slots = NULL;
1323 s->legacy_unicast_reflect_id = 0;
1326 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1327 s->host_name[strcspn(s->host_name, ".")] = 0;
1328 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1329 s->host_name_fqdn = NULL;
1332 s->record_list = avahi_record_list_new();
1334 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1335 s->register_time_event = NULL;
1337 s->state = AVAHI_SERVER_INVALID;
1339 s->monitor = avahi_interface_monitor_new(s);
1340 avahi_interface_monitor_sync(s->monitor);
1342 register_localhost(s);
1344 s->hinfo_entry_group = NULL;
1345 s->browse_domain_entry_group = NULL;
1351 void avahi_server_free(AvahiServer* s) {
1355 free_entry(s, s->entries);
1357 avahi_interface_monitor_free(s->monitor);
1360 free_group(s, s->groups);
1364 while (s->host_name_resolvers)
1365 avahi_host_name_resolver_free(s->host_name_resolvers);
1366 while (s->address_resolvers)
1367 avahi_address_resolver_free(s->address_resolvers);
1368 while (s->domain_browsers)
1369 avahi_domain_browser_free(s->domain_browsers);
1370 while (s->service_type_browsers)
1371 avahi_service_type_browser_free(s->service_type_browsers);
1372 while (s->service_browsers)
1373 avahi_service_browser_free(s->service_browsers);
1374 while (s->service_resolvers)
1375 avahi_service_resolver_free(s->service_resolvers);
1376 while (s->record_browsers)
1377 avahi_record_browser_destroy(s->record_browsers);
1378 g_hash_table_destroy(s->record_browser_hashtable);
1380 g_hash_table_destroy(s->entries_by_key);
1382 if (s->register_time_event)
1383 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1384 avahi_time_event_queue_free(s->time_event_queue);
1386 avahi_record_list_free(s->record_list);
1388 if (s->fd_ipv4 >= 0)
1390 if (s->fd_ipv6 >= 0)
1392 if (s->fd_legacy_unicast_ipv4 >= 0)
1393 close(s->fd_legacy_unicast_ipv4);
1394 if (s->fd_legacy_unicast_ipv6 >= 0)
1395 close(s->fd_legacy_unicast_ipv6);
1397 g_free(s->host_name);
1398 g_free(s->domain_name);
1399 g_free(s->host_name_fqdn);
1401 g_source_destroy(s->source);
1402 g_source_unref(s->source);
1403 g_main_context_unref(s->context);
1405 avahi_server_config_free(&s->config);
1410 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1416 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1420 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1423 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1426 if (interface <= 0 ||
1427 e->interface <= 0 ||
1428 e->interface == interface ||
1429 protocol == AF_UNSPEC ||
1430 e->protocol == AF_UNSPEC ||
1431 e->protocol == protocol)
1440 gint avahi_server_add(
1445 AvahiEntryFlags flags,
1452 g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1454 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1457 e = g_new(AvahiEntry, 1);
1459 e->record = avahi_record_ref(r);
1461 e->interface = interface;
1462 e->protocol = protocol;
1466 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1468 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1470 /* Insert into hash table indexed by name */
1471 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1472 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1473 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1475 /* Insert into group list */
1477 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1479 avahi_announce_entry(s, e);
1484 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1485 AvahiEntry **e = (AvahiEntry**) state;
1490 *e = g ? g->entries : s->entries;
1492 while (*e && (*e)->dead)
1493 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1498 return avahi_record_ref((*e)->record);
1501 void avahi_server_dump(AvahiServer *s, FILE *f) {
1506 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1508 for (e = s->entries; e; e = e->entries_next) {
1514 t = avahi_record_to_string(e->record);
1515 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1519 avahi_dump_caches(s->monitor, f);
1522 gint avahi_server_add_ptr(
1527 AvahiEntryFlags flags,
1529 const gchar *dest) {
1536 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1537 r->data.ptr.name = avahi_normalize_name(dest);
1538 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1539 avahi_record_unref(r);
1543 gint avahi_server_add_address(
1548 AvahiEntryFlags flags,
1557 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1559 if (a->family == AF_INET) {
1563 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1564 r->data.a.address = a->data.ipv4;
1565 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1566 avahi_record_unref(r);
1568 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1569 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1576 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1577 r->data.aaaa.address = a->data.ipv6;
1578 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1579 avahi_record_unref(r);
1581 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1582 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1585 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1586 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1595 gint avahi_server_add_text_strlst(
1600 AvahiEntryFlags flags,
1602 AvahiStringList *strlst) {
1609 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1610 r->data.txt.string_list = strlst;
1611 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1612 avahi_record_unref(r);
1617 gint avahi_server_add_text_va(
1622 AvahiEntryFlags flags,
1628 return avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1631 gint avahi_server_add_text(
1636 AvahiEntryFlags flags,
1646 ret = avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1652 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1657 while (*s && size >= 2) {
1658 if (*s == '.' || *s == '\\') {
1674 gint avahi_server_add_service_strlst(
1681 const gchar *domain,
1684 AvahiStringList *strlst) {
1686 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1694 escape_service_name(ename, sizeof(ename), name);
1697 while (domain[0] == '.')
1700 domain = s->domain_name;
1703 host = s->host_name_fqdn;
1705 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1706 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1708 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1710 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1711 r->data.srv.priority = 0;
1712 r->data.srv.weight = 0;
1713 r->data.srv.port = port;
1714 r->data.srv.name = avahi_normalize_name(host);
1715 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1716 avahi_record_unref(r);
1718 ret |= avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1720 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1721 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1726 gint avahi_server_add_service_va(
1733 const gchar *domain,
1742 return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1745 gint avahi_server_add_service(
1752 const gchar *domain,
1765 ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1770 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1771 AvahiKey *k = userdata;
1777 avahi_interface_post_query(i, k, FALSE);
1780 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1784 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1787 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1790 if (g->state == state)
1796 g->callback(g->server, g, state, g->userdata);
1801 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1806 g = g_new(AvahiEntryGroup, 1);
1808 g->callback = callback;
1809 g->userdata = userdata;
1811 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1813 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1815 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1819 void avahi_entry_group_free(AvahiEntryGroup *g) {
1823 g_assert(g->server);
1825 for (e = g->entries; e; e = e->by_group_next) {
1826 avahi_goodbye_entry(g->server, e, TRUE);
1832 g->server->need_group_cleanup = TRUE;
1833 g->server->need_entry_cleanup = TRUE;
1836 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1840 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1843 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1844 avahi_announce_group(g->server, g);
1845 avahi_entry_group_check_probed(g, FALSE);
1850 gboolean avahi_entry_commited(AvahiEntry *e) {
1855 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1856 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1859 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1866 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1869 g->userdata = userdata;
1872 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1878 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1881 return s->domain_name;
1884 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1887 return s->host_name;
1890 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1893 return s->host_name_fqdn;
1896 gpointer avahi_server_get_data(AvahiServer *s) {
1902 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1905 s->userdata = userdata;
1908 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1914 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1917 memset(c, 0, sizeof(AvahiServerConfig));
1920 c->host_name = NULL;
1921 c->domain_name = NULL;
1922 c->check_response_ttl = TRUE;
1923 c->publish_hinfo = TRUE;
1924 c->publish_addresses = TRUE;
1925 c->publish_workstation = TRUE;
1926 c->publish_domain = TRUE;
1927 c->use_iff_running = FALSE;
1928 c->enable_reflector = FALSE;
1929 c->reflect_ipv = FALSE;
1934 void avahi_server_config_free(AvahiServerConfig *c) {
1937 g_free(c->host_name);
1938 g_free(c->domain_name);
1941 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1947 ret->host_name = g_strdup(c->host_name);
1948 ret->domain_name = g_strdup(c->domain_name);