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>
39 #define AVAHI_HOST_RR_HOLDOFF_MSEC 1000
41 static void free_entry(AvahiServer*s, AvahiEntry *e) {
47 avahi_goodbye_entry(s, e, TRUE);
49 /* Remove from linked list */
50 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
52 /* Remove from hash table indexed by name */
53 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
54 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
56 g_hash_table_replace(s->entries_by_key, t->record->key, t);
58 g_hash_table_remove(s->entries_by_key, e->record->key);
60 /* Remove from associated group */
62 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
64 avahi_record_unref(e->record);
68 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
73 free_entry(s, g->entries);
75 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
79 static void cleanup_dead(AvahiServer *s) {
80 AvahiEntryGroup *g, *ng;
85 if (s->need_group_cleanup) {
86 for (g = s->groups; g; g = ng) {
93 s->need_group_cleanup = FALSE;
96 if (s->need_entry_cleanup) {
97 for (e = s->entries; e; e = ne) {
104 s->need_entry_cleanup = FALSE;
107 if (s->need_browser_cleanup)
108 avahi_browser_cleanup(s);
111 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) {
120 g_assert(type != AVAHI_DNS_TYPE_ANY);
122 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
124 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
125 if (!e->dead && avahi_entry_registered(s, e, i))
126 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
131 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) {
137 if (r->key->class == AVAHI_DNS_CLASS_IN) {
138 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
139 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
140 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
141 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
142 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
143 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
148 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
153 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
156 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
164 /* g_message("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
167 if (avahi_key_is_pattern(k)) {
169 /* Handle ANY query */
171 for (e = s->entries; e; e = e->entries_next)
172 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
173 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
177 /* Handle all other queries */
179 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
180 if (!e->dead && avahi_entry_registered(s, e, i))
181 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
185 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
192 for (k = e->group->entries; k; k = k->by_group_next) {
193 avahi_goodbye_entry(s, k, FALSE);
197 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
199 avahi_goodbye_entry(s, e, FALSE);
203 s->need_entry_cleanup = TRUE;
206 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
212 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
213 withdraw_entry(s, e);
216 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
219 gboolean ours = FALSE, won = FALSE, lost = FALSE;
225 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
229 if (e->dead || !avahi_entry_probing(s, e, i))
232 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
241 t = avahi_record_to_string(record);
246 g_message("Recieved conflicting probe [%s]. Local host won.", t);
248 g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
249 withdraw_rrset(s, record->key);
256 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
257 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
258 AvahiEntry *e, *n, *conflicting_entry = NULL;
265 /* g_message("CHECKING FOR CONFLICT: [%s]", t); */
267 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
270 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
273 /* Either our entry or the other is intended to be unique, so let's check */
275 if (avahi_entry_registered(s, e, i)) {
277 if (avahi_record_equal_no_ttl(e->record, record)) {
278 ours = TRUE; /* We have an identical record, so this is no conflict */
280 /* Check wheter there is a TTL conflict */
281 if (record->ttl <= e->record->ttl/2) {
284 t = avahi_record_to_string(record);
286 g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
287 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
293 /* There's no need to check the other entries of this RRset */
296 /* A conflict => we have to return to probe mode */
298 conflicting_entry = e;
301 } else if (avahi_entry_probing(s, e, i)) {
303 if (!avahi_record_equal_no_ttl(record, e->record)) {
305 /* We are currently registering a matching record, but
306 * someone else already claimed it, so let's
309 withdraw_immediately = TRUE;
314 /* g_message("ours=%i conflict=%i", ours, conflict); */
316 if (!ours && conflict) {
321 t = avahi_record_to_string(record);
323 if (withdraw_immediately) {
324 g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
325 withdraw_rrset(s, record->key);
327 g_assert(conflicting_entry);
328 g_message("Recieved conflicting record [%s]. Resetting our record.", t);
329 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
331 /* Local unique records are returned to probin
332 * state. Local shared records are reannounced. */
341 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
342 gboolean *unicast_response = userdata;
346 g_assert(unicast_response);
348 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
351 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
355 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
358 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
362 g_assert(!legacy_unicast || (a && port > 0 && p));
364 if (legacy_unicast) {
365 AvahiDnsPacket *reply;
368 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
370 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
372 append_aux_records_to_list(s, i, r, FALSE);
374 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
375 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
377 gchar *t = avahi_record_to_string(r);
378 g_warning("Record [%s] not fitting in legacy unicast packet, dropping.", t);
382 avahi_record_unref(r);
385 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
386 avahi_interface_send_packet_unicast(i, reply, a, port);
388 avahi_dns_packet_free(reply);
391 gboolean unicast_response, flush_cache, auxiliary;
392 AvahiDnsPacket *reply = NULL;
395 /* In case the query packet was truncated never respond
396 immediately, because known answer suppression records might be
397 contained in later packets */
398 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
400 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
402 if (!avahi_interface_post_response(i, r, flush_cache, a, !tc && flush_cache && !auxiliary) && unicast_response) {
404 append_aux_records_to_list(s, i, r, unicast_response);
406 /* Due to some reasons the record has not been scheduled.
407 * The client requested an unicast response in that
408 * case. Therefore we prepare such a response */
414 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
417 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
419 /* Appending this record succeeded, so incremeant
420 * the specific header field, and return to the caller */
422 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
427 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
430 /* The record is too large for one packet, so create a larger packet */
432 avahi_dns_packet_free(reply);
433 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
434 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
435 size = AVAHI_DNS_PACKET_MAX_SIZE;
436 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
438 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
439 avahi_dns_packet_free(reply);
441 gchar *t = avahi_record_to_string(r);
442 g_warning("Record [%s] too large, doesn't fit in any packet!", t);
446 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
449 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
450 avahi_interface_send_packet_unicast(i, reply, a, port);
451 avahi_dns_packet_free(reply);
456 avahi_record_unref(r);
460 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
461 avahi_interface_send_packet_unicast(i, reply, a, port);
462 avahi_dns_packet_free(reply);
466 avahi_record_list_flush(s->record_list);
470 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
477 if (!s->config.enable_reflector)
480 for (j = s->monitor->interfaces; j; j = j->interface_next)
481 if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol))
482 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
485 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
486 AvahiServer *s = userdata;
493 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
497 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
504 if (!s->config.enable_reflector)
507 for (j = s->monitor->interfaces; j; j = j->interface_next)
508 if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol)) {
509 /* Post the query to other networks */
510 avahi_interface_post_query(j, k, TRUE);
512 /* Reply from caches of other network. This is needed to
513 * "work around" known answer suppression. */
515 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
519 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
526 if (!s->config.enable_reflector)
529 for (j = s->monitor->interfaces; j; j = j->interface_next)
530 if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol))
531 avahi_interface_post_probe(j, r, TRUE);
534 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
542 /* g_message("query"); */
544 g_assert(avahi_record_list_empty(s->record_list));
546 /* Handle the questions */
547 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
549 gboolean unicast_response = FALSE;
551 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
552 g_warning("Packet too short (1)");
557 reflect_query(s, i, key);
558 avahi_query_scheduler_incoming(i->query_scheduler, key);
559 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
560 avahi_key_unref(key);
563 /* Known Answer Suppression */
564 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
566 gboolean unique = FALSE;
568 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
569 g_warning("Packet too short (2)");
573 if (handle_conflict(s, i, record, unique, a)) {
574 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
575 avahi_record_list_drop(s->record_list, record);
578 avahi_record_unref(record);
582 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
584 gboolean unique = FALSE;
586 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
587 g_warning("Packet too short (3)");
591 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
592 reflect_probe(s, i, record);
593 incoming_probe(s, record, i);
596 avahi_record_unref(record);
599 if (!avahi_record_list_empty(s->record_list))
600 avahi_server_generate_response(s, i, p, a, port, legacy_unicast);
605 avahi_record_list_flush(s->record_list);
609 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
617 /* g_message("response"); */
619 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
620 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
622 gboolean cache_flush = FALSE;
625 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
626 g_warning("Packet too short (4)");
630 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
632 /* g_message("Handling response: %s", txt = avahi_record_to_string(record)); */
635 if (handle_conflict(s, i, record, cache_flush, a)) {
636 reflect_response(s, i, record, cache_flush);
637 avahi_cache_update(i->cache, record, cache_flush, a);
638 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
642 avahi_record_unref(record);
646 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
647 guint n, index = (guint) -1;
648 AvahiLegacyUnicastReflectSlot *slot;
652 if (!s->legacy_unicast_reflect_slots)
653 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
655 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
656 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
658 if (!s->legacy_unicast_reflect_slots[index])
662 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
665 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
666 slot->id = s->legacy_unicast_reflect_id++;
671 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
677 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
679 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
681 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
684 s->legacy_unicast_reflect_slots[index] = NULL;
687 static void free_slots(AvahiServer *s) {
691 if (!s->legacy_unicast_reflect_slots)
694 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
695 if (s->legacy_unicast_reflect_slots[index])
696 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
698 g_free(s->legacy_unicast_reflect_slots);
699 s->legacy_unicast_reflect_slots = NULL;
702 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
707 if (!s->legacy_unicast_reflect_slots)
710 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
712 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
715 return s->legacy_unicast_reflect_slots[index];
718 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
719 AvahiLegacyUnicastReflectSlot *slot = userdata;
723 g_assert(slot->time_event == e);
725 deallocate_slot(slot->server, slot);
728 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
729 AvahiLegacyUnicastReflectSlot *slot;
737 g_assert(i->protocol == a->family);
739 if (!s->config.enable_reflector)
742 /* g_message("legacy unicast reflectr"); */
744 /* Reflecting legacy unicast queries is a little more complicated
745 than reflecting normal queries, since we must route the
746 responses back to the right client. Therefore we must store
747 some information for finding the right client contact data for
748 response packets. In contrast to normal queries legacy
749 unicast query and response packets are reflected untouched and
750 are not reassembled into larger packets */
752 if (!(slot = allocate_slot(s))) {
753 /* No slot available, we drop this legacy unicast query */
754 g_warning("No slot available for legacy unicast reflection, dropping query packet.");
758 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
761 slot->interface = i->hardware->index;
763 avahi_elapse_time(&slot->elapse_time, 2000, 0);
764 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
766 /* Patch the packet with our new locally generatedt id */
767 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
769 for (j = s->monitor->interfaces; j; j = j->interface_next)
770 if (avahi_interface_relevant(j) &&
772 (s->config.ipv_reflect || j->protocol == i->protocol)) {
774 if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
775 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
776 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
777 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
781 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
784 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
789 if (!s->config.enable_reflector)
792 avahi_address_from_sockaddr(sa, &a);
794 if (!avahi_address_is_local(s->monitor, &a))
797 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
798 struct sockaddr_in lsa;
799 socklen_t l = sizeof(lsa);
801 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
802 g_warning("getsockname(): %s", strerror(errno));
804 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
808 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
809 struct sockaddr_in6 lsa;
810 socklen_t l = sizeof(lsa);
812 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
813 g_warning("getsockname(): %s", strerror(errno));
815 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
821 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
831 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
832 !avahi_interface_relevant(i)) {
833 g_warning("Recieved packet from invalid interface.");
837 /* g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
839 port = avahi_port_from_sockaddr(sa);
840 avahi_address_from_sockaddr(sa, &a);
842 if (avahi_address_is_ipv4_in_ipv6(&a))
843 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
846 if (originates_from_local_legacy_unicast_socket(s, sa))
847 /* This originates from our local reflector, so let's ignore it */
850 if (avahi_dns_packet_check_valid(p) < 0) {
851 g_warning("Recieved invalid packet.");
855 if (avahi_dns_packet_is_query(p)) {
856 gboolean legacy_unicast = FALSE;
858 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
859 g_warning("Invalid query packet.");
863 if (port != AVAHI_MDNS_PORT) {
866 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
867 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
868 g_warning("Invalid legacy unicast query packet.");
872 legacy_unicast = TRUE;
876 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
878 handle_query_packet(s, p, i, &a, port, legacy_unicast);
880 /* g_message("Handled query"); */
883 if (port != AVAHI_MDNS_PORT) {
884 g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
889 g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
890 if (s->config.check_response_ttl)
894 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
895 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
896 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
897 g_warning("Invalid response packet.");
901 handle_response_packet(s, p, i, &a);
902 /* g_message("Handled response"); */
906 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
907 AvahiInterface *i, *j;
910 AvahiLegacyUnicastReflectSlot *slot;
917 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
918 !avahi_interface_relevant(i)) {
919 g_warning("Recieved packet from invalid interface.");
923 /* g_message("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
925 port = avahi_port_from_sockaddr(sa);
926 avahi_address_from_sockaddr(sa, &a);
928 if (avahi_address_is_ipv4_in_ipv6(&a))
929 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
932 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
933 g_warning("Recieved invalid packet.");
937 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
938 g_warning("Recieved legacy unicast response with unknown id");
942 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
943 !avahi_interface_relevant(j))
946 /* Patch the original ID into this response */
947 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
949 /* Forward the response to the correct client */
950 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
952 /* Undo changes to packet */
953 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
956 static void work(AvahiServer *s) {
957 struct sockaddr_in6 sa6;
958 struct sockaddr_in sa;
965 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
966 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
967 dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
968 avahi_dns_packet_free(p);
972 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
973 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
974 dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
975 avahi_dns_packet_free(p);
979 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
980 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
981 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
982 avahi_dns_packet_free(p);
986 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
987 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
988 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
989 avahi_dns_packet_free(p);
994 static gboolean prepare_func(GSource *source, gint *timeout) {
1002 static gboolean check_func(GSource *source) {
1004 gushort revents = 0;
1008 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1011 if (s->fd_ipv4 >= 0)
1012 revents |= s->pollfd_ipv4.revents;
1013 if (s->fd_ipv6 >= 0)
1014 revents |= s->pollfd_ipv6.revents;
1015 if (s->fd_legacy_unicast_ipv4 >= 0)
1016 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1017 if (s->fd_legacy_unicast_ipv6 >= 0)
1018 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1020 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1023 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1027 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1036 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1039 if (s->state == state)
1045 s->callback(s, state, s->userdata);
1048 static void withdraw_host_rrs(AvahiServer *s) {
1051 if (s->hinfo_entry_group) {
1052 avahi_entry_group_free(s->hinfo_entry_group);
1053 s->hinfo_entry_group = NULL;
1056 if (s->browse_domain_entry_group) {
1057 avahi_entry_group_free(s->browse_domain_entry_group);
1058 s->browse_domain_entry_group = NULL;
1061 avahi_update_host_rrs(s->monitor, TRUE);
1062 s->n_host_rr_pending = 0;
1065 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1068 g_assert(s->n_host_rr_pending > 0);
1070 if (--s->n_host_rr_pending == 0)
1071 server_set_state(s, AVAHI_SERVER_RUNNING);
1074 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1077 s->n_host_rr_pending ++;
1080 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1084 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1085 s->state == AVAHI_SERVER_REGISTERING)
1086 avahi_server_increase_host_rr_pending(s);
1087 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1088 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1089 withdraw_host_rrs(s);
1090 server_set_state(s, AVAHI_SERVER_COLLISION);
1091 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1092 s->state == AVAHI_SERVER_REGISTERING)
1093 avahi_server_decrease_host_rr_pending(s);
1096 static void register_hinfo(AvahiServer *s) {
1097 struct utsname utsname;
1102 if (!s->config.register_hinfo || s->hinfo_entry_group)
1105 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1107 /* Fill in HINFO rr */
1108 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1110 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1111 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1112 avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1113 avahi_record_unref(r);
1115 avahi_entry_group_commit(s->hinfo_entry_group);
1118 static void register_localhost(AvahiServer *s) {
1122 /* Add localhost entries */
1123 avahi_address_parse("127.0.0.1", AF_INET, &a);
1124 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1126 avahi_address_parse("::1", AF_INET6, &a);
1127 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1130 static void register_browse_domain(AvahiServer *s) {
1133 if (!s->config.announce_domain || s->browse_domain_entry_group)
1136 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1137 avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1138 avahi_entry_group_commit(s->browse_domain_entry_group);
1141 static void register_stuff(AvahiServer *s) {
1144 server_set_state(s, AVAHI_SERVER_REGISTERING);
1146 register_browse_domain(s);
1147 avahi_update_host_rrs(s->monitor, FALSE);
1149 if (s->n_host_rr_pending == 0)
1150 server_set_state(s, AVAHI_SERVER_RUNNING);
1153 static void update_fqdn(AvahiServer *s) {
1156 g_assert(s->host_name);
1157 g_assert(s->domain_name);
1159 g_free(s->host_name_fqdn);
1160 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1163 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1164 AvahiServer *s = userdata;
1169 g_assert(e == s->register_time_event);
1170 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1171 s->register_time_event = NULL;
1173 if (s->state == AVAHI_SERVER_SLEEPING)
1177 static void delayed_register_stuff(AvahiServer *s) {
1182 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1184 if (s->register_time_event)
1185 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1187 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1190 void avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1192 g_assert(host_name);
1194 server_set_state(s, AVAHI_SERVER_SLEEPING);
1195 withdraw_host_rrs(s);
1197 g_free(s->host_name);
1198 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1199 s->host_name[strcspn(s->host_name, ".")] = 0;
1202 delayed_register_stuff(s);
1205 void avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1207 g_assert(domain_name);
1209 server_set_state(s, AVAHI_SERVER_SLEEPING);
1210 withdraw_host_rrs(s);
1212 g_free(s->domain_name);
1213 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1216 delayed_register_stuff(s);
1220 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1225 memset(pollfd, 0, sizeof(GPollFD));
1227 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1228 g_source_add_poll(s->source, pollfd);
1231 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1234 static GSourceFuncs source_funcs = {
1243 s = g_new(AvahiServer, 1);
1244 s->n_host_rr_pending = 0;
1245 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1248 avahi_server_config_copy(&s->config, sc);
1250 avahi_server_config_init(&s->config);
1252 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1253 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1255 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1256 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1257 avahi_server_config_free(&s->config);
1262 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1263 g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1264 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1265 g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1267 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1268 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1271 g_main_context_ref(s->context = c);
1273 s->context = g_main_context_default();
1275 /* Prepare IO source registration */
1276 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1277 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1279 if (s->fd_ipv4 >= 0)
1280 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1281 if (s->fd_ipv6 >= 0)
1282 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1283 if (s->fd_legacy_unicast_ipv4 >= 0)
1284 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1285 if (s->fd_legacy_unicast_ipv6 >= 0)
1286 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1288 g_source_attach(s->source, s->context);
1290 s->callback = callback;
1291 s->userdata = userdata;
1293 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1294 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1295 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1297 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1298 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1299 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1300 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1301 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1302 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1303 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1304 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1306 s->legacy_unicast_reflect_slots = NULL;
1307 s->legacy_unicast_reflect_id = 0;
1310 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1311 s->host_name[strcspn(s->host_name, ".")] = 0;
1312 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1313 s->host_name_fqdn = NULL;
1316 s->record_list = avahi_record_list_new();
1318 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1319 s->register_time_event = NULL;
1321 s->state = AVAHI_SERVER_INVALID;
1323 s->monitor = avahi_interface_monitor_new(s);
1324 avahi_interface_monitor_sync(s->monitor);
1326 register_localhost(s);
1328 s->hinfo_entry_group = NULL;
1329 s->browse_domain_entry_group = NULL;
1335 void avahi_server_free(AvahiServer* s) {
1339 free_entry(s, s->entries);
1341 avahi_interface_monitor_free(s->monitor);
1344 free_group(s, s->groups);
1348 while (s->host_name_resolvers)
1349 avahi_host_name_resolver_free(s->host_name_resolvers);
1350 while (s->address_resolvers)
1351 avahi_address_resolver_free(s->address_resolvers);
1352 while (s->domain_browsers)
1353 avahi_domain_browser_free(s->domain_browsers);
1354 while (s->service_type_browsers)
1355 avahi_service_type_browser_free(s->service_type_browsers);
1356 while (s->service_browsers)
1357 avahi_service_browser_free(s->service_browsers);
1358 while (s->service_resolvers)
1359 avahi_service_resolver_free(s->service_resolvers);
1360 while (s->record_browsers)
1361 avahi_record_browser_destroy(s->record_browsers);
1362 g_hash_table_destroy(s->record_browser_hashtable);
1364 g_hash_table_destroy(s->entries_by_key);
1366 if (s->register_time_event)
1367 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1368 avahi_time_event_queue_free(s->time_event_queue);
1370 avahi_record_list_free(s->record_list);
1372 if (s->fd_ipv4 >= 0)
1374 if (s->fd_ipv6 >= 0)
1376 if (s->fd_legacy_unicast_ipv4 >= 0)
1377 close(s->fd_legacy_unicast_ipv4);
1378 if (s->fd_legacy_unicast_ipv6 >= 0)
1379 close(s->fd_legacy_unicast_ipv6);
1381 g_free(s->host_name);
1382 g_free(s->domain_name);
1383 g_free(s->host_name_fqdn);
1385 g_source_destroy(s->source);
1386 g_source_unref(s->source);
1387 g_main_context_unref(s->context);
1389 avahi_server_config_free(&s->config);
1394 void avahi_server_add(
1399 AvahiEntryFlags flags,
1406 g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1408 e = g_new(AvahiEntry, 1);
1410 e->record = avahi_record_ref(r);
1412 e->interface = interface;
1413 e->protocol = protocol;
1417 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1419 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1421 /* Insert into hash table indexed by name */
1422 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1423 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1424 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1426 /* Insert into group list */
1428 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1430 avahi_announce_entry(s, e);
1432 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1433 AvahiEntry **e = (AvahiEntry**) state;
1438 *e = g ? g->entries : s->entries;
1440 while (*e && (*e)->dead)
1441 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1446 return avahi_record_ref((*e)->record);
1449 void avahi_server_dump(AvahiServer *s, FILE *f) {
1454 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1456 for (e = s->entries; e; e = e->entries_next) {
1462 t = avahi_record_to_string(e->record);
1463 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1467 avahi_dump_caches(s->monitor, f);
1470 void avahi_server_add_ptr(
1475 AvahiEntryFlags flags,
1477 const gchar *dest) {
1483 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1484 r->data.ptr.name = avahi_normalize_name(dest);
1485 avahi_server_add(s, g, interface, protocol, flags, r);
1486 avahi_record_unref(r);
1489 void avahi_server_add_address(
1494 AvahiEntryFlags flags,
1502 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1504 if (a->family == AF_INET) {
1508 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1509 r->data.a.address = a->data.ipv4;
1510 avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1511 avahi_record_unref(r);
1513 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1514 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1521 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1522 r->data.aaaa.address = a->data.ipv6;
1523 avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1524 avahi_record_unref(r);
1526 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1527 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1530 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1531 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1538 void avahi_server_add_text_strlst(
1543 AvahiEntryFlags flags,
1545 AvahiStringList *strlst) {
1551 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1552 r->data.txt.string_list = strlst;
1553 avahi_server_add(s, g, interface, protocol, flags, r);
1554 avahi_record_unref(r);
1557 void avahi_server_add_text_va(
1562 AvahiEntryFlags flags,
1568 avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1571 void avahi_server_add_text(
1576 AvahiEntryFlags flags,
1585 avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1589 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1594 while (*s && size >= 2) {
1595 if (*s == '.' || *s == '\\') {
1611 void avahi_server_add_service_strlst(
1618 const gchar *domain,
1621 AvahiStringList *strlst) {
1623 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1630 escape_service_name(ename, sizeof(ename), name);
1633 while (domain[0] == '.')
1636 domain = s->domain_name;
1639 host = s->host_name_fqdn;
1641 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1642 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1644 avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1646 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1647 r->data.srv.priority = 0;
1648 r->data.srv.weight = 0;
1649 r->data.srv.port = port;
1650 r->data.srv.name = avahi_normalize_name(host);
1651 avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1652 avahi_record_unref(r);
1654 avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1656 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1657 avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1660 void avahi_server_add_service_va(
1667 const gchar *domain,
1676 avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1679 void avahi_server_add_service(
1686 const gchar *domain,
1698 avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1702 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1703 AvahiKey *k = userdata;
1709 avahi_interface_post_query(i, k, FALSE);
1712 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1716 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1719 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1722 if (g->state == state)
1728 g->callback(g->server, g, state, g->userdata);
1733 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1738 g = g_new(AvahiEntryGroup, 1);
1740 g->callback = callback;
1741 g->userdata = userdata;
1743 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1745 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1747 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1751 void avahi_entry_group_free(AvahiEntryGroup *g) {
1755 g_assert(g->server);
1757 for (e = g->entries; e; e = e->by_group_next) {
1758 avahi_goodbye_entry(g->server, e, TRUE);
1764 g->server->need_group_cleanup = TRUE;
1765 g->server->need_entry_cleanup = TRUE;
1768 void avahi_entry_group_commit(AvahiEntryGroup *g) {
1772 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1775 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1776 avahi_announce_group(g->server, g);
1777 avahi_entry_group_check_probed(g, FALSE);
1780 gboolean avahi_entry_commited(AvahiEntry *e) {
1785 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1786 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1789 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1796 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1799 g->userdata = userdata;
1802 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1808 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1811 return s->domain_name;
1814 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1817 return s->host_name;
1820 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1823 return s->host_name_fqdn;
1826 gpointer avahi_server_get_data(AvahiServer *s) {
1832 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1835 s->userdata = userdata;
1838 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1844 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1847 memset(c, 0, sizeof(AvahiServerConfig));
1848 c->register_hinfo = TRUE;
1849 c->register_addresses = TRUE;
1852 c->host_name = NULL;
1853 c->domain_name = NULL;
1854 c->check_response_ttl = TRUE;
1855 c->announce_domain = TRUE;
1856 c->use_iff_running = FALSE;
1857 c->enable_reflector = FALSE;
1858 c->ipv_reflect = FALSE;
1859 c->register_workstation = TRUE;
1864 void avahi_server_config_free(AvahiServerConfig *c) {
1867 g_free(c->host_name);
1868 g_free(c->domain_name);
1871 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1877 ret->host_name = g_strdup(c->host_name);
1878 ret->domain_name = g_strdup(c->domain_name);