From 14f8d9beb7ef14b0aab5512345e09109bdd8cb0c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 28 Jun 2005 22:09:17 +0000 Subject: [PATCH] * recreate DNS query in simple protocol on host or domain name changes * C++ compatibility git-svn-id: file:///home/lennart/svn/public/avahi/trunk@151 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-common/address.h | 5 +++++ avahi-common/alternative.h | 6 +++++ avahi-common/cdecl.h | 40 ++++++++++++++++++++++++++++++++++ avahi-common/rr.c | 14 ++++++------ avahi-common/rr.h | 12 ++++++---- avahi-common/strlst.h | 5 +++++ avahi-common/util.h | 6 +++++ avahi-core/core.h | 10 +++++++++ avahi-core/dns.c | 4 ++-- avahi-core/log.h | 5 +++++ avahi-core/probe-sched.c | 6 ++--- avahi-core/server.c | 2 +- avahi-daemon/main.c | 2 ++ avahi-daemon/simple-protocol.c | 31 +++++++++++++++++++++----- avahi-daemon/simple-protocol.h | 1 + doxygen/doxygen.conf.in | 2 +- todo | 8 +++++-- 17 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 avahi-common/cdecl.h diff --git a/avahi-common/address.h b/avahi-common/address.h index f304345..b22b720 100644 --- a/avahi-common/address.h +++ b/avahi-common/address.h @@ -24,6 +24,9 @@ #include #include +#include + +AVAHI_C_DECL_BEGIN /** Protocol family specification, takes the values AVAHI_INET, AVAHI_INET6, AVAHI_UNSPEC */ typedef guchar AvahiProtocol; @@ -97,4 +100,6 @@ gchar* avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a); * encapsulated IPv4 address */ gboolean avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a); +AVAHI_C_DECL_END + #endif diff --git a/avahi-common/alternative.h b/avahi-common/alternative.h index 3aed5b2..13620ff 100644 --- a/avahi-common/alternative.h +++ b/avahi-common/alternative.h @@ -24,6 +24,10 @@ #include +#include + +AVAHI_C_DECL_BEGIN + /** Find an alternative for the specified host name. If called with an * original host name, "2" is appended, Afterwards the number is * increased on each call. (i.e. "foo" becomes "foo2" becomes "foo3" @@ -36,4 +40,6 @@ gchar *avahi_alternative_host_name(const gchar *s); "foo #3" and so on.)*/ gchar *avahi_alternative_service_name(const gchar *s); +AVAHI_C_DECL_END + #endif diff --git a/avahi-common/cdecl.h b/avahi-common/cdecl.h new file mode 100644 index 0000000..82c687d --- /dev/null +++ b/avahi-common/cdecl.h @@ -0,0 +1,40 @@ +#ifndef foocdeclhfoo +#define foocdeclhfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + + +#ifdef __cplusplus +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN extern "C" { +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END } + +#else +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END + +#endif + +#endif diff --git a/avahi-common/rr.c b/avahi-common/rr.c index b0c03a9..2a25321 100644 --- a/avahi-common/rr.c +++ b/avahi-common/rr.c @@ -39,7 +39,7 @@ AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type) { k = g_new(AvahiKey, 1); k->ref = 1; k->name = avahi_normalize_name(name); - k->class = class; + k->clazz = class; k->type = type; /* g_message("%p %% ref=1", k); */ @@ -189,7 +189,7 @@ gchar *avahi_key_to_string(const AvahiKey *k) { return g_strdup_printf("%s\t%s\t%s", k->name, - avahi_dns_class_to_string(k->class), + avahi_dns_class_to_string(k->clazz), avahi_dns_type_to_string(k->type)); } @@ -254,7 +254,7 @@ gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { return avahi_domain_equal(a->name, b->name) && a->type == b->type && - a->class == b->class; + a->clazz == b->clazz; } gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { @@ -270,7 +270,7 @@ gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { return avahi_domain_equal(pattern->name, k->name) && (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && - (pattern->class == k->class || pattern->type == AVAHI_DNS_CLASS_ANY); + (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY); } gboolean avahi_key_is_pattern(const AvahiKey *k) { @@ -278,7 +278,7 @@ gboolean avahi_key_is_pattern(const AvahiKey *k) { return k->type == AVAHI_DNS_TYPE_ANY || - k->class == AVAHI_DNS_CLASS_ANY; + k->clazz == AVAHI_DNS_CLASS_ANY; } guint avahi_key_hash(const AvahiKey *k) { @@ -287,7 +287,7 @@ guint avahi_key_hash(const AvahiKey *k) { return avahi_domain_hash(k->name) + k->type + - k->class; + k->clazz; } static gboolean rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { @@ -481,7 +481,7 @@ gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { if (a == b) return 0; - if ((r = uint16_cmp(a->key->class, b->key->class)) || + if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) || (r = uint16_cmp(a->key->type, b->key->type))) return r; diff --git a/avahi-common/rr.h b/avahi-common/rr.h index 7a5c0e3..1f6a4c6 100644 --- a/avahi-common/rr.h +++ b/avahi-common/rr.h @@ -26,7 +26,9 @@ #include #include +#include +AVAHI_C_DECL_BEGIN /** DNS record types, see RFC 1035 */ enum { @@ -64,7 +66,7 @@ enum { typedef struct { guint ref; /**< Reference counter */ gchar *name; /**< Record name */ - guint16 class; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */ + guint16 clazz; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */ guint16 type; /**< Record type, one of the AVAHI_DNS_TYPE_xxx constants */ } AvahiKey; @@ -119,7 +121,7 @@ typedef struct { } AvahiRecord; /** Create a new AvahiKey object. The reference counter will be set to 1. */ -AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type); +AvahiKey *avahi_key_new(const gchar *name, guint16 clazz, guint16 type); /** Increase the reference counter of an AvahiKey object by one */ AvahiKey *avahi_key_ref(AvahiKey *k); @@ -150,7 +152,7 @@ guint avahi_key_hash(const AvahiKey *k); AvahiRecord *avahi_record_new(AvahiKey *k, guint32 ttl); /** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ -AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type, guint32 ttl); +AvahiRecord *avahi_record_new_full(const gchar *name, guint16 clazz, guint16 type, guint32 ttl); /** Increase the reference counter of an AvahiRecord by one. */ AvahiRecord *avahi_record_ref(AvahiRecord *r); @@ -160,7 +162,7 @@ void avahi_record_unref(AvahiRecord *r); /** Return a textual representation of the specified DNS class. The * returned pointer points to a read only internal string. */ -const gchar *avahi_dns_class_to_string(guint16 class); +const gchar *avahi_dns_class_to_string(guint16 clazz); /** Return a textual representation of the specified DNS class. The * returned pointer points to a read only internal string. */ @@ -196,4 +198,6 @@ gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); /** Return TRUE if the specified record is an mDNS goodbye record. i.e. TTL is zero. */ gboolean avahi_record_is_goodbye(AvahiRecord *r); +AVAHI_C_DECL_END + #endif diff --git a/avahi-common/strlst.h b/avahi-common/strlst.h index 13a6b2a..1d9ef34 100644 --- a/avahi-common/strlst.h +++ b/avahi-common/strlst.h @@ -23,6 +23,9 @@ ***/ #include +#include + +AVAHI_C_DECL_BEGIN /** Linked list of strings that can contain any number of binary * characters, including NUL bytes. An empty list is created by @@ -83,5 +86,7 @@ gboolean avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList /** Copy a string list */ AvahiStringList *avahi_string_list_copy(const AvahiStringList *l); +AVAHI_C_DECL_END + #endif diff --git a/avahi-common/util.h b/avahi-common/util.h index 68d367c..d590423 100644 --- a/avahi-common/util.h +++ b/avahi-common/util.h @@ -24,6 +24,10 @@ #include +#include + +AVAHI_C_DECL_BEGIN + gchar *avahi_normalize_name(const gchar *s); /* g_free() the result! */ gchar *avahi_get_host_name(void); /* g_free() the result! */ @@ -54,4 +58,6 @@ guint avahi_domain_hash(const gchar *s); gchar *avahi_format_mac_address(const guint8* mac, guint size); +AVAHI_C_DECL_END + #endif diff --git a/avahi-core/core.h b/avahi-core/core.h index 02e1db9..06eb07a 100644 --- a/avahi-core/core.h +++ b/avahi-core/core.h @@ -25,6 +25,10 @@ #include #include +#include + +AVAHI_C_DECL_BEGIN + /** An mDNS responder object */ typedef struct AvahiServer AvahiServer; @@ -34,10 +38,14 @@ typedef struct AvahiEntry AvahiEntry; /** A group of locally registered DNS RRs */ typedef struct AvahiEntryGroup AvahiEntryGroup; +AVAHI_C_DECL_END + #include #include #include +AVAHI_C_DECL_BEGIN + /** States of a server object */ typedef enum { AVAHI_SERVER_INVALID = -1, /**< Invalid state (initial) */ @@ -376,4 +384,6 @@ typedef void (*AvahiDNSServerBrowserCallback)(AvahiDNSServerBrowser *b, AvahiIfI AvahiDNSServerBrowser *avahi_dns_server_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const gchar *domain, AvahiDNSServerType type, AvahiProtocol aprotocol, AvahiDNSServerBrowserCallback callback, gpointer userdata); void avahi_dns_server_browser_free(AvahiDNSServerBrowser *b); +AVAHI_C_DECL_END + #endif diff --git a/avahi-core/dns.c b/avahi-core/dns.c index 6a7fd58..5213e97 100644 --- a/avahi-core/dns.c +++ b/avahi-core/dns.c @@ -616,7 +616,7 @@ guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean uni if (!(t = avahi_dns_packet_append_name(p, k->name)) || !avahi_dns_packet_append_uint16(p, k->type) || - !avahi_dns_packet_append_uint16(p, k->class | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { + !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { p->size = size; return NULL; } @@ -635,7 +635,7 @@ guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboole if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || !avahi_dns_packet_append_uint16(p, r->key->type) || - !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->class | AVAHI_DNS_CACHE_FLUSH) : (r->key->class &~ AVAHI_DNS_CACHE_FLUSH)) || + !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) || !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) || !(l = avahi_dns_packet_append_uint16(p, 0))) goto fail; diff --git a/avahi-core/log.h b/avahi-core/log.h index e75b6c0..280b2da 100644 --- a/avahi-core/log.h +++ b/avahi-core/log.h @@ -24,6 +24,9 @@ #include #include +#include + +AVAHI_C_DECL_BEGIN #ifdef __GNUC__ #define AVAHI_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) @@ -74,4 +77,6 @@ void avahi_log_info(const gchar*format, ...) AVAHI_GCC_PRINTF_ATTR12; /** Shortcut for avahi_log(AVAHI_LOG_DEBUG, ...) */ void avahi_log_debug(const gchar*format, ...) AVAHI_GCC_PRINTF_ATTR12; +AVAHI_C_DECL_END + #endif diff --git a/avahi-core/probe-sched.c b/avahi-core/probe-sched.c index 26071d3..0e36bb4 100644 --- a/avahi-core/probe-sched.c +++ b/avahi-core/probe-sched.c @@ -171,7 +171,7 @@ static gboolean packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p return FALSE; /* Create the probe query */ - k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY); + k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY); b = !!avahi_dns_packet_append_key(p, k, FALSE); g_assert(b); @@ -184,7 +184,7 @@ static gboolean packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p continue; /* Does the record match the probe? */ - if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name)) + if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name)) continue; /* This job wouldn't fit in */ @@ -238,7 +238,7 @@ static void elapse_callback(AvahiTimeEvent *e, gpointer data) { p = avahi_dns_packet_new_query(size); - k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY); + k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY); b = avahi_dns_packet_append_key(p, k, FALSE) && avahi_dns_packet_append_record(p, pj->record, FALSE, 0); avahi_key_unref(k); diff --git a/avahi-core/server.c b/avahi-core/server.c index 668e098..b3ae039 100644 --- a/avahi-core/server.c +++ b/avahi-core/server.c @@ -135,7 +135,7 @@ void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, Avahi g_assert(r); g_assert(callback); - if (r->key->class == AVAHI_DNS_CLASS_IN) { + if (r->key->clazz == AVAHI_DNS_CLASS_IN) { if (r->key->type == AVAHI_DNS_TYPE_PTR) { enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata); enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata); diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c index 40e140c..fd95d93 100644 --- a/avahi-daemon/main.c +++ b/avahi-daemon/main.c @@ -185,6 +185,8 @@ static void server_callback(AvahiServer *s, AvahiServerState state, gpointer use if (config->publish_dns_servers && config->publish_dns_servers[0]) dns_servers_entry_group = add_dns_servers(s, config->publish_dns_servers); + + simple_protocol_restart_queries(); } else if (state == AVAHI_SERVER_COLLISION) { gchar *n; diff --git a/avahi-daemon/simple-protocol.c b/avahi-daemon/simple-protocol.c index 39580dd..274568c 100644 --- a/avahi-daemon/simple-protocol.c +++ b/avahi-daemon/simple-protocol.c @@ -70,6 +70,8 @@ struct Client { AvahiHostNameResolver *host_name_resolver; AvahiAddressResolver *address_resolver; AvahiDNSServerBrowser *dns_server_browser; + + AvahiProtocol afquery; AVAHI_LLIST_FIELDS(Client, clients); }; @@ -163,6 +165,7 @@ static void client_output_printf(Client *c, gchar *format, ...) { g_free(t); } + static void host_name_resolver_callback(AvahiHostNameResolver *r, gint iface, guchar protocol, AvahiBrowserEvent event, const gchar *hostname, const AvahiAddress *a, gpointer userdata) { Client *c = userdata; @@ -239,13 +242,13 @@ static void handle_line(Client *c, const gchar *s) { c->state = CLIENT_DEAD; } else if (strcmp(cmd, "RESOLVE-HOSTNAME-IPV4") == 0 && n_args == 2) { c->state = CLIENT_RESOLVE_HOSTNAME; - c->host_name_resolver = avahi_host_name_resolver_new(avahi_server, -1, AF_UNSPEC, arg, AF_INET, host_name_resolver_callback, c); + c->host_name_resolver = avahi_host_name_resolver_new(avahi_server, -1, AF_UNSPEC, arg, c->afquery = AF_INET, host_name_resolver_callback, c); } else if (strcmp(cmd, "RESOLVE-HOSTNAME-IPV6") == 0 && n_args == 2) { c->state = CLIENT_RESOLVE_HOSTNAME; - c->host_name_resolver = avahi_host_name_resolver_new(avahi_server, -1, AF_UNSPEC, arg, AF_INET6, host_name_resolver_callback, c); + c->host_name_resolver = avahi_host_name_resolver_new(avahi_server, -1, AF_UNSPEC, arg, c->afquery = AF_INET6, host_name_resolver_callback, c); } else if (strcmp(cmd, "RESOLVE-HOSTNAME") == 0 && n_args == 2) { c->state = CLIENT_RESOLVE_HOSTNAME; - c->host_name_resolver = avahi_host_name_resolver_new(avahi_server, -1, AF_UNSPEC, arg, AF_UNSPEC, host_name_resolver_callback, c); + c->host_name_resolver = avahi_host_name_resolver_new(avahi_server, -1, AF_UNSPEC, arg, c->afquery = AF_UNSPEC, host_name_resolver_callback, c); } else if (strcmp(cmd, "RESOLVE-ADDRESS") == 0 && n_args == 2) { AvahiAddress addr; @@ -258,15 +261,15 @@ static void handle_line(Client *c, const gchar *s) { } } else if (strcmp(cmd, "BROWSE-DNS-SERVERS-IPV4") == 0 && n_args == 1) { c->state = CLIENT_BROWSE_DNS_SERVERS; - c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, AF_INET, dns_server_browser_callback, c); + c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery = AF_INET, dns_server_browser_callback, c); client_output_printf(c, "+ Browsing ...\n"); } else if (strcmp(cmd, "BROWSE-DNS-SERVERS-IPV6") == 0 && n_args == 1) { c->state = CLIENT_BROWSE_DNS_SERVERS; - c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, AF_INET6, dns_server_browser_callback, c); + c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery = AF_INET6, dns_server_browser_callback, c); client_output_printf(c, "+ Browsing ...\n"); } else if (strcmp(cmd, "BROWSE-DNS-SERVERS") == 0 && n_args == 1) { c->state = CLIENT_BROWSE_DNS_SERVERS; - c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, AF_UNSPEC, dns_server_browser_callback, c); + c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery = AF_UNSPEC, dns_server_browser_callback, c); client_output_printf(c, "+ Browsing ...\n"); } else { client_output_printf(c, "- Invalid command \"%s\", try \"HELP\".\n", cmd); @@ -481,3 +484,19 @@ void simple_protocol_shutdown(void) { server = NULL; } } + +void simple_protocol_restart_queries(void) { + Client *c; + + /* Restart queries in case of local domain name changes */ + + g_assert(server); + + for (c = server->clients; c; c = c->clients_next) + if (c->state == CLIENT_BROWSE_DNS_SERVERS && c->dns_server_browser) { + avahi_dns_server_browser_free(c->dns_server_browser); + c->dns_server_browser = avahi_dns_server_browser_new(avahi_server, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery, dns_server_browser_callback, c); + } +} + + diff --git a/avahi-daemon/simple-protocol.h b/avahi-daemon/simple-protocol.h index 2b82ef9..0ca219c 100644 --- a/avahi-daemon/simple-protocol.h +++ b/avahi-daemon/simple-protocol.h @@ -24,5 +24,6 @@ int simple_protocol_setup(GMainContext *c); void simple_protocol_shutdown(void); +void simple_protocol_restart_queries(void); #endif diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in index 191b0e8..e7a5ca2 100644 --- a/doxygen/doxygen.conf.in +++ b/doxygen/doxygen.conf.in @@ -417,7 +417,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../avahi-core/core.h ../avahi-common/address.h ../avahi-common/rr.h ../avahi-common/strlst.h ../avahi-common/alternative.h ../avahi-core/log.h +INPUT = ../avahi-common/cdecl.h ../avahi-core/core.h ../avahi-common/address.h ../avahi-common/rr.h ../avahi-common/strlst.h ../avahi-common/alternative.h ../avahi-core/log.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp diff --git a/todo b/todo index 4bee34e..1822fa9 100644 --- a/todo +++ b/todo @@ -1,8 +1,11 @@ todo: * release! -* support for special domain PTR records based on local IP subnet address * drop trailing dot on avahi_normalize_name() -* c++ support +later: +* support for special domain PTR records based on local IP subnet address +* Changes resulting in updated RFC of 7th June 2005: + * Defer responses to replies with TC bit set by 400-500msec + * Defer unicast responses the same way as multicast responses done: * Probing/Conflict resolution @@ -40,3 +43,4 @@ done: * reflector * test against apple test suite * sensible logging +* c++ support -- 2.39.2