4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
40 #define AVAHI_HOST_RR_HOLDOFF_MSEC 2000
42 static void free_entry(AvahiServer*s, AvahiEntry *e) {
48 avahi_goodbye_entry(s, e, TRUE);
50 /* Remove from linked list */
51 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
53 /* Remove from hash table indexed by name */
54 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
55 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
57 g_hash_table_replace(s->entries_by_key, t->record->key, t);
59 g_hash_table_remove(s->entries_by_key, e->record->key);
61 /* Remove from associated group */
63 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
65 avahi_record_unref(e->record);
69 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
74 free_entry(s, g->entries);
76 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
80 static void cleanup_dead(AvahiServer *s) {
81 AvahiEntryGroup *g, *ng;
86 if (s->need_group_cleanup) {
87 for (g = s->groups; g; g = ng) {
94 s->need_group_cleanup = FALSE;
97 if (s->need_entry_cleanup) {
98 for (e = s->entries; e; e = ne) {
105 s->need_entry_cleanup = FALSE;
108 if (s->need_browser_cleanup)
109 avahi_browser_cleanup(s);
112 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
121 g_assert(type != AVAHI_DNS_TYPE_ANY);
123 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
125 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
126 if (!e->dead && avahi_entry_registered(s, e, i))
127 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
132 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
138 if (r->key->class == AVAHI_DNS_CLASS_IN) {
139 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
140 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
141 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
142 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
143 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
144 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
149 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
154 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
157 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
165 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
168 if (avahi_key_is_pattern(k)) {
170 /* Handle ANY query */
172 for (e = s->entries; e; e = e->entries_next)
173 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
174 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
178 /* Handle all other queries */
180 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
181 if (!e->dead && avahi_entry_registered(s, e, i))
182 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
186 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
193 for (k = e->group->entries; k; k = k->by_group_next) {
194 avahi_goodbye_entry(s, k, FALSE);
198 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
200 avahi_goodbye_entry(s, e, FALSE);
204 s->need_entry_cleanup = TRUE;
207 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
213 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
214 withdraw_entry(s, e);
217 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
220 gboolean ours = FALSE, won = FALSE, lost = FALSE;
226 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
233 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
238 if (avahi_entry_probing(s, e, i)) {
247 t = avahi_record_to_string(record);
252 avahi_log_debug("xxx Recieved conflicting probe [%s]. Local host won.", t);
254 avahi_log_debug("yyy Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
255 withdraw_rrset(s, record->key);
262 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
263 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
264 AvahiEntry *e, *n, *conflicting_entry = NULL;
271 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
273 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
276 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
279 /* Either our entry or the other is intended to be unique, so let's check */
281 if (avahi_record_equal_no_ttl(e->record, record)) {
282 ours = TRUE; /* We have an identical record, so this is no conflict */
284 /* Check wheter there is a TTL conflict */
285 if (record->ttl <= e->record->ttl/2 &&
286 avahi_entry_registered(s, e, i)) {
289 t = avahi_record_to_string(record);
291 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
292 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
298 /* There's no need to check the other entries of this RRset */
303 if (avahi_entry_registered(s, e, i)) {
305 /* A conflict => we have to return to probe mode */
307 conflicting_entry = e;
309 } else if (avahi_entry_probing(s, e, i)) {
311 /* We are currently registering a matching record, but
312 * someone else already claimed it, so let's
315 withdraw_immediately = TRUE;
320 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
322 if (!ours && conflict) {
327 t = avahi_record_to_string(record);
329 if (withdraw_immediately) {
330 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
331 withdraw_rrset(s, record->key);
333 g_assert(conflicting_entry);
334 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
335 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
337 /* Local unique records are returned to probin
338 * state. Local shared records are reannounced. */
347 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
348 gboolean *unicast_response = userdata;
352 g_assert(unicast_response);
354 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
357 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
361 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
364 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
368 g_assert(!legacy_unicast || (a && port > 0 && p));
370 if (legacy_unicast) {
371 AvahiDnsPacket *reply;
374 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
376 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
378 append_aux_records_to_list(s, i, r, FALSE);
380 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
381 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
383 gchar *t = avahi_record_to_string(r);
384 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
388 avahi_record_unref(r);
391 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
392 avahi_interface_send_packet_unicast(i, reply, a, port);
394 avahi_dns_packet_free(reply);
397 gboolean unicast_response, flush_cache, auxiliary;
398 AvahiDnsPacket *reply = NULL;
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
406 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
408 if (!avahi_interface_post_response(i, r, flush_cache, a, !tc && flush_cache && !auxiliary) && unicast_response) {
410 append_aux_records_to_list(s, i, r, unicast_response);
412 /* Due to some reasons the record has not been scheduled.
413 * The client requested an unicast response in that
414 * case. Therefore we prepare such a response */
420 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
423 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
425 /* Appending this record succeeded, so incremeant
426 * the specific header field, and return to the caller */
428 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
433 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
436 /* The record is too large for one packet, so create a larger packet */
438 avahi_dns_packet_free(reply);
439 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
440 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
441 size = AVAHI_DNS_PACKET_MAX_SIZE;
442 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
444 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
445 avahi_dns_packet_free(reply);
447 gchar *t = avahi_record_to_string(r);
448 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
452 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
455 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
456 avahi_interface_send_packet_unicast(i, reply, a, port);
457 avahi_dns_packet_free(reply);
462 avahi_record_unref(r);
466 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
467 avahi_interface_send_packet_unicast(i, reply, a, port);
468 avahi_dns_packet_free(reply);
472 avahi_record_list_flush(s->record_list);
476 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
483 if (!s->config.enable_reflector)
486 for (j = s->monitor->interfaces; j; j = j->interface_next)
487 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
488 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
491 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
492 AvahiServer *s = userdata;
499 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
503 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
510 if (!s->config.enable_reflector)
513 for (j = s->monitor->interfaces; j; j = j->interface_next)
514 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
515 /* Post the query to other networks */
516 avahi_interface_post_query(j, k, TRUE);
518 /* Reply from caches of other network. This is needed to
519 * "work around" known answer suppression. */
521 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
525 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
532 if (!s->config.enable_reflector)
535 for (j = s->monitor->interfaces; j; j = j->interface_next)
536 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
537 avahi_interface_post_probe(j, r, TRUE);
540 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
548 /* avahi_log_debug("query"); */
550 g_assert(avahi_record_list_empty(s->record_list));
552 /* Handle the questions */
553 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
555 gboolean unicast_response = FALSE;
557 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
558 avahi_log_warn("Packet too short (1)");
563 reflect_query(s, i, key);
565 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
566 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
567 /* Allow our own queries to be suppressed by incoming
568 * queries only when they do not include known answers */
569 avahi_query_scheduler_incoming(i->query_scheduler, key);
571 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
572 avahi_key_unref(key);
575 /* Known Answer Suppression */
576 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
578 gboolean unique = FALSE;
580 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
581 avahi_log_warn("Packet too short (2)");
585 if (handle_conflict(s, i, record, unique, a)) {
586 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
587 avahi_record_list_drop(s->record_list, record);
590 avahi_record_unref(record);
594 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
596 gboolean unique = FALSE;
598 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
599 avahi_log_warn("Packet too short (3)");
603 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
604 reflect_probe(s, i, record);
605 incoming_probe(s, record, i);
608 avahi_record_unref(record);
611 if (!avahi_record_list_empty(s->record_list))
612 avahi_server_generate_response(s, i, p, a, port, legacy_unicast);
617 avahi_record_list_flush(s->record_list);
620 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
628 /* avahi_log_debug("response"); */
630 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
631 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
633 gboolean cache_flush = FALSE;
636 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
637 avahi_log_warn("Packet too short (4)");
641 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
643 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
646 if (handle_conflict(s, i, record, cache_flush, a)) {
647 reflect_response(s, i, record, cache_flush);
648 avahi_cache_update(i->cache, record, cache_flush, a);
649 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
653 avahi_record_unref(record);
656 /* If the incoming response contained a conflicting record, some
657 records have been scheduling for sending. We need to flush them
659 if (!avahi_record_list_empty(s->record_list))
660 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE);
663 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
664 guint n, index = (guint) -1;
665 AvahiLegacyUnicastReflectSlot *slot;
669 if (!s->legacy_unicast_reflect_slots)
670 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
672 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
673 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
675 if (!s->legacy_unicast_reflect_slots[index])
679 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
682 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
683 slot->id = s->legacy_unicast_reflect_id++;
688 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
694 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
696 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
698 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
701 s->legacy_unicast_reflect_slots[index] = NULL;
704 static void free_slots(AvahiServer *s) {
708 if (!s->legacy_unicast_reflect_slots)
711 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
712 if (s->legacy_unicast_reflect_slots[index])
713 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
715 g_free(s->legacy_unicast_reflect_slots);
716 s->legacy_unicast_reflect_slots = NULL;
719 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
724 if (!s->legacy_unicast_reflect_slots)
727 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
729 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
732 return s->legacy_unicast_reflect_slots[index];
735 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
736 AvahiLegacyUnicastReflectSlot *slot = userdata;
740 g_assert(slot->time_event == e);
742 deallocate_slot(slot->server, slot);
745 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
746 AvahiLegacyUnicastReflectSlot *slot;
754 g_assert(i->protocol == a->family);
756 if (!s->config.enable_reflector)
759 /* avahi_log_debug("legacy unicast reflectr"); */
761 /* Reflecting legacy unicast queries is a little more complicated
762 than reflecting normal queries, since we must route the
763 responses back to the right client. Therefore we must store
764 some information for finding the right client contact data for
765 response packets. In contrast to normal queries legacy
766 unicast query and response packets are reflected untouched and
767 are not reassembled into larger packets */
769 if (!(slot = allocate_slot(s))) {
770 /* No slot available, we drop this legacy unicast query */
771 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
775 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
778 slot->interface = i->hardware->index;
780 avahi_elapse_time(&slot->elapse_time, 2000, 0);
781 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
783 /* Patch the packet with our new locally generatedt id */
784 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
786 for (j = s->monitor->interfaces; j; j = j->interface_next)
787 if (avahi_interface_relevant(j) &&
789 (s->config.reflect_ipv || j->protocol == i->protocol)) {
791 if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
792 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
793 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
794 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
798 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
801 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
806 if (!s->config.enable_reflector)
809 avahi_address_from_sockaddr(sa, &a);
811 if (!avahi_address_is_local(s->monitor, &a))
814 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
815 struct sockaddr_in lsa;
816 socklen_t l = sizeof(lsa);
818 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
819 avahi_log_warn("getsockname(): %s", strerror(errno));
821 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
825 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
826 struct sockaddr_in6 lsa;
827 socklen_t l = sizeof(lsa);
829 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
830 avahi_log_warn("getsockname(): %s", strerror(errno));
832 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
838 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
848 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
849 !avahi_interface_relevant(i)) {
850 avahi_log_warn("Recieved packet from invalid interface.");
854 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
856 port = avahi_port_from_sockaddr(sa);
857 avahi_address_from_sockaddr(sa, &a);
859 if (avahi_address_is_ipv4_in_ipv6(&a))
860 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
863 if (originates_from_local_legacy_unicast_socket(s, sa))
864 /* This originates from our local reflector, so let's ignore it */
867 if (avahi_dns_packet_check_valid(p) < 0) {
868 avahi_log_warn("Recieved invalid packet.");
872 if (avahi_dns_packet_is_query(p)) {
873 gboolean legacy_unicast = FALSE;
875 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
876 avahi_log_warn("Invalid query packet.");
880 if (port != AVAHI_MDNS_PORT) {
883 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
884 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
885 avahi_log_warn("Invalid legacy unicast query packet.");
889 legacy_unicast = TRUE;
893 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
895 handle_query_packet(s, p, i, &a, port, legacy_unicast);
897 /* avahi_log_debug("Handled query"); */
900 if (port != AVAHI_MDNS_PORT) {
901 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
906 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
907 if (s->config.check_response_ttl)
911 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
912 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
913 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
914 avahi_log_warn("Invalid response packet.");
918 handle_response_packet(s, p, i, &a);
919 /* avahi_log_debug("Handled response"); */
923 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
924 AvahiInterface *i, *j;
927 AvahiLegacyUnicastReflectSlot *slot;
934 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
935 !avahi_interface_relevant(i)) {
936 avahi_log_warn("Recieved packet from invalid interface.");
940 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
942 port = avahi_port_from_sockaddr(sa);
943 avahi_address_from_sockaddr(sa, &a);
945 if (avahi_address_is_ipv4_in_ipv6(&a))
946 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
949 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
950 avahi_log_warn("Recieved invalid packet.");
954 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
955 avahi_log_warn("Recieved legacy unicast response with unknown id");
959 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
960 !avahi_interface_relevant(j))
963 /* Patch the original ID into this response */
964 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
966 /* Forward the response to the correct client */
967 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
969 /* Undo changes to packet */
970 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
973 static void work(AvahiServer *s) {
974 struct sockaddr_in6 sa6;
975 struct sockaddr_in sa;
982 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
983 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
984 dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
985 avahi_dns_packet_free(p);
989 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
990 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
991 dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
992 avahi_dns_packet_free(p);
996 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
997 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
998 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
999 avahi_dns_packet_free(p);
1003 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1004 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
1005 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1006 avahi_dns_packet_free(p);
1011 static gboolean prepare_func(GSource *source, gint *timeout) {
1019 static gboolean check_func(GSource *source) {
1021 gushort revents = 0;
1025 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1028 if (s->fd_ipv4 >= 0)
1029 revents |= s->pollfd_ipv4.revents;
1030 if (s->fd_ipv6 >= 0)
1031 revents |= s->pollfd_ipv6.revents;
1032 if (s->fd_legacy_unicast_ipv4 >= 0)
1033 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1034 if (s->fd_legacy_unicast_ipv6 >= 0)
1035 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1037 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1040 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1044 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1053 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1056 if (s->state == state)
1062 s->callback(s, state, s->userdata);
1065 static void withdraw_host_rrs(AvahiServer *s) {
1068 if (s->hinfo_entry_group) {
1069 avahi_entry_group_free(s->hinfo_entry_group);
1070 s->hinfo_entry_group = NULL;
1073 if (s->browse_domain_entry_group) {
1074 avahi_entry_group_free(s->browse_domain_entry_group);
1075 s->browse_domain_entry_group = NULL;
1078 avahi_update_host_rrs(s->monitor, TRUE);
1079 s->n_host_rr_pending = 0;
1082 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1085 g_assert(s->n_host_rr_pending > 0);
1087 if (--s->n_host_rr_pending == 0)
1088 server_set_state(s, AVAHI_SERVER_RUNNING);
1091 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1094 s->n_host_rr_pending ++;
1097 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1101 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1102 s->state == AVAHI_SERVER_REGISTERING)
1103 avahi_server_increase_host_rr_pending(s);
1104 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1105 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1106 withdraw_host_rrs(s);
1107 server_set_state(s, AVAHI_SERVER_COLLISION);
1108 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1109 s->state == AVAHI_SERVER_REGISTERING)
1110 avahi_server_decrease_host_rr_pending(s);
1113 static void register_hinfo(AvahiServer *s) {
1114 struct utsname utsname;
1119 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1122 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1124 /* Fill in HINFO rr */
1125 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1127 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1128 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1129 avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1130 avahi_record_unref(r);
1132 avahi_entry_group_commit(s->hinfo_entry_group);
1135 static void register_localhost(AvahiServer *s) {
1139 /* Add localhost entries */
1140 avahi_address_parse("127.0.0.1", AF_INET, &a);
1141 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1143 avahi_address_parse("::1", AF_INET6, &a);
1144 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1147 static void register_browse_domain(AvahiServer *s) {
1150 if (!s->config.publish_domain || s->browse_domain_entry_group)
1153 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1154 avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "b._dns-sd._udp.local", s->domain_name);
1155 avahi_entry_group_commit(s->browse_domain_entry_group);
1158 static void register_stuff(AvahiServer *s) {
1161 server_set_state(s, AVAHI_SERVER_REGISTERING);
1163 register_browse_domain(s);
1164 avahi_update_host_rrs(s->monitor, FALSE);
1166 if (s->n_host_rr_pending == 0)
1167 server_set_state(s, AVAHI_SERVER_RUNNING);
1170 static void update_fqdn(AvahiServer *s) {
1173 g_assert(s->host_name);
1174 g_assert(s->domain_name);
1176 g_free(s->host_name_fqdn);
1177 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1180 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1181 AvahiServer *s = userdata;
1186 g_assert(e == s->register_time_event);
1187 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1188 s->register_time_event = NULL;
1190 if (s->state == AVAHI_SERVER_SLEEPING)
1194 static void delayed_register_stuff(AvahiServer *s) {
1199 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1201 if (s->register_time_event)
1202 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1204 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1207 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1209 g_assert(host_name);
1211 server_set_state(s, AVAHI_SERVER_SLEEPING);
1212 withdraw_host_rrs(s);
1214 g_free(s->host_name);
1215 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1216 s->host_name[strcspn(s->host_name, ".")] = 0;
1219 delayed_register_stuff(s);
1223 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1225 g_assert(domain_name);
1227 server_set_state(s, AVAHI_SERVER_SLEEPING);
1228 withdraw_host_rrs(s);
1230 g_free(s->domain_name);
1231 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1234 delayed_register_stuff(s);
1239 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1244 memset(pollfd, 0, sizeof(GPollFD));
1246 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1247 g_source_add_poll(s->source, pollfd);
1250 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1253 static GSourceFuncs source_funcs = {
1262 s = g_new(AvahiServer, 1);
1263 s->n_host_rr_pending = 0;
1264 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1267 avahi_server_config_copy(&s->config, sc);
1269 avahi_server_config_init(&s->config);
1271 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1272 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1274 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1275 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1276 avahi_server_config_free(&s->config);
1281 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1282 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1283 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1284 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1286 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1287 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1289 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1291 /* Prepare IO source registration */
1292 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1293 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1295 if (s->fd_ipv4 >= 0)
1296 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1297 if (s->fd_ipv6 >= 0)
1298 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1299 if (s->fd_legacy_unicast_ipv4 >= 0)
1300 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1301 if (s->fd_legacy_unicast_ipv6 >= 0)
1302 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1304 g_source_attach(s->source, s->context);
1306 s->callback = callback;
1307 s->userdata = userdata;
1309 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1310 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1311 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1313 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1314 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1315 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1316 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1317 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1318 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1319 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1320 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1321 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1323 s->legacy_unicast_reflect_slots = NULL;
1324 s->legacy_unicast_reflect_id = 0;
1327 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1328 s->host_name[strcspn(s->host_name, ".")] = 0;
1329 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1330 s->host_name_fqdn = NULL;
1333 s->record_list = avahi_record_list_new();
1335 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1336 s->register_time_event = NULL;
1338 s->state = AVAHI_SERVER_INVALID;
1340 s->monitor = avahi_interface_monitor_new(s);
1341 avahi_interface_monitor_sync(s->monitor);
1343 register_localhost(s);
1345 s->hinfo_entry_group = NULL;
1346 s->browse_domain_entry_group = NULL;
1352 void avahi_server_free(AvahiServer* s) {
1356 free_entry(s, s->entries);
1358 avahi_interface_monitor_free(s->monitor);
1361 free_group(s, s->groups);
1365 while (s->dns_server_browsers)
1366 avahi_dns_server_browser_free(s->dns_server_browsers);
1367 while (s->host_name_resolvers)
1368 avahi_host_name_resolver_free(s->host_name_resolvers);
1369 while (s->address_resolvers)
1370 avahi_address_resolver_free(s->address_resolvers);
1371 while (s->domain_browsers)
1372 avahi_domain_browser_free(s->domain_browsers);
1373 while (s->service_type_browsers)
1374 avahi_service_type_browser_free(s->service_type_browsers);
1375 while (s->service_browsers)
1376 avahi_service_browser_free(s->service_browsers);
1377 while (s->service_resolvers)
1378 avahi_service_resolver_free(s->service_resolvers);
1379 while (s->record_browsers)
1380 avahi_record_browser_destroy(s->record_browsers);
1381 g_hash_table_destroy(s->record_browser_hashtable);
1383 g_hash_table_destroy(s->entries_by_key);
1385 if (s->register_time_event)
1386 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1387 avahi_time_event_queue_free(s->time_event_queue);
1389 avahi_record_list_free(s->record_list);
1391 if (s->fd_ipv4 >= 0)
1393 if (s->fd_ipv6 >= 0)
1395 if (s->fd_legacy_unicast_ipv4 >= 0)
1396 close(s->fd_legacy_unicast_ipv4);
1397 if (s->fd_legacy_unicast_ipv6 >= 0)
1398 close(s->fd_legacy_unicast_ipv6);
1400 g_free(s->host_name);
1401 g_free(s->domain_name);
1402 g_free(s->host_name_fqdn);
1404 g_source_destroy(s->source);
1405 g_source_unref(s->source);
1406 g_main_context_unref(s->context);
1408 avahi_server_config_free(&s->config);
1413 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1419 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1423 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1426 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1429 if (interface <= 0 ||
1430 e->interface <= 0 ||
1431 e->interface == interface ||
1432 protocol == AF_UNSPEC ||
1433 e->protocol == AF_UNSPEC ||
1434 e->protocol == protocol)
1443 gint avahi_server_add(
1448 AvahiEntryFlags flags,
1455 g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1457 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1460 e = g_new(AvahiEntry, 1);
1462 e->record = avahi_record_ref(r);
1464 e->interface = interface;
1465 e->protocol = protocol;
1469 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1471 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1473 /* Insert into hash table indexed by name */
1474 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1475 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1476 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1478 /* Insert into group list */
1480 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1482 avahi_announce_entry(s, e);
1487 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1488 AvahiEntry **e = (AvahiEntry**) state;
1493 *e = g ? g->entries : s->entries;
1495 while (*e && (*e)->dead)
1496 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1501 return avahi_record_ref((*e)->record);
1504 void avahi_server_dump(AvahiServer *s, FILE *f) {
1509 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1511 for (e = s->entries; e; e = e->entries_next) {
1517 t = avahi_record_to_string(e->record);
1518 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1522 avahi_dump_caches(s->monitor, f);
1525 gint avahi_server_add_ptr(
1530 AvahiEntryFlags flags,
1532 const gchar *dest) {
1539 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1540 r->data.ptr.name = avahi_normalize_name(dest);
1541 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1542 avahi_record_unref(r);
1546 gint avahi_server_add_address(
1551 AvahiEntryFlags flags,
1560 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1562 if (a->family == AF_INET) {
1566 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1567 r->data.a.address = a->data.ipv4;
1568 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1569 avahi_record_unref(r);
1571 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1572 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1579 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1580 r->data.aaaa.address = a->data.ipv6;
1581 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1582 avahi_record_unref(r);
1584 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1585 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1588 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1589 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1598 gint avahi_server_add_text_strlst(
1603 AvahiEntryFlags flags,
1605 AvahiStringList *strlst) {
1612 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1613 r->data.txt.string_list = strlst;
1614 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1615 avahi_record_unref(r);
1620 gint avahi_server_add_text_va(
1625 AvahiEntryFlags flags,
1631 return avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1634 gint avahi_server_add_text(
1639 AvahiEntryFlags flags,
1649 ret = avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1655 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1660 while (*s && size >= 2) {
1661 if (*s == '.' || *s == '\\') {
1677 gint avahi_server_add_service_strlst(
1684 const gchar *domain,
1687 AvahiStringList *strlst) {
1689 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1697 escape_service_name(ename, sizeof(ename), name);
1700 while (domain[0] == '.')
1703 domain = s->domain_name;
1706 host = s->host_name_fqdn;
1708 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1709 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1711 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1713 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1714 r->data.srv.priority = 0;
1715 r->data.srv.weight = 0;
1716 r->data.srv.port = port;
1717 r->data.srv.name = avahi_normalize_name(host);
1718 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1719 avahi_record_unref(r);
1721 ret |= avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1723 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1724 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1729 gint avahi_server_add_service_va(
1736 const gchar *domain,
1745 return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1748 gint avahi_server_add_service(
1755 const gchar *domain,
1768 ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1773 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1774 static const gchar hex[] = "0123456789abcdef";
1776 const guint8 *k = p;
1778 while (sl > 1 && pl > 0) {
1779 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1795 gint avahi_server_add_dns_server_address(
1800 const gchar *domain,
1801 AvahiDNSServerType type,
1802 const AvahiAddress *address,
1803 guint16 port /** should be 53 */) {
1811 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1812 g_assert(address->family == AF_INET || address->family == AF_INET6);
1814 if (address->family == AF_INET) {
1815 hexstring(n+2, sizeof(n)-2, &address->data, 4);
1816 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1817 r->data.a.address = address->data.ipv4;
1819 hexstring(n+2, sizeof(n)-2, &address->data, 6);
1820 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1821 r->data.aaaa.address = address->data.ipv6;
1824 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1825 avahi_record_unref(r);
1827 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1832 gint avahi_server_add_dns_server_name(
1837 const gchar *domain,
1838 AvahiDNSServerType type,
1840 guint16 port /** should be 53 */) {
1848 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1851 while (domain[0] == '.')
1854 domain = s->domain_name;
1856 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", domain);
1858 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1859 r->data.srv.priority = 0;
1860 r->data.srv.weight = 0;
1861 r->data.srv.port = port;
1862 r->data.srv.name = avahi_normalize_name(name);
1863 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1864 avahi_record_unref(r);
1870 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1871 AvahiKey *k = userdata;
1877 avahi_interface_post_query(i, k, FALSE);
1880 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1884 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1887 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1890 if (g->state == state)
1896 g->callback(g->server, g, state, g->userdata);
1901 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1906 g = g_new(AvahiEntryGroup, 1);
1908 g->callback = callback;
1909 g->userdata = userdata;
1911 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1913 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1915 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1919 void avahi_entry_group_free(AvahiEntryGroup *g) {
1923 g_assert(g->server);
1925 for (e = g->entries; e; e = e->by_group_next) {
1926 avahi_goodbye_entry(g->server, e, TRUE);
1932 g->server->need_group_cleanup = TRUE;
1933 g->server->need_entry_cleanup = TRUE;
1936 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1940 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1943 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1944 avahi_announce_group(g->server, g);
1945 avahi_entry_group_check_probed(g, FALSE);
1950 gboolean avahi_entry_commited(AvahiEntry *e) {
1955 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1956 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1959 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1966 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1969 g->userdata = userdata;
1972 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1978 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1981 return s->domain_name;
1984 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1987 return s->host_name;
1990 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1993 return s->host_name_fqdn;
1996 gpointer avahi_server_get_data(AvahiServer *s) {
2002 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2005 s->userdata = userdata;
2008 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2014 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2017 memset(c, 0, sizeof(AvahiServerConfig));
2020 c->host_name = NULL;
2021 c->domain_name = NULL;
2022 c->check_response_ttl = TRUE;
2023 c->publish_hinfo = TRUE;
2024 c->publish_addresses = TRUE;
2025 c->publish_workstation = TRUE;
2026 c->publish_domain = TRUE;
2027 c->use_iff_running = FALSE;
2028 c->enable_reflector = FALSE;
2029 c->reflect_ipv = FALSE;
2034 void avahi_server_config_free(AvahiServerConfig *c) {
2037 g_free(c->host_name);
2038 g_free(c->domain_name);
2041 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2047 ret->host_name = g_strdup(c->host_name);
2048 ret->domain_name = g_strdup(c->domain_name);