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 (!avahi_key_is_pattern(record->key)) {
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 (!avahi_key_is_pattern(record->key)) {
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 == AVAHI_PROTO_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 == AVAHI_PROTO_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 gboolean is_mdns_mcast_address(const AvahiAddress *a) {
842 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
843 return avahi_address_cmp(a, &b) == 0;
846 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
857 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
858 !avahi_interface_relevant(i)) {
859 avahi_log_warn("Recieved packet from invalid interface.");
863 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
865 port = avahi_port_from_sockaddr(sa);
866 avahi_address_from_sockaddr(sa, &a);
868 if (avahi_address_is_ipv4_in_ipv6(&a))
869 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
872 if (originates_from_local_legacy_unicast_socket(s, sa))
873 /* This originates from our local reflector, so let's ignore it */
876 if (avahi_dns_packet_check_valid(p) < 0) {
877 avahi_log_warn("Recieved invalid packet.");
881 if (avahi_dns_packet_is_query(p)) {
882 gboolean legacy_unicast = FALSE;
884 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
885 avahi_log_warn("Invalid query packet.");
889 if (port != AVAHI_MDNS_PORT) {
892 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
893 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
894 avahi_log_warn("Invalid legacy unicast query packet.");
898 legacy_unicast = TRUE;
902 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
904 handle_query_packet(s, p, i, &a, port, legacy_unicast);
906 /* avahi_log_debug("Handled query"); */
908 if (port != AVAHI_MDNS_PORT) {
909 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
913 if (ttl != 255 && s->config.check_response_ttl) {
914 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
918 if (!is_mdns_mcast_address(dest) &&
919 !avahi_interface_address_on_link(i, &a)) {
920 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
924 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
925 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
926 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
927 avahi_log_warn("Invalid response packet.");
931 handle_response_packet(s, p, i, &a);
932 /* avahi_log_debug("Handled response"); */
936 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
937 AvahiInterface *i, *j;
940 AvahiLegacyUnicastReflectSlot *slot;
947 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
948 !avahi_interface_relevant(i)) {
949 avahi_log_warn("Recieved packet from invalid interface.");
953 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
955 port = avahi_port_from_sockaddr(sa);
956 avahi_address_from_sockaddr(sa, &a);
958 if (avahi_address_is_ipv4_in_ipv6(&a))
959 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
962 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
963 avahi_log_warn("Recieved invalid packet.");
967 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
968 avahi_log_warn("Recieved legacy unicast response with unknown id");
972 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
973 !avahi_interface_relevant(j))
976 /* Patch the original ID into this response */
977 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
979 /* Forward the response to the correct client */
980 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
982 /* Undo changes to packet */
983 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
986 static void work(AvahiServer *s) {
987 struct sockaddr_in6 sa6;
988 struct sockaddr_in sa;
996 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
997 dest.family = AVAHI_PROTO_INET;
998 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
999 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1000 avahi_dns_packet_free(p);
1004 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1005 dest.family = AVAHI_PROTO_INET6;
1006 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1007 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1008 avahi_dns_packet_free(p);
1012 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1013 dest.family = AVAHI_PROTO_INET;
1014 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1015 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1016 avahi_dns_packet_free(p);
1020 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1021 dest.family = AVAHI_PROTO_INET6;
1022 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1023 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1024 avahi_dns_packet_free(p);
1029 static gboolean prepare_func(GSource *source, gint *timeout) {
1037 static gboolean check_func(GSource *source) {
1039 gushort revents = 0;
1043 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1046 if (s->fd_ipv4 >= 0)
1047 revents |= s->pollfd_ipv4.revents;
1048 if (s->fd_ipv6 >= 0)
1049 revents |= s->pollfd_ipv6.revents;
1050 if (s->fd_legacy_unicast_ipv4 >= 0)
1051 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1052 if (s->fd_legacy_unicast_ipv6 >= 0)
1053 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1055 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1058 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1062 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1071 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1074 if (s->state == state)
1080 s->callback(s, state, s->userdata);
1083 static void withdraw_host_rrs(AvahiServer *s) {
1086 if (s->hinfo_entry_group) {
1087 avahi_entry_group_free(s->hinfo_entry_group);
1088 s->hinfo_entry_group = NULL;
1091 if (s->browse_domain_entry_group) {
1092 avahi_entry_group_free(s->browse_domain_entry_group);
1093 s->browse_domain_entry_group = NULL;
1096 avahi_update_host_rrs(s->monitor, TRUE);
1097 s->n_host_rr_pending = 0;
1100 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1103 g_assert(s->n_host_rr_pending > 0);
1105 if (--s->n_host_rr_pending == 0)
1106 server_set_state(s, AVAHI_SERVER_RUNNING);
1109 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1112 s->n_host_rr_pending ++;
1115 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1119 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1120 s->state == AVAHI_SERVER_REGISTERING)
1121 avahi_server_increase_host_rr_pending(s);
1122 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1123 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1124 withdraw_host_rrs(s);
1125 server_set_state(s, AVAHI_SERVER_COLLISION);
1126 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1127 s->state == AVAHI_SERVER_REGISTERING)
1128 avahi_server_decrease_host_rr_pending(s);
1131 static void register_hinfo(AvahiServer *s) {
1132 struct utsname utsname;
1137 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1140 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1142 /* Fill in HINFO rr */
1143 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1145 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1146 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1147 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1148 avahi_record_unref(r);
1150 avahi_entry_group_commit(s->hinfo_entry_group);
1153 static void register_localhost(AvahiServer *s) {
1157 /* Add localhost entries */
1158 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1159 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1161 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1162 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1165 static void register_browse_domain(AvahiServer *s) {
1168 if (!s->config.publish_domain || s->browse_domain_entry_group)
1171 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1172 avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "b._dns-sd._udp.local", s->domain_name);
1173 avahi_entry_group_commit(s->browse_domain_entry_group);
1176 static void register_stuff(AvahiServer *s) {
1179 server_set_state(s, AVAHI_SERVER_REGISTERING);
1181 register_browse_domain(s);
1182 avahi_update_host_rrs(s->monitor, FALSE);
1184 if (s->n_host_rr_pending == 0)
1185 server_set_state(s, AVAHI_SERVER_RUNNING);
1188 static void update_fqdn(AvahiServer *s) {
1191 g_assert(s->host_name);
1192 g_assert(s->domain_name);
1194 g_free(s->host_name_fqdn);
1195 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1198 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1199 AvahiServer *s = userdata;
1204 g_assert(e == s->register_time_event);
1205 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1206 s->register_time_event = NULL;
1208 if (s->state == AVAHI_SERVER_SLEEPING)
1212 static void delayed_register_stuff(AvahiServer *s) {
1217 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1219 if (s->register_time_event)
1220 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1222 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1225 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1227 g_assert(host_name);
1229 server_set_state(s, AVAHI_SERVER_SLEEPING);
1230 withdraw_host_rrs(s);
1232 g_free(s->host_name);
1233 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1234 s->host_name[strcspn(s->host_name, ".")] = 0;
1237 delayed_register_stuff(s);
1241 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1243 g_assert(domain_name);
1245 server_set_state(s, AVAHI_SERVER_SLEEPING);
1246 withdraw_host_rrs(s);
1248 g_free(s->domain_name);
1249 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1252 delayed_register_stuff(s);
1257 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1262 memset(pollfd, 0, sizeof(GPollFD));
1264 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1265 g_source_add_poll(s->source, pollfd);
1268 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1271 static GSourceFuncs source_funcs = {
1280 s = g_new(AvahiServer, 1);
1281 s->n_host_rr_pending = 0;
1282 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1285 avahi_server_config_copy(&s->config, sc);
1287 avahi_server_config_init(&s->config);
1289 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1290 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1292 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1293 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1294 avahi_server_config_free(&s->config);
1299 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1300 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1301 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1302 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1304 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1305 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1307 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1309 /* Prepare IO source registration */
1310 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1311 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1313 if (s->fd_ipv4 >= 0)
1314 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1315 if (s->fd_ipv6 >= 0)
1316 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1317 if (s->fd_legacy_unicast_ipv4 >= 0)
1318 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1319 if (s->fd_legacy_unicast_ipv6 >= 0)
1320 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1322 g_source_attach(s->source, s->context);
1324 s->callback = callback;
1325 s->userdata = userdata;
1327 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1328 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1329 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1331 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1332 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1333 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1334 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1335 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1336 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1337 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1338 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1339 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1341 s->legacy_unicast_reflect_slots = NULL;
1342 s->legacy_unicast_reflect_id = 0;
1345 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1346 s->host_name[strcspn(s->host_name, ".")] = 0;
1347 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1348 s->host_name_fqdn = NULL;
1351 s->record_list = avahi_record_list_new();
1353 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1354 s->register_time_event = NULL;
1356 s->state = AVAHI_SERVER_INVALID;
1358 s->monitor = avahi_interface_monitor_new(s);
1359 avahi_interface_monitor_sync(s->monitor);
1361 register_localhost(s);
1363 s->hinfo_entry_group = NULL;
1364 s->browse_domain_entry_group = NULL;
1370 void avahi_server_free(AvahiServer* s) {
1374 free_entry(s, s->entries);
1376 avahi_interface_monitor_free(s->monitor);
1379 free_group(s, s->groups);
1383 while (s->dns_server_browsers)
1384 avahi_dns_server_browser_free(s->dns_server_browsers);
1385 while (s->host_name_resolvers)
1386 avahi_host_name_resolver_free(s->host_name_resolvers);
1387 while (s->address_resolvers)
1388 avahi_address_resolver_free(s->address_resolvers);
1389 while (s->domain_browsers)
1390 avahi_domain_browser_free(s->domain_browsers);
1391 while (s->service_type_browsers)
1392 avahi_service_type_browser_free(s->service_type_browsers);
1393 while (s->service_browsers)
1394 avahi_service_browser_free(s->service_browsers);
1395 while (s->service_resolvers)
1396 avahi_service_resolver_free(s->service_resolvers);
1397 while (s->record_browsers)
1398 avahi_record_browser_destroy(s->record_browsers);
1399 g_hash_table_destroy(s->record_browser_hashtable);
1401 g_hash_table_destroy(s->entries_by_key);
1403 if (s->register_time_event)
1404 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1405 avahi_time_event_queue_free(s->time_event_queue);
1407 avahi_record_list_free(s->record_list);
1409 if (s->fd_ipv4 >= 0)
1411 if (s->fd_ipv6 >= 0)
1413 if (s->fd_legacy_unicast_ipv4 >= 0)
1414 close(s->fd_legacy_unicast_ipv4);
1415 if (s->fd_legacy_unicast_ipv6 >= 0)
1416 close(s->fd_legacy_unicast_ipv6);
1418 g_free(s->host_name);
1419 g_free(s->domain_name);
1420 g_free(s->host_name_fqdn);
1422 g_source_destroy(s->source);
1423 g_source_unref(s->source);
1424 g_main_context_unref(s->context);
1426 avahi_server_config_free(&s->config);
1431 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1437 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1441 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1444 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1447 if (interface <= 0 ||
1448 e->interface <= 0 ||
1449 e->interface == interface ||
1450 protocol == AVAHI_PROTO_UNSPEC ||
1451 e->protocol == AVAHI_PROTO_UNSPEC ||
1452 e->protocol == protocol)
1461 gint avahi_server_add(
1466 AvahiEntryFlags flags,
1474 if (avahi_key_is_pattern(r->key))
1477 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1480 e = g_new(AvahiEntry, 1);
1482 e->record = avahi_record_ref(r);
1484 e->interface = interface;
1485 e->protocol = protocol;
1489 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1491 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1493 /* Insert into hash table indexed by name */
1494 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1495 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1496 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1498 /* Insert into group list */
1500 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1502 avahi_announce_entry(s, e);
1507 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1508 AvahiEntry **e = (AvahiEntry**) state;
1513 *e = g ? g->entries : s->entries;
1515 while (*e && (*e)->dead)
1516 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1521 return avahi_record_ref((*e)->record);
1524 void avahi_server_dump(AvahiServer *s, FILE *f) {
1529 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1531 for (e = s->entries; e; e = e->entries_next) {
1537 t = avahi_record_to_string(e->record);
1538 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1542 avahi_dump_caches(s->monitor, f);
1545 gint avahi_server_add_ptr(
1550 AvahiEntryFlags flags,
1552 const gchar *dest) {
1559 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1560 r->data.ptr.name = avahi_normalize_name(dest);
1561 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1562 avahi_record_unref(r);
1566 gint avahi_server_add_address(
1571 AvahiEntryFlags flags,
1580 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1582 if (a->family == AVAHI_PROTO_INET) {
1586 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1587 r->data.a.address = a->data.ipv4;
1588 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1589 avahi_record_unref(r);
1591 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1592 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1599 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1600 r->data.aaaa.address = a->data.ipv6;
1601 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1602 avahi_record_unref(r);
1604 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1605 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1608 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1609 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1618 gint avahi_server_add_text_strlst(
1623 AvahiEntryFlags flags,
1625 AvahiStringList *strlst) {
1632 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1633 r->data.txt.string_list = strlst;
1634 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1635 avahi_record_unref(r);
1640 gint avahi_server_add_text_va(
1645 AvahiEntryFlags flags,
1651 return avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1654 gint avahi_server_add_text(
1659 AvahiEntryFlags flags,
1669 ret = avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1675 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1680 while (*s && size >= 2) {
1681 if (*s == '.' || *s == '\\') {
1697 gint avahi_server_add_service_strlst(
1704 const gchar *domain,
1707 AvahiStringList *strlst) {
1709 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1717 escape_service_name(ename, sizeof(ename), name);
1720 while (domain[0] == '.')
1723 domain = s->domain_name;
1726 host = s->host_name_fqdn;
1728 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1729 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1731 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1733 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1734 r->data.srv.priority = 0;
1735 r->data.srv.weight = 0;
1736 r->data.srv.port = port;
1737 r->data.srv.name = avahi_normalize_name(host);
1738 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1739 avahi_record_unref(r);
1741 ret |= avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1743 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1744 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1749 gint avahi_server_add_service_va(
1756 const gchar *domain,
1765 return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1768 gint avahi_server_add_service(
1775 const gchar *domain,
1788 ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1793 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1794 static const gchar hex[] = "0123456789abcdef";
1796 const guint8 *k = p;
1798 while (sl > 1 && pl > 0) {
1799 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1815 gint avahi_server_add_dns_server_address(
1820 const gchar *domain,
1821 AvahiDNSServerType type,
1822 const AvahiAddress *address,
1823 guint16 port /** should be 53 */) {
1831 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1832 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1834 if (address->family == AVAHI_PROTO_INET) {
1835 hexstring(n+2, sizeof(n)-2, &address->data, 4);
1836 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1837 r->data.a.address = address->data.ipv4;
1839 hexstring(n+2, sizeof(n)-2, &address->data, 6);
1840 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1841 r->data.aaaa.address = address->data.ipv6;
1844 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1845 avahi_record_unref(r);
1847 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1852 gint avahi_server_add_dns_server_name(
1857 const gchar *domain,
1858 AvahiDNSServerType type,
1860 guint16 port /** should be 53 */) {
1868 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1871 while (domain[0] == '.')
1874 domain = s->domain_name;
1876 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", domain);
1878 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1879 r->data.srv.priority = 0;
1880 r->data.srv.weight = 0;
1881 r->data.srv.port = port;
1882 r->data.srv.name = avahi_normalize_name(name);
1883 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1884 avahi_record_unref(r);
1890 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1891 AvahiKey *k = userdata;
1897 avahi_interface_post_query(i, k, FALSE);
1900 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1904 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1907 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1910 if (g->state == state)
1916 g->callback(g->server, g, state, g->userdata);
1921 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1926 g = g_new(AvahiEntryGroup, 1);
1928 g->callback = callback;
1929 g->userdata = userdata;
1931 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1933 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1935 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1939 void avahi_entry_group_free(AvahiEntryGroup *g) {
1943 g_assert(g->server);
1945 for (e = g->entries; e; e = e->by_group_next) {
1946 avahi_goodbye_entry(g->server, e, TRUE);
1952 g->server->need_group_cleanup = TRUE;
1953 g->server->need_entry_cleanup = TRUE;
1956 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1960 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1963 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1964 avahi_announce_group(g->server, g);
1965 avahi_entry_group_check_probed(g, FALSE);
1970 gboolean avahi_entry_commited(AvahiEntry *e) {
1975 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1976 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1979 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1986 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1989 g->userdata = userdata;
1992 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1998 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2001 return s->domain_name;
2004 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2007 return s->host_name;
2010 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2013 return s->host_name_fqdn;
2016 gpointer avahi_server_get_data(AvahiServer *s) {
2022 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2025 s->userdata = userdata;
2028 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2034 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2037 memset(c, 0, sizeof(AvahiServerConfig));
2040 c->host_name = NULL;
2041 c->domain_name = NULL;
2042 c->check_response_ttl = FALSE;
2043 c->publish_hinfo = TRUE;
2044 c->publish_addresses = TRUE;
2045 c->publish_workstation = TRUE;
2046 c->publish_domain = TRUE;
2047 c->use_iff_running = FALSE;
2048 c->enable_reflector = FALSE;
2049 c->reflect_ipv = FALSE;
2054 void avahi_server_config_free(AvahiServerConfig *c) {
2057 g_free(c->host_name);
2058 g_free(c->domain_name);
2061 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2067 ret->host_name = g_strdup(c->host_name);
2068 ret->domain_name = g_strdup(c->domain_name);