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
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
36 #include <catta/domain.h>
37 #include <catta/timeval.h>
38 #include <catta/malloc.h>
39 #include <catta/error.h>
40 #include <catta/log.h>
47 #include "dns-srv-rr.h"
48 #include "addr-util.h"
49 #include "domain-util.h"
52 #define CATTA_DEFAULT_CACHE_ENTRIES_MAX 4096
54 static void enum_aux_records(CattaServer *s, CattaInterface *i, const char *name, uint16_t type, void (*callback)(CattaServer *s, CattaRecord *r, int flush_cache, void* userdata), void* userdata) {
60 if (type == CATTA_DNS_TYPE_ANY) {
63 for (e = s->entries; e; e = e->entries_next)
65 catta_entry_is_registered(s, e, i) &&
66 e->record->key->clazz == CATTA_DNS_CLASS_IN &&
67 catta_domain_equal(name, e->record->key->name))
68 callback(s, e->record, e->flags & CATTA_PUBLISH_UNIQUE, userdata);
74 if (!(k = catta_key_new(name, CATTA_DNS_CLASS_IN, type)))
77 for (e = catta_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
78 if (!e->dead && catta_entry_is_registered(s, e, i))
79 callback(s, e->record, e->flags & CATTA_PUBLISH_UNIQUE, userdata);
85 void catta_server_enumerate_aux_records(CattaServer *s, CattaInterface *i, CattaRecord *r, void (*callback)(CattaServer *s, CattaRecord *r, int flush_cache, void* userdata), void* userdata) {
91 /* Call the specified callback far all records referenced by the one specified in *r */
93 if (r->key->clazz == CATTA_DNS_CLASS_IN) {
94 if (r->key->type == CATTA_DNS_TYPE_PTR) {
95 enum_aux_records(s, i, r->data.ptr.name, CATTA_DNS_TYPE_SRV, callback, userdata);
96 enum_aux_records(s, i, r->data.ptr.name, CATTA_DNS_TYPE_TXT, callback, userdata);
97 } else if (r->key->type == CATTA_DNS_TYPE_SRV) {
98 enum_aux_records(s, i, r->data.srv.name, CATTA_DNS_TYPE_A, callback, userdata);
99 enum_aux_records(s, i, r->data.srv.name, CATTA_DNS_TYPE_AAAA, callback, userdata);
100 } else if (r->key->type == CATTA_DNS_TYPE_CNAME)
101 enum_aux_records(s, i, r->data.cname.name, CATTA_DNS_TYPE_ANY, callback, userdata);
105 void catta_server_prepare_response(CattaServer *s, CattaInterface *i, CattaEntry *e, int unicast_response, int auxiliary) {
110 catta_record_list_push(s->record_list, e->record, e->flags & CATTA_PUBLISH_UNIQUE, unicast_response, auxiliary);
113 void catta_server_prepare_matching_responses(CattaServer *s, CattaInterface *i, CattaKey *k, int unicast_response) {
118 /* Push all records that match the specified key to the record list */
120 if (catta_key_is_pattern(k)) {
123 /* Handle ANY query */
125 for (e = s->entries; e; e = e->entries_next)
126 if (!e->dead && catta_key_pattern_match(k, e->record->key) && catta_entry_is_registered(s, e, i))
127 catta_server_prepare_response(s, i, e, unicast_response, 0);
132 /* Handle all other queries */
134 for (e = catta_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
135 if (!e->dead && catta_entry_is_registered(s, e, i))
136 catta_server_prepare_response(s, i, e, unicast_response, 0);
139 /* Look for CNAME records */
141 if ((k->clazz == CATTA_DNS_CLASS_IN || k->clazz == CATTA_DNS_CLASS_ANY)
142 && k->type != CATTA_DNS_TYPE_CNAME && k->type != CATTA_DNS_TYPE_ANY) {
146 if (!(cname_key = catta_key_new(k->name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_CNAME)))
149 catta_server_prepare_matching_responses(s, i, cname_key, unicast_response);
150 catta_key_unref(cname_key);
154 static void withdraw_entry(CattaServer *s, CattaEntry *e) {
158 /* Withdraw the specified entry, and if is part of an entry group,
159 * put that into COLLISION state */
167 for (k = e->group->entries; k; k = k->by_group_next)
169 catta_goodbye_entry(s, k, 0, 1);
173 e->group->n_probing = 0;
175 catta_s_entry_group_change_state(e->group, CATTA_ENTRY_GROUP_COLLISION);
177 catta_goodbye_entry(s, e, 0, 1);
181 s->need_entry_cleanup = 1;
184 static void withdraw_rrset(CattaServer *s, CattaKey *key) {
190 /* Withdraw an entry RRSset */
192 for (e = catta_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
193 withdraw_entry(s, e);
196 static void incoming_probe(CattaServer *s, CattaRecord *record, CattaInterface *i) {
198 int ours = 0, won = 0, lost = 0;
204 /* Handle incoming probes and check if they conflict our own probes */
206 for (e = catta_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
213 if ((cmp = catta_record_lexicographical_compare(e->record, record)) == 0) {
218 if (catta_entry_is_probing(s, e, i)) {
228 char *t = catta_record_to_string(record);
231 catta_log_debug("Received conflicting probe [%s]. Local host won.", t);
233 catta_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t);
234 withdraw_rrset(s, record->key);
241 static int handle_conflict(CattaServer *s, CattaInterface *i, CattaRecord *record, int unique) {
242 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
243 CattaEntry *e, *n, *conflicting_entry = NULL;
249 /* Check whether an incoming record conflicts with one of our own */
251 for (e = catta_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
257 /* Check if the incoming is a goodbye record */
258 if (catta_record_is_goodbye(record)) {
260 if (catta_record_equal_no_ttl(e->record, record)) {
264 t = catta_record_to_string(record);
265 catta_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t);
266 catta_server_prepare_matching_responses(s, i, e->record->key, 0);
273 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
277 if (!(e->flags & CATTA_PUBLISH_UNIQUE) && !unique)
280 /* Either our entry or the other is intended to be unique, so let's check */
282 if (catta_record_equal_no_ttl(e->record, record)) {
283 ours = 1; /* We have an identical record, so this is no conflict */
285 /* Check wheter there is a TTL conflict */
286 if (record->ttl <= e->record->ttl/2 &&
287 catta_entry_is_registered(s, e, i)) {
290 t = catta_record_to_string(record);
292 catta_log_debug("Received record with bad TTL [%s]. Refreshing.", t);
293 catta_server_prepare_matching_responses(s, i, e->record->key, 0);
299 /* There's no need to check the other entries of this RRset */
304 if (catta_entry_is_registered(s, e, i)) {
306 /* A conflict => we have to return to probe mode */
308 conflicting_entry = e;
310 } else if (catta_entry_is_probing(s, e, i)) {
312 /* We are currently registering a matching record, but
313 * someone else already claimed it, so let's
316 withdraw_immediately = 1;
321 if (!ours && conflict) {
326 t = catta_record_to_string(record);
328 if (withdraw_immediately) {
329 catta_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t);
330 withdraw_rrset(s, record->key);
332 assert(conflicting_entry);
333 catta_log_debug("Received conflicting record [%s]. Resetting our record.", t);
334 catta_entry_return_to_initial_state(s, conflicting_entry, i);
336 /* Local unique records are returned to probing
337 * state. Local shared records are reannounced. */
346 static void append_aux_callback(CattaServer *s, CattaRecord *r, int flush_cache, void* userdata) {
347 int *unicast_response = userdata;
351 assert(unicast_response);
353 catta_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
356 static void append_aux_records_to_list(CattaServer *s, CattaInterface *i, CattaRecord *r, int unicast_response) {
360 catta_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
363 void catta_server_generate_response(CattaServer *s, CattaInterface *i, CattaDnsPacket *p, const CattaAddress *a, uint16_t port, int legacy_unicast, int immediately) {
367 assert(!legacy_unicast || (a && port > 0 && p));
369 if (legacy_unicast) {
370 CattaDnsPacket *reply;
373 if (!(reply = catta_dns_packet_new_reply(p, 512 + CATTA_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1)))
376 while ((r = catta_record_list_next(s->record_list, NULL, NULL, NULL))) {
378 append_aux_records_to_list(s, i, r, 0);
380 if (catta_dns_packet_append_record(reply, r, 0, 10))
381 catta_dns_packet_inc_field(reply, CATTA_DNS_FIELD_ANCOUNT);
383 char *t = catta_record_to_string(r);
384 catta_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
388 catta_record_unref(r);
391 if (catta_dns_packet_get_field(reply, CATTA_DNS_FIELD_ANCOUNT) != 0)
392 catta_interface_send_packet_unicast(i, reply, a, port);
394 catta_dns_packet_free(reply);
397 int unicast_response, flush_cache, auxiliary;
398 CattaDnsPacket *reply = NULL;
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 int tc = p && !!(catta_dns_packet_get_field(p, CATTA_DNS_FIELD_FLAGS) & CATTA_DNS_FLAG_TC);
406 while ((r = catta_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
408 int im = immediately;
410 /* Only send the response immediately if it contains a
411 * unique entry AND it is not in reply to a truncated
412 * packet AND it is not an auxiliary record AND all other
413 * responses for this record are unique too. */
415 if (flush_cache && !tc && !auxiliary && catta_record_list_all_flush_cache(s->record_list))
418 if (!catta_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
420 /* Due to some reasons the record has not been scheduled.
421 * The client requested an unicast response in that
422 * case. Therefore we prepare such a response */
424 append_aux_records_to_list(s, i, r, unicast_response);
431 if (!(reply = catta_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
435 if (catta_dns_packet_append_record(reply, r, flush_cache, 0)) {
437 /* Appending this record succeeded, so incremeant
438 * the specific header field, and return to the caller */
440 catta_dns_packet_inc_field(reply, CATTA_DNS_FIELD_ANCOUNT);
444 if (catta_dns_packet_get_field(reply, CATTA_DNS_FIELD_ANCOUNT) == 0) {
447 /* The record is too large for one packet, so create a larger packet */
449 catta_dns_packet_free(reply);
450 size = catta_record_get_estimate_size(r) + CATTA_DNS_PACKET_HEADER_SIZE;
452 if (!(reply = catta_dns_packet_new_reply(p, size + CATTA_DNS_PACKET_EXTRA_SIZE, 0, 1)))
455 if (catta_dns_packet_append_record(reply, r, flush_cache, 0)) {
457 /* Appending this record succeeded, so incremeant
458 * the specific header field, and return to the caller */
460 catta_dns_packet_inc_field(reply, CATTA_DNS_FIELD_ANCOUNT);
465 /* We completely fucked up, there's
466 * nothing we can do. The RR just doesn't
467 * fit in. Let's ignore it. */
470 catta_dns_packet_free(reply);
472 t = catta_record_to_string(r);
473 catta_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
479 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
480 catta_interface_send_packet_unicast(i, reply, a, port);
481 catta_dns_packet_free(reply);
486 catta_record_unref(r);
490 if (catta_dns_packet_get_field(reply, CATTA_DNS_FIELD_ANCOUNT) != 0)
491 catta_interface_send_packet_unicast(i, reply, a, port);
492 catta_dns_packet_free(reply);
496 catta_record_list_flush(s->record_list);
499 static void reflect_response(CattaServer *s, CattaInterface *i, CattaRecord *r, int flush_cache) {
506 if (!s->config.enable_reflector)
509 for (j = s->monitor->interfaces; j; j = j->iface_next)
510 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
511 catta_interface_post_response(j, r, flush_cache, NULL, 1);
514 static void* reflect_cache_walk_callback(CattaCache *c, CattaKey *pattern, CattaCacheEntry *e, void* userdata) {
515 CattaServer *s = userdata;
523 /* Don't reflect cache entry with ipv6 link-local addresses. */
525 if ((r->key->type == CATTA_DNS_TYPE_AAAA) &&
526 (r->data.aaaa.address.address[0] == 0xFE) &&
527 (r->data.aaaa.address.address[1] == 0x80))
530 catta_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
534 static void reflect_query(CattaServer *s, CattaInterface *i, CattaKey *k) {
541 if (!s->config.enable_reflector)
544 for (j = s->monitor->interfaces; j; j = j->iface_next)
545 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
546 /* Post the query to other networks */
547 catta_interface_post_query(j, k, 1, NULL);
549 /* Reply from caches of other network. This is needed to
550 * "work around" known answer suppression. */
552 catta_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
556 static void reflect_probe(CattaServer *s, CattaInterface *i, CattaRecord *r) {
563 if (!s->config.enable_reflector)
566 for (j = s->monitor->interfaces; j; j = j->iface_next)
567 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
568 catta_interface_post_probe(j, r, 1);
571 static void handle_query_packet(CattaServer *s, CattaDnsPacket *p, CattaInterface *i, const CattaAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
580 assert(catta_record_list_is_empty(s->record_list));
582 is_probe = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT) > 0;
584 /* Handle the questions */
585 for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_QDCOUNT); n > 0; n --) {
587 int unicast_response = 0;
589 if (!(key = catta_dns_packet_consume_key(p, &unicast_response))) {
590 catta_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
594 if (!legacy_unicast && !from_local_iface) {
595 reflect_query(s, i, key);
596 if (!unicast_response)
597 catta_cache_start_poof(i->cache, key, a);
600 if (catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) == 0 &&
601 !(catta_dns_packet_get_field(p, CATTA_DNS_FIELD_FLAGS) & CATTA_DNS_FLAG_TC))
602 /* Allow our own queries to be suppressed by incoming
603 * queries only when they do not include known answers */
604 catta_query_scheduler_incoming(i->query_scheduler, key);
606 catta_server_prepare_matching_responses(s, i, key, unicast_response);
607 catta_key_unref(key);
610 if (!legacy_unicast) {
612 /* Known Answer Suppression */
613 for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT); n > 0; n --) {
617 if (!(record = catta_dns_packet_consume_record(p, &unique))) {
618 catta_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
622 catta_response_scheduler_suppress(i->response_scheduler, record, a);
623 catta_record_list_drop(s->record_list, record);
624 catta_cache_stop_poof(i->cache, record, a);
626 catta_record_unref(record);
630 for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT); n > 0; n --) {
634 if (!(record = catta_dns_packet_consume_record(p, &unique))) {
635 catta_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
639 if (!catta_key_is_pattern(record->key)) {
640 if (!from_local_iface)
641 reflect_probe(s, i, record);
642 incoming_probe(s, record, i);
645 catta_record_unref(record);
649 if (!catta_record_list_is_empty(s->record_list))
650 catta_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
655 catta_record_list_flush(s->record_list);
658 static void handle_response_packet(CattaServer *s, CattaDnsPacket *p, CattaInterface *i, const CattaAddress *a, int from_local_iface) {
666 for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) +
667 catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ARCOUNT); n > 0; n--) {
671 if (!(record = catta_dns_packet_consume_record(p, &cache_flush))) {
672 catta_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
676 if (!catta_key_is_pattern(record->key)) {
678 if (handle_conflict(s, i, record, cache_flush)) {
679 if (!from_local_iface && !catta_record_is_link_local_address(record))
680 reflect_response(s, i, record, cache_flush);
681 catta_cache_update(i->cache, record, cache_flush, a);
682 catta_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
686 catta_record_unref(record);
689 /* If the incoming response contained a conflicting record, some
690 records have been scheduled for sending. We need to flush them
692 if (!catta_record_list_is_empty(s->record_list))
693 catta_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
696 static CattaLegacyUnicastReflectSlot* allocate_slot(CattaServer *s) {
697 unsigned n, idx = (unsigned) -1;
698 CattaLegacyUnicastReflectSlot *slot;
702 if (!s->legacy_unicast_reflect_slots)
703 s->legacy_unicast_reflect_slots = catta_new0(CattaLegacyUnicastReflectSlot*, CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
705 for (n = 0; n < CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
706 idx = s->legacy_unicast_reflect_id % CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
708 if (!s->legacy_unicast_reflect_slots[idx])
712 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
715 if (!(slot = catta_new(CattaLegacyUnicastReflectSlot, 1)))
716 return NULL; /* OOM */
718 s->legacy_unicast_reflect_slots[idx] = slot;
719 slot->id = s->legacy_unicast_reflect_id++;
725 static void deallocate_slot(CattaServer *s, CattaLegacyUnicastReflectSlot *slot) {
731 idx = slot->id % CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
733 assert(s->legacy_unicast_reflect_slots[idx] == slot);
735 catta_time_event_free(slot->time_event);
738 s->legacy_unicast_reflect_slots[idx] = NULL;
741 static void free_slots(CattaServer *s) {
745 if (!s->legacy_unicast_reflect_slots)
748 for (idx = 0; idx < CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
749 if (s->legacy_unicast_reflect_slots[idx])
750 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
752 catta_free(s->legacy_unicast_reflect_slots);
753 s->legacy_unicast_reflect_slots = NULL;
756 static CattaLegacyUnicastReflectSlot* find_slot(CattaServer *s, uint16_t id) {
761 if (!s->legacy_unicast_reflect_slots)
764 idx = id % CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
766 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
769 return s->legacy_unicast_reflect_slots[idx];
772 static void legacy_unicast_reflect_slot_timeout(CattaTimeEvent *e, void *userdata) {
773 CattaLegacyUnicastReflectSlot *slot = userdata;
777 assert(slot->time_event == e);
779 deallocate_slot(slot->server, slot);
782 static void reflect_legacy_unicast_query_packet(CattaServer *s, CattaDnsPacket *p, CattaInterface *i, const CattaAddress *a, uint16_t port) {
783 CattaLegacyUnicastReflectSlot *slot;
791 assert(i->protocol == a->proto);
793 if (!s->config.enable_reflector)
796 /* Reflecting legacy unicast queries is a little more complicated
797 than reflecting normal queries, since we must route the
798 responses back to the right client. Therefore we must store
799 some information for finding the right client contact data for
800 response packets. In contrast to normal queries legacy
801 unicast query and response packets are reflected untouched and
802 are not reassembled into larger packets */
804 if (!(slot = allocate_slot(s))) {
805 /* No slot available, we drop this legacy unicast query */
806 catta_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
810 slot->original_id = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ID);
813 slot->iface = i->hardware->index;
815 catta_elapse_time(&slot->elapse_time, 2000, 0);
816 slot->time_event = catta_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
818 /* Patch the packet with our new locally generatet id */
819 catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->id);
821 for (j = s->monitor->interfaces; j; j = j->iface_next)
824 (s->config.reflect_ipv || j->protocol == i->protocol)) {
826 if (j->protocol == CATTA_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
827 catta_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
828 } else if (j->protocol == CATTA_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
829 catta_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
833 catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->original_id);
836 static int originates_from_local_legacy_unicast_socket(CattaServer *s, const CattaAddress *address, uint16_t port) {
841 if (!s->config.enable_reflector)
844 if (!catta_address_is_local(s->monitor, address))
847 if (address->proto == CATTA_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
848 struct sockaddr_in lsa;
849 socklen_t l = sizeof(lsa);
851 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
852 catta_log_warn("getsockname(): %s", errnostrsocket());
854 return catta_port_from_sockaddr((struct sockaddr*) &lsa) == port;
858 if (address->proto == CATTA_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
859 struct sockaddr_in6 lsa;
860 socklen_t l = sizeof(lsa);
862 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
863 catta_log_warn("getsockname(): %s", errnostrsocket());
865 return catta_port_from_sockaddr((struct sockaddr*) &lsa) == port;
871 static int is_mdns_mcast_address(const CattaAddress *a) {
875 catta_address_parse(a->proto == CATTA_PROTO_INET ? CATTA_IPV4_MCAST_GROUP : CATTA_IPV6_MCAST_GROUP, a->proto, &b);
876 return catta_address_cmp(a, &b) == 0;
879 static int originates_from_local_iface(CattaServer *s, CattaIfIndex iface, const CattaAddress *a, uint16_t port) {
881 assert(iface != CATTA_IF_UNSPEC);
884 /* If it isn't the MDNS port it can't be generated by us */
885 if (port != CATTA_MDNS_PORT)
888 return catta_interface_has_address(s->monitor, iface, a);
891 static void dispatch_packet(CattaServer *s, CattaDnsPacket *p, const CattaAddress *src_address, uint16_t port, const CattaAddress *dst_address, CattaIfIndex iface, int ttl) {
893 int from_local_iface = 0;
900 assert(src_address->proto == dst_address->proto);
902 if (!(i = catta_interface_monitor_get_interface(s->monitor, iface, src_address->proto))) {
903 catta_log_warn("Received packet from unrecognized interface (%d).", iface);
906 if (!i->announcing) {
907 catta_log_warn("Received packet from invalid interface %d (not announcing).", iface);
912 /* This fixes RHBZ #475394 */
913 catta_log_warn("Received packet from invalid source port %u.", (unsigned) port);
917 if (catta_address_is_ipv4_in_ipv6(src_address))
918 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
921 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
922 /* This originates from our local reflector, so let's ignore it */
925 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
926 if (s->config.enable_reflector)
927 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
929 if (catta_dns_packet_check_valid_multicast(p) < 0) {
930 catta_log_warn("Received invalid packet.");
934 if (catta_dns_packet_is_query(p)) {
935 int legacy_unicast = 0;
937 /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the
938 * AR section completely here, so far. Until the day we add
941 if (port != CATTA_MDNS_PORT) {
944 if ((catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) != 0 ||
945 catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT) != 0)) {
946 catta_log_warn("Invalid legacy unicast query packet.");
954 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
956 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
959 char t[CATTA_ADDRESS_STR_MAX];
961 if (port != CATTA_MDNS_PORT) {
962 catta_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", catta_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
966 if (ttl != 255 && s->config.check_response_ttl) {
967 catta_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", catta_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol);
971 if (!is_mdns_mcast_address(dst_address) &&
972 !catta_interface_address_on_link(i, src_address)) {
974 catta_log_warn("Received non-local response from host %s on interface '%s.%i'.", catta_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
978 if (catta_dns_packet_get_field(p, CATTA_DNS_FIELD_QDCOUNT) != 0 ||
979 catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) == 0 ||
980 catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT) != 0) {
982 catta_log_warn("Invalid response packet from host %s.", catta_address_snprint(t, sizeof(t), src_address));
986 handle_response_packet(s, p, i, src_address, from_local_iface);
990 static void dispatch_legacy_unicast_packet(CattaServer *s, CattaDnsPacket *p) {
992 CattaLegacyUnicastReflectSlot *slot;
997 if (catta_dns_packet_check_valid(p) < 0 || catta_dns_packet_is_query(p)) {
998 catta_log_warn("Received invalid packet.");
1002 if (!(slot = find_slot(s, catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ID)))) {
1003 catta_log_warn("Received legacy unicast response with unknown id");
1007 if (!(j = catta_interface_monitor_get_interface(s->monitor, slot->iface, slot->address.proto)) ||
1011 /* Patch the original ID into this response */
1012 catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->original_id);
1014 /* Forward the response to the correct client */
1015 catta_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1017 /* Undo changes to packet */
1018 catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->id);
1021 static void mcast_socket_event(CattaWatch *w, int fd, CattaWatchEvent events, void *userdata) {
1022 CattaServer *s = userdata;
1023 CattaAddress dest, src;
1024 CattaDnsPacket *p = NULL;
1031 assert(events & CATTA_WATCH_IN);
1033 if (fd == s->fd_ipv4) {
1034 dest.proto = src.proto = CATTA_PROTO_INET;
1035 p = catta_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1037 assert(fd == s->fd_ipv6);
1038 dest.proto = src.proto = CATTA_PROTO_INET6;
1039 p = catta_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1043 if (iface == CATTA_IF_UNSPEC)
1044 iface = catta_find_interface_for_address(s->monitor, &dest);
1046 if (iface != CATTA_IF_UNSPEC)
1047 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1049 catta_log_error("Incoming packet received on address that isn't local.");
1051 catta_dns_packet_free(p);
1053 catta_cleanup_dead_entries(s);
1057 static void legacy_unicast_socket_event(CattaWatch *w, int fd, CattaWatchEvent events, void *userdata) {
1058 CattaServer *s = userdata;
1059 CattaDnsPacket *p = NULL;
1063 assert(events & CATTA_WATCH_IN);
1065 if (fd == s->fd_legacy_unicast_ipv4)
1066 p = catta_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1068 assert(fd == s->fd_legacy_unicast_ipv6);
1069 p = catta_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1073 dispatch_legacy_unicast_packet(s, p);
1074 catta_dns_packet_free(p);
1076 catta_cleanup_dead_entries(s);
1080 static void server_set_state(CattaServer *s, CattaServerState state) {
1083 if (s->state == state)
1088 catta_interface_monitor_update_rrs(s->monitor, 0);
1091 s->callback(s, state, s->userdata);
1094 static void withdraw_host_rrs(CattaServer *s) {
1097 if (s->hinfo_entry_group)
1098 catta_s_entry_group_reset(s->hinfo_entry_group);
1100 if (s->browse_domain_entry_group)
1101 catta_s_entry_group_reset(s->browse_domain_entry_group);
1103 catta_interface_monitor_update_rrs(s->monitor, 1);
1104 s->n_host_rr_pending = 0;
1107 void catta_server_decrease_host_rr_pending(CattaServer *s) {
1110 assert(s->n_host_rr_pending > 0);
1112 if (--s->n_host_rr_pending == 0)
1113 server_set_state(s, CATTA_SERVER_RUNNING);
1116 void catta_host_rr_entry_group_callback(CattaServer *s, CattaSEntryGroup *g, CattaEntryGroupState state, CATTA_GCC_UNUSED void *userdata) {
1120 if (state == CATTA_ENTRY_GROUP_REGISTERING &&
1121 s->state == CATTA_SERVER_REGISTERING)
1122 s->n_host_rr_pending ++;
1124 else if (state == CATTA_ENTRY_GROUP_COLLISION &&
1125 (s->state == CATTA_SERVER_REGISTERING || s->state == CATTA_SERVER_RUNNING)) {
1126 withdraw_host_rrs(s);
1127 server_set_state(s, CATTA_SERVER_COLLISION);
1129 } else if (state == CATTA_ENTRY_GROUP_ESTABLISHED &&
1130 s->state == CATTA_SERVER_REGISTERING)
1131 catta_server_decrease_host_rr_pending(s);
1134 static void register_hinfo(CattaServer *s) {
1135 struct utsname utsname;
1140 if (!s->config.publish_hinfo)
1143 if (s->hinfo_entry_group)
1144 assert(catta_s_entry_group_is_empty(s->hinfo_entry_group));
1146 s->hinfo_entry_group = catta_s_entry_group_new(s, catta_host_rr_entry_group_callback, NULL);
1148 if (!s->hinfo_entry_group) {
1149 catta_log_warn("Failed to create HINFO entry group: %s", catta_strerror(s->error));
1153 /* Fill in HINFO rr */
1154 if ((r = catta_record_new_full(s->host_name_fqdn, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_HINFO, CATTA_DEFAULT_TTL_HOST_NAME))) {
1156 if (uname(&utsname) < 0)
1157 catta_log_warn("uname() failed: %s\n", catta_strerror(errno));
1160 r->data.hinfo.cpu = catta_strdup(catta_strup(utsname.machine));
1161 r->data.hinfo.os = catta_strdup(catta_strup(utsname.sysname));
1163 catta_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1165 if (catta_server_add(s, s->hinfo_entry_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, CATTA_PUBLISH_UNIQUE, r) < 0) {
1166 catta_log_warn("Failed to add HINFO RR: %s", catta_strerror(s->error));
1171 catta_record_unref(r);
1174 if (catta_s_entry_group_commit(s->hinfo_entry_group) < 0)
1175 catta_log_warn("Failed to commit HINFO entry group: %s", catta_strerror(s->error));
1179 static void register_localhost(CattaServer *s) {
1183 /* Add localhost entries */
1184 catta_address_parse("127.0.0.1", CATTA_PROTO_INET, &a);
1185 catta_server_add_address(s, NULL, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, CATTA_PUBLISH_NO_PROBE|CATTA_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1187 catta_address_parse("::1", CATTA_PROTO_INET6, &a);
1188 catta_server_add_address(s, NULL, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, CATTA_PUBLISH_NO_PROBE|CATTA_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1191 static void register_browse_domain(CattaServer *s) {
1194 if (!s->config.publish_domain)
1197 if (catta_domain_equal(s->domain_name, "local"))
1200 if (s->browse_domain_entry_group)
1201 assert(catta_s_entry_group_is_empty(s->browse_domain_entry_group));
1203 s->browse_domain_entry_group = catta_s_entry_group_new(s, NULL, NULL);
1205 if (!s->browse_domain_entry_group) {
1206 catta_log_warn("Failed to create browse domain entry group: %s", catta_strerror(s->error));
1210 if (catta_server_add_ptr(s, s->browse_domain_entry_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, 0, CATTA_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) {
1211 catta_log_warn("Failed to add browse domain RR: %s", catta_strerror(s->error));
1215 if (catta_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1216 catta_log_warn("Failed to commit browse domain entry group: %s", catta_strerror(s->error));
1219 static void register_stuff(CattaServer *s) {
1222 server_set_state(s, CATTA_SERVER_REGISTERING);
1223 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp CATTA_SERVER_RUNNING too early */
1226 register_browse_domain(s);
1227 catta_interface_monitor_update_rrs(s->monitor, 0);
1229 assert(s->n_host_rr_pending > 0);
1230 s->n_host_rr_pending --;
1232 if (s->n_host_rr_pending == 0)
1233 server_set_state(s, CATTA_SERVER_RUNNING);
1236 static void update_fqdn(CattaServer *s) {
1240 assert(s->host_name);
1241 assert(s->domain_name);
1243 if (!(n = catta_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1246 catta_free(s->host_name_fqdn);
1247 s->host_name_fqdn = n;
1250 int catta_server_set_host_name(CattaServer *s, const char *host_name) {
1254 CATTA_CHECK_VALIDITY(s, !host_name || catta_is_valid_host_name(host_name), CATTA_ERR_INVALID_HOST_NAME);
1257 hn = catta_get_host_name_strdup();
1259 hn = catta_normalize_name_strdup(host_name);
1261 hn[strcspn(hn, ".")] = 0;
1263 if (catta_domain_equal(s->host_name, hn) && s->state != CATTA_SERVER_COLLISION) {
1265 return catta_server_set_errno(s, CATTA_ERR_NO_CHANGE);
1268 withdraw_host_rrs(s);
1270 catta_free(s->host_name);
1279 int catta_server_set_domain_name(CattaServer *s, const char *domain_name) {
1283 CATTA_CHECK_VALIDITY(s, !domain_name || catta_is_valid_domain_name(domain_name), CATTA_ERR_INVALID_DOMAIN_NAME);
1286 dn = catta_strdup("local");
1288 dn = catta_normalize_name_strdup(domain_name);
1290 if (catta_domain_equal(s->domain_name, domain_name)) {
1292 return catta_server_set_errno(s, CATTA_ERR_NO_CHANGE);
1295 withdraw_host_rrs(s);
1297 catta_free(s->domain_name);
1298 s->domain_name = dn;
1307 static int valid_server_config(const CattaServerConfig *sc) {
1312 if (sc->n_wide_area_servers > CATTA_WIDE_AREA_SERVERS_MAX)
1313 return CATTA_ERR_INVALID_CONFIG;
1315 if (sc->host_name && !catta_is_valid_host_name(sc->host_name))
1316 return CATTA_ERR_INVALID_HOST_NAME;
1318 if (sc->domain_name && !catta_is_valid_domain_name(sc->domain_name))
1319 return CATTA_ERR_INVALID_DOMAIN_NAME;
1321 for (l = sc->browse_domains; l; l = l->next)
1322 if (!catta_is_valid_domain_name((char*) l->text))
1323 return CATTA_ERR_INVALID_DOMAIN_NAME;
1328 static int setup_sockets(CattaServer *s) {
1331 s->fd_ipv4 = s->config.use_ipv4 ? catta_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1332 s->fd_ipv6 = s->config.use_ipv6 ? catta_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1334 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1335 return CATTA_ERR_NO_NETWORK;
1337 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1338 catta_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1339 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1340 catta_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1342 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? catta_open_unicast_socket_ipv4() : -1;
1343 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? catta_open_unicast_socket_ipv6() : -1;
1347 s->watch_legacy_unicast_ipv4 =
1348 s->watch_legacy_unicast_ipv6 = NULL;
1350 if (s->fd_ipv4 >= 0)
1351 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, CATTA_WATCH_IN, mcast_socket_event, s);
1352 if (s->fd_ipv6 >= 0)
1353 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, CATTA_WATCH_IN, mcast_socket_event, s);
1355 if (s->fd_legacy_unicast_ipv4 >= 0)
1356 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, CATTA_WATCH_IN, legacy_unicast_socket_event, s);
1357 if (s->fd_legacy_unicast_ipv6 >= 0)
1358 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, CATTA_WATCH_IN, legacy_unicast_socket_event, s);
1363 CattaServer *catta_server_new(const CattaPoll *poll_api, const CattaServerConfig *sc, CattaServerCallback callback, void* userdata, int *error) {
1367 if (sc && (e = valid_server_config(sc)) < 0) {
1373 if (!(s = catta_new(CattaServer, 1))) {
1375 *error = CATTA_ERR_NO_MEMORY;
1380 s->poll_api = poll_api;
1383 catta_server_config_copy(&s->config, sc);
1385 catta_server_config_init(&s->config);
1387 winsock_init(); // on Windows, call WSAStartup; no-op on other platforms
1388 if ((e = setup_sockets(s)) < 0) {
1392 catta_server_config_free(&s->config);
1399 s->n_host_rr_pending = 0;
1400 s->need_entry_cleanup = 0;
1401 s->need_group_cleanup = 0;
1402 s->need_browser_cleanup = 0;
1403 s->cleanup_time_event = NULL;
1404 s->hinfo_entry_group = NULL;
1405 s->browse_domain_entry_group = NULL;
1406 s->error = CATTA_OK;
1407 s->state = CATTA_SERVER_INVALID;
1409 s->callback = callback;
1410 s->userdata = userdata;
1412 s->time_event_queue = catta_time_event_queue_new(poll_api);
1414 s->entries_by_key = catta_hashmap_new((CattaHashFunc) catta_key_hash, (CattaEqualFunc) catta_key_equal, NULL, NULL);
1415 CATTA_LLIST_HEAD_INIT(CattaEntry, s->entries);
1416 CATTA_LLIST_HEAD_INIT(CattaGroup, s->groups);
1418 s->record_browser_hashmap = catta_hashmap_new((CattaHashFunc) catta_key_hash, (CattaEqualFunc) catta_key_equal, NULL, NULL);
1419 CATTA_LLIST_HEAD_INIT(CattaSRecordBrowser, s->record_browsers);
1420 CATTA_LLIST_HEAD_INIT(CattaSHostNameResolver, s->host_name_resolvers);
1421 CATTA_LLIST_HEAD_INIT(CattaSAddressResolver, s->address_resolvers);
1422 CATTA_LLIST_HEAD_INIT(CattaSDomainBrowser, s->domain_browsers);
1423 CATTA_LLIST_HEAD_INIT(CattaSServiceTypeBrowser, s->service_type_browsers);
1424 CATTA_LLIST_HEAD_INIT(CattaSServiceBrowser, s->service_browsers);
1425 CATTA_LLIST_HEAD_INIT(CattaSServiceResolver, s->service_resolvers);
1426 CATTA_LLIST_HEAD_INIT(CattaSDNSServerBrowser, s->dns_server_browsers);
1428 s->legacy_unicast_reflect_slots = NULL;
1429 s->legacy_unicast_reflect_id = 0;
1431 s->record_list = catta_record_list_new();
1434 s->host_name = s->config.host_name ? catta_normalize_name_strdup(s->config.host_name) : catta_get_host_name_strdup();
1435 s->host_name[strcspn(s->host_name, ".")] = 0;
1436 s->domain_name = s->config.domain_name ? catta_normalize_name_strdup(s->config.domain_name) : catta_strdup("local");
1437 s->host_name_fqdn = NULL;
1441 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1442 } while (s->local_service_cookie == CATTA_SERVICE_COOKIE_INVALID);
1444 if (s->config.enable_wide_area) {
1445 s->wide_area_lookup_engine = catta_wide_area_engine_new(s);
1446 catta_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1448 s->wide_area_lookup_engine = NULL;
1450 s->multicast_lookup_engine = catta_multicast_lookup_engine_new(s);
1452 s->monitor = catta_interface_monitor_new(s);
1453 catta_interface_monitor_sync(s->monitor);
1455 register_localhost(s);
1461 void catta_server_free(CattaServer* s) {
1464 /* Remove all browsers */
1466 while (s->dns_server_browsers)
1467 catta_s_dns_server_browser_free(s->dns_server_browsers);
1468 while (s->host_name_resolvers)
1469 catta_s_host_name_resolver_free(s->host_name_resolvers);
1470 while (s->address_resolvers)
1471 catta_s_address_resolver_free(s->address_resolvers);
1472 while (s->domain_browsers)
1473 catta_s_domain_browser_free(s->domain_browsers);
1474 while (s->service_type_browsers)
1475 catta_s_service_type_browser_free(s->service_type_browsers);
1476 while (s->service_browsers)
1477 catta_s_service_browser_free(s->service_browsers);
1478 while (s->service_resolvers)
1479 catta_s_service_resolver_free(s->service_resolvers);
1480 while (s->record_browsers)
1481 catta_s_record_browser_destroy(s->record_browsers);
1483 /* Remove all locally rgeistered stuff */
1486 catta_entry_free(s, s->entries);
1488 catta_interface_monitor_free(s->monitor);
1491 catta_entry_group_free(s, s->groups);
1495 catta_hashmap_free(s->entries_by_key);
1496 catta_record_list_free(s->record_list);
1497 catta_hashmap_free(s->record_browser_hashmap);
1499 if (s->wide_area_lookup_engine)
1500 catta_wide_area_engine_free(s->wide_area_lookup_engine);
1501 catta_multicast_lookup_engine_free(s->multicast_lookup_engine);
1503 if (s->cleanup_time_event)
1504 catta_time_event_free(s->cleanup_time_event);
1506 catta_time_event_queue_free(s->time_event_queue);
1511 s->poll_api->watch_free(s->watch_ipv4);
1513 s->poll_api->watch_free(s->watch_ipv6);
1515 if (s->watch_legacy_unicast_ipv4)
1516 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1517 if (s->watch_legacy_unicast_ipv6)
1518 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1522 if (s->fd_ipv4 >= 0)
1523 closesocket(s->fd_ipv4);
1524 if (s->fd_ipv6 >= 0)
1525 closesocket(s->fd_ipv6);
1527 if (s->fd_legacy_unicast_ipv4 >= 0)
1528 closesocket(s->fd_legacy_unicast_ipv4);
1529 if (s->fd_legacy_unicast_ipv6 >= 0)
1530 closesocket(s->fd_legacy_unicast_ipv6);
1532 /* Free other stuff */
1534 catta_free(s->host_name);
1535 catta_free(s->domain_name);
1536 catta_free(s->host_name_fqdn);
1538 catta_server_config_free(&s->config);
1541 winsock_exit(); // on Windows, call WSACleanup(); no-op on other platforms
1544 const char* catta_server_get_domain_name(CattaServer *s) {
1547 return s->domain_name;
1550 const char* catta_server_get_host_name(CattaServer *s) {
1553 return s->host_name;
1556 const char* catta_server_get_host_name_fqdn(CattaServer *s) {
1559 return s->host_name_fqdn;
1562 void* catta_server_get_data(CattaServer *s) {
1568 void catta_server_set_data(CattaServer *s, void* userdata) {
1571 s->userdata = userdata;
1574 CattaServerState catta_server_get_state(CattaServer *s) {
1580 CattaServerConfig* catta_server_config_init(CattaServerConfig *c) {
1583 memset(c, 0, sizeof(CattaServerConfig));
1586 c->allow_interfaces = NULL;
1587 c->deny_interfaces = NULL;
1588 c->host_name = NULL;
1589 c->domain_name = NULL;
1590 c->check_response_ttl = 0;
1591 c->publish_hinfo = 0;
1592 c->publish_addresses = 1;
1593 c->publish_no_reverse = 0;
1594 c->publish_workstation = 0;
1595 c->publish_domain = 1;
1596 c->use_iff_running = 0;
1597 c->enable_reflector = 0;
1599 c->add_service_cookie = 0;
1600 c->enable_wide_area = 0;
1601 c->n_wide_area_servers = 0;
1602 c->disallow_other_stacks = 0;
1603 c->browse_domains = NULL;
1604 c->disable_publishing = 0;
1605 c->allow_point_to_point = 0;
1606 c->publish_aaaa_on_ipv4 = 1;
1607 c->publish_a_on_ipv6 = 0;
1608 c->n_cache_entries_max = CATTA_DEFAULT_CACHE_ENTRIES_MAX;
1609 c->ratelimit_interval = 0;
1610 c->ratelimit_burst = 0;
1615 void catta_server_config_free(CattaServerConfig *c) {
1618 catta_free(c->host_name);
1619 catta_free(c->domain_name);
1620 catta_string_list_free(c->browse_domains);
1621 catta_string_list_free(c->allow_interfaces);
1622 catta_string_list_free(c->deny_interfaces);
1625 CattaServerConfig* catta_server_config_copy(CattaServerConfig *ret, const CattaServerConfig *c) {
1626 char *d = NULL, *h = NULL;
1627 CattaStringList *browse = NULL, *allow = NULL, *deny = NULL;
1632 if (!(h = catta_strdup(c->host_name)))
1636 if (!(d = catta_strdup(c->domain_name))) {
1641 if (!(browse = catta_string_list_copy(c->browse_domains)) && c->browse_domains) {
1647 if (!(allow = catta_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1648 catta_string_list_free(browse);
1654 if (!(deny = catta_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1655 catta_string_list_free(allow);
1656 catta_string_list_free(browse);
1664 ret->domain_name = d;
1665 ret->browse_domains = browse;
1666 ret->allow_interfaces = allow;
1667 ret->deny_interfaces = deny;
1672 int catta_server_errno(CattaServer *s) {
1678 /* Just for internal use */
1679 int catta_server_set_errno(CattaServer *s, int error) {
1682 return s->error = error;
1685 uint32_t catta_server_get_local_service_cookie(CattaServer *s) {
1688 return s->local_service_cookie;
1691 static CattaEntry *find_entry(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, CattaKey *key) {
1697 for (e = catta_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1699 if ((e->iface == iface || e->iface <= 0 || iface <= 0) &&
1700 (e->protocol == protocol || e->protocol == CATTA_PROTO_UNSPEC || protocol == CATTA_PROTO_UNSPEC) &&
1701 (!e->group || e->group->state == CATTA_ENTRY_GROUP_ESTABLISHED || e->group->state == CATTA_ENTRY_GROUP_REGISTERING))
1708 int catta_server_get_group_of_service(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, const char *name, const char *type, const char *domain, CattaSEntryGroup** ret_group) {
1709 CattaKey *key = NULL;
1712 char n[CATTA_DOMAIN_NAME_MAX];
1719 CATTA_CHECK_VALIDITY(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
1720 CATTA_CHECK_VALIDITY(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
1721 CATTA_CHECK_VALIDITY(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
1722 CATTA_CHECK_VALIDITY(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
1723 CATTA_CHECK_VALIDITY(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
1725 if ((ret = catta_service_name_join(n, sizeof(n), name, type, domain) < 0))
1726 return catta_server_set_errno(s, ret);
1728 if (!(key = catta_key_new(n, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV)))
1729 return catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
1731 e = find_entry(s, iface, protocol, key);
1732 catta_key_unref(key);
1735 *ret_group = e->group;
1739 return catta_server_set_errno(s, CATTA_ERR_NOT_FOUND);
1742 int catta_server_is_service_local(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, const char *name) {
1743 CattaKey *key = NULL;
1749 if (!s->host_name_fqdn)
1752 if (!(key = catta_key_new(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV)))
1755 e = find_entry(s, iface, protocol, key);
1756 catta_key_unref(key);
1761 return catta_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1764 int catta_server_is_record_local(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, CattaRecord *record) {
1770 for (e = catta_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1772 if ((e->iface == iface || e->iface <= 0 || iface <= 0) &&
1773 (e->protocol == protocol || e->protocol == CATTA_PROTO_UNSPEC || protocol == CATTA_PROTO_UNSPEC) &&
1774 (!e->group || e->group->state == CATTA_ENTRY_GROUP_ESTABLISHED || e->group->state == CATTA_ENTRY_GROUP_REGISTERING) &&
1775 catta_record_equal_no_ttl(record, e->record))
1781 /** Set the wide area DNS servers */
1782 int catta_server_set_wide_area_servers(CattaServer *s, const CattaAddress *a, unsigned n) {
1785 if (!s->wide_area_lookup_engine)
1786 return catta_server_set_errno(s, CATTA_ERR_INVALID_CONFIG);
1788 catta_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1792 const CattaServerConfig* catta_server_get_config(CattaServer *s) {
1798 /** Set the browsing domains */
1799 int catta_server_set_browse_domains(CattaServer *s, CattaStringList *domains) {
1804 for (l = s->config.browse_domains; l; l = l->next)
1805 if (!catta_is_valid_domain_name((char*) l->text))
1806 return catta_server_set_errno(s, CATTA_ERR_INVALID_DOMAIN_NAME);
1808 catta_string_list_free(s->config.browse_domains);
1809 s->config.browse_domains = catta_string_list_copy(domains);