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);
564 avahi_query_scheduler_incoming(i->query_scheduler, key);
565 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
566 avahi_key_unref(key);
569 /* Known Answer Suppression */
570 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
572 gboolean unique = FALSE;
574 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
575 avahi_log_warn("Packet too short (2)");
579 if (handle_conflict(s, i, record, unique, a)) {
580 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
581 avahi_record_list_drop(s->record_list, record);
584 avahi_record_unref(record);
588 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
590 gboolean unique = FALSE;
592 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
593 avahi_log_warn("Packet too short (3)");
597 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
598 reflect_probe(s, i, record);
599 incoming_probe(s, record, i);
602 avahi_record_unref(record);
605 if (!avahi_record_list_empty(s->record_list))
606 avahi_server_generate_response(s, i, p, a, port, legacy_unicast);
611 avahi_record_list_flush(s->record_list);
615 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
623 /* avahi_log_debug("response"); */
625 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
626 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
628 gboolean cache_flush = FALSE;
631 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
632 avahi_log_warn("Packet too short (4)");
636 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
638 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
641 if (handle_conflict(s, i, record, cache_flush, a)) {
642 reflect_response(s, i, record, cache_flush);
643 avahi_cache_update(i->cache, record, cache_flush, a);
644 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
648 avahi_record_unref(record);
652 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
653 guint n, index = (guint) -1;
654 AvahiLegacyUnicastReflectSlot *slot;
658 if (!s->legacy_unicast_reflect_slots)
659 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
661 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
662 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
664 if (!s->legacy_unicast_reflect_slots[index])
668 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
671 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
672 slot->id = s->legacy_unicast_reflect_id++;
677 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
683 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
685 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
687 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
690 s->legacy_unicast_reflect_slots[index] = NULL;
693 static void free_slots(AvahiServer *s) {
697 if (!s->legacy_unicast_reflect_slots)
700 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
701 if (s->legacy_unicast_reflect_slots[index])
702 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
704 g_free(s->legacy_unicast_reflect_slots);
705 s->legacy_unicast_reflect_slots = NULL;
708 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
713 if (!s->legacy_unicast_reflect_slots)
716 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
718 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
721 return s->legacy_unicast_reflect_slots[index];
724 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
725 AvahiLegacyUnicastReflectSlot *slot = userdata;
729 g_assert(slot->time_event == e);
731 deallocate_slot(slot->server, slot);
734 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
735 AvahiLegacyUnicastReflectSlot *slot;
743 g_assert(i->protocol == a->family);
745 if (!s->config.enable_reflector)
748 /* avahi_log_debug("legacy unicast reflectr"); */
750 /* Reflecting legacy unicast queries is a little more complicated
751 than reflecting normal queries, since we must route the
752 responses back to the right client. Therefore we must store
753 some information for finding the right client contact data for
754 response packets. In contrast to normal queries legacy
755 unicast query and response packets are reflected untouched and
756 are not reassembled into larger packets */
758 if (!(slot = allocate_slot(s))) {
759 /* No slot available, we drop this legacy unicast query */
760 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
764 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
767 slot->interface = i->hardware->index;
769 avahi_elapse_time(&slot->elapse_time, 2000, 0);
770 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
772 /* Patch the packet with our new locally generatedt id */
773 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
775 for (j = s->monitor->interfaces; j; j = j->interface_next)
776 if (avahi_interface_relevant(j) &&
778 (s->config.reflect_ipv || j->protocol == i->protocol)) {
780 if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
781 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
782 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
783 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
787 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
790 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
795 if (!s->config.enable_reflector)
798 avahi_address_from_sockaddr(sa, &a);
800 if (!avahi_address_is_local(s->monitor, &a))
803 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
804 struct sockaddr_in lsa;
805 socklen_t l = sizeof(lsa);
807 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
808 avahi_log_warn("getsockname(): %s", strerror(errno));
810 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
814 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
815 struct sockaddr_in6 lsa;
816 socklen_t l = sizeof(lsa);
818 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
819 avahi_log_warn("getsockname(): %s", strerror(errno));
821 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
827 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
837 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
838 !avahi_interface_relevant(i)) {
839 avahi_log_warn("Recieved packet from invalid interface.");
843 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
845 port = avahi_port_from_sockaddr(sa);
846 avahi_address_from_sockaddr(sa, &a);
848 if (avahi_address_is_ipv4_in_ipv6(&a))
849 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
852 if (originates_from_local_legacy_unicast_socket(s, sa))
853 /* This originates from our local reflector, so let's ignore it */
856 if (avahi_dns_packet_check_valid(p) < 0) {
857 avahi_log_warn("Recieved invalid packet.");
861 if (avahi_dns_packet_is_query(p)) {
862 gboolean legacy_unicast = FALSE;
864 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
865 avahi_log_warn("Invalid query packet.");
869 if (port != AVAHI_MDNS_PORT) {
872 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
873 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
874 avahi_log_warn("Invalid legacy unicast query packet.");
878 legacy_unicast = TRUE;
882 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
884 handle_query_packet(s, p, i, &a, port, legacy_unicast);
886 /* avahi_log_debug("Handled query"); */
889 if (port != AVAHI_MDNS_PORT) {
890 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
895 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
896 if (s->config.check_response_ttl)
900 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
901 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
902 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
903 avahi_log_warn("Invalid response packet.");
907 handle_response_packet(s, p, i, &a);
908 /* avahi_log_debug("Handled response"); */
912 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
913 AvahiInterface *i, *j;
916 AvahiLegacyUnicastReflectSlot *slot;
923 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
924 !avahi_interface_relevant(i)) {
925 avahi_log_warn("Recieved packet from invalid interface.");
929 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
931 port = avahi_port_from_sockaddr(sa);
932 avahi_address_from_sockaddr(sa, &a);
934 if (avahi_address_is_ipv4_in_ipv6(&a))
935 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
938 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
939 avahi_log_warn("Recieved invalid packet.");
943 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
944 avahi_log_warn("Recieved legacy unicast response with unknown id");
948 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
949 !avahi_interface_relevant(j))
952 /* Patch the original ID into this response */
953 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
955 /* Forward the response to the correct client */
956 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
958 /* Undo changes to packet */
959 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
962 static void work(AvahiServer *s) {
963 struct sockaddr_in6 sa6;
964 struct sockaddr_in sa;
971 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
972 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
973 dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
974 avahi_dns_packet_free(p);
978 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
979 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
980 dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
981 avahi_dns_packet_free(p);
985 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
986 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
987 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
988 avahi_dns_packet_free(p);
992 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
993 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
994 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
995 avahi_dns_packet_free(p);
1000 static gboolean prepare_func(GSource *source, gint *timeout) {
1008 static gboolean check_func(GSource *source) {
1010 gushort revents = 0;
1014 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1017 if (s->fd_ipv4 >= 0)
1018 revents |= s->pollfd_ipv4.revents;
1019 if (s->fd_ipv6 >= 0)
1020 revents |= s->pollfd_ipv6.revents;
1021 if (s->fd_legacy_unicast_ipv4 >= 0)
1022 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1023 if (s->fd_legacy_unicast_ipv6 >= 0)
1024 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1026 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1029 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1033 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1042 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1045 if (s->state == state)
1051 s->callback(s, state, s->userdata);
1054 static void withdraw_host_rrs(AvahiServer *s) {
1057 if (s->hinfo_entry_group) {
1058 avahi_entry_group_free(s->hinfo_entry_group);
1059 s->hinfo_entry_group = NULL;
1062 if (s->browse_domain_entry_group) {
1063 avahi_entry_group_free(s->browse_domain_entry_group);
1064 s->browse_domain_entry_group = NULL;
1067 avahi_update_host_rrs(s->monitor, TRUE);
1068 s->n_host_rr_pending = 0;
1071 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1074 g_assert(s->n_host_rr_pending > 0);
1076 if (--s->n_host_rr_pending == 0)
1077 server_set_state(s, AVAHI_SERVER_RUNNING);
1080 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1083 s->n_host_rr_pending ++;
1086 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1090 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1091 s->state == AVAHI_SERVER_REGISTERING)
1092 avahi_server_increase_host_rr_pending(s);
1093 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1094 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1095 withdraw_host_rrs(s);
1096 server_set_state(s, AVAHI_SERVER_COLLISION);
1097 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1098 s->state == AVAHI_SERVER_REGISTERING)
1099 avahi_server_decrease_host_rr_pending(s);
1102 static void register_hinfo(AvahiServer *s) {
1103 struct utsname utsname;
1108 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1111 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1113 /* Fill in HINFO rr */
1114 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1116 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1117 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1118 avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1119 avahi_record_unref(r);
1121 avahi_entry_group_commit(s->hinfo_entry_group);
1124 static void register_localhost(AvahiServer *s) {
1128 /* Add localhost entries */
1129 avahi_address_parse("127.0.0.1", AF_INET, &a);
1130 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1132 avahi_address_parse("::1", AF_INET6, &a);
1133 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1136 static void register_browse_domain(AvahiServer *s) {
1139 if (!s->config.publish_domain || s->browse_domain_entry_group)
1142 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1143 avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1144 avahi_entry_group_commit(s->browse_domain_entry_group);
1147 static void register_stuff(AvahiServer *s) {
1150 server_set_state(s, AVAHI_SERVER_REGISTERING);
1152 register_browse_domain(s);
1153 avahi_update_host_rrs(s->monitor, FALSE);
1155 if (s->n_host_rr_pending == 0)
1156 server_set_state(s, AVAHI_SERVER_RUNNING);
1159 static void update_fqdn(AvahiServer *s) {
1162 g_assert(s->host_name);
1163 g_assert(s->domain_name);
1165 g_free(s->host_name_fqdn);
1166 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1169 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1170 AvahiServer *s = userdata;
1175 g_assert(e == s->register_time_event);
1176 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1177 s->register_time_event = NULL;
1179 if (s->state == AVAHI_SERVER_SLEEPING)
1183 static void delayed_register_stuff(AvahiServer *s) {
1188 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1190 if (s->register_time_event)
1191 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1193 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1196 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1198 g_assert(host_name);
1200 server_set_state(s, AVAHI_SERVER_SLEEPING);
1201 withdraw_host_rrs(s);
1203 g_free(s->host_name);
1204 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1205 s->host_name[strcspn(s->host_name, ".")] = 0;
1208 delayed_register_stuff(s);
1212 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1214 g_assert(domain_name);
1216 server_set_state(s, AVAHI_SERVER_SLEEPING);
1217 withdraw_host_rrs(s);
1219 g_free(s->domain_name);
1220 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1223 delayed_register_stuff(s);
1228 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1233 memset(pollfd, 0, sizeof(GPollFD));
1235 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1236 g_source_add_poll(s->source, pollfd);
1239 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1242 static GSourceFuncs source_funcs = {
1251 s = g_new(AvahiServer, 1);
1252 s->n_host_rr_pending = 0;
1253 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1256 avahi_server_config_copy(&s->config, sc);
1258 avahi_server_config_init(&s->config);
1260 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1261 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1263 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1264 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1265 avahi_server_config_free(&s->config);
1270 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1271 avahi_log_debug("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1272 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1273 avahi_log_debug("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1275 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1276 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1279 g_main_context_ref(s->context = c);
1281 s->context = g_main_context_default();
1283 /* Prepare IO source registration */
1284 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1285 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1287 if (s->fd_ipv4 >= 0)
1288 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1289 if (s->fd_ipv6 >= 0)
1290 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1291 if (s->fd_legacy_unicast_ipv4 >= 0)
1292 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1293 if (s->fd_legacy_unicast_ipv6 >= 0)
1294 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1296 g_source_attach(s->source, s->context);
1298 s->callback = callback;
1299 s->userdata = userdata;
1301 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1302 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1303 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1305 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1306 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1307 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1308 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1309 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1310 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1311 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1312 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1314 s->legacy_unicast_reflect_slots = NULL;
1315 s->legacy_unicast_reflect_id = 0;
1318 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1319 s->host_name[strcspn(s->host_name, ".")] = 0;
1320 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1321 s->host_name_fqdn = NULL;
1324 s->record_list = avahi_record_list_new();
1326 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1327 s->register_time_event = NULL;
1329 s->state = AVAHI_SERVER_INVALID;
1331 s->monitor = avahi_interface_monitor_new(s);
1332 avahi_interface_monitor_sync(s->monitor);
1334 register_localhost(s);
1336 s->hinfo_entry_group = NULL;
1337 s->browse_domain_entry_group = NULL;
1343 void avahi_server_free(AvahiServer* s) {
1347 free_entry(s, s->entries);
1349 avahi_interface_monitor_free(s->monitor);
1352 free_group(s, s->groups);
1356 while (s->host_name_resolvers)
1357 avahi_host_name_resolver_free(s->host_name_resolvers);
1358 while (s->address_resolvers)
1359 avahi_address_resolver_free(s->address_resolvers);
1360 while (s->domain_browsers)
1361 avahi_domain_browser_free(s->domain_browsers);
1362 while (s->service_type_browsers)
1363 avahi_service_type_browser_free(s->service_type_browsers);
1364 while (s->service_browsers)
1365 avahi_service_browser_free(s->service_browsers);
1366 while (s->service_resolvers)
1367 avahi_service_resolver_free(s->service_resolvers);
1368 while (s->record_browsers)
1369 avahi_record_browser_destroy(s->record_browsers);
1370 g_hash_table_destroy(s->record_browser_hashtable);
1372 g_hash_table_destroy(s->entries_by_key);
1374 if (s->register_time_event)
1375 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1376 avahi_time_event_queue_free(s->time_event_queue);
1378 avahi_record_list_free(s->record_list);
1380 if (s->fd_ipv4 >= 0)
1382 if (s->fd_ipv6 >= 0)
1384 if (s->fd_legacy_unicast_ipv4 >= 0)
1385 close(s->fd_legacy_unicast_ipv4);
1386 if (s->fd_legacy_unicast_ipv6 >= 0)
1387 close(s->fd_legacy_unicast_ipv6);
1389 g_free(s->host_name);
1390 g_free(s->domain_name);
1391 g_free(s->host_name_fqdn);
1393 g_source_destroy(s->source);
1394 g_source_unref(s->source);
1395 g_main_context_unref(s->context);
1397 avahi_server_config_free(&s->config);
1402 gint avahi_server_add(
1407 AvahiEntryFlags flags,
1414 g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1416 e = g_new(AvahiEntry, 1);
1418 e->record = avahi_record_ref(r);
1420 e->interface = interface;
1421 e->protocol = protocol;
1425 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1427 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1429 /* Insert into hash table indexed by name */
1430 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1431 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1432 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1434 /* Insert into group list */
1436 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1438 avahi_announce_entry(s, e);
1443 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1444 AvahiEntry **e = (AvahiEntry**) state;
1449 *e = g ? g->entries : s->entries;
1451 while (*e && (*e)->dead)
1452 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1457 return avahi_record_ref((*e)->record);
1460 void avahi_server_dump(AvahiServer *s, FILE *f) {
1465 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1467 for (e = s->entries; e; e = e->entries_next) {
1473 t = avahi_record_to_string(e->record);
1474 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1478 avahi_dump_caches(s->monitor, f);
1481 gint avahi_server_add_ptr(
1486 AvahiEntryFlags flags,
1488 const gchar *dest) {
1494 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1495 r->data.ptr.name = avahi_normalize_name(dest);
1496 avahi_server_add(s, g, interface, protocol, flags, r);
1497 avahi_record_unref(r);
1501 gint avahi_server_add_address(
1506 AvahiEntryFlags flags,
1514 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1516 if (a->family == AF_INET) {
1520 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1521 r->data.a.address = a->data.ipv4;
1522 avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1523 avahi_record_unref(r);
1525 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1526 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1533 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1534 r->data.aaaa.address = a->data.ipv6;
1535 avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1536 avahi_record_unref(r);
1538 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1539 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1542 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1543 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1552 gint avahi_server_add_text_strlst(
1557 AvahiEntryFlags flags,
1559 AvahiStringList *strlst) {
1565 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1566 r->data.txt.string_list = strlst;
1567 avahi_server_add(s, g, interface, protocol, flags, r);
1568 avahi_record_unref(r);
1573 gint avahi_server_add_text_va(
1578 AvahiEntryFlags flags,
1584 avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1588 gint avahi_server_add_text(
1593 AvahiEntryFlags flags,
1602 avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1608 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1613 while (*s && size >= 2) {
1614 if (*s == '.' || *s == '\\') {
1630 gint avahi_server_add_service_strlst(
1637 const gchar *domain,
1640 AvahiStringList *strlst) {
1642 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1649 escape_service_name(ename, sizeof(ename), name);
1652 while (domain[0] == '.')
1655 domain = s->domain_name;
1658 host = s->host_name_fqdn;
1660 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1661 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1663 avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1665 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1666 r->data.srv.priority = 0;
1667 r->data.srv.weight = 0;
1668 r->data.srv.port = port;
1669 r->data.srv.name = avahi_normalize_name(host);
1670 avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1671 avahi_record_unref(r);
1673 avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1675 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1676 avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1681 gint avahi_server_add_service_va(
1688 const gchar *domain,
1697 avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1701 gint avahi_server_add_service(
1708 const gchar *domain,
1720 avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1725 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1726 AvahiKey *k = userdata;
1732 avahi_interface_post_query(i, k, FALSE);
1735 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1739 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1742 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1745 if (g->state == state)
1751 g->callback(g->server, g, state, g->userdata);
1756 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1761 g = g_new(AvahiEntryGroup, 1);
1763 g->callback = callback;
1764 g->userdata = userdata;
1766 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1768 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1770 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1774 void avahi_entry_group_free(AvahiEntryGroup *g) {
1778 g_assert(g->server);
1780 for (e = g->entries; e; e = e->by_group_next) {
1781 avahi_goodbye_entry(g->server, e, TRUE);
1787 g->server->need_group_cleanup = TRUE;
1788 g->server->need_entry_cleanup = TRUE;
1791 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1795 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1798 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1799 avahi_announce_group(g->server, g);
1800 avahi_entry_group_check_probed(g, FALSE);
1805 gboolean avahi_entry_commited(AvahiEntry *e) {
1810 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1811 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1814 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1821 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1824 g->userdata = userdata;
1827 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1833 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1836 return s->domain_name;
1839 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1842 return s->host_name;
1845 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1848 return s->host_name_fqdn;
1851 gpointer avahi_server_get_data(AvahiServer *s) {
1857 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1860 s->userdata = userdata;
1863 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1869 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1872 memset(c, 0, sizeof(AvahiServerConfig));
1875 c->host_name = NULL;
1876 c->domain_name = NULL;
1877 c->check_response_ttl = TRUE;
1878 c->publish_hinfo = TRUE;
1879 c->publish_addresses = TRUE;
1880 c->publish_workstation = TRUE;
1881 c->publish_domain = TRUE;
1882 c->use_iff_running = FALSE;
1883 c->enable_reflector = FALSE;
1884 c->reflect_ipv = FALSE;
1889 void avahi_server_config_free(AvahiServerConfig *c) {
1892 g_free(c->host_name);
1893 g_free(c->domain_name);
1896 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1902 ret->host_name = g_strdup(c->host_name);
1903 ret->domain_name = g_strdup(c->domain_name);