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 2000
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) {
232 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
237 if (avahi_entry_probing(s, e, i)) {
246 t = avahi_record_to_string(record);
251 g_message("xxx Recieved conflicting probe [%s]. Local host won.", t);
253 g_message("yyy Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
254 withdraw_rrset(s, record->key);
261 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
262 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
263 AvahiEntry *e, *n, *conflicting_entry = NULL;
270 /* g_message("CHECKING FOR CONFLICT: [%s]", t); */
272 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
275 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
278 /* Either our entry or the other is intended to be unique, so let's check */
280 if (avahi_record_equal_no_ttl(e->record, record)) {
281 ours = TRUE; /* We have an identical record, so this is no conflict */
283 /* Check wheter there is a TTL conflict */
284 if (record->ttl <= e->record->ttl/2 &&
285 avahi_entry_registered(s, e, i)) {
288 t = avahi_record_to_string(record);
290 g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
291 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
297 /* There's no need to check the other entries of this RRset */
302 if (avahi_entry_registered(s, e, i)) {
304 /* A conflict => we have to return to probe mode */
306 conflicting_entry = e;
308 } else if (avahi_entry_probing(s, e, i)) {
310 /* We are currently registering a matching record, but
311 * someone else already claimed it, so let's
314 withdraw_immediately = TRUE;
319 /* g_message("ours=%i conflict=%i", ours, conflict); */
321 if (!ours && conflict) {
326 t = avahi_record_to_string(record);
328 if (withdraw_immediately) {
329 g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
330 withdraw_rrset(s, record->key);
332 g_assert(conflicting_entry);
333 g_message("Recieved conflicting record [%s]. Resetting our record.", t);
334 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
336 /* Local unique records are returned to probin
337 * state. Local shared records are reannounced. */
346 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
347 gboolean *unicast_response = userdata;
351 g_assert(unicast_response);
353 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
356 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
360 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
363 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
367 g_assert(!legacy_unicast || (a && port > 0 && p));
369 if (legacy_unicast) {
370 AvahiDnsPacket *reply;
373 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
375 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
377 append_aux_records_to_list(s, i, r, FALSE);
379 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
380 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
382 gchar *t = avahi_record_to_string(r);
383 g_warning("Record [%s] not fitting in legacy unicast packet, dropping.", t);
387 avahi_record_unref(r);
390 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
391 avahi_interface_send_packet_unicast(i, reply, a, port);
393 avahi_dns_packet_free(reply);
396 gboolean unicast_response, flush_cache, auxiliary;
397 AvahiDnsPacket *reply = NULL;
400 /* In case the query packet was truncated never respond
401 immediately, because known answer suppression records might be
402 contained in later packets */
403 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
405 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
407 if (!avahi_interface_post_response(i, r, flush_cache, a, !tc && flush_cache && !auxiliary) && unicast_response) {
409 append_aux_records_to_list(s, i, r, unicast_response);
411 /* Due to some reasons the record has not been scheduled.
412 * The client requested an unicast response in that
413 * case. Therefore we prepare such a response */
419 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
422 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
424 /* Appending this record succeeded, so incremeant
425 * the specific header field, and return to the caller */
427 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
432 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
435 /* The record is too large for one packet, so create a larger packet */
437 avahi_dns_packet_free(reply);
438 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
439 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
440 size = AVAHI_DNS_PACKET_MAX_SIZE;
441 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
443 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
444 avahi_dns_packet_free(reply);
446 gchar *t = avahi_record_to_string(r);
447 g_warning("Record [%s] too large, doesn't fit in any packet!", t);
451 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
454 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
455 avahi_interface_send_packet_unicast(i, reply, a, port);
456 avahi_dns_packet_free(reply);
461 avahi_record_unref(r);
465 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
466 avahi_interface_send_packet_unicast(i, reply, a, port);
467 avahi_dns_packet_free(reply);
471 avahi_record_list_flush(s->record_list);
475 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
482 if (!s->config.enable_reflector)
485 for (j = s->monitor->interfaces; j; j = j->interface_next)
486 if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol))
487 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
490 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
491 AvahiServer *s = userdata;
498 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
502 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
509 if (!s->config.enable_reflector)
512 for (j = s->monitor->interfaces; j; j = j->interface_next)
513 if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol)) {
514 /* Post the query to other networks */
515 avahi_interface_post_query(j, k, TRUE);
517 /* Reply from caches of other network. This is needed to
518 * "work around" known answer suppression. */
520 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
524 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
531 if (!s->config.enable_reflector)
534 for (j = s->monitor->interfaces; j; j = j->interface_next)
535 if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol))
536 avahi_interface_post_probe(j, r, TRUE);
539 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
547 /* g_message("query"); */
549 g_assert(avahi_record_list_empty(s->record_list));
551 /* Handle the questions */
552 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
554 gboolean unicast_response = FALSE;
556 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
557 g_warning("Packet too short (1)");
562 reflect_query(s, i, key);
563 avahi_query_scheduler_incoming(i->query_scheduler, key);
564 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
565 avahi_key_unref(key);
568 /* Known Answer Suppression */
569 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
571 gboolean unique = FALSE;
573 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
574 g_warning("Packet too short (2)");
578 if (handle_conflict(s, i, record, unique, a)) {
579 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
580 avahi_record_list_drop(s->record_list, record);
583 avahi_record_unref(record);
587 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
589 gboolean unique = FALSE;
591 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
592 g_warning("Packet too short (3)");
596 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
597 reflect_probe(s, i, record);
598 incoming_probe(s, record, i);
601 avahi_record_unref(record);
604 if (!avahi_record_list_empty(s->record_list))
605 avahi_server_generate_response(s, i, p, a, port, legacy_unicast);
610 avahi_record_list_flush(s->record_list);
614 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
622 /* g_message("response"); */
624 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
625 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
627 gboolean cache_flush = FALSE;
630 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
631 g_warning("Packet too short (4)");
635 if (record->key->type != AVAHI_DNS_TYPE_ANY) {
637 /* g_message("Handling response: %s", txt = avahi_record_to_string(record)); */
640 if (handle_conflict(s, i, record, cache_flush, a)) {
641 reflect_response(s, i, record, cache_flush);
642 avahi_cache_update(i->cache, record, cache_flush, a);
643 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
647 avahi_record_unref(record);
651 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
652 guint n, index = (guint) -1;
653 AvahiLegacyUnicastReflectSlot *slot;
657 if (!s->legacy_unicast_reflect_slots)
658 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
660 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
661 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
663 if (!s->legacy_unicast_reflect_slots[index])
667 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
670 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
671 slot->id = s->legacy_unicast_reflect_id++;
676 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
682 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
684 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
686 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
689 s->legacy_unicast_reflect_slots[index] = NULL;
692 static void free_slots(AvahiServer *s) {
696 if (!s->legacy_unicast_reflect_slots)
699 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
700 if (s->legacy_unicast_reflect_slots[index])
701 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
703 g_free(s->legacy_unicast_reflect_slots);
704 s->legacy_unicast_reflect_slots = NULL;
707 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
712 if (!s->legacy_unicast_reflect_slots)
715 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
717 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
720 return s->legacy_unicast_reflect_slots[index];
723 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
724 AvahiLegacyUnicastReflectSlot *slot = userdata;
728 g_assert(slot->time_event == e);
730 deallocate_slot(slot->server, slot);
733 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
734 AvahiLegacyUnicastReflectSlot *slot;
742 g_assert(i->protocol == a->family);
744 if (!s->config.enable_reflector)
747 /* g_message("legacy unicast reflectr"); */
749 /* Reflecting legacy unicast queries is a little more complicated
750 than reflecting normal queries, since we must route the
751 responses back to the right client. Therefore we must store
752 some information for finding the right client contact data for
753 response packets. In contrast to normal queries legacy
754 unicast query and response packets are reflected untouched and
755 are not reassembled into larger packets */
757 if (!(slot = allocate_slot(s))) {
758 /* No slot available, we drop this legacy unicast query */
759 g_warning("No slot available for legacy unicast reflection, dropping query packet.");
763 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
766 slot->interface = i->hardware->index;
768 avahi_elapse_time(&slot->elapse_time, 2000, 0);
769 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
771 /* Patch the packet with our new locally generatedt id */
772 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
774 for (j = s->monitor->interfaces; j; j = j->interface_next)
775 if (avahi_interface_relevant(j) &&
777 (s->config.ipv_reflect || j->protocol == i->protocol)) {
779 if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
780 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
781 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
782 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
786 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
789 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
794 if (!s->config.enable_reflector)
797 avahi_address_from_sockaddr(sa, &a);
799 if (!avahi_address_is_local(s->monitor, &a))
802 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
803 struct sockaddr_in lsa;
804 socklen_t l = sizeof(lsa);
806 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
807 g_warning("getsockname(): %s", strerror(errno));
809 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
813 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
814 struct sockaddr_in6 lsa;
815 socklen_t l = sizeof(lsa);
817 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
818 g_warning("getsockname(): %s", strerror(errno));
820 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
826 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
836 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
837 !avahi_interface_relevant(i)) {
838 g_warning("Recieved packet from invalid interface.");
842 /* g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
844 port = avahi_port_from_sockaddr(sa);
845 avahi_address_from_sockaddr(sa, &a);
847 if (avahi_address_is_ipv4_in_ipv6(&a))
848 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
851 if (originates_from_local_legacy_unicast_socket(s, sa))
852 /* This originates from our local reflector, so let's ignore it */
855 if (avahi_dns_packet_check_valid(p) < 0) {
856 g_warning("Recieved invalid packet.");
860 if (avahi_dns_packet_is_query(p)) {
861 gboolean legacy_unicast = FALSE;
863 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
864 g_warning("Invalid query packet.");
868 if (port != AVAHI_MDNS_PORT) {
871 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
872 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
873 g_warning("Invalid legacy unicast query packet.");
877 legacy_unicast = TRUE;
881 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
883 handle_query_packet(s, p, i, &a, port, legacy_unicast);
885 /* g_message("Handled query"); */
888 if (port != AVAHI_MDNS_PORT) {
889 g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
894 g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
895 if (s->config.check_response_ttl)
899 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
900 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
901 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
902 g_warning("Invalid response packet.");
906 handle_response_packet(s, p, i, &a);
907 /* g_message("Handled response"); */
911 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
912 AvahiInterface *i, *j;
915 AvahiLegacyUnicastReflectSlot *slot;
922 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
923 !avahi_interface_relevant(i)) {
924 g_warning("Recieved packet from invalid interface.");
928 /* g_message("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
930 port = avahi_port_from_sockaddr(sa);
931 avahi_address_from_sockaddr(sa, &a);
933 if (avahi_address_is_ipv4_in_ipv6(&a))
934 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
937 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
938 g_warning("Recieved invalid packet.");
942 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
943 g_warning("Recieved legacy unicast response with unknown id");
947 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
948 !avahi_interface_relevant(j))
951 /* Patch the original ID into this response */
952 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
954 /* Forward the response to the correct client */
955 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
957 /* Undo changes to packet */
958 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
961 static void work(AvahiServer *s) {
962 struct sockaddr_in6 sa6;
963 struct sockaddr_in sa;
970 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
971 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
972 dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
973 avahi_dns_packet_free(p);
977 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
978 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
979 dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
980 avahi_dns_packet_free(p);
984 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
985 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
986 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
987 avahi_dns_packet_free(p);
991 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
992 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
993 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
994 avahi_dns_packet_free(p);
999 static gboolean prepare_func(GSource *source, gint *timeout) {
1007 static gboolean check_func(GSource *source) {
1009 gushort revents = 0;
1013 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1016 if (s->fd_ipv4 >= 0)
1017 revents |= s->pollfd_ipv4.revents;
1018 if (s->fd_ipv6 >= 0)
1019 revents |= s->pollfd_ipv6.revents;
1020 if (s->fd_legacy_unicast_ipv4 >= 0)
1021 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1022 if (s->fd_legacy_unicast_ipv6 >= 0)
1023 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1025 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1028 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1032 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1041 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1044 if (s->state == state)
1050 s->callback(s, state, s->userdata);
1053 static void withdraw_host_rrs(AvahiServer *s) {
1056 if (s->hinfo_entry_group) {
1057 avahi_entry_group_free(s->hinfo_entry_group);
1058 s->hinfo_entry_group = NULL;
1061 if (s->browse_domain_entry_group) {
1062 avahi_entry_group_free(s->browse_domain_entry_group);
1063 s->browse_domain_entry_group = NULL;
1066 avahi_update_host_rrs(s->monitor, TRUE);
1067 s->n_host_rr_pending = 0;
1070 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1073 g_assert(s->n_host_rr_pending > 0);
1075 if (--s->n_host_rr_pending == 0)
1076 server_set_state(s, AVAHI_SERVER_RUNNING);
1079 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1082 s->n_host_rr_pending ++;
1085 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1089 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1090 s->state == AVAHI_SERVER_REGISTERING)
1091 avahi_server_increase_host_rr_pending(s);
1092 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1093 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1094 withdraw_host_rrs(s);
1095 server_set_state(s, AVAHI_SERVER_COLLISION);
1096 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1097 s->state == AVAHI_SERVER_REGISTERING)
1098 avahi_server_decrease_host_rr_pending(s);
1101 static void register_hinfo(AvahiServer *s) {
1102 struct utsname utsname;
1107 if (!s->config.register_hinfo || s->hinfo_entry_group)
1110 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1112 /* Fill in HINFO rr */
1113 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1115 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1116 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1117 avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1118 avahi_record_unref(r);
1120 avahi_entry_group_commit(s->hinfo_entry_group);
1123 static void register_localhost(AvahiServer *s) {
1127 /* Add localhost entries */
1128 avahi_address_parse("127.0.0.1", AF_INET, &a);
1129 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1131 avahi_address_parse("::1", AF_INET6, &a);
1132 avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1135 static void register_browse_domain(AvahiServer *s) {
1138 if (!s->config.announce_domain || s->browse_domain_entry_group)
1141 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1142 avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1143 avahi_entry_group_commit(s->browse_domain_entry_group);
1146 static void register_stuff(AvahiServer *s) {
1149 server_set_state(s, AVAHI_SERVER_REGISTERING);
1151 register_browse_domain(s);
1152 avahi_update_host_rrs(s->monitor, FALSE);
1154 if (s->n_host_rr_pending == 0)
1155 server_set_state(s, AVAHI_SERVER_RUNNING);
1158 static void update_fqdn(AvahiServer *s) {
1161 g_assert(s->host_name);
1162 g_assert(s->domain_name);
1164 g_free(s->host_name_fqdn);
1165 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1168 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1169 AvahiServer *s = userdata;
1174 g_assert(e == s->register_time_event);
1175 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1176 s->register_time_event = NULL;
1178 if (s->state == AVAHI_SERVER_SLEEPING)
1182 static void delayed_register_stuff(AvahiServer *s) {
1187 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1189 if (s->register_time_event)
1190 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1192 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1195 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1197 g_assert(host_name);
1199 server_set_state(s, AVAHI_SERVER_SLEEPING);
1200 withdraw_host_rrs(s);
1202 g_free(s->host_name);
1203 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1204 s->host_name[strcspn(s->host_name, ".")] = 0;
1207 delayed_register_stuff(s);
1211 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1213 g_assert(domain_name);
1215 server_set_state(s, AVAHI_SERVER_SLEEPING);
1216 withdraw_host_rrs(s);
1218 g_free(s->domain_name);
1219 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1222 delayed_register_stuff(s);
1227 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1232 memset(pollfd, 0, sizeof(GPollFD));
1234 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1235 g_source_add_poll(s->source, pollfd);
1238 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1241 static GSourceFuncs source_funcs = {
1250 s = g_new(AvahiServer, 1);
1251 s->n_host_rr_pending = 0;
1252 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1255 avahi_server_config_copy(&s->config, sc);
1257 avahi_server_config_init(&s->config);
1259 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1260 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1262 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1263 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1264 avahi_server_config_free(&s->config);
1269 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1270 g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1271 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1272 g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1274 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1275 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1278 g_main_context_ref(s->context = c);
1280 s->context = g_main_context_default();
1282 /* Prepare IO source registration */
1283 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1284 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1286 if (s->fd_ipv4 >= 0)
1287 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1288 if (s->fd_ipv6 >= 0)
1289 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1290 if (s->fd_legacy_unicast_ipv4 >= 0)
1291 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1292 if (s->fd_legacy_unicast_ipv6 >= 0)
1293 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1295 g_source_attach(s->source, s->context);
1297 s->callback = callback;
1298 s->userdata = userdata;
1300 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1301 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1302 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1304 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1305 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1306 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1307 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1308 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1309 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1310 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1311 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1313 s->legacy_unicast_reflect_slots = NULL;
1314 s->legacy_unicast_reflect_id = 0;
1317 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1318 s->host_name[strcspn(s->host_name, ".")] = 0;
1319 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1320 s->host_name_fqdn = NULL;
1323 s->record_list = avahi_record_list_new();
1325 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1326 s->register_time_event = NULL;
1328 s->state = AVAHI_SERVER_INVALID;
1330 s->monitor = avahi_interface_monitor_new(s);
1331 avahi_interface_monitor_sync(s->monitor);
1333 register_localhost(s);
1335 s->hinfo_entry_group = NULL;
1336 s->browse_domain_entry_group = NULL;
1342 void avahi_server_free(AvahiServer* s) {
1346 free_entry(s, s->entries);
1348 avahi_interface_monitor_free(s->monitor);
1351 free_group(s, s->groups);
1355 while (s->host_name_resolvers)
1356 avahi_host_name_resolver_free(s->host_name_resolvers);
1357 while (s->address_resolvers)
1358 avahi_address_resolver_free(s->address_resolvers);
1359 while (s->domain_browsers)
1360 avahi_domain_browser_free(s->domain_browsers);
1361 while (s->service_type_browsers)
1362 avahi_service_type_browser_free(s->service_type_browsers);
1363 while (s->service_browsers)
1364 avahi_service_browser_free(s->service_browsers);
1365 while (s->service_resolvers)
1366 avahi_service_resolver_free(s->service_resolvers);
1367 while (s->record_browsers)
1368 avahi_record_browser_destroy(s->record_browsers);
1369 g_hash_table_destroy(s->record_browser_hashtable);
1371 g_hash_table_destroy(s->entries_by_key);
1373 if (s->register_time_event)
1374 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1375 avahi_time_event_queue_free(s->time_event_queue);
1377 avahi_record_list_free(s->record_list);
1379 if (s->fd_ipv4 >= 0)
1381 if (s->fd_ipv6 >= 0)
1383 if (s->fd_legacy_unicast_ipv4 >= 0)
1384 close(s->fd_legacy_unicast_ipv4);
1385 if (s->fd_legacy_unicast_ipv6 >= 0)
1386 close(s->fd_legacy_unicast_ipv6);
1388 g_free(s->host_name);
1389 g_free(s->domain_name);
1390 g_free(s->host_name_fqdn);
1392 g_source_destroy(s->source);
1393 g_source_unref(s->source);
1394 g_main_context_unref(s->context);
1396 avahi_server_config_free(&s->config);
1401 gint avahi_server_add(
1406 AvahiEntryFlags flags,
1413 g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1415 e = g_new(AvahiEntry, 1);
1417 e->record = avahi_record_ref(r);
1419 e->interface = interface;
1420 e->protocol = protocol;
1424 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1426 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1428 /* Insert into hash table indexed by name */
1429 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1430 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1431 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1433 /* Insert into group list */
1435 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1437 avahi_announce_entry(s, e);
1442 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1443 AvahiEntry **e = (AvahiEntry**) state;
1448 *e = g ? g->entries : s->entries;
1450 while (*e && (*e)->dead)
1451 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1456 return avahi_record_ref((*e)->record);
1459 void avahi_server_dump(AvahiServer *s, FILE *f) {
1464 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1466 for (e = s->entries; e; e = e->entries_next) {
1472 t = avahi_record_to_string(e->record);
1473 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1477 avahi_dump_caches(s->monitor, f);
1480 gint avahi_server_add_ptr(
1485 AvahiEntryFlags flags,
1487 const gchar *dest) {
1493 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1494 r->data.ptr.name = avahi_normalize_name(dest);
1495 avahi_server_add(s, g, interface, protocol, flags, r);
1496 avahi_record_unref(r);
1500 gint avahi_server_add_address(
1505 AvahiEntryFlags flags,
1513 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1515 if (a->family == AF_INET) {
1519 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1520 r->data.a.address = a->data.ipv4;
1521 avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1522 avahi_record_unref(r);
1524 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1525 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1532 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1533 r->data.aaaa.address = a->data.ipv6;
1534 avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1535 avahi_record_unref(r);
1537 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1538 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1541 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1542 avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1551 gint avahi_server_add_text_strlst(
1556 AvahiEntryFlags flags,
1558 AvahiStringList *strlst) {
1564 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1565 r->data.txt.string_list = strlst;
1566 avahi_server_add(s, g, interface, protocol, flags, r);
1567 avahi_record_unref(r);
1572 gint avahi_server_add_text_va(
1577 AvahiEntryFlags flags,
1583 avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1587 gint avahi_server_add_text(
1592 AvahiEntryFlags flags,
1601 avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1607 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1612 while (*s && size >= 2) {
1613 if (*s == '.' || *s == '\\') {
1629 gint avahi_server_add_service_strlst(
1636 const gchar *domain,
1639 AvahiStringList *strlst) {
1641 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1648 escape_service_name(ename, sizeof(ename), name);
1651 while (domain[0] == '.')
1654 domain = s->domain_name;
1657 host = s->host_name_fqdn;
1659 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1660 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1662 avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1664 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1665 r->data.srv.priority = 0;
1666 r->data.srv.weight = 0;
1667 r->data.srv.port = port;
1668 r->data.srv.name = avahi_normalize_name(host);
1669 avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1670 avahi_record_unref(r);
1672 avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1674 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1675 avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1680 gint avahi_server_add_service_va(
1687 const gchar *domain,
1696 avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1700 gint avahi_server_add_service(
1707 const gchar *domain,
1719 avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1724 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1725 AvahiKey *k = userdata;
1731 avahi_interface_post_query(i, k, FALSE);
1734 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1738 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1741 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1744 if (g->state == state)
1750 g->callback(g->server, g, state, g->userdata);
1755 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1760 g = g_new(AvahiEntryGroup, 1);
1762 g->callback = callback;
1763 g->userdata = userdata;
1765 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1767 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1769 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1773 void avahi_entry_group_free(AvahiEntryGroup *g) {
1777 g_assert(g->server);
1779 for (e = g->entries; e; e = e->by_group_next) {
1780 avahi_goodbye_entry(g->server, e, TRUE);
1786 g->server->need_group_cleanup = TRUE;
1787 g->server->need_entry_cleanup = TRUE;
1790 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1794 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1797 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1798 avahi_announce_group(g->server, g);
1799 avahi_entry_group_check_probed(g, FALSE);
1804 gboolean avahi_entry_commited(AvahiEntry *e) {
1809 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1810 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1813 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1820 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1823 g->userdata = userdata;
1826 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1832 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1835 return s->domain_name;
1838 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1841 return s->host_name;
1844 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1847 return s->host_name_fqdn;
1850 gpointer avahi_server_get_data(AvahiServer *s) {
1856 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1859 s->userdata = userdata;
1862 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1868 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1871 memset(c, 0, sizeof(AvahiServerConfig));
1872 c->register_hinfo = TRUE;
1873 c->register_addresses = TRUE;
1876 c->host_name = NULL;
1877 c->domain_name = NULL;
1878 c->check_response_ttl = TRUE;
1879 c->announce_domain = TRUE;
1880 c->use_iff_running = FALSE;
1881 c->enable_reflector = FALSE;
1882 c->ipv_reflect = FALSE;
1883 c->register_workstation = TRUE;
1888 void avahi_server_config_free(AvahiServerConfig *c) {
1891 g_free(c->host_name);
1892 g_free(c->domain_name);
1895 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1901 ret->host_name = g_strdup(c->host_name);
1902 ret->domain_name = g_strdup(c->domain_name);