2 This file is part of catta.
4 catta is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 catta is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with catta; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31 #include <arpa/inet.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
36 #include <catta/domain.h>
37 #include <catta/timeval.h>
38 #include <catta/malloc.h>
39 #include <catta/error.h>
40 #include <catta/domain.h>
41 #include <catta/log.h>
48 #include "dns-srv-rr.h"
50 #include "domain-util.h"
52 static void transport_flags_from_domain(CattaServer *s, CattaPublishFlags *flags, const char *domain) {
56 assert(!((*flags & CATTA_PUBLISH_USE_MULTICAST) && (*flags & CATTA_PUBLISH_USE_WIDE_AREA)));
58 if (*flags & (CATTA_PUBLISH_USE_MULTICAST|CATTA_PUBLISH_USE_WIDE_AREA))
61 if (!s->wide_area_lookup_engine ||
62 !catta_wide_area_has_servers(s->wide_area_lookup_engine) ||
63 catta_domain_ends_with(domain, CATTA_MDNS_SUFFIX_LOCAL) ||
64 catta_domain_ends_with(domain, CATTA_MDNS_SUFFIX_ADDR_IPV4) ||
65 catta_domain_ends_with(domain, CATTA_MDNS_SUFFIX_ADDR_IPV6))
66 *flags |= CATTA_PUBLISH_USE_MULTICAST;
68 *flags |= CATTA_PUBLISH_USE_WIDE_AREA;
71 void catta_entry_free(CattaServer*s, CattaEntry *e) {
77 catta_goodbye_entry(s, e, 1, 1);
79 /* Remove from linked list */
80 CATTA_LLIST_REMOVE(CattaEntry, entries, s->entries, e);
82 /* Remove from hash table indexed by name */
83 t = catta_hashmap_lookup(s->entries_by_key, e->record->key);
84 CATTA_LLIST_REMOVE(CattaEntry, by_key, t, e);
86 catta_hashmap_replace(s->entries_by_key, t->record->key, t);
88 catta_hashmap_remove(s->entries_by_key, e->record->key);
90 /* Remove from associated group */
92 CATTA_LLIST_REMOVE(CattaEntry, by_group, e->group->entries, e);
94 catta_record_unref(e->record);
98 void catta_entry_group_free(CattaServer *s, CattaSEntryGroup *g) {
103 catta_entry_free(s, g->entries);
105 if (g->register_time_event)
106 catta_time_event_free(g->register_time_event);
108 CATTA_LLIST_REMOVE(CattaSEntryGroup, groups, s->groups, g);
112 void catta_cleanup_dead_entries(CattaServer *s) {
115 if (s->need_group_cleanup) {
116 CattaSEntryGroup *g, *next;
118 for (g = s->groups; g; g = next) {
119 next = g->groups_next;
122 catta_entry_group_free(s, g);
125 s->need_group_cleanup = 0;
128 if (s->need_entry_cleanup) {
129 CattaEntry *e, *next;
131 for (e = s->entries; e; e = next) {
132 next = e->entries_next;
135 catta_entry_free(s, e);
138 s->need_entry_cleanup = 0;
141 if (s->need_browser_cleanup)
142 catta_browser_cleanup(s);
144 if (s->cleanup_time_event) {
145 catta_time_event_free(s->cleanup_time_event);
146 s->cleanup_time_event = NULL;
150 static int check_record_conflict(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, CattaRecord *r, CattaPublishFlags flags) {
156 for (e = catta_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
160 if (!(flags & CATTA_PUBLISH_UNIQUE) && !(e->flags & CATTA_PUBLISH_UNIQUE))
163 if ((flags & CATTA_PUBLISH_ALLOW_MULTIPLE) && (e->flags & CATTA_PUBLISH_ALLOW_MULTIPLE) )
166 if (catta_record_equal_no_ttl(r, e->record)) {
167 /* The records are the same, not a conflict in any case */
173 e->iface == iface) &&
174 (protocol == CATTA_PROTO_UNSPEC ||
175 e->protocol == CATTA_PROTO_UNSPEC ||
176 e->protocol == protocol))
184 static CattaEntry * server_add_internal(
188 CattaProtocol protocol,
189 CattaPublishFlags flags,
197 CATTA_CHECK_VALIDITY_RETURN_NULL(s, s->state != CATTA_SERVER_FAILURE && s->state != CATTA_SERVER_INVALID, CATTA_ERR_BAD_STATE);
198 CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
199 CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
200 CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_FLAGS_VALID(
202 CATTA_PUBLISH_NO_ANNOUNCE|
203 CATTA_PUBLISH_NO_PROBE|
204 CATTA_PUBLISH_UNIQUE|
205 CATTA_PUBLISH_ALLOW_MULTIPLE|
206 CATTA_PUBLISH_UPDATE|
207 CATTA_PUBLISH_USE_WIDE_AREA|
208 CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
209 CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_is_valid_domain_name(r->key->name), CATTA_ERR_INVALID_HOST_NAME);
210 CATTA_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, CATTA_ERR_INVALID_TTL);
211 CATTA_CHECK_VALIDITY_RETURN_NULL(s, !catta_key_is_pattern(r->key), CATTA_ERR_IS_PATTERN);
212 CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_record_is_valid(r), CATTA_ERR_INVALID_RECORD);
213 CATTA_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == CATTA_DNS_CLASS_IN, CATTA_ERR_INVALID_DNS_CLASS);
214 CATTA_CHECK_VALIDITY_RETURN_NULL(s,
215 (r->key->type != 0) &&
216 (r->key->type != CATTA_DNS_TYPE_ANY) &&
217 (r->key->type != CATTA_DNS_TYPE_OPT) &&
218 (r->key->type != CATTA_DNS_TYPE_TKEY) &&
219 (r->key->type != CATTA_DNS_TYPE_TSIG) &&
220 (r->key->type != CATTA_DNS_TYPE_IXFR) &&
221 (r->key->type != CATTA_DNS_TYPE_AXFR), CATTA_ERR_INVALID_DNS_TYPE);
223 transport_flags_from_domain(s, &flags, r->key->name);
224 CATTA_CHECK_VALIDITY_RETURN_NULL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
225 CATTA_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, CATTA_ERR_NOT_PERMITTED);
226 CATTA_CHECK_VALIDITY_RETURN_NULL(s,
228 (g->state != CATTA_ENTRY_GROUP_ESTABLISHED && g->state != CATTA_ENTRY_GROUP_REGISTERING) ||
229 (flags & CATTA_PUBLISH_UPDATE), CATTA_ERR_BAD_STATE);
231 if (flags & CATTA_PUBLISH_UPDATE) {
232 CattaRecord *old_record;
235 /* Update and existing record */
237 /* Find the first matching entry */
238 for (e = catta_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
239 if (!e->dead && e->group == g && e->iface == iface && e->protocol == protocol)
245 /* Hmm, nothing found? */
247 catta_server_set_errno(s, CATTA_ERR_NOT_FOUND);
251 /* Update the entry */
252 old_record = e->record;
253 e->record = catta_record_ref(r);
256 /* Announce our changes when needed */
257 if (!catta_record_equal_no_ttl(old_record, r) && (!g || g->state != CATTA_ENTRY_GROUP_UNCOMMITED)) {
259 /* Remove the old entry from all caches, if needed */
260 if (!(e->flags & CATTA_PUBLISH_UNIQUE))
261 catta_goodbye_entry(s, e, 1, 0);
263 /* Reannounce our updated entry */
264 catta_reannounce_entry(s, e);
267 /* If we were the first entry in the list, we need to update the key */
269 catta_hashmap_replace(s->entries_by_key, e->record->key, e);
271 catta_record_unref(old_record);
276 /* Add a new record */
278 if (check_record_conflict(s, iface, protocol, r, flags) < 0) {
279 catta_server_set_errno(s, CATTA_ERR_COLLISION);
283 if (!(e = catta_new(CattaEntry, 1))) {
284 catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
289 e->record = catta_record_ref(r);
292 e->protocol = protocol;
296 CATTA_LLIST_HEAD_INIT(CattaAnnouncer, e->announcers);
298 CATTA_LLIST_PREPEND(CattaEntry, entries, s->entries, e);
300 /* Insert into hash table indexed by name */
301 t = catta_hashmap_lookup(s->entries_by_key, e->record->key);
302 CATTA_LLIST_PREPEND(CattaEntry, by_key, t, e);
303 catta_hashmap_replace(s->entries_by_key, e->record->key, t);
305 /* Insert into group list */
307 CATTA_LLIST_PREPEND(CattaEntry, by_group, g->entries, e);
309 catta_announce_entry(s, e);
315 int catta_server_add(
319 CattaProtocol protocol,
320 CattaPublishFlags flags,
323 if (!server_add_internal(s, g, iface, protocol, flags, r))
324 return catta_server_errno(s);
329 const CattaRecord *catta_server_iterate(CattaServer *s, CattaSEntryGroup *g, void **state) {
330 CattaEntry **e = (CattaEntry**) state;
335 *e = g ? g->entries : s->entries;
337 while (*e && (*e)->dead)
338 *e = g ? (*e)->by_group_next : (*e)->entries_next;
343 return catta_record_ref((*e)->record);
346 int catta_server_dump(CattaServer *s, CattaDumpCallback callback, void* userdata) {
352 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
354 for (e = s->entries; e; e = e->entries_next) {
361 if (!(t = catta_record_to_string(e->record)))
362 return catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
364 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->iface, e->protocol);
367 callback(ln, userdata);
370 catta_dump_caches(s->monitor, callback, userdata);
372 if (s->wide_area_lookup_engine)
373 catta_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
377 static CattaEntry *server_add_ptr_internal(
381 CattaProtocol protocol,
382 CattaPublishFlags flags,
393 CATTA_CHECK_VALIDITY_RETURN_NULL(s, !name || catta_is_valid_domain_name(name), CATTA_ERR_INVALID_HOST_NAME);
394 CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_is_valid_domain_name(dest), CATTA_ERR_INVALID_HOST_NAME);
397 name = s->host_name_fqdn;
399 if (!(r = catta_record_new_full(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_PTR, ttl))) {
400 catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
404 r->data.ptr.name = catta_normalize_name_strdup(dest);
405 e = server_add_internal(s, g, iface, protocol, flags, r);
406 catta_record_unref(r);
410 int catta_server_add_ptr(
414 CattaProtocol protocol,
415 CattaPublishFlags flags,
424 if (!(e = server_add_ptr_internal(s, g, iface, protocol, flags, ttl, name, dest)))
425 return catta_server_errno(s);
430 int catta_server_add_address(
434 CattaProtocol protocol,
435 CattaPublishFlags flags,
439 char n[CATTA_DOMAIN_NAME_MAX];
441 CattaEntry *entry = NULL, *reverse = NULL;
447 CATTA_CHECK_VALIDITY(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
448 CATTA_CHECK_VALIDITY(s, CATTA_PROTO_VALID(protocol) && CATTA_PROTO_VALID(a->proto), CATTA_ERR_INVALID_PROTOCOL);
449 CATTA_CHECK_VALIDITY(s, CATTA_FLAGS_VALID(flags,
450 CATTA_PUBLISH_NO_REVERSE|
451 CATTA_PUBLISH_NO_ANNOUNCE|
452 CATTA_PUBLISH_NO_PROBE|
453 CATTA_PUBLISH_UPDATE|
454 CATTA_PUBLISH_USE_WIDE_AREA|
455 CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
456 CATTA_CHECK_VALIDITY(s, !name || catta_is_valid_fqdn(name), CATTA_ERR_INVALID_HOST_NAME);
458 /* Prepare the host naem */
461 name = s->host_name_fqdn;
463 CATTA_ASSERT_TRUE(catta_normalize_name(name, n, sizeof(n)));
467 transport_flags_from_domain(s, &flags, name);
468 CATTA_CHECK_VALIDITY(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
470 /* Create the A/AAAA record */
472 if (a->proto == CATTA_PROTO_INET) {
474 if (!(r = catta_record_new_full(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_A, CATTA_DEFAULT_TTL_HOST_NAME))) {
475 ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
479 r->data.a.address = a->data.ipv4;
482 assert(a->proto == CATTA_PROTO_INET6);
484 if (!(r = catta_record_new_full(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_AAAA, CATTA_DEFAULT_TTL_HOST_NAME))) {
485 ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
489 r->data.aaaa.address = a->data.ipv6;
492 entry = server_add_internal(s, g, iface, protocol, (flags & ~ CATTA_PUBLISH_NO_REVERSE) | CATTA_PUBLISH_UNIQUE | CATTA_PUBLISH_ALLOW_MULTIPLE, r);
493 catta_record_unref(r);
496 ret = catta_server_errno(s);
500 /* Create the reverse lookup entry */
502 if (!(flags & CATTA_PUBLISH_NO_REVERSE)) {
503 char reverse_n[CATTA_DOMAIN_NAME_MAX];
504 catta_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
506 if (!(reverse = server_add_ptr_internal(s, g, iface, protocol, flags | CATTA_PUBLISH_UNIQUE, CATTA_DEFAULT_TTL_HOST_NAME, reverse_n, name))) {
507 ret = catta_server_errno(s);
514 if (ret != CATTA_OK && !(flags & CATTA_PUBLISH_UPDATE)) {
516 catta_entry_free(s, entry);
518 catta_entry_free(s, reverse);
524 static CattaEntry *server_add_txt_strlst_nocopy(
528 CattaProtocol protocol,
529 CattaPublishFlags flags,
532 CattaStringList *strlst) {
539 if (!(r = catta_record_new_full(name ? name : s->host_name_fqdn, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_TXT, ttl))) {
540 catta_string_list_free(strlst);
541 catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
545 r->data.txt.string_list = strlst;
546 e = server_add_internal(s, g, iface, protocol, flags, r);
547 catta_record_unref(r);
552 static CattaStringList *add_magic_cookie(
554 CattaStringList *strlst) {
558 if (!s->config.add_service_cookie)
561 if (catta_string_list_find(strlst, CATTA_SERVICE_COOKIE))
562 /* This string list already contains a magic cookie */
565 return catta_string_list_add_printf(strlst, CATTA_SERVICE_COOKIE"=%u", s->local_service_cookie);
568 static int server_add_service_strlst_nocopy(
572 CattaProtocol protocol,
573 CattaPublishFlags flags,
579 CattaStringList *strlst) {
581 char ptr_name[CATTA_DOMAIN_NAME_MAX], svc_name[CATTA_DOMAIN_NAME_MAX], enum_ptr[CATTA_DOMAIN_NAME_MAX], *h = NULL;
582 CattaRecord *r = NULL;
584 CattaEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
590 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
591 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
592 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_FLAGS_VALID(flags,
593 CATTA_PUBLISH_NO_COOKIE|
594 CATTA_PUBLISH_UPDATE|
595 CATTA_PUBLISH_USE_WIDE_AREA|
596 CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
597 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
598 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
599 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
600 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || catta_is_valid_fqdn(host), CATTA_ERR_INVALID_HOST_NAME);
603 domain = s->domain_name;
606 host = s->host_name_fqdn;
608 transport_flags_from_domain(s, &flags, domain);
609 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
611 if (!(h = catta_normalize_name_strdup(host))) {
612 ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
616 if ((ret = catta_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
617 (ret = catta_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
618 (ret = catta_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
619 catta_server_set_errno(s, ret);
623 /* Add service enumeration PTR record */
625 if (!(ptr_entry = server_add_ptr_internal(s, g, iface, protocol, 0, CATTA_DEFAULT_TTL, ptr_name, svc_name))) {
626 ret = catta_server_errno(s);
632 if (!(r = catta_record_new_full(svc_name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV, CATTA_DEFAULT_TTL_HOST_NAME))) {
633 ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
637 r->data.srv.priority = 0;
638 r->data.srv.weight = 0;
639 r->data.srv.port = port;
640 r->data.srv.name = h;
642 srv_entry = server_add_internal(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE, r);
643 catta_record_unref(r);
646 ret = catta_server_errno(s);
652 if (!(flags & CATTA_PUBLISH_NO_COOKIE))
653 strlst = add_magic_cookie(s, strlst);
655 txt_entry = server_add_txt_strlst_nocopy(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE, CATTA_DEFAULT_TTL, svc_name, strlst);
659 ret = catta_server_errno(s);
663 /* Add service type enumeration record */
665 if (!(enum_entry = server_add_ptr_internal(s, g, iface, protocol, 0, CATTA_DEFAULT_TTL, enum_ptr, ptr_name))) {
666 ret = catta_server_errno(s);
671 if (ret != CATTA_OK && !(flags & CATTA_PUBLISH_UPDATE)) {
673 catta_entry_free(s, srv_entry);
675 catta_entry_free(s, txt_entry);
677 catta_entry_free(s, ptr_entry);
679 catta_entry_free(s, enum_entry);
682 catta_string_list_free(strlst);
688 int catta_server_add_service_strlst(
692 CattaProtocol protocol,
693 CattaPublishFlags flags,
699 CattaStringList *strlst) {
705 return server_add_service_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, host, port, catta_string_list_copy(strlst));
708 int catta_server_add_service(
712 CattaProtocol protocol,
713 CattaPublishFlags flags,
725 ret = server_add_service_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, host, port, catta_string_list_new_va(va));
731 static int server_update_service_txt_strlst_nocopy(
735 CattaProtocol protocol,
736 CattaPublishFlags flags,
740 CattaStringList *strlst) {
742 char svc_name[CATTA_DOMAIN_NAME_MAX];
750 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
751 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
752 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_FLAGS_VALID(flags,
753 CATTA_PUBLISH_NO_COOKIE|
754 CATTA_PUBLISH_USE_WIDE_AREA|
755 CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
756 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
757 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
758 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
761 domain = s->domain_name;
763 transport_flags_from_domain(s, &flags, domain);
764 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
766 if ((ret = catta_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
767 catta_server_set_errno(s, ret);
772 if (!(flags & CATTA_PUBLISH_NO_COOKIE))
773 strlst = add_magic_cookie(s, strlst);
775 e = server_add_txt_strlst_nocopy(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE | CATTA_PUBLISH_UPDATE, CATTA_DEFAULT_TTL, svc_name, strlst);
779 ret = catta_server_errno(s);
783 catta_string_list_free(strlst);
788 int catta_server_update_service_txt_strlst(
792 CattaProtocol protocol,
793 CattaPublishFlags flags,
797 CattaStringList *strlst) {
799 return server_update_service_txt_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, catta_string_list_copy(strlst));
802 /** Update the TXT record for a service with the NULL termonate list of strings */
803 int catta_server_update_service_txt(
807 CattaProtocol protocol,
808 CattaPublishFlags flags,
817 va_start(va, domain);
818 ret = server_update_service_txt_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, catta_string_list_new_va(va));
824 int catta_server_add_service_subtype(
828 CattaProtocol protocol,
829 CattaPublishFlags flags,
833 const char *subtype) {
836 char svc_name[CATTA_DOMAIN_NAME_MAX], ptr_name[CATTA_DOMAIN_NAME_MAX];
842 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
843 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
844 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_FLAGS_VALID(flags, CATTA_PUBLISH_USE_MULTICAST|CATTA_PUBLISH_USE_WIDE_AREA), CATTA_ERR_INVALID_FLAGS);
845 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
846 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
847 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
848 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_subtype(subtype), CATTA_ERR_INVALID_SERVICE_SUBTYPE);
851 domain = s->domain_name;
853 transport_flags_from_domain(s, &flags, domain);
854 CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
856 if ((ret = catta_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
857 (ret = catta_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
858 catta_server_set_errno(s, ret);
862 if ((ret = catta_server_add_ptr(s, g, iface, protocol, 0, CATTA_DEFAULT_TTL, ptr_name, svc_name)) < 0)
870 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
871 static const char hex[] = "0123456789abcdef";
873 const uint8_t *k = p;
875 while (sl > 1 && pl > 0) {
876 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
892 static CattaEntry *server_add_dns_server_name(
896 CattaProtocol protocol,
897 CattaPublishFlags flags,
899 CattaDNSServerType type,
901 uint16_t port /** should be 53 */) {
904 char t[CATTA_DOMAIN_NAME_MAX], normalized_d[CATTA_DOMAIN_NAME_MAX], *n;
911 CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_FLAGS_VALID(flags, CATTA_PUBLISH_USE_WIDE_AREA|CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
912 CATTA_CHECK_VALIDITY_RETURN_NULL(s, type == CATTA_DNS_SERVER_UPDATE || type == CATTA_DNS_SERVER_RESOLVE, CATTA_ERR_INVALID_FLAGS);
913 CATTA_CHECK_VALIDITY_RETURN_NULL(s, port != 0, CATTA_ERR_INVALID_PORT);
914 CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_is_valid_fqdn(name), CATTA_ERR_INVALID_HOST_NAME);
915 CATTA_CHECK_VALIDITY_RETURN_NULL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
918 domain = s->domain_name;
920 transport_flags_from_domain(s, &flags, domain);
921 CATTA_CHECK_VALIDITY_RETURN_NULL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
923 if (!(n = catta_normalize_name_strdup(name))) {
924 catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
928 CATTA_ASSERT_TRUE(catta_normalize_name(domain, normalized_d, sizeof(normalized_d)));
930 snprintf(t, sizeof(t), "%s.%s", type == CATTA_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
932 if (!(r = catta_record_new_full(t, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV, CATTA_DEFAULT_TTL_HOST_NAME))) {
933 catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
938 r->data.srv.priority = 0;
939 r->data.srv.weight = 0;
940 r->data.srv.port = port;
941 r->data.srv.name = n;
942 e = server_add_internal(s, g, iface, protocol, 0, r);
943 catta_record_unref(r);
948 int catta_server_add_dns_server_address(
952 CattaProtocol protocol,
953 CattaPublishFlags flags,
955 CattaDNSServerType type,
956 const CattaAddress *address,
957 uint16_t port /** should be 53 */) {
961 CattaEntry *a_entry, *s_entry;
966 CATTA_CHECK_VALIDITY(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
967 CATTA_CHECK_VALIDITY(s, CATTA_PROTO_VALID(protocol) && CATTA_PROTO_VALID(address->proto), CATTA_ERR_INVALID_PROTOCOL);
968 CATTA_CHECK_VALIDITY(s, CATTA_FLAGS_VALID(flags, CATTA_PUBLISH_USE_MULTICAST|CATTA_PUBLISH_USE_WIDE_AREA), CATTA_ERR_INVALID_FLAGS);
969 CATTA_CHECK_VALIDITY(s, type == CATTA_DNS_SERVER_UPDATE || type == CATTA_DNS_SERVER_RESOLVE, CATTA_ERR_INVALID_FLAGS);
970 CATTA_CHECK_VALIDITY(s, port != 0, CATTA_ERR_INVALID_PORT);
971 CATTA_CHECK_VALIDITY(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
974 domain = s->domain_name;
976 transport_flags_from_domain(s, &flags, domain);
977 CATTA_CHECK_VALIDITY(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
979 if (address->proto == CATTA_PROTO_INET) {
980 hexstring(h, sizeof(h), &address->data, sizeof(CattaIPv4Address));
981 snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
982 r = catta_record_new_full(n, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_A, CATTA_DEFAULT_TTL_HOST_NAME);
983 r->data.a.address = address->data.ipv4;
985 hexstring(h, sizeof(h), &address->data, sizeof(CattaIPv6Address));
986 snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
987 r = catta_record_new_full(n, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_AAAA, CATTA_DEFAULT_TTL_HOST_NAME);
988 r->data.aaaa.address = address->data.ipv6;
992 return catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
994 a_entry = server_add_internal(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE | CATTA_PUBLISH_ALLOW_MULTIPLE, r);
995 catta_record_unref(r);
998 return catta_server_errno(s);
1000 if (!(s_entry = server_add_dns_server_name(s, g, iface, protocol, flags, domain, type, n, port))) {
1001 if (!(flags & CATTA_PUBLISH_UPDATE))
1002 catta_entry_free(s, a_entry);
1003 return catta_server_errno(s);
1009 void catta_s_entry_group_change_state(CattaSEntryGroup *g, CattaEntryGroupState state) {
1012 if (g->state == state)
1015 assert(state <= CATTA_ENTRY_GROUP_COLLISION);
1017 if (g->state == CATTA_ENTRY_GROUP_ESTABLISHED) {
1019 /* If the entry group was established for a time longer then
1020 * 5s, reset the establishment trial counter */
1022 if (catta_age(&g->established_at) > 5000000)
1023 g->n_register_try = 0;
1024 } else if (g->state == CATTA_ENTRY_GROUP_REGISTERING) {
1025 if (g->register_time_event) {
1026 catta_time_event_free(g->register_time_event);
1027 g->register_time_event = NULL;
1031 if (state == CATTA_ENTRY_GROUP_ESTABLISHED)
1033 /* If the entry group is now established, remember the time
1036 gettimeofday(&g->established_at, NULL);
1041 g->callback(g->server, g, state, g->userdata);
1044 CattaSEntryGroup *catta_s_entry_group_new(CattaServer *s, CattaSEntryGroupCallback callback, void* userdata) {
1045 CattaSEntryGroup *g;
1049 if (!(g = catta_new(CattaSEntryGroup, 1))) {
1050 catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
1055 g->callback = callback;
1056 g->userdata = userdata;
1058 g->state = CATTA_ENTRY_GROUP_UNCOMMITED;
1060 g->n_register_try = 0;
1061 g->register_time_event = NULL;
1062 g->register_time.tv_sec = 0;
1063 g->register_time.tv_usec = 0;
1064 CATTA_LLIST_HEAD_INIT(CattaEntry, g->entries);
1066 CATTA_LLIST_PREPEND(CattaSEntryGroup, groups, s->groups, g);
1070 static void cleanup_time_event_callback(CATTA_GCC_UNUSED CattaTimeEvent *e, void* userdata) {
1071 CattaServer *s = userdata;
1075 catta_cleanup_dead_entries(s);
1078 static void schedule_cleanup(CattaServer *s) {
1083 if (!s->cleanup_time_event)
1084 s->cleanup_time_event = catta_time_event_new(s->time_event_queue, catta_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s);
1087 void catta_s_entry_group_free(CattaSEntryGroup *g) {
1093 for (e = g->entries; e; e = e->by_group_next) {
1095 catta_goodbye_entry(g->server, e, 1, 1);
1100 if (g->register_time_event) {
1101 catta_time_event_free(g->register_time_event);
1102 g->register_time_event = NULL;
1107 g->server->need_group_cleanup = 1;
1108 g->server->need_entry_cleanup = 1;
1110 schedule_cleanup(g->server);
1113 static void entry_group_commit_real(CattaSEntryGroup *g) {
1116 gettimeofday(&g->register_time, NULL);
1118 catta_s_entry_group_change_state(g, CATTA_ENTRY_GROUP_REGISTERING);
1123 catta_announce_group(g->server, g);
1124 catta_s_entry_group_check_probed(g, 0);
1127 static void entry_group_register_time_event_callback(CATTA_GCC_UNUSED CattaTimeEvent *e, void* userdata) {
1128 CattaSEntryGroup *g = userdata;
1131 catta_time_event_free(g->register_time_event);
1132 g->register_time_event = NULL;
1134 /* Holdoff time passed, so let's start probing */
1135 entry_group_commit_real(g);
1138 int catta_s_entry_group_commit(CattaSEntryGroup *g) {
1144 if (g->state != CATTA_ENTRY_GROUP_UNCOMMITED && g->state != CATTA_ENTRY_GROUP_COLLISION)
1145 return catta_server_set_errno(g->server, CATTA_ERR_BAD_STATE);
1147 if (catta_s_entry_group_is_empty(g))
1148 return catta_server_set_errno(g->server, CATTA_ERR_IS_EMPTY);
1150 g->n_register_try++;
1152 catta_timeval_add(&g->register_time,
1153 1000*(g->n_register_try >= CATTA_RR_RATE_LIMIT_COUNT ?
1154 CATTA_RR_HOLDOFF_MSEC_RATE_LIMIT :
1155 CATTA_RR_HOLDOFF_MSEC));
1157 gettimeofday(&now, NULL);
1159 if (catta_timeval_compare(&g->register_time, &now) <= 0) {
1161 /* Holdoff time passed, so let's start probing */
1162 entry_group_commit_real(g);
1165 /* Holdoff time has not yet passed, so let's wait */
1166 assert(!g->register_time_event);
1167 g->register_time_event = catta_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
1169 catta_s_entry_group_change_state(g, CATTA_ENTRY_GROUP_REGISTERING);
1175 void catta_s_entry_group_reset(CattaSEntryGroup *g) {
1179 for (e = g->entries; e; e = e->by_group_next) {
1181 catta_goodbye_entry(g->server, e, 1, 1);
1185 g->server->need_entry_cleanup = 1;
1189 catta_s_entry_group_change_state(g, CATTA_ENTRY_GROUP_UNCOMMITED);
1191 schedule_cleanup(g->server);
1194 int catta_entry_is_commited(CattaEntry *e) {
1199 e->group->state == CATTA_ENTRY_GROUP_REGISTERING ||
1200 e->group->state == CATTA_ENTRY_GROUP_ESTABLISHED;
1203 CattaEntryGroupState catta_s_entry_group_get_state(CattaSEntryGroup *g) {
1210 void catta_s_entry_group_set_data(CattaSEntryGroup *g, void* userdata) {
1213 g->userdata = userdata;
1216 void* catta_s_entry_group_get_data(CattaSEntryGroup *g) {
1222 int catta_s_entry_group_is_empty(CattaSEntryGroup *g) {
1226 /* Look for an entry that is not dead */
1227 for (e = g->entries; e; e = e->by_group_next)