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->clazz == 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 t = avahi_record_to_string(record);
228 /* avahi_log_debug("incoming_probe()"); */
230 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
237 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
242 if (avahi_entry_probing(s, e, i)) {
254 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
256 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
257 withdraw_rrset(s, record->key);
259 /* avahi_log_debug("Not conflicting probe"); */
265 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
266 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
267 AvahiEntry *e, *n, *conflicting_entry = NULL;
274 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
276 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
279 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
282 /* Either our entry or the other is intended to be unique, so let's check */
284 if (avahi_record_equal_no_ttl(e->record, record)) {
285 ours = TRUE; /* We have an identical record, so this is no conflict */
287 /* Check wheter there is a TTL conflict */
288 if (record->ttl <= e->record->ttl/2 &&
289 avahi_entry_registered(s, e, i)) {
292 t = avahi_record_to_string(record);
294 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
295 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
301 /* There's no need to check the other entries of this RRset */
306 if (avahi_entry_registered(s, e, i)) {
308 /* A conflict => we have to return to probe mode */
310 conflicting_entry = e;
312 } else if (avahi_entry_probing(s, e, i)) {
314 /* We are currently registering a matching record, but
315 * someone else already claimed it, so let's
318 withdraw_immediately = TRUE;
323 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
325 if (!ours && conflict) {
330 t = avahi_record_to_string(record);
332 if (withdraw_immediately) {
333 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
334 withdraw_rrset(s, record->key);
336 g_assert(conflicting_entry);
337 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
338 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
340 /* Local unique records are returned to probing
341 * state. Local shared records are reannounced. */
350 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
351 gboolean *unicast_response = userdata;
355 g_assert(unicast_response);
357 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
360 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
364 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
367 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
371 g_assert(!legacy_unicast || (a && port > 0 && p));
373 if (legacy_unicast) {
374 AvahiDnsPacket *reply;
377 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
379 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
381 append_aux_records_to_list(s, i, r, FALSE);
383 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
384 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
386 gchar *t = avahi_record_to_string(r);
387 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
391 avahi_record_unref(r);
394 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
395 avahi_interface_send_packet_unicast(i, reply, a, port);
397 avahi_dns_packet_free(reply);
400 gboolean unicast_response, flush_cache, auxiliary;
401 AvahiDnsPacket *reply = NULL;
404 /* In case the query packet was truncated never respond
405 immediately, because known answer suppression records might be
406 contained in later packets */
407 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
409 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
411 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
413 append_aux_records_to_list(s, i, r, unicast_response);
415 /* Due to some reasons the record has not been scheduled.
416 * The client requested an unicast response in that
417 * case. Therefore we prepare such a response */
423 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
426 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
428 /* Appending this record succeeded, so incremeant
429 * the specific header field, and return to the caller */
431 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
436 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
439 /* The record is too large for one packet, so create a larger packet */
441 avahi_dns_packet_free(reply);
442 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
443 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
444 size = AVAHI_DNS_PACKET_MAX_SIZE;
445 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
447 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
448 avahi_dns_packet_free(reply);
450 gchar *t = avahi_record_to_string(r);
451 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
455 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
458 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
459 avahi_interface_send_packet_unicast(i, reply, a, port);
460 avahi_dns_packet_free(reply);
465 avahi_record_unref(r);
469 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
470 avahi_interface_send_packet_unicast(i, reply, a, port);
471 avahi_dns_packet_free(reply);
475 avahi_record_list_flush(s->record_list);
479 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
486 if (!s->config.enable_reflector)
489 for (j = s->monitor->interfaces; j; j = j->interface_next)
490 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
491 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
494 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
495 AvahiServer *s = userdata;
502 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
506 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
513 if (!s->config.enable_reflector)
516 for (j = s->monitor->interfaces; j; j = j->interface_next)
517 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
518 /* Post the query to other networks */
519 avahi_interface_post_query(j, k, TRUE);
521 /* Reply from caches of other network. This is needed to
522 * "work around" known answer suppression. */
524 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
528 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
535 if (!s->config.enable_reflector)
538 for (j = s->monitor->interfaces; j; j = j->interface_next)
539 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
540 avahi_interface_post_probe(j, r, TRUE);
543 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
552 /* avahi_log_debug("query"); */
554 g_assert(avahi_record_list_empty(s->record_list));
556 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
558 /* Handle the questions */
559 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
561 gboolean unicast_response = FALSE;
563 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
564 avahi_log_warn("Packet too short (1)");
569 reflect_query(s, i, key);
571 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
572 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
573 /* Allow our own queries to be suppressed by incoming
574 * queries only when they do not include known answers */
575 avahi_query_scheduler_incoming(i->query_scheduler, key);
577 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
578 avahi_key_unref(key);
581 /* Known Answer Suppression */
582 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
584 gboolean unique = FALSE;
586 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
587 avahi_log_warn("Packet too short (2)");
591 if (handle_conflict(s, i, record, unique, a)) {
592 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
593 avahi_record_list_drop(s->record_list, record);
596 avahi_record_unref(record);
600 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
602 gboolean unique = FALSE;
604 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
605 avahi_log_warn("Packet too short (3)");
609 if (!avahi_key_is_pattern(record->key)) {
610 reflect_probe(s, i, record);
611 incoming_probe(s, record, i);
614 avahi_record_unref(record);
617 if (!avahi_record_list_empty(s->record_list))
618 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
623 avahi_record_list_flush(s->record_list);
626 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
634 /* avahi_log_debug("response"); */
636 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
637 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
639 gboolean cache_flush = FALSE;
642 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
643 avahi_log_warn("Packet too short (4)");
647 if (!avahi_key_is_pattern(record->key)) {
649 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
652 if (handle_conflict(s, i, record, cache_flush, a)) {
653 reflect_response(s, i, record, cache_flush);
654 avahi_cache_update(i->cache, record, cache_flush, a);
655 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
659 avahi_record_unref(record);
662 /* If the incoming response contained a conflicting record, some
663 records have been scheduling for sending. We need to flush them
665 if (!avahi_record_list_empty(s->record_list))
666 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
669 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
670 guint n, index = (guint) -1;
671 AvahiLegacyUnicastReflectSlot *slot;
675 if (!s->legacy_unicast_reflect_slots)
676 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
678 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
679 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
681 if (!s->legacy_unicast_reflect_slots[index])
685 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
688 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
689 slot->id = s->legacy_unicast_reflect_id++;
694 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
700 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
702 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
704 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
707 s->legacy_unicast_reflect_slots[index] = NULL;
710 static void free_slots(AvahiServer *s) {
714 if (!s->legacy_unicast_reflect_slots)
717 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
718 if (s->legacy_unicast_reflect_slots[index])
719 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
721 g_free(s->legacy_unicast_reflect_slots);
722 s->legacy_unicast_reflect_slots = NULL;
725 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
730 if (!s->legacy_unicast_reflect_slots)
733 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
735 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
738 return s->legacy_unicast_reflect_slots[index];
741 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
742 AvahiLegacyUnicastReflectSlot *slot = userdata;
746 g_assert(slot->time_event == e);
748 deallocate_slot(slot->server, slot);
751 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
752 AvahiLegacyUnicastReflectSlot *slot;
760 g_assert(i->protocol == a->family);
762 if (!s->config.enable_reflector)
765 /* avahi_log_debug("legacy unicast reflectr"); */
767 /* Reflecting legacy unicast queries is a little more complicated
768 than reflecting normal queries, since we must route the
769 responses back to the right client. Therefore we must store
770 some information for finding the right client contact data for
771 response packets. In contrast to normal queries legacy
772 unicast query and response packets are reflected untouched and
773 are not reassembled into larger packets */
775 if (!(slot = allocate_slot(s))) {
776 /* No slot available, we drop this legacy unicast query */
777 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
781 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
784 slot->interface = i->hardware->index;
786 avahi_elapse_time(&slot->elapse_time, 2000, 0);
787 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
789 /* Patch the packet with our new locally generatedt id */
790 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
792 for (j = s->monitor->interfaces; j; j = j->interface_next)
793 if (avahi_interface_relevant(j) &&
795 (s->config.reflect_ipv || j->protocol == i->protocol)) {
797 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
798 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
799 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
800 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
804 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
807 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
812 if (!s->config.enable_reflector)
815 avahi_address_from_sockaddr(sa, &a);
817 if (!avahi_address_is_local(s->monitor, &a))
820 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
821 struct sockaddr_in lsa;
822 socklen_t l = sizeof(lsa);
824 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
825 avahi_log_warn("getsockname(): %s", strerror(errno));
827 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
831 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
832 struct sockaddr_in6 lsa;
833 socklen_t l = sizeof(lsa);
835 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
836 avahi_log_warn("getsockname(): %s", strerror(errno));
838 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
844 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
848 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
849 return avahi_address_cmp(a, &b) == 0;
852 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
863 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
864 !avahi_interface_relevant(i)) {
865 avahi_log_warn("Recieved packet from invalid interface.");
869 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
871 port = avahi_port_from_sockaddr(sa);
872 avahi_address_from_sockaddr(sa, &a);
874 if (avahi_address_is_ipv4_in_ipv6(&a))
875 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
878 if (originates_from_local_legacy_unicast_socket(s, sa))
879 /* This originates from our local reflector, so let's ignore it */
882 if (avahi_dns_packet_check_valid(p) < 0) {
883 avahi_log_warn("Recieved invalid packet.");
887 if (avahi_dns_packet_is_query(p)) {
888 gboolean legacy_unicast = FALSE;
890 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
891 avahi_log_warn("Invalid query packet.");
895 if (port != AVAHI_MDNS_PORT) {
898 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
899 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
900 avahi_log_warn("Invalid legacy unicast query packet.");
904 legacy_unicast = TRUE;
908 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
910 handle_query_packet(s, p, i, &a, port, legacy_unicast);
912 /* avahi_log_debug("Handled query"); */
914 if (port != AVAHI_MDNS_PORT) {
915 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
919 if (ttl != 255 && s->config.check_response_ttl) {
920 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
924 if (!is_mdns_mcast_address(dest) &&
925 !avahi_interface_address_on_link(i, &a)) {
926 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
930 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
931 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
932 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
933 avahi_log_warn("Invalid response packet.");
937 handle_response_packet(s, p, i, &a);
938 /* avahi_log_debug("Handled response"); */
942 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
943 AvahiInterface *i, *j;
946 AvahiLegacyUnicastReflectSlot *slot;
953 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
954 !avahi_interface_relevant(i)) {
955 avahi_log_warn("Recieved packet from invalid interface.");
959 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
961 port = avahi_port_from_sockaddr(sa);
962 avahi_address_from_sockaddr(sa, &a);
964 if (avahi_address_is_ipv4_in_ipv6(&a))
965 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
968 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
969 avahi_log_warn("Recieved invalid packet.");
973 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
974 avahi_log_warn("Recieved legacy unicast response with unknown id");
978 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
979 !avahi_interface_relevant(j))
982 /* Patch the original ID into this response */
983 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
985 /* Forward the response to the correct client */
986 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
988 /* Undo changes to packet */
989 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
992 static void work(AvahiServer *s) {
993 struct sockaddr_in6 sa6;
994 struct sockaddr_in sa;
1002 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1003 dest.family = AVAHI_PROTO_INET;
1004 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1005 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1006 avahi_dns_packet_free(p);
1010 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1011 dest.family = AVAHI_PROTO_INET6;
1012 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1013 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1014 avahi_dns_packet_free(p);
1018 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1019 dest.family = AVAHI_PROTO_INET;
1020 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1021 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1022 avahi_dns_packet_free(p);
1026 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1027 dest.family = AVAHI_PROTO_INET6;
1028 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1029 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1030 avahi_dns_packet_free(p);
1035 static gboolean prepare_func(GSource *source, gint *timeout) {
1043 static gboolean check_func(GSource *source) {
1045 gushort revents = 0;
1049 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1052 if (s->fd_ipv4 >= 0)
1053 revents |= s->pollfd_ipv4.revents;
1054 if (s->fd_ipv6 >= 0)
1055 revents |= s->pollfd_ipv6.revents;
1056 if (s->fd_legacy_unicast_ipv4 >= 0)
1057 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1058 if (s->fd_legacy_unicast_ipv6 >= 0)
1059 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1061 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1064 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1068 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1077 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1080 if (s->state == state)
1086 s->callback(s, state, s->userdata);
1089 static void withdraw_host_rrs(AvahiServer *s) {
1092 if (s->hinfo_entry_group) {
1093 avahi_entry_group_free(s->hinfo_entry_group);
1094 s->hinfo_entry_group = NULL;
1097 if (s->browse_domain_entry_group) {
1098 avahi_entry_group_free(s->browse_domain_entry_group);
1099 s->browse_domain_entry_group = NULL;
1102 avahi_update_host_rrs(s->monitor, TRUE);
1103 s->n_host_rr_pending = 0;
1106 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1109 g_assert(s->n_host_rr_pending > 0);
1111 if (--s->n_host_rr_pending == 0)
1112 server_set_state(s, AVAHI_SERVER_RUNNING);
1115 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1118 s->n_host_rr_pending ++;
1121 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1125 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1126 s->state == AVAHI_SERVER_REGISTERING)
1127 avahi_server_increase_host_rr_pending(s);
1128 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1129 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1130 withdraw_host_rrs(s);
1131 server_set_state(s, AVAHI_SERVER_COLLISION);
1132 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1133 s->state == AVAHI_SERVER_REGISTERING)
1134 avahi_server_decrease_host_rr_pending(s);
1137 static void register_hinfo(AvahiServer *s) {
1138 struct utsname utsname;
1143 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1146 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1148 /* Fill in HINFO rr */
1149 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1151 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1152 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1153 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1154 avahi_record_unref(r);
1156 avahi_entry_group_commit(s->hinfo_entry_group);
1159 static void register_localhost(AvahiServer *s) {
1163 /* Add localhost entries */
1164 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1165 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1167 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1168 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1171 static void register_browse_domain(AvahiServer *s) {
1174 if (!s->config.publish_domain || s->browse_domain_entry_group)
1177 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1178 avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name);
1179 avahi_entry_group_commit(s->browse_domain_entry_group);
1182 static void register_stuff(AvahiServer *s) {
1185 server_set_state(s, AVAHI_SERVER_REGISTERING);
1187 register_browse_domain(s);
1188 avahi_update_host_rrs(s->monitor, FALSE);
1190 if (s->n_host_rr_pending == 0)
1191 server_set_state(s, AVAHI_SERVER_RUNNING);
1194 static void update_fqdn(AvahiServer *s) {
1197 g_assert(s->host_name);
1198 g_assert(s->domain_name);
1200 g_free(s->host_name_fqdn);
1201 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1204 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1205 AvahiServer *s = userdata;
1210 g_assert(e == s->register_time_event);
1211 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1212 s->register_time_event = NULL;
1214 if (s->state == AVAHI_SERVER_SLEEPING)
1218 static void delayed_register_stuff(AvahiServer *s) {
1223 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1225 if (s->register_time_event)
1226 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1228 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1231 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1233 g_assert(host_name);
1235 server_set_state(s, AVAHI_SERVER_SLEEPING);
1236 withdraw_host_rrs(s);
1238 g_free(s->host_name);
1239 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1240 s->host_name[strcspn(s->host_name, ".")] = 0;
1243 delayed_register_stuff(s);
1247 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1249 g_assert(domain_name);
1251 server_set_state(s, AVAHI_SERVER_SLEEPING);
1252 withdraw_host_rrs(s);
1254 g_free(s->domain_name);
1255 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1258 delayed_register_stuff(s);
1263 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1268 memset(pollfd, 0, sizeof(GPollFD));
1270 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1271 g_source_add_poll(s->source, pollfd);
1274 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1277 static GSourceFuncs source_funcs = {
1286 s = g_new(AvahiServer, 1);
1287 s->n_host_rr_pending = 0;
1288 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1291 avahi_server_config_copy(&s->config, sc);
1293 avahi_server_config_init(&s->config);
1295 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1296 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1298 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1299 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1300 avahi_server_config_free(&s->config);
1305 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1306 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1307 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1308 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1310 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1311 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1313 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1315 /* Prepare IO source registration */
1316 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1317 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1319 if (s->fd_ipv4 >= 0)
1320 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1321 if (s->fd_ipv6 >= 0)
1322 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1323 if (s->fd_legacy_unicast_ipv4 >= 0)
1324 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1325 if (s->fd_legacy_unicast_ipv6 >= 0)
1326 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1328 g_source_attach(s->source, s->context);
1330 s->callback = callback;
1331 s->userdata = userdata;
1333 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1334 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1335 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1337 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1338 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1339 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1340 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1341 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1342 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1343 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1344 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1345 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1347 s->legacy_unicast_reflect_slots = NULL;
1348 s->legacy_unicast_reflect_id = 0;
1351 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1352 s->host_name[strcspn(s->host_name, ".")] = 0;
1353 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1354 s->host_name_fqdn = NULL;
1357 s->record_list = avahi_record_list_new();
1359 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1360 s->register_time_event = NULL;
1362 s->state = AVAHI_SERVER_INVALID;
1364 s->monitor = avahi_interface_monitor_new(s);
1365 avahi_interface_monitor_sync(s->monitor);
1367 register_localhost(s);
1369 s->hinfo_entry_group = NULL;
1370 s->browse_domain_entry_group = NULL;
1376 void avahi_server_free(AvahiServer* s) {
1380 free_entry(s, s->entries);
1382 avahi_interface_monitor_free(s->monitor);
1385 free_group(s, s->groups);
1389 while (s->dns_server_browsers)
1390 avahi_dns_server_browser_free(s->dns_server_browsers);
1391 while (s->host_name_resolvers)
1392 avahi_host_name_resolver_free(s->host_name_resolvers);
1393 while (s->address_resolvers)
1394 avahi_address_resolver_free(s->address_resolvers);
1395 while (s->domain_browsers)
1396 avahi_domain_browser_free(s->domain_browsers);
1397 while (s->service_type_browsers)
1398 avahi_service_type_browser_free(s->service_type_browsers);
1399 while (s->service_browsers)
1400 avahi_service_browser_free(s->service_browsers);
1401 while (s->service_resolvers)
1402 avahi_service_resolver_free(s->service_resolvers);
1403 while (s->record_browsers)
1404 avahi_record_browser_destroy(s->record_browsers);
1405 g_hash_table_destroy(s->record_browser_hashtable);
1407 g_hash_table_destroy(s->entries_by_key);
1409 if (s->register_time_event)
1410 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1411 avahi_time_event_queue_free(s->time_event_queue);
1413 avahi_record_list_free(s->record_list);
1415 if (s->fd_ipv4 >= 0)
1417 if (s->fd_ipv6 >= 0)
1419 if (s->fd_legacy_unicast_ipv4 >= 0)
1420 close(s->fd_legacy_unicast_ipv4);
1421 if (s->fd_legacy_unicast_ipv6 >= 0)
1422 close(s->fd_legacy_unicast_ipv6);
1424 g_free(s->host_name);
1425 g_free(s->domain_name);
1426 g_free(s->host_name_fqdn);
1428 g_source_destroy(s->source);
1429 g_source_unref(s->source);
1430 g_main_context_unref(s->context);
1432 avahi_server_config_free(&s->config);
1437 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1443 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1447 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1450 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1453 if (interface <= 0 ||
1454 e->interface <= 0 ||
1455 e->interface == interface ||
1456 protocol == AVAHI_PROTO_UNSPEC ||
1457 e->protocol == AVAHI_PROTO_UNSPEC ||
1458 e->protocol == protocol)
1467 gint avahi_server_add(
1472 AvahiEntryFlags flags,
1483 if (avahi_key_is_pattern(r->key))
1486 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1489 e = g_new(AvahiEntry, 1);
1491 e->record = avahi_record_ref(r);
1493 e->interface = interface;
1494 e->protocol = protocol;
1498 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1500 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1502 /* Insert into hash table indexed by name */
1503 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1504 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1505 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1507 /* Insert into group list */
1509 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1511 avahi_announce_entry(s, e);
1516 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1517 AvahiEntry **e = (AvahiEntry**) state;
1522 *e = g ? g->entries : s->entries;
1524 while (*e && (*e)->dead)
1525 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1530 return avahi_record_ref((*e)->record);
1533 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1539 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1541 for (e = s->entries; e; e = e->entries_next) {
1548 t = avahi_record_to_string(e->record);
1549 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1552 callback(ln, userdata);
1555 avahi_dump_caches(s->monitor, callback, userdata);
1558 gint avahi_server_add_ptr(
1563 AvahiEntryFlags flags,
1566 const gchar *dest) {
1573 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1574 r->data.ptr.name = avahi_normalize_name(dest);
1575 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1576 avahi_record_unref(r);
1580 gint avahi_server_add_address(
1585 AvahiEntryFlags flags,
1594 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1596 if (a->family == AVAHI_PROTO_INET) {
1600 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1601 r->data.a.address = a->data.ipv4;
1602 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1603 avahi_record_unref(r);
1605 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1606 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1613 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1614 r->data.aaaa.address = a->data.ipv6;
1615 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1616 avahi_record_unref(r);
1618 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1619 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1622 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1623 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1632 gint avahi_server_add_txt_strlst(
1637 AvahiEntryFlags flags,
1640 AvahiStringList *strlst) {
1647 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1648 r->data.txt.string_list = strlst;
1649 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1650 avahi_record_unref(r);
1655 gint avahi_server_add_txt_va(
1660 AvahiEntryFlags flags,
1667 return avahi_server_add_txt_strlst(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1670 gint avahi_server_add_txt(
1675 AvahiEntryFlags flags,
1686 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1692 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1697 while (*s && size >= 2) {
1698 if (*s == '.' || *s == '\\') {
1714 gint avahi_server_add_service_strlst(
1721 const gchar *domain,
1724 AvahiStringList *strlst) {
1726 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1735 escape_service_name(ename, sizeof(ename), name);
1738 while (domain[0] == '.')
1741 domain = s->domain_name;
1744 host = s->host_name_fqdn;
1746 d = avahi_normalize_name(domain);
1747 t = avahi_normalize_name(type);
1749 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1750 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1752 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1754 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1755 r->data.srv.priority = 0;
1756 r->data.srv.weight = 0;
1757 r->data.srv.port = port;
1758 r->data.srv.name = avahi_normalize_name(host);
1759 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1760 avahi_record_unref(r);
1762 ret |= avahi_server_add_txt_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1764 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1765 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1773 gint avahi_server_add_service_va(
1780 const gchar *domain,
1789 return avahi_server_add_service_strlst(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1792 gint avahi_server_add_service(
1799 const gchar *domain,
1812 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1817 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1818 static const gchar hex[] = "0123456789abcdef";
1820 const guint8 *k = p;
1822 while (sl > 1 && pl > 0) {
1823 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1839 gint avahi_server_add_dns_server_address(
1844 const gchar *domain,
1845 AvahiDNSServerType type,
1846 const AvahiAddress *address,
1847 guint16 port /** should be 53 */) {
1851 gchar n[64] = "ip-";
1855 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1856 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1858 if (address->family == AVAHI_PROTO_INET) {
1859 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1860 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1861 r->data.a.address = address->data.ipv4;
1863 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1864 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1865 r->data.aaaa.address = address->data.ipv6;
1868 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1869 avahi_record_unref(r);
1871 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1876 gint avahi_server_add_dns_server_name(
1881 const gchar *domain,
1882 AvahiDNSServerType type,
1884 guint16 port /** should be 53 */) {
1892 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1895 while (domain[0] == '.')
1898 domain = s->domain_name;
1900 d = avahi_normalize_name(domain);
1901 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
1904 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1905 r->data.srv.priority = 0;
1906 r->data.srv.weight = 0;
1907 r->data.srv.port = port;
1908 r->data.srv.name = avahi_normalize_name(name);
1909 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1910 avahi_record_unref(r);
1915 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1916 AvahiKey *k = userdata;
1922 avahi_interface_post_query(i, k, FALSE);
1925 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1929 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1932 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1935 if (g->state == state)
1941 g->callback(g->server, g, state, g->userdata);
1946 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1951 g = g_new(AvahiEntryGroup, 1);
1953 g->callback = callback;
1954 g->userdata = userdata;
1956 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1958 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1960 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1964 void avahi_entry_group_free(AvahiEntryGroup *g) {
1968 g_assert(g->server);
1970 for (e = g->entries; e; e = e->by_group_next) {
1971 avahi_goodbye_entry(g->server, e, TRUE);
1977 g->server->need_group_cleanup = TRUE;
1978 g->server->need_entry_cleanup = TRUE;
1981 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1985 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1988 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1989 avahi_announce_group(g->server, g);
1990 avahi_entry_group_check_probed(g, FALSE);
1995 gboolean avahi_entry_commited(AvahiEntry *e) {
2000 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2001 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2004 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2011 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2014 g->userdata = userdata;
2017 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2023 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2026 return s->domain_name;
2029 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2032 return s->host_name;
2035 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2038 return s->host_name_fqdn;
2041 gpointer avahi_server_get_data(AvahiServer *s) {
2047 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2050 s->userdata = userdata;
2053 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2059 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2062 memset(c, 0, sizeof(AvahiServerConfig));
2065 c->host_name = NULL;
2066 c->domain_name = NULL;
2067 c->check_response_ttl = FALSE;
2068 c->publish_hinfo = TRUE;
2069 c->publish_addresses = TRUE;
2070 c->publish_workstation = TRUE;
2071 c->publish_domain = TRUE;
2072 c->use_iff_running = FALSE;
2073 c->enable_reflector = FALSE;
2074 c->reflect_ipv = FALSE;
2079 void avahi_server_config_free(AvahiServerConfig *c) {
2082 g_free(c->host_name);
2083 g_free(c->domain_name);
2086 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2092 ret->host_name = g_strdup(c->host_name);
2093 ret->domain_name = g_strdup(c->domain_name);