Set the mDNS host name of a currently running Avahi
+ daemon. The effect of this operation is not persistant across
+ daemon restarts. This operation is usually priviliged.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The avahi developers <@PACKAGE_BUGREPORT@>; avahi is
+ available from
+
+
+
+
+
+
+
+
+
+
This man page was written using by Oliver Kurth.
+
+
+
--
cgit
From 5aebf2a92db63c8fa0412a09433dfd9b11cb3b9b Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 23 Aug 2006 23:04:46 +0000
Subject: - allow access to the DBUS SetHostName method only to users in the
group "netdev" (and make that group name configurable on ./configure - bump
version number
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1265 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/avahi-dbus.conf.in | 16 +++++-----
configure.ac | 66 ++++++++++++++++++++++++-----------------
2 files changed, 47 insertions(+), 35 deletions(-)
diff --git a/avahi-daemon/avahi-dbus.conf.in b/avahi-daemon/avahi-dbus.conf.in
index 416c8dc..c570754 100644
--- a/avahi-daemon/avahi-dbus.conf.in
+++ b/avahi-daemon/avahi-dbus.conf.in
@@ -11,15 +11,17 @@
-
+
-
+
+
-
+
- 512
- 20
-
+
+
+
+
+
diff --git a/configure.ac b/configure.ac
index deac981..ca6d431 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,7 +21,7 @@
# USA.
AC_PREREQ(2.57)
-AC_INIT([avahi],[0.6.12],[avahi (at) lists (dot) freedesktop (dot) org])
+AC_INIT([avahi],[0.6.13],[avahi (at) lists (dot) freedesktop (dot) org])
AC_CONFIG_SRCDIR([avahi-core/server.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
@@ -680,6 +680,15 @@ fi
AC_SUBST(AVAHI_GROUP)
AC_DEFINE_UNQUOTED(AVAHI_GROUP,"$AVAHI_GROUP", [Group for Avahi])
+AC_ARG_WITH(avahi_priv_access_group,AS_HELP_STRING([--with-avahi-priv-access-group=],[Priviliged access group for Avahi clients (netdev)]))
+if test -z "$with_priv_access_group" ; then
+ AVAHI_PRIV_ACCESS_GROUP=netdev
+else
+ AVAHI_PRIV_ACCESS_GROUP=$with_priv_access_group
+fi
+AC_SUBST(AVAHI_PRIV_ACCESS_GROUP)
+AC_DEFINE_UNQUOTED(AVAHI_PRIV_ACCESS_GROUP,"$AVAHI_PRIV_ACCESS_GROUP", [Privileged access group for Avahi clients])
+
#
# Avahi runtime dir
#
@@ -842,33 +851,34 @@ AC_OUTPUT
echo "
---{ $PACKAGE_NAME $VERSION }---
- prefix: ${prefix}
- sysconfdir: ${sysconfdir}
- localstatedir: ${localstatedir}
- avahi socket: ${avahi_socket}
- dbus-1 system.d dir: ${DBUS_SYS_DIR}
- dbus-1 version: ${DBUS_VERSION}
- dbus-1 system socket ${DBUS_SYSTEM_BUS_DEFAULT_ADDRESS}
- compiler: ${CC}
- cflags: ${CFLAGS}
- Enable GLIB: ${HAVE_GLIB}
- Enable GTK: ${HAVE_GTK}
- Enable D-BUS: ${HAVE_DBUS}
- Enable Expat: ${HAVE_EXPAT}
- Enable GDBM: ${HAVE_GDBM}
- Enable DBM: ${HAVE_DBM}
- Enable libdaemon: ${HAVE_LIBDAEMON}
- Enable Python: ${HAVE_PYTHON}
- Enable pygtk: ${HAVE_PYGTK}
- Enable python-dbus: ${HAVE_PYTHON_DBUS}
- Enable QT3: ${HAVE_QT3}
- Enable QT4: ${HAVE_QT4}
- Enable Mono: ${HAVE_MONO}
- Enable Monodoc: ${HAVE_MONODOC}
- Distribution/OS: ${with_distro}
- User for Avahi: ${AVAHI_USER}
- Group for Avahi: ${AVAHI_GROUP}
- Enable chroot(): ${enable_chroot}
+ prefix: ${prefix}
+ sysconfdir: ${sysconfdir}
+ localstatedir: ${localstatedir}
+ avahi socket: ${avahi_socket}
+ dbus-1 system.d dir: ${DBUS_SYS_DIR}
+ dbus-1 version: ${DBUS_VERSION}
+ dbus-1 system socket: ${DBUS_SYSTEM_BUS_DEFAULT_ADDRESS}
+ C Compiler: ${CC}
+ CFLAGS: ${CFLAGS}
+ Enable GLIB: ${HAVE_GLIB}
+ Enable GTK: ${HAVE_GTK}
+ Enable D-BUS: ${HAVE_DBUS}
+ Enable Expat: ${HAVE_EXPAT}
+ Enable GDBM: ${HAVE_GDBM}
+ Enable DBM: ${HAVE_DBM}
+ Enable libdaemon: ${HAVE_LIBDAEMON}
+ Enable Python: ${HAVE_PYTHON}
+ Enable pygtk: ${HAVE_PYGTK}
+ Enable python-dbus: ${HAVE_PYTHON_DBUS}
+ Enable QT3: ${HAVE_QT3}
+ Enable QT4: ${HAVE_QT4}
+ Enable Mono: ${HAVE_MONO}
+ Enable Monodoc: ${HAVE_MONODOC}
+ Distribution/OS: ${with_distro}
+ User for Avahi daemon: ${AVAHI_USER}
+ Group for Avahi daemon: ${AVAHI_GROUP}
+ Priviliged Access Group for Avahi Clients: ${AVAHI_PRIV_ACCESS_GROUP}
+ Enable chroot(): ${enable_chroot}
"
BUILD_DAEMON="no (You need libdaemon and expat!)"
--
cgit
From 83d9a20d5add460cba13dcd0d767dbb7bd8c5916 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 23 Aug 2006 23:35:46 +0000
Subject: add new error code AVAHI_ERR_NO_CHANGE
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1266 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-common/dbus.c | 3 ++-
avahi-common/dbus.h | 1 +
avahi-common/error.c | 3 ++-
avahi-common/error.h | 3 ++-
4 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/avahi-common/dbus.c b/avahi-common/dbus.c
index e044b53..ac54848 100644
--- a/avahi-common/dbus.c
+++ b/avahi-common/dbus.c
@@ -88,7 +88,8 @@ static const char * const table[- AVAHI_ERR_MAX] = {
AVAHI_DBUS_ERR_NOT_PERMITTED,
AVAHI_DBUS_ERR_INVALID_ARGUMENT,
- AVAHI_DBUS_ERR_IS_EMPTY
+ AVAHI_DBUS_ERR_IS_EMPTY,
+ AVAHI_DBUS_ERR_NO_CHANGE
};
struct error_map {
diff --git a/avahi-common/dbus.h b/avahi-common/dbus.h
index 39718d1..f11eb17 100644
--- a/avahi-common/dbus.h
+++ b/avahi-common/dbus.h
@@ -108,6 +108,7 @@ Avahi 0.6.1 implements API version 0x0202 */
#define AVAHI_DBUS_ERR_NOT_PERMITTED "org.freedesktop.Avahi.NotPermittedError"
#define AVAHI_DBUS_ERR_INVALID_ARGUMENT "org.freedesktop.Avahi.InvalidArgumentError"
#define AVAHI_DBUS_ERR_IS_EMPTY "org.freedesktop.Avahi.IsEmptyError"
+#define AVAHI_DBUS_ERR_NO_CHANGE "org.freedesktop.Avahi.NoChangeError"
/** Convert a DBus error string into an Avahi error number */
int avahi_error_dbus_to_number(const char *s);
diff --git a/avahi-common/error.c b/avahi-common/error.c
index f6f561a..53ff4f2 100644
--- a/avahi-common/error.c
+++ b/avahi-common/error.c
@@ -85,7 +85,8 @@ const char *avahi_strerror(int error) {
"Not permitted",
"Invalid argument",
- "Is empty"
+ "Is empty",
+ "The requested operation is invalid because redundant"
};
if (-error < 0 || -error >= -AVAHI_ERR_MAX)
diff --git a/avahi-common/error.h b/avahi-common/error.h
index 2cf3bc2..ee0626f 100644
--- a/avahi-common/error.h
+++ b/avahi-common/error.h
@@ -88,6 +88,7 @@ enum {
AVAHI_ERR_NOT_PERMITTED = -50, /**< Operation not permitted */
AVAHI_ERR_INVALID_ARGUMENT = -51, /**< Invalid argument */
AVAHI_ERR_IS_EMPTY = -52, /**< Is empty */
+ AVAHI_ERR_NO_CHANGE = -53, /**< The requested operation is invalid because redundant */
/****
**** IF YOU ADD A NEW ERROR CODE HERE, PLEASE DON'T FORGET TO ADD
@@ -97,7 +98,7 @@ enum {
**** Also remember to update the MAX value below.
****/
- AVAHI_ERR_MAX = -53
+ AVAHI_ERR_MAX = -54
};
/** Return a human readable error string for the specified error code */
--
cgit
From bebd091e93466d0b9b0bb1ebd8a5d55a2ad735a8 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 23 Aug 2006 23:36:28 +0000
Subject: do not alllow setting the host name to the one that is currently used
anyway. return AVAHI_ERR_NO_CHANGE in that case
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1267 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/server.c | 39 ++++++++++++++++++++++++++++++---------
1 file changed, 30 insertions(+), 9 deletions(-)
diff --git a/avahi-core/server.c b/avahi-core/server.c
index 9d4026d..5bfbc50 100644
--- a/avahi-core/server.c
+++ b/avahi-core/server.c
@@ -1226,17 +1226,27 @@ static void update_fqdn(AvahiServer *s) {
}
int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
+ char *hn = NULL;
assert(s);
- assert(host_name);
+
+ AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
- if (host_name && !avahi_is_valid_host_name(host_name))
- return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+ if (!host_name) {
+ hn = avahi_get_host_name_strdup();
+ hn[strcspn(hn, ".")] = 0;
+ host_name = hn;
+ }
+ if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
+ avahi_free(hn);
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
+ }
+
withdraw_host_rrs(s);
avahi_free(s->host_name);
- s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
- s->host_name[strcspn(s->host_name, ".")] = 0;
+ s->host_name = hn ? hn : avahi_strdup(host_name);
+
update_fqdn(s);
register_stuff(s);
@@ -1244,19 +1254,30 @@ int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
}
int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
+ char *dn = NULL;
assert(s);
- assert(domain_name);
- if (domain_name && !avahi_is_valid_domain_name(domain_name))
- return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
+
+ if (!domain_name) {
+ dn = avahi_strdup("local");
+ domain_name = dn;
+ }
+
+ if (avahi_domain_equal(s->domain_name, domain_name)) {
+ avahi_free(dn);
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
+ }
withdraw_host_rrs(s);
avahi_free(s->domain_name);
- s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
+ s->domain_name = avahi_normalize_name_strdup(domain_name);
update_fqdn(s);
register_stuff(s);
+
+ avahi_free(dn);
return AVAHI_OK;
}
--
cgit
From e521a9a2fad8314d5e7d5bbde47b2c852a1426a7 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 24 Aug 2006 17:58:02 +0000
Subject: follow Bonjour's handling of subtypes when browsing/registering
services (closes #41)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1268 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-compat-libdns_sd/compat.c | 181 ++++++++++++++++++++++++++++++-------
avahi-compat-libdns_sd/null-test.c | 33 ++++++-
2 files changed, 177 insertions(+), 37 deletions(-)
diff --git a/avahi-compat-libdns_sd/compat.c b/avahi-compat-libdns_sd/compat.c
index e5692bb..6433f6d 100644
--- a/avahi-compat-libdns_sd/compat.c
+++ b/avahi-compat-libdns_sd/compat.c
@@ -56,6 +56,12 @@ enum {
COMMAND_POLL_FAILED = 'F'
};
+struct type_info {
+ char *type;
+ AvahiStringList *subtypes;
+ int n_subtypes;
+};
+
struct _DNSServiceRef_t {
int n_ref;
@@ -79,7 +85,8 @@ struct _DNSServiceRef_t {
AvahiServiceResolver *service_resolver;
AvahiDomainBrowser *domain_browser;
- char *service_name, *service_name_chosen, *service_regtype, *service_domain, *service_host;
+ struct type_info type_info;
+ char *service_name, *service_name_chosen, *service_domain, *service_host;
uint16_t service_port;
AvahiIfIndex service_interface;
AvahiStringList *service_txt;
@@ -154,6 +161,91 @@ static DNSServiceErrorType map_error(int error) {
return kDNSServiceErr_Unknown;
}
+static void type_info_init(struct type_info *i) {
+ assert(i);
+ i->type = NULL;
+ i->subtypes = NULL;
+ i->n_subtypes = 0;
+}
+
+static void type_info_free(struct type_info *i) {
+ assert(i);
+
+ avahi_free(i->type);
+ avahi_string_list_free(i->subtypes);
+
+ type_info_init(i);
+}
+
+static int type_info_parse(struct type_info *i, const char *t) {
+ char *token = NULL;
+
+ assert(i);
+ assert(t);
+
+ type_info_init(i);
+
+ for (;;) {
+ size_t l;
+
+ if (*t == 0)
+ break;
+
+ l = strcspn(t, ",");
+
+ if (l <= 0)
+ goto fail;
+
+ token = avahi_strndup(t, l);
+
+ if (!token)
+ goto fail;
+
+ if (!i->type) {
+ /* This is the first token, hence the main type */
+
+ if (!avahi_is_valid_service_type_strict(token))
+ goto fail;
+
+ i->type = token;
+ token = NULL;
+ } else {
+ char *fst;
+
+ /* This is not the first token, hence a subtype */
+
+ if (!(fst = avahi_strdup_printf("%s._sub.%s", token, i->type)))
+ goto fail;
+
+ if (!avahi_is_valid_service_subtype(fst)) {
+ avahi_free(fst);
+ goto fail;
+ }
+
+ i->subtypes = avahi_string_list_add(i->subtypes, fst);
+ avahi_free(fst);
+
+ avahi_free(token);
+ token = NULL;
+
+ i->n_subtypes++;
+ }
+
+ t += l;
+
+ if (*t == ',')
+ t++;
+ }
+
+ if (i->type)
+ return 0;
+
+fail:
+ type_info_free(i);
+ avahi_free(token);
+ return -1;
+}
+
static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
if (!s)
return NULL;
@@ -287,9 +379,11 @@ static DNSServiceRef sdref_new(void) {
sdref->domain_browser = NULL;
sdref->entry_group = NULL;
- sdref->service_name = sdref->service_name_chosen = sdref->service_regtype = sdref->service_domain = sdref->service_host = NULL;
+ sdref->service_name = sdref->service_name_chosen = sdref->service_domain = sdref->service_host = NULL;
sdref->service_txt = NULL;
+ type_info_init(&sdref->type_info);
+
ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
ASSERT_SUCCESS(pthread_mutex_init(&sdref->mutex, &mutex_attr));
@@ -349,10 +443,11 @@ static void sdref_free(DNSServiceRef sdref) {
avahi_free(sdref->service_name);
avahi_free(sdref->service_name_chosen);
- avahi_free(sdref->service_regtype);
avahi_free(sdref->service_domain);
avahi_free(sdref->service_host);
+ type_info_free(&sdref->type_info);
+
avahi_string_list_free(sdref->service_txt);
avahi_free(sdref);
@@ -500,18 +595,19 @@ static void generic_client_callback(AvahiClient *s, AvahiClientState state, void
}
DNSServiceErrorType DNSSD_API DNSServiceBrowse(
- DNSServiceRef *ret_sdref,
- DNSServiceFlags flags,
- uint32_t interface,
- const char *regtype,
- const char *domain,
- DNSServiceBrowseReply callback,
- void *context) {
+ DNSServiceRef *ret_sdref,
+ DNSServiceFlags flags,
+ uint32_t interface,
+ const char *regtype,
+ const char *domain,
+ DNSServiceBrowseReply callback,
+ void *context) {
DNSServiceErrorType ret = kDNSServiceErr_Unknown;
int error;
DNSServiceRef sdref = NULL;
AvahiIfIndex ifindex;
+ struct type_info type_info;
AVAHI_WARN_LINKAGE;
@@ -524,8 +620,20 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse(
return kDNSServiceErr_Unsupported;
}
- if (!(sdref = sdref_new()))
+ type_info_init(&type_info);
+
+ if (type_info_parse(&type_info, regtype) < 0 || type_info.n_subtypes > 1) {
+ type_info_free(&type_info);
+
+ if (!avahi_is_valid_service_type_generic(regtype))
+ return kDNSServiceErr_Unsupported;
+ } else
+ regtype = type_info.subtypes ? (char*) type_info.subtypes->text : type_info.type;
+
+ if (!(sdref = sdref_new())) {
+ type_info_free(&type_info);
return kDNSServiceErr_Unknown;
+ }
sdref->context = context;
sdref->service_browser_callback = callback;
@@ -554,6 +662,8 @@ finish:
if (ret != kDNSServiceErr_NoError)
DNSServiceRefDeallocate(sdref);
+ type_info_free(&type_info);
+
return ret;
}
@@ -793,7 +903,7 @@ static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) {
if (!sdref->service_register_callback)
return;
- regtype = add_trailing_dot(sdref->service_regtype, regtype_fixed, sizeof(regtype_fixed));
+ regtype = add_trailing_dot(sdref->type_info.type, regtype_fixed, sizeof(regtype_fixed));
domain = add_trailing_dot(sdref->service_domain, domain_fixed, sizeof(domain_fixed));
sdref->service_register_callback(
@@ -806,28 +916,25 @@ static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) {
static int reg_create_service(DNSServiceRef sdref) {
int ret;
- const char *real_type;
+ AvahiStringList *l;
assert(sdref);
assert(sdref->n_ref >= 1);
- real_type = avahi_get_type_from_subtype(sdref->service_regtype);
-
if ((ret = avahi_entry_group_add_service_strlst(
sdref->entry_group,
sdref->service_interface,
AVAHI_PROTO_UNSPEC,
0,
sdref->service_name_chosen,
- real_type ? real_type : sdref->service_regtype,
+ sdref->type_info.type,
sdref->service_domain,
sdref->service_host,
sdref->service_port,
sdref->service_txt)) < 0)
return ret;
-
- if (real_type) {
+ for (l = sdref->type_info.subtypes; l; l = l->next) {
/* Create a subtype entry */
if (avahi_entry_group_add_service_subtype(
@@ -836,11 +943,10 @@ static int reg_create_service(DNSServiceRef sdref) {
AVAHI_PROTO_UNSPEC,
0,
sdref->service_name_chosen,
- real_type,
+ sdref->type_info.type,
sdref->service_domain,
- sdref->service_regtype) < 0)
+ (const char*) l->text) < 0)
return ret;
-
}
if ((ret = avahi_entry_group_commit(sdref->entry_group)) < 0)
@@ -963,23 +1069,24 @@ static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState st
}
DNSServiceErrorType DNSSD_API DNSServiceRegister (
- DNSServiceRef *ret_sdref,
- DNSServiceFlags flags,
- uint32_t interface,
- const char *name,
- const char *regtype,
- const char *domain,
- const char *host,
- uint16_t port,
- uint16_t txtLen,
- const void *txtRecord,
- DNSServiceRegisterReply callback,
- void *context) {
+ DNSServiceRef *ret_sdref,
+ DNSServiceFlags flags,
+ uint32_t interface,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ uint16_t txtLen,
+ const void *txtRecord,
+ DNSServiceRegisterReply callback,
+ void *context) {
DNSServiceErrorType ret = kDNSServiceErr_Unknown;
int error;
DNSServiceRef sdref = NULL;
AvahiStringList *txt = NULL;
+ struct type_info type_info;
AVAHI_WARN_LINKAGE;
@@ -994,17 +1101,23 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister (
if (txtRecord && txtLen > 0)
if (avahi_string_list_parse(txtRecord, txtLen, &txt) < 0)
return kDNSServiceErr_Invalid;
+
+ if (type_info_parse(&type_info, regtype) < 0) {
+ avahi_string_list_free(txt);
+ return kDNSServiceErr_Invalid;
+ }
if (!(sdref = sdref_new())) {
avahi_string_list_free(txt);
+ type_info_free(&type_info);
return kDNSServiceErr_Unknown;
}
sdref->context = context;
sdref->service_register_callback = callback;
+ sdref->type_info = type_info;
sdref->service_name = avahi_strdup(name);
- sdref->service_regtype = regtype ? avahi_normalize_name_strdup(regtype) : NULL;
sdref->service_domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
sdref->service_host = host ? avahi_normalize_name_strdup(host) : NULL;
sdref->service_interface = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
diff --git a/avahi-compat-libdns_sd/null-test.c b/avahi-compat-libdns_sd/null-test.c
index 272e9c3..a6c03df 100644
--- a/avahi-compat-libdns_sd/null-test.c
+++ b/avahi-compat-libdns_sd/null-test.c
@@ -32,15 +32,42 @@
#include
#include
+static void reply(
+ AVAHI_GCC_UNUSED DNSServiceRef sdRef,
+ AVAHI_GCC_UNUSED DNSServiceFlags flags,
+ AVAHI_GCC_UNUSED uint32_t interfaceIndex,
+ AVAHI_GCC_UNUSED DNSServiceErrorType errorCode,
+ AVAHI_GCC_UNUSED const char *serviceName,
+ AVAHI_GCC_UNUSED const char *regtype,
+ AVAHI_GCC_UNUSED const char *replyDomain,
+ AVAHI_GCC_UNUSED void *context) {
+}
+
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
- DNSServiceRef ref;
+ DNSServiceRef ref1, ref2, ref3, ref4 = NULL;
+
+ DNSServiceRegister(&ref1, 0, 0, "simple", "_simple._tcp", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ DNSServiceRegister(&ref2, 0, 0, "subtype #1", "_simple._tcp,_subtype1", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ DNSServiceRegister(&ref3, 0, 0, "subtype #2", "_simple._tcp,_subtype1,_subtype2", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+
+ DNSServiceRegister(&ref4, 0, 0, "subtype #3", "_simple._tcp,,", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ assert(!ref4);
+ DNSServiceRegister(&ref4, 0, 0, "subtype #3", "", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ assert(!ref4);
+ DNSServiceRegister(&ref4, 0, 0, "subtype #3", ",", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ assert(!ref4);
+ DNSServiceRegister(&ref4, 0, 0, "subtype #3", ",,", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ assert(!ref4);
- DNSServiceRegister(&ref, 0, 0, "fucker", "_fuck._tcp", NULL, NULL, 4711, 0, NULL, NULL, NULL);
+ DNSServiceBrowse(&ref4, 0, 0, "_simple._tcp,_gurke", NULL, reply, NULL);
sleep(20);
- DNSServiceRefDeallocate(ref);
+ DNSServiceRefDeallocate(ref1);
+ DNSServiceRefDeallocate(ref2);
+ DNSServiceRefDeallocate(ref3);
+ DNSServiceRefDeallocate(ref4);
return 0;
}
--
cgit
From 8a0e03b4fc8687a91b9a6800145e613c3a009247 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 24 Aug 2006 21:02:46 +0000
Subject: If the client calls EntryGroup::Commit update the local state
immediately and do not delay this until the server informed us about the
state change asynchronously. The reason for this change is that otherwise all
functions that access the entry group depend on the out-of-date state for
their validity checks, which is obviously a bad idea.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1269 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-client/entrygroup.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/avahi-client/entrygroup.c b/avahi-client/entrygroup.c
index e43a44f..ea9b676 100644
--- a/avahi-client/entrygroup.c
+++ b/avahi-client/entrygroup.c
@@ -249,21 +249,31 @@ int avahi_entry_group_free(AvahiEntryGroup *group) {
}
int avahi_entry_group_commit(AvahiEntryGroup *group) {
+ int ret;
assert(group);
if (!group->path || !avahi_client_is_connected(group->client))
return avahi_client_set_errno(group->client, AVAHI_ERR_BAD_STATE);
- return entry_group_simple_method_call(group, "Commit");
+ if ((ret = entry_group_simple_method_call(group, "Commit")) < 0)
+ return ret;
+
+ avahi_entry_group_set_state(group, AVAHI_ENTRY_GROUP_REGISTERING);
+ return 0;
}
int avahi_entry_group_reset(AvahiEntryGroup *group) {
+ int ret;
assert(group);
if (!group->path || !avahi_client_is_connected(group->client))
return avahi_client_set_errno(group->client, AVAHI_ERR_BAD_STATE);
- return entry_group_simple_method_call(group, "Reset");
+ if ((ret = entry_group_simple_method_call(group, "Reset")) < 0)
+ return ret;
+
+ avahi_entry_group_set_state(group, AVAHI_ENTRY_GROUP_UNCOMMITED);
+ return 0;
}
int avahi_entry_group_get_state (AvahiEntryGroup *group) {
--
cgit
From fc3830c24fd0341d762a2c4f05198f27fc3ccac0 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 24 Aug 2006 21:04:24 +0000
Subject: implement DNSServiceUpdateRecord() for updating existing TXT records
(closes #56)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1270 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-compat-libdns_sd/compat.c | 64 +++++++++++++++++++++++++++++++++++-
avahi-compat-libdns_sd/unsupported.c | 13 --------
2 files changed, 63 insertions(+), 14 deletions(-)
diff --git a/avahi-compat-libdns_sd/compat.c b/avahi-compat-libdns_sd/compat.c
index 6433f6d..88a98a7 100644
--- a/avahi-compat-libdns_sd/compat.c
+++ b/avahi-compat-libdns_sd/compat.c
@@ -1024,6 +1024,7 @@ static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState st
switch (state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED:
+
/* Inform the user */
reg_report_error(sdref, kDNSServiceErr_NoError);
@@ -1092,13 +1093,14 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister (
assert(ret_sdref);
assert(regtype);
+ assert(txtRecord || txtLen == 0);
if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) {
AVAHI_WARN_UNSUPPORTED;
return kDNSServiceErr_Unsupported;
}
- if (txtRecord && txtLen > 0)
+ if (txtLen > 0)
if (avahi_string_list_parse(txtRecord, txtLen, &txt) < 0)
return kDNSServiceErr_Invalid;
@@ -1189,3 +1191,63 @@ finish:
return ret;
}
+DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord(
+ DNSServiceRef sdref,
+ DNSRecordRef rref,
+ DNSServiceFlags flags,
+ uint16_t rdlen,
+ const void *rdata,
+ AVAHI_GCC_UNUSED uint32_t ttl) {
+
+ int ret = kDNSServiceErr_Unknown;
+ AvahiStringList *txt = NULL;
+ assert(sdref);
+
+ AVAHI_WARN_LINKAGE;
+
+ if (flags || rref) {
+ AVAHI_WARN_UNSUPPORTED;
+ return kDNSServiceErr_Unsupported;
+ }
+
+ if (rdlen > 0)
+ if (avahi_string_list_parse(rdata, rdlen, &txt) < 0)
+ return kDNSServiceErr_Invalid;
+
+ ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
+
+ if (!avahi_string_list_equal(txt, sdref->service_txt)) {
+
+ avahi_string_list_free(sdref->service_txt);
+ sdref->service_txt = txt;
+
+ if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING &&
+ sdref->entry_group &&
+ (avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_ESTABLISHED ||
+ avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING))
+
+ if (avahi_entry_group_update_service_txt_strlst(
+ sdref->entry_group,
+ sdref->service_interface,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ sdref->service_name_chosen,
+ sdref->type_info.type,
+ sdref->service_domain,
+ sdref->service_txt) < 0) {
+
+ ret = map_error(avahi_client_errno(sdref->client));
+ goto finish;
+ }
+
+ } else
+ avahi_string_list_free(txt);
+
+ ret = kDNSServiceErr_NoError;
+
+finish:
+ ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
+
+ return ret;
+}
+
diff --git a/avahi-compat-libdns_sd/unsupported.c b/avahi-compat-libdns_sd/unsupported.c
index 0e08ad6..2df3847 100644
--- a/avahi-compat-libdns_sd/unsupported.c
+++ b/avahi-compat-libdns_sd/unsupported.c
@@ -96,19 +96,6 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord(
return kDNSServiceErr_Unsupported;
}
-DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord(
- AVAHI_GCC_UNUSED DNSServiceRef sdRef,
- AVAHI_GCC_UNUSED DNSRecordRef RecordRef,
- AVAHI_GCC_UNUSED DNSServiceFlags flags,
- AVAHI_GCC_UNUSED uint16_t rdlen,
- AVAHI_GCC_UNUSED const void *rdata,
- AVAHI_GCC_UNUSED uint32_t ttl) {
-
- AVAHI_WARN_UNSUPPORTED;
-
- return kDNSServiceErr_Unsupported;
-}
-
DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord(
AVAHI_GCC_UNUSED DNSServiceRef sdRef,
AVAHI_GCC_UNUSED DNSRecordRef RecordRef,
--
cgit
From e7bcb6b9a448332e34855128b4d496b852dfd2af Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 24 Aug 2006 21:37:18 +0000
Subject: rethink r1269 once again: instead of updating the entry group state
in EntryGroup::Commit() immediately (which would cause the state change
callback function to be called from the current stack frame which might be
roblem for some programs), do it asynchronously again, like in pre-r1269.
However, modify GetState() to always return the real state by asking the
server if we have doubt that our locally cached state is out-of-date.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1271 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-client/entrygroup.c | 31 +++++++++++++++++++------------
avahi-client/internal.h | 1 +
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/avahi-client/entrygroup.c b/avahi-client/entrygroup.c
index ea9b676..e2fc3b9 100644
--- a/avahi-client/entrygroup.c
+++ b/avahi-client/entrygroup.c
@@ -41,10 +41,11 @@
void avahi_entry_group_set_state(AvahiEntryGroup *group, AvahiEntryGroupState state) {
assert(group);
- if (group->state == state)
+ if (group->state_valid && group->state == state)
return;
group->state = state;
+ group->state_valid = 1;
if (group->callback)
group->callback(group, state, group->userdata);
@@ -82,9 +83,7 @@ static int retrieve_state(AvahiEntryGroup *group) {
dbus_message_unref(message);
dbus_message_unref(reply);
- avahi_entry_group_set_state(group, (AvahiEntryGroupState) state);
-
- return AVAHI_OK;
+ return state;
fail:
if (dbus_error_is_set(&error)) {
@@ -106,6 +105,7 @@ AvahiEntryGroup* avahi_entry_group_new (AvahiClient *client, AvahiEntryGroupCall
DBusMessage *message = NULL, *reply = NULL;
DBusError error;
char *path;
+ int state;
assert(client);
@@ -124,7 +124,7 @@ AvahiEntryGroup* avahi_entry_group_new (AvahiClient *client, AvahiEntryGroupCall
group->client = client;
group->callback = callback;
group->userdata = userdata;
- group->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
+ group->state_valid = 0;
group->path = NULL;
AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, client->groups, group);
@@ -157,8 +157,12 @@ AvahiEntryGroup* avahi_entry_group_new (AvahiClient *client, AvahiEntryGroupCall
goto fail;
}
- if (retrieve_state(group) < 0)
+ if ((state = retrieve_state(group)) < 0) {
+ avahi_client_set_errno(client, state);
goto fail;
+ }
+
+ avahi_entry_group_set_state(group, (AvahiEntryGroupState) state);
dbus_message_unref(message);
dbus_message_unref(reply);
@@ -258,8 +262,8 @@ int avahi_entry_group_commit(AvahiEntryGroup *group) {
if ((ret = entry_group_simple_method_call(group, "Commit")) < 0)
return ret;
- avahi_entry_group_set_state(group, AVAHI_ENTRY_GROUP_REGISTERING);
- return 0;
+ group->state_valid = 0;
+ return ret;
}
int avahi_entry_group_reset(AvahiEntryGroup *group) {
@@ -271,15 +275,18 @@ int avahi_entry_group_reset(AvahiEntryGroup *group) {
if ((ret = entry_group_simple_method_call(group, "Reset")) < 0)
return ret;
-
- avahi_entry_group_set_state(group, AVAHI_ENTRY_GROUP_UNCOMMITED);
- return 0;
+
+ group->state_valid = 0;
+ return ret;
}
int avahi_entry_group_get_state (AvahiEntryGroup *group) {
assert (group);
- return group->state;
+ if (group->state_valid)
+ return group->state;
+
+ return retrieve_state(group);
}
AvahiClient* avahi_entry_group_get_client (AvahiEntryGroup *group) {
diff --git a/avahi-client/internal.h b/avahi-client/internal.h
index 7a79dd7..e49dd15 100644
--- a/avahi-client/internal.h
+++ b/avahi-client/internal.h
@@ -56,6 +56,7 @@ struct AvahiClient {
struct AvahiEntryGroup {
char *path;
AvahiEntryGroupState state;
+ int state_valid;
AvahiClient *client;
AvahiEntryGroupCallback callback;
void *userdata;
--
cgit
From 852c1dd6fabd985b063c18c3d0aac7f44182d9bb Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 24 Aug 2006 23:24:28 +0000
Subject: update NEWS file
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1272 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/NEWS | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/docs/NEWS b/docs/NEWS
index dab54d0..ce6ab19 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -1,3 +1,49 @@
+Avahi 0.6.13
+============
+
+This release fixes some bugs and includes minor enhancements.
+
+Changes:
+ * Add a new D-Bus method for changing the mDNS host name during
+ runtime. This functionality is only available to members of the
+ UNIX group "netdev", which is the same access group that is
+ enforced by GNOME's NetworkManager daemon. Since NM will probably
+ be the most prominent user of this new method, we decided to limit
+ access to the same group. The access group can be set by passing
+ --with-avahi-priv-access-group= to "configure". If you need more
+ sophisticated access control you can freely edit
+ /etc/dbus/system.d/avahi-dbus.conf.
+ * Add a new utility "avahi-set-host-name" which is a command line
+ wrapper around the aforementioned SetHostName() method.
+ * Bonjour API compatibility library:
+ * Implement DNSServiceUpdateRecord()
+ * Allow passing NULL as callback function for
+ DNSServiceRegister()
+ * Implement subtype registration in DNSServiceRegister() in a
+ way that is compatible with Bonjour.
+ * If the host name changes update names of static services wich
+ contain wildcards.
+ * Don't build documentation about embedding the Avahi mDNS stack into
+ other programs by default. This is a feature used only by embedded
+ developers. Pass --enable-core-docs to "configure" to enable
+ building these docs, like in Avahi <= 0.6.12.
+ * Build Qt documentation only when Qt support is enabled in
+ the configuration. Same for GLib.
+ * Change algorithm used to find a new host name on conflict. In
+ Avahi <= 0.6.12 a conflicting host name of "foobar" would be
+ changed to the new name "foobar2". With 0.6.13 "foobar-2" will be
+ picked instead. This follows Bonjour's behaviour and has the
+ advantage not confusing people with regular host names ending in
+ numbers.
+ * Don't disable all static services when SIGHUP is recieved.
+ * Fix build when Avahi is configured without Gtk+ but with Python
+ support
+ * Support using Solaris DBM instead of gdbm for the service type
+ database. The latter is still recommended
+ * Minor other fixes and documentation updates
+
+This release is backwards compatible with Avahi 0.6.x with x < 13.
+
Avahi 0.6.12
============
--
cgit
From bff43bda174f79624f470aa46fd3aa6ff70fe2a4 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 24 Aug 2006 23:27:05 +0000
Subject: bump sonames
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1273 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/configure.ac b/configure.ac
index ca6d431..6b21c30 100644
--- a/configure.ac
+++ b/configure.ac
@@ -28,9 +28,9 @@ AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
AC_SUBST(PACKAGE_URL, [http://avahi.org/])
-AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [7:1:4])
-AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [4:3:0])
-AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [4:3:1])
+AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [7:2:4])
+AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [4:4:0])
+AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:0:2])
AC_SUBST(LIBAVAHI_GLIB_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT3_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT4_VERSION_INFO, [1:1:0])
--
cgit
From 82aabd487bd00b79147ee23f4f76d692fdd0ced3 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 18:45:17 +0000
Subject: update to newer dns_sd.h copy from Apple
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1274 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-compat-libdns_sd/compat.c | 2 +-
avahi-compat-libdns_sd/dns_sd.h | 86 +++++++++++++++++++++++++++++-------
avahi-compat-libdns_sd/unsupported.c | 4 +-
3 files changed, 72 insertions(+), 20 deletions(-)
diff --git a/avahi-compat-libdns_sd/compat.c b/avahi-compat-libdns_sd/compat.c
index 88a98a7..e6b6042 100644
--- a/avahi-compat-libdns_sd/compat.c
+++ b/avahi-compat-libdns_sd/compat.c
@@ -707,7 +707,7 @@ static void service_resolver_callback(
strcat(full_name, ".");
- sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, p, sdref->context);
+ sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, (unsigned char*) p, sdref->context);
avahi_free(p);
break;
diff --git a/avahi-compat-libdns_sd/dns_sd.h b/avahi-compat-libdns_sd/dns_sd.h
index 0d8a583..b7eb8a2 100644
--- a/avahi-compat-libdns_sd/dns_sd.h
+++ b/avahi-compat-libdns_sd/dns_sd.h
@@ -1,4 +1,5 @@
-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
* Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,17 +34,32 @@
#endif
/* standard calling convention under Win32 is __stdcall */
-#if defined(_WIN32)
+/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */
+/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */
+#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
#define DNSSD_API __stdcall
#else
#define DNSSD_API
#endif
-#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000) || defined(__OpenBSD__)
/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */
+#if defined(__FreeBSD__) && (__FreeBSD__ < 5)
#include
+
+/* Likewise, on Sun, standard integer types are in sys/types.h */
#elif defined(__sun__)
#include
+
+/* EFI does not have stdint.h, or anything else equivalent */
+#elif defined(EFI32) || defined(EFI64)
+typedef UINT8 uint8_t;
+typedef INT8 int8_t;
+typedef UINT16 uint16_t;
+typedef INT16 int16_t;
+typedef UINT32 uint32_t;
+typedef INT32 int32_t;
+
+/* Windows has its own differences */
#elif defined(_WIN32)
#include
#define _UNUSED
@@ -56,6 +72,8 @@ typedef INT16 int16_t;
typedef UINT32 uint32_t;
typedef INT32 int32_t;
#endif
+
+/* All other Posix platforms use stdint.h */
#else
#include
#endif
@@ -128,10 +146,15 @@ enum
* (queries from hosts more than one hop away; hosts not directly connected to the local link).
*/
- kDNSServiceFlagsForceMulticast = 0x400
+ kDNSServiceFlagsForceMulticast = 0x400,
/* Flag for signifying that a query or registration should be performed exclusively via multicast DNS,
* even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
*/
+
+ kDNSServiceFlagsReturnCNAME = 0x800
+ /* Flag for returning CNAME records in the DNSServiceQueryRecord call. CNAME records are
+ * normally followed without indicating to the client that there was a CNAME record.
+ */
};
/*
@@ -179,7 +202,7 @@ enum
kDNSServiceType_KEY = 25, /* Security key. */
kDNSServiceType_PX = 26, /* X.400 mail mapping. */
kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */
- kDNSServiceType_AAAA = 28, /* Ip6 Address. */
+ kDNSServiceType_AAAA = 28, /* IPv6 Address. */
kDNSServiceType_LOC = 29, /* Location Information. */
kDNSServiceType_NXT = 30, /* Next domain (security). */
kDNSServiceType_EID = 31, /* Endpoint identifier. */
@@ -189,7 +212,7 @@ enum
kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */
kDNSServiceType_KX = 36, /* Key Exchange */
kDNSServiceType_CERT = 37, /* Certification record */
- kDNSServiceType_A6 = 38, /* IPv6 address (deprecates AAAA) */
+ kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */
kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */
kDNSServiceType_SINK = 40, /* Kitchen sink (experimentatl) */
kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */
@@ -301,7 +324,7 @@ enum
*
* If the client passes 0 for interface index, that means "do the right thing",
* which (at present) means, "if the name is in an mDNS local multicast domain
- * (e.g. 'local.', '254.169.in-addr.arpa.', '0.8.E.F.ip6.arpa.') then multicast
+ * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast
* on all applicable interfaces, otherwise send via unicast to the appropriate
* DNS server." Normally, most clients will use 0 for interface index to
* automatically get the default sensible behaviour.
@@ -606,6 +629,9 @@ typedef void (DNSSD_API *DNSServiceRegisterReply)
* i.e. it creates a TXT record of length one containing a single empty string.
* RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty
* string is the smallest legal DNS TXT record.
+ * As with the other parameters, the DNSServiceRegister call copies the txtRecord
+ * data; e.g. if you allocated the storage for the txtRecord parameter with malloc()
+ * then you can safely free that memory right after the DNSServiceRegister call returns.
*
* callBack: The function to be called when the registration completes or asynchronously
* fails. The client MAY pass NULL for the callback - The client will NOT be notified
@@ -621,7 +647,6 @@ typedef void (DNSSD_API *DNSServiceRegisterReply)
* errors are delivered to the callback), otherwise returns an error code indicating
* the error that occurred (the callback is never invoked and the DNSServiceRef
* is not initialized.)
- *
*/
DNSServiceErrorType DNSSD_API DNSServiceRegister
@@ -648,6 +673,12 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister
* The record can later be updated or deregistered by passing the RecordRef initialized
* by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
*
+ * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe
+ * with respect to a single DNSServiceRef. If you plan to have multiple threads
+ * in your program simultaneously add, update, or remove records from the same
+ * DNSServiceRef, then it's the caller's responsibility to use a mutext lock
+ * or take similar appropriate precautions to serialize those calls.
+ *
*
* Parameters;
*
@@ -897,9 +928,23 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse
*
* txtRecord: The service's primary txt record, in standard txt record format.
*
-
* context: The context pointer that was passed to the callout.
*
+ * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *"
+ * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127.
+ * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings.
+ * These should be fixed by updating your own callback function definition to match the corrected
+ * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent
+ * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250
+ * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes.
+ * If you need to maintain portable code that will compile cleanly with both the old and new versions of
+ * this header file, you should update your callback function definition to use the correct unsigned value,
+ * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate
+ * the compiler warning, e.g.:
+ * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context);
+ * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly)
+ * with both the old header and with the new corrected version.
+ *
*/
typedef void (DNSSD_API *DNSServiceResolveReply)
@@ -912,7 +957,7 @@ typedef void (DNSSD_API *DNSServiceResolveReply)
const char *hosttarget,
uint16_t port,
uint16_t txtLen,
- const char *txtRecord,
+ const unsigned char *txtRecord,
void *context
);
@@ -1005,7 +1050,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef);
* DNSServiceRegisterRecordReply() parameters:
*
* sdRef: The connected DNSServiceRef initialized by
- * DNSServiceDiscoveryConnect().
+ * DNSServiceCreateConnection().
*
* RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above
* DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is
@@ -1220,7 +1265,7 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
*
*/
-void DNSSD_API DNSServiceReconfirmRecord
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
(
DNSServiceFlags flags,
uint32_t interfaceIndex,
@@ -1300,7 +1345,7 @@ int DNSSD_API DNSServiceConstructFullName
* Note: Represents a DNS-SD TXT record.
*/
-typedef struct _TXTRecordRef_t { char privatedata[16]; } TXTRecordRef;
+typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
/* TXTRecordCreate()
@@ -1426,7 +1471,6 @@ DNSServiceErrorType DNSSD_API TXTRecordSetValue
* return value: Returns kDNSServiceErr_NoError on success.
* Returns kDNSServiceErr_NoSuchKey if the "key" does not
* exist in the TXTRecordRef.
- *
*/
DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
@@ -1446,7 +1490,6 @@ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
* which you can pass directly to DNSServiceRegister() or
* to DNSServiceUpdateRecord().
* Returns 0 if the TXTRecordRef is empty.
- *
*/
uint16_t DNSSD_API TXTRecordGetLength
@@ -1464,7 +1507,6 @@ uint16_t DNSSD_API TXTRecordGetLength
* return value: Returns a pointer to the raw bytes inside the TXTRecordRef
* which you can pass directly to DNSServiceRegister() or
* to DNSServiceUpdateRecord().
- *
*/
const void * DNSSD_API TXTRecordGetBytesPtr
@@ -1519,7 +1561,6 @@ const void * DNSSD_API TXTRecordGetBytesPtr
*
* return value: Returns 1 if the TXT Record contains the specified key.
* Otherwise, it returns 0.
- *
*/
int DNSSD_API TXTRecordContainsKey
@@ -1663,6 +1704,17 @@ DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser
#endif //__APPLE_API_PRIVATE
+// Some C compiler cleverness. We can make the compiler check certain things for us,
+// and report errors at compile-time if anything is wrong. The usual way to do this would
+// be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but
+// then you don't find out what's wrong until you run the software. This way, if the assertion
+// condition is false, the array size is negative, and the complier complains immediately.
+
+struct DNS_SD_CompileTimeAssertionChecks
+ {
+ char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1];
+ };
+
#ifdef __cplusplus
}
#endif
diff --git a/avahi-compat-libdns_sd/unsupported.c b/avahi-compat-libdns_sd/unsupported.c
index 2df3847..a460b41 100644
--- a/avahi-compat-libdns_sd/unsupported.c
+++ b/avahi-compat-libdns_sd/unsupported.c
@@ -62,7 +62,7 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord (
return kDNSServiceErr_Unsupported;
}
-void DNSSD_API DNSServiceReconfirmRecord (
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord (
AVAHI_GCC_UNUSED DNSServiceFlags flags,
AVAHI_GCC_UNUSED uint32_t interfaceIndex,
AVAHI_GCC_UNUSED const char *fullname,
@@ -73,7 +73,7 @@ void DNSSD_API DNSServiceReconfirmRecord (
AVAHI_WARN_UNSUPPORTED;
- return;
+ return kDNSServiceErr_Unsupported;
}
DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(AVAHI_GCC_UNUSED DNSServiceRef *sdRef) {
--
cgit
From 7c834d2011923511da00eaf4779d0cfefa070162 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 18:45:36 +0000
Subject: fix a gcc warning
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1275 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/cache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/avahi-core/cache.c b/avahi-core/cache.c
index 3a8f849..4ba88b5 100644
--- a/avahi-core/cache.c
+++ b/avahi-core/cache.c
@@ -480,7 +480,7 @@ void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a)
assert(c);
assert(key);
- avahi_cache_walk(c, key, start_poof_callback, a);
+ avahi_cache_walk(c, key, start_poof_callback, (void*) a);
}
void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a) {
--
cgit
From 830178084ad66af4314af888eb661ec1a3920fcc Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 18:46:16 +0000
Subject: update NEWS file
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1276 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/NEWS | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/NEWS b/docs/NEWS
index ce6ab19..9f8d15b 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -21,6 +21,7 @@ Changes:
DNSServiceRegister()
* Implement subtype registration in DNSServiceRegister() in a
way that is compatible with Bonjour.
+ * Update to newer copy of dns_sd.h
* If the host name changes update names of static services wich
contain wildcards.
* Don't build documentation about embedding the Avahi mDNS stack into
--
cgit
From d51f9df5c0ec917e0f223133782d942f152d9aca Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 18:55:50 +0000
Subject: fix build on MacOSX (patch from Sjoerd Simmons, closes #51)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1277 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
common/acx_pthread.m4 | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/common/acx_pthread.m4 b/common/acx_pthread.m4
index 75f6a80..dcf6332 100644
--- a/common/acx_pthread.m4
+++ b/common/acx_pthread.m4
@@ -43,7 +43,7 @@ dnl We are also grateful for the helpful feedback of numerous users.
dnl
dnl @category InstalledPackages
dnl @author Steven G. Johnson
-dnl @version 2005-06-15
+dnl @version 2006-05-29
dnl @license GPLWithACException
dnl
dnl Checks for GCC shared/pthread inconsistency based on work by
@@ -219,19 +219,24 @@ if test "x$acx_pthread_ok" = xyes; then
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
-
- # More AIX lossage: must compile with cc_r
- AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+ else
+ PTHREAD_CC=$CC
+ fi
# The next part tries to detect GCC inconsistency with -shared on some
# architectures and systems. The problem is that in certain
# configurations, when -shared is specified, GCC "forgets" to
# internally use various flags which are still necessary.
- # First, check whether caller wants us to skip -shared checks
- # this is useful
AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies])
- if test x"$GCC" != xyes; then
+ check_inconsistencies=yes
+ case "${host_cpu}-${host_os}" in
+ *-darwin*) check_inconsistencies=no ;;
+ esac
+ if test x"$GCC" != xyes -o "x$check_inconsistencies" != xyes ; then
AC_MSG_RESULT([no])
else
AC_MSG_RESULT([yes])
--
cgit
From 8c74afa380e584685b3cdb25ad2e92ea66f5cecf Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 19:17:08 +0000
Subject: fix python bindings for the service type database when GDBM is used
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1278 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-python/avahi/Makefile.am | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/avahi-python/avahi/Makefile.am b/avahi-python/avahi/Makefile.am
index 20864c3..db604bc 100644
--- a/avahi-python/avahi/Makefile.am
+++ b/avahi-python/avahi/Makefile.am
@@ -32,7 +32,7 @@ ServiceTypeDatabase.py: ServiceTypeDatabase.py.in
-e 's,@DBM\@,gdbm,g' \
-e 's,@FIRST_KEY\@,key = self.db.firstkey(),g' \
-e 's,@CHECK_KEY\@,while key is not None:,g' \
- -e 's,@NEXT_KEY\@,key = self.db.nextkey(),g' \
+ -e 's,@NEXT_KEY\@,key = self.db.nextkey(key),g' \
-e 's,@pkgdatadir\@,$(pkgdatadir),g' $< > $@
chmod +x $@
endif
--
cgit
From 2a0700fde1690a8c367e5fcb6d6296bbaec20bed Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 19:20:54 +0000
Subject: update NEWS file
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1279 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/NEWS | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/NEWS b/docs/NEWS
index 9f8d15b..4f5e4fc 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -35,10 +35,11 @@ Changes:
changed to the new name "foobar2". With 0.6.13 "foobar-2" will be
picked instead. This follows Bonjour's behaviour and has the
advantage not confusing people with regular host names ending in
- numbers.
+ digits.
* Don't disable all static services when SIGHUP is recieved.
* Fix build when Avahi is configured without Gtk+ but with Python
support
+ * Fix build on MacOS X
* Support using Solaris DBM instead of gdbm for the service type
database. The latter is still recommended
* Minor other fixes and documentation updates
--
cgit
From 36abf208ca92f341b2c4fd4854089434c5f2520e Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 19:22:57 +0000
Subject: add some Avahi artwork to SVN. (This is not shipped in the tarball,
just needed a safe place)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1280 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/avahi-favicon.png | Bin 0 -> 684 bytes
docs/avahi-logo.png | Bin 0 -> 8417 bytes
docs/avahi-trac.png | Bin 0 -> 11406 bytes
docs/mdns-paket.dia | 1243 +++++++++++++++++++++++++++++++
docs/multicast.dia | 651 +++++++++++++++++
docs/utilities-avahi1.svg | 1745 ++++++++++++++++++++++++++++++++++++++++++++
docs/utilities-avahi2.svg | 1627 +++++++++++++++++++++++++++++++++++++++++
docs/zeroconf-stack-de.dia | 563 ++++++++++++++
8 files changed, 5829 insertions(+)
create mode 100644 docs/avahi-favicon.png
create mode 100644 docs/avahi-logo.png
create mode 100644 docs/avahi-trac.png
create mode 100644 docs/mdns-paket.dia
create mode 100644 docs/multicast.dia
create mode 100644 docs/utilities-avahi1.svg
create mode 100644 docs/utilities-avahi2.svg
create mode 100644 docs/zeroconf-stack-de.dia
diff --git a/docs/avahi-favicon.png b/docs/avahi-favicon.png
new file mode 100644
index 0000000..c39fb1b
Binary files /dev/null and b/docs/avahi-favicon.png differ
diff --git a/docs/avahi-logo.png b/docs/avahi-logo.png
new file mode 100644
index 0000000..fa17260
Binary files /dev/null and b/docs/avahi-logo.png differ
diff --git a/docs/avahi-trac.png b/docs/avahi-trac.png
new file mode 100644
index 0000000..1ff44c7
Binary files /dev/null and b/docs/avahi-trac.png differ
diff --git a/docs/mdns-paket.dia b/docs/mdns-paket.dia
new file mode 100644
index 0000000..db3cf81
--- /dev/null
+++ b/docs/mdns-paket.dia
@@ -0,0 +1,1243 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ #A4#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ID#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Flags#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #QDCOUNT - Anzahl der RR-Queries#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ANCOUNT - Anzahl der bereits bekannten Antwort-RRs#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #NSCOUNT - Anzahl der zu registrierenden RRs#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ARCOUNT = 0#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #...#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #16bits#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # #
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #RR-Queries#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Bereits bekannte Antwort-RRs#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #...#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ID#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Flags#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #QDCOUNT = 0#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ANCOUNT - Anzahl der Antwort-RRs#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #NSCOUNT = 0#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ARCOUNT = 0#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #...#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #16bits#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # #
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Antwort-RRs#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Zu registrierende RRs#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #...#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Query-Pakete#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Antwort-Pakete#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/multicast.dia b/docs/multicast.dia
new file mode 100644
index 0000000..7a1c5be
--- /dev/null
+++ b/docs/multicast.dia
@@ -0,0 +1,651 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ #A4#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Lokale
+mDNS/DNS-SD-Multicast-Gruppe
+224.0.0.251#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Gesamtes Netzwerk#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/utilities-avahi1.svg b/docs/utilities-avahi1.svg
new file mode 100644
index 0000000..c39457a
--- /dev/null
+++ b/docs/utilities-avahi1.svg
@@ -0,0 +1,1745 @@
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+ Adobe Illustrator CS2
+ 2005-11-18T17:59:54+01:00
+ 2005-11-18T17:59:54+01:00
+ 2005-11-18T17:59:54+01:00
+
+
+
+ 224
+ 256
+ JPEG
+ /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
+AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
+DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
+Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAADgAwER
+AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
+AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
+UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
+1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
+qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
+obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
+0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FVK7u7Wztp
+Lq7mS3toVLzTysERFHUszUAGAkDcpAt4d56/5ybsrSSSy8o2q3si1U6lchlhr/xXEOLv82K/IjNf
+m14G0XMx6QneTx7W/wA2fzF1mRmvNeukRj/c2zm2jp2HGHgD9NcwJ6nJLmXKjhgOiQDX9dEnqDUr
+oSUpz9aStBvSvLK+OXe2cI7mT+Xfzm/MfQpVaDWJryFftW18TcxkDt+8JdR/qMMuhqskerVLBCXR
+6Nc/85WXx0+NbXy/Euo8f30ss7NCGHdY1VWofd9vfMo9omthu0DRi+bDdW/5yG/M+/ZvSv4dPjbr
+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
+FXYq/wD/2Q==
+
+
+
+
+
+ uuid:0A41642B59EE11DA9346CE657E5F1B06
+ uuid:0A41642C59EE11DA9346CE657E5F1B06
+
+ uuid:c939d50e-5853-11da-9437-000a95dac8e4
+ uuid:FDEFD071588811DA91E5E11227A4C4DF
+
+
+ image/svg+xmlAVAHIInkscapeizo@aucuneid.netavahi tango freedesktop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
\ No newline at end of file
diff --git a/docs/utilities-avahi2.svg b/docs/utilities-avahi2.svg
new file mode 100644
index 0000000..4397ea5
--- /dev/null
+++ b/docs/utilities-avahi2.svg
@@ -0,0 +1,1627 @@
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+ Adobe Illustrator CS2
+ 2005-11-18T17:57:36+01:00
+ 2005-11-18T17:59:06+01:00
+ 2005-11-18T17:59:06+01:00
+
+
+
+ 256
+ 192
+ JPEG
+ /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
+AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
+DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
+Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAwAEAAwER
+AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
+AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
+UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
+1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
+qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
+obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
+0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYqlHmbzd5c8sW
+Bvdcvo7OHf01Y1kkI/ZjjWrufkMhkyRgLJZwgZGg8R81/wDOUkxd4PK2lqqCoW+v6kn3WGMintVz
+8s12TtD+aHMho/5xeZ6v+cv5mao5M2v3MCnoloRagewMIRvvOYktVkPVyI4IDokEnmrzRI6vJrF8
+7r9lmuZiRTfYlsr8SXeWfBHuTLTfzO/MLTXD2nmG+FDUJLM86f8AASl0/DJx1ExyJYnDA9HpHlP/
+AJyf161kSHzNZR6jb7Brq2AhuB4sU/un+QC/PMrH2hIfULceejB+l6jdf85AflfBp8d2NTeZ5V5L
+aRQyGYeKsCAqn5t8szDrMYF2440075MI1n/nKu0Usmi6DJIP2ZryZY/viiEn/E8x5dojoG6Oj7yw
+zU/+clPzJu+X1ZrPTwahfQg5keG87TCv0Zjy12Q9wbRpIBjN9+b/AOZt7X1vMV2la19Bhb9fD0RH
+TrlR1OQ9S2DBAdEmufOHm26NbnW7+c1rWS6mffpX4mOVnLI9SzEI9yAk1C/k5CS5lcPXnydjWvWt
+TkeIsqCHwKqxXVzCCsUzxqTUhGKiv0YbWkbb+ZPMVvT6vql5DxFF9OeVaDwFGGEZJDqWJgO5NrP8
+0fzFtCDD5k1A06CW4kmH3Slxlg1GQfxFicMD0DItO/5yH/NGzI9XUIb5R0W5t4v1xCJvxy2OtyDr
+bWdLAsv0j/nKvVEKrrGhQTA/aks5Xhp7hJBNX/gsuj2ieoapaMdCz7QP+cify31RljubibSZm2C3
+kR4V/wCMkRkUD3amZUNbjPk0S0sx5sr1j8xfJWk6INaudXtnsHqIHgkWYysP2I1jLFm8fDvl0s8I
+iydmuOKRNU8M82f85P69dSPD5Zso9Ot9wt1cgTXB8GCf3SfIhvnmuydoSP0inMhowPqeb6l+Z35h
+ak5e78w3xqalIpngT/gIiifhmLLUTPMlyBhgOiWx+avNEbs8esXyO32mW5mBNd9yGyHiS7yy4I9y
+f6R+cv5maW4MOv3M6jql2RdA+xMwdvuOWR1WQdWEsED0emeVP+cpJg6QeadLVkNA19YVBHu0MhNf
+ejj5Zl4+0P5wceej/ml7d5Z83eXPM9gL3Q76O8h29RVNJIyf2ZI2o6H5jNjjyRmLBcOcDE0U3ybB
+2KuxV2KuxV5T+bX546f5SMmkaOEvvMNKSct4bao2MlKcn8EB+fgcLU6sQ2H1OTg05lueT5g1zX9Y
+13UZdS1e7kvL2U/FLIa0HZVHRVHZRsM1E5mRsuyjERFBAKrMQqgknoBucgSyRkWkXripURj/ACzT
+8BU5VLPENowyK+TSGjQvJMqgd6GmRGezsEnDXMoFgoYhTyHj0y8NJW4UOxVtVZjRQST2GC0ouHR9
+Tm/u7Zz8xx/XTKpZ4DmWQgUbH5U1h+qKn+s39AcqOsgO9PhFEJ5Mvz9qVF+W/wDTIHXR7mXheaoP
+JU/e4HuOP/N2R/P+S+F5tnyY4/3f/wAL/bg/P+SfCHesbydL2uB/wP8Abj/KA7keF5qL+U7sfZlU
+/RT+OTGuj3L4SHk8t6kvQK3yOWDWQLHwyhpdJ1GP7UDU8Rv+rLI54HqgwKFZHQ0dSp8CKZaCCxpb
+hQ7FXYqibaye4+y6inUE7/dlc8nC2Qx8SJ/QspG0i1+Ryr8yO5s8A96lLpF6gqFEg/yDX8DTJxzx
+LE4ZBBsrKSrAgjqDsctBakfoev6xoWoxalpF3JZ3sR+GWM0qO6sOjKe6nY5OEzE2GMoiQovp/wDK
+X88dP82mPSNYCWPmGlI+O0NzQbmOteL+KE/LwG302rE9j9Trc+nMdxyerZmuM7FXYq8n/PH83T5T
+sxomjSKfMN4nJ5BQ/VYW25n/AIsb9gdup7VwtXqeAUPqcrT4OLc8nyrLLLNK80ztJLIxeSRyWZmY
+1JJO5JOaYl2SL07SZ7w8vsQjrIe/sMozZxD3t2PCZe5kNvYW9snGJKHux3Y/M5r55TLm5scYjyQm
+o6nDbVRfjm/l7D55diwmW/RqyZRH3pBPczTvylap7DsPkMz4wEeThykTzUwCTQbk9BkmKbWHljUr
+qjOvoRn9qTr9C9fvzFyauEeW7YMZKf2fk/TogDMWnfvU8V+4Zhz1kzy2bBjATm30+0gFIYUjH+So
+GY8pE8zbMBEiPIppv08Vd6eKtGPFaWmPFVNo8FKotHkaVSdMIKFFlyQKFCWCNxR1DDwIrlkZEKgJ
+9E0+XcJ6beKGn4dMvjqZhgYhK7ry7cpVoGEo/lPwt/TMqGrieezA40rkikjcpIpRh1VhQ5lCQPJg
+QtVmVgykhh0IxItQaTaw1ZSRHc7HtJ2+nMXLg6xcnHm6FOlFRUdMwi5S24sLe5TjKlT2YbMPkcMM
+pjyYyxiXNj2o6VPZnl9uE9HHb2ObDFnE/e4eTCY+5CRSywypNC7RyxsHjkQlWVlNQQRuCDl4LS+q
+vyO/N3/FlkdF1mQDzDZpyWQ0X61Cu3Mf8WL+2O/Ud6bnSanjFH6nW6jBw7jk9YzNcVIvO/myy8qe
+WL7XLv4hbJ+5hrQyzN8McY/1mO/gKnK8uQQiSWeOHFKnxJrOr6hrOq3WqahKZr28kaWeQ92Y9AOw
+HQDsM5+UjI2XcRiAKCpo+ltezcnqLeP7Z8T/ACjMXUZ+AebkYcXGfJlSxJGgRFCoooFHQDNUZE7l
+2AFJRrOrC3rBCazn7Tfyg/xzL0+Di3PJxs2bh2HNjhJJJJqT1JzZOEjdM0e71B6RjjED8crdB8vE
+5Rm1EcY35pjEll1ho+m6bEZTxDKKvcSkCn0nZc1k8uTKa+wN9CItBXvnzQrUlIS924/32KLX/Wan
+4VzY4OxM095VH3uJk7Qxx5bpY/5mvy/d6cAv+VKSfwXM8ez46z+z9rjHtM9I/avh/NCjUm074fFJ
+dx9BXBL2f7p/Z+1I7T74/an2neffLt4QhdreU9EmAUV/1gSv45rdR2Tnxi64h5fq5uXi1uOfWven
+YuwQCqVB3BrmpM3O4WjdSdlH44OMrwLGuJfAfdjxJ4VjXE3t92NrwqTXM3t92EI4VNrqXuq/jk0c
+Km10e6fjjwI4VNrqPuCMIgUUld/5m0a0qrzepIOscY5H+g+k5sNP2Zny7gUO8uJl1eOGxO/kkk3n
+lOR9G0JHYu9PwAP682kOwT/FP5BxJdpd0VMeepK72YI9pCP+NcmewR/P+z9rH+Uj/N+1EDzTot6v
+p3sLRV6MRyA+RX4vwzHn2Pmx7wIl9n7Ptbo6+EvqFKV1pa+l9ZspBc23ipBK/OmUDKQeGY4ZOQKI
+uJsJfl7FNNJ1UwMIZzWE7Kx/Z/szFz4OLcc3Iw5q2PJkqgEAjcHoc1xc4LmiSRCjqGRhRlPQ4BIj
+cJItiusaW1lMClTbyfYPgf5Tm00+fjHm6/Ni4T5KejaxqGjara6rp0phvbORZYJB2K9j4qRsR3G2
+ZUZGJsOPKIIovtvyR5ssvNflix1y0+EXKfvoa1MUy/DJGf8AVYbeIoc6DFkE4gh0+SHDKnhf/OUP
+mxrjWNP8sQP+5sU+t3ig7GaUUjB90jqf9lmu7QyWRFzdHDYyeHRRvLIsaCruQqj3O2a0mhZc0CzT
+ObKzjtLVIE/ZHxHxJ6nNHkyGcrLtoQERSE1jUFsrao3mk2iH6z9GW6fFxy8mvNk4R5sPZmZizGrM
+aknqSc24FOtJTTRNFe+k9SSq2qH4j3Y+AzG1OoEBQ+pnCFsj1TVtP0OxVmUVpxgt02LEfqHicxNH
+o56me3LqVz544o2Xn2paxq2t3So5aQs1ILWIEip6BVFST+OdjptJi08fT8SXR5c88p3+TNPLP5G+
+aNVRZr9102FtwjD1Jqe6Aqq/S1fbNfqO3cUTUBxn5D5uRj7Pkd5Hh+9ndl/zjloAQfWby6lem9Gj
+RfuCE/8ADZr5duZzyEB8y5A0WIcySsv/APnG7R2Q/UtRuYZO3q8JVH0BYz/w2Th25mH1RifmP1sT
+osZ5Eh555q/JnzfoKvPFENSs13MtuDzAHdotz/wNc2mm7XxZNpeg+fL5/rpxsuinHceofjox3QPN
+WoaRIqVM1nX47dj0H+Qf2Tktf2Xj1AvlPv8A196NNrJ4j3x7np+mX9nqdml3aPzjbqP2lburDsRn
+D6nTzwzMJjd6LDljkjxRRJjyi21Y0eSBQpMmSBQoumTBVCXUkUETzTMEiQVZz0Ay7HCU5CMRZLCc
+hEWeTAtb8z3N67RWxMNr022Zx7nt8s6/Q9lQxDin6p/YHQ6nXSntHaKY+U/yu82eZVWe1t/q9i3S
+7uKohH+QKcm+YFPfLtV2niwmvql3BpxaWc9+Q83pemf843WAQHUdTnlfwgVIh/wwlzUZO3Mp+mMR
+77P6nMjocY5klHXH/OOXlxk/c3d3G9NjzjIr7gx/xykdt6gdIH5/rZfk8R6yYb5i/IDzBYo8ul3K
+Xqrv6Mg9J/krVZCfmVzNwdvwO2SJj58w0z7PP8Bv7HnTDWNCv3ikSSzu4zSSGQFaj3U9Rm3nDFqI
+dJRLhxlPFLuKbRz22qRNNAoivEHKe2HRh3eP+IzR59NLAaO8DyP6C7XDnGQd0kNkG1kHl7UuVLOU
+7j+5Y+H8v9MwNXh/iDm6bL/CWQAZr3MUryzju7V4H/aHwnwPY5LHkMJWGM4cQpg0sbxSNG4o6Eqw
+9xtm8BsWHUkUae5f84vebGt9Y1DyxO/7m+T63ZqTsJohSQD3eOh/2ObLs/JRMXC1kNhJ5h+ZWsNr
+Hn7XtQJJWS8lSInr6ULelH/wiDMPPLimT5uRijUQEF5XthLqJkI2hUsPmdh+vNdrZ1Cu9zdLG5X3
+MsagBJ2A3JzVB2LB9Vvje3jy/wC6x8MY8FH9eubvBi4I06rLk4pW1ptg97drCuy9ZG8FHXDmyiEb
+YRjZZsz2mnWDStSO3t0rT2Hb5nNPCEs2QRG8pFunIQjZ5B5nd3Oo69q68UaW5uXEVvAu9Kmiov35
+3eDDDTYq5RjzP6Xnsk5ZZ31L6G/LL8rrHy/bLc3KrcatKP31xSoSv7Edei+J6t+Gcf2j2nLUSobY
+x07/ADLuMGnGEf0nqNrZqqgAUHhmvEkSmlOpefPIekzNBf65ZQzoeMkPqq8ikdmVOTL9OZuPR5pi
+4xNOPLNEdUTo/m3yhrjiLSdXtLycgkQRyoZaDqfTJD0+jI5dNlx7yiQFjlB5FH3FopBFMo4m6M3i
+/wCbn5QwahDNrehwiLU4wZLm2QUW4A3JAH+7P+JfPN32Z2oYEQmfR08v2fc1anTCY4o/V97xnyn5
+hk0bU1MhP1OYhLqPwHZwPFc3HamgGpxUPrH0/q+LiaPUnFP+ieb1/irKGUgqRUEbgjPPNxsXqFNo
+8kCqi6ZMFCi6ZMFDzjznrhu7w2MDf6NbmjkdHkHX6F6Z2fY2h8OHiS+qX2D9rz/aGp45cI+kfez3
+8oPynh1COLX9dh527ENYWTj4XH+/ZB3X+Ud+vTKe1e0zE+HA+8/oTpdKK45fB9B2diiIqqoVQKAD
+YAZzXG5k5qera/5b0RVOr6na2HIVRbiVI2Yf5Kk8m+gZbiw5Mn0xJ9zjyyAcylln+ZP5dXsvpW/m
+Cy9TsJJRFUnsDJwrl09DniLMJMRnieqfy28ciBloyMKqw3BB8MwSW+M2B+f/AMutI8yWDRTxiO5Q
+H6tdKPjjb28V8V/jvl+j109PPih9PWPe2TxxyxqXPvfMeraXqvlvXJLO4HpXlo9VdfssvVXXxVh/
+bncYsmPU4rG8ZB00oyxTrqEyn9K5to9QgXikp4zRjoko6j5HqM0EsZxTOM9OXmHb48gnESCHjd43
+V0PF1IKkdiMSLFFmDTONOulu7SOcbFh8Q8GGxGaTLDgkQ7bHPijaLAypmxLzPbCLURIBtMoY/MbH
+9WbXRTuFdzrtVGpX3o38tdYbR/P2g6gCQsd5EkpHX0pm9KT/AIRzmxwS4Zg+bhZY3Ehj08zTTSTP
+TnIxdqdKsanKiWxkPk9Rxum71Qf8SzW9oHk52j6o7zHd/V9OZVNHnPpj5H7X4ZRpMfFP3N2pnUfe
+wzNy6tmHlqx9CyEzD95P8R/1f2R/HNRrMvFKugcjGKCW/mDftHaW9ihp6zGSX/VT7I+kn8M2vs/g
+uUsh6bB1/aWSgI97IPyG8rx3N5da9OnIWx+r2lezstZG+YVgv0nLfaHV8MRiHXc+7p+PJh2di5zP
+TYPoawiVUBOwG5PQZx4nbmZC8S88/mFqfmy8nsNKuHtfK8LGINCSkl6VNGdnFCIj+yo69T4Ds9D2
+fDTwE5i8h/2P7XVzmZmhyYidItoYuMUSoKfsgDLcuvIbYaZjGoWg+smSOqshHF12NQeoIwY9dL4J
+npXsX5Nfm/qM2oQ+VvM1wbn6x+70zUZTWT1O0MzH7fL9hjvXbeu2D2joYSgcuIVXMfpDCEzE0Xst
+9CCpzneNz8ZfLP50eV49F81tdW6cLTVAZ1UdFlBpKB9NG+nO37F1Xi4aPOO36nW63FwzsciyD8vt
+Ra/8txK55S2jG3YnrRQCn/CkD6M5bt7T+FqSRyn6v1u67Oy8eIf0dmQumacFzlB0yYKEn8w3v1DS
+Lq6GzohEZ/y2+FfxOZ/Z+Dxc0YdCfs5lx9Tk4MZkwDyD5cHmLzXZ6fKC1sWM12d/7qP4mFR/N9n6
+c7rX6jwcJkOfR5vT4uOYD630q2jjjREUIigBVUUAA2AAGefSyWbd3kLAvzl/Ni48sqmgaCyjXLhB
+JcXVA31aJvs0Ugj1H7V6DfuM3XZegGQeJk+gch3/ALHXZspuhzfPoS5u7xry9le5uZW5zTysXdie
+pZmqSc3OTW8O0RQY49NfNk1rpsEkQDorqR0IBGUw7QJLZLTMj8qeb9c8kzq9q8l3oNR9c0liWCpW
+rSW3I/A43PGvFu/bJajTY9VHuydD+to3xnye/R3dlqWnwX9lIJrS6jWaCVejI4qp336HOMyiUJGM
+tiHYYpXu8Y/PryrHc6OmuQp/pNgwSZh1MEjU/wCFcgj5nN77O6zhyHGfpn94/Yw1+LihxdY/c8j8
+qSrLJcadIf3dynJfZ06EfR+rN32zjqMcg5xP2FxtBP1GPe6SNo5GjcUZCVYe4zXg2Ldgn3lS7pLL
+asdnHNPmNj+GYOuhsJOXpJ7kMnAzWOex3zgo42rd6uP+I5sOzzzcLWdGPQTNDNHMlOcbB1r0qpqM
+2QLgph5o06TTfMuq6fICHtLyeEg/5EjL/DJ5I1IjzYwNgFF+UJwt1NATT1UDD3KH/m7Nbr43EHuc
+7Ry3Ia823HK9igB2iSpHux/oBh0EaiT3o1cvUAk1pAZ7mKEf7sYLX2J3OZc5cMSXFAsvQYUVVCqK
+KBQD2GaCRcpgPn6Uvrip2jhRR9JZv452PYUK0998i6PtE3k+D3L8lbNIPIlgyj4p2lkc+JMrAf8A
+CgZzXb0ydRLyofY7DRisQZD+Zuo3Gn/lvrc9seMzwpbhh1AuZUgYj/YyHNf2JEZNVCJ77+QJ/Q16
+s1AvHdOto4bdI0FERQqj2G2djrchtxtPBvUj6dq7DrSg+k0zQylcwHZ44sZkjBGZILOUUovOcEiz
+QsY5YmDxuuxVlNQR8jmz0U/VR5F1eqhs+xNP1H9JaFYaiV4m9tobgr4erGHp38c4vUjgySj/ADSR
+8nIwmwC8d/5yFs0fy9YXhpzgu/TB70lRif8Ak3nR+zOT1yj3xv5H9rX2jH0A+bCvyhkJGqQk7D0X
+Udv2wf4ZL2qj/dy/rD7mzsc/UPd+l6C6ZyILukttr+1vHuUgJJtJjBLX+dQCae3xZl5dPLGImX8c
+eIe5phlErroaYv8AmS7JoKKOklwit8uLN/xrm79nReoPlE/eHB7UNYvijP8AnHy0RtW1S7P244Ui
+X5O/I/8AEBm19opkYojvP4+91/Zo9RPk+iNPpQe2cPxufkfIvmXUJ9W84axqFwxaSe8mI5GvFFcq
+ifJUAUZ3sx4eGEB0iHW4RxSJVYIgFGauUnbY4sh0Y8oaH9k0+jrmHkNTZTimE0YKHNppchtwc0Ho
+35H30j+Ur7TnYlNM1CaG2HZYZFWYL9DSNmm9o4iOYSH8UR8+TXpeRHcU1892a3flbV4GFfUtJgPn
+wND9BzX9mZKywP8ASH3ufMXAjyL5Y8vSGPWrRgaVfj/wQK/xz0TtGN4J+50mlNZIp95gt/TvvUHS
+VQ30jY5zellca7ndTG6G0u4+r6hby1oFcBj/AJJ2P4HLM0eKBCcUqkCz8DNC7hivm6cNdQQg19JC
+x9i5/wCbc2mgjUSe91+sluAhPK+nSal5l0rT4wS93eQQgD/LkVf45sscbkB5uDM0CXo3/OSPlSTS
+vPA1iNKWWtxiXkBsLiIBJV+kcX+nMrXY+Gd97RpZ3Gu55XaXMlrcx3Ef2ozWniO4+kZr8kBKJBcu
+EjE2FXVbtby/luFqFenEHqAFAyODHwQAZZZ8UiUV5bi56orf77Vm/wCNf+Nsq1kqh70Y+bNIxmmL
+kMA/MGHhrUcg6SQKa+4Zh/TOx7CneAjuk6TtGNZL7w9r/JDUEufI1rEDV7SWWGT58zIP+FkGc77Q
+Yqzy86P2V+hztFK8Q8mVeftGn1vyPq+mW4LXEsPqQINy0kDrMiD/AFmjAzQdm6oafVwnL6b39x2v
+4Wuox8UCA8W0m9jubSKZDs4BI8D3H0HO/wBbiNuHp5quoAS27p4jb5jcZoMkTGVuyhJjEsgAIPXM
+mIbJSS36rc6lfW+n2i+pdXciwQJ0q8hCjfsN82OmMYA5JbRiLPwdXqZXsOZfXdvbQ2GmWmnwHlDa
+QxwRn/JiQIPwGefZNR4szL+cb+bmYoUKeO/85C6giaNp1hy+OedpuPtEvH/mZnZ+zGLeUu4V8/7H
+G7Rl6QGK/k7an0NUuSNmaGNT7qHZv+JDB7Uz3xx95+5u7HjtI+5n8oABJ2A6k5yQ5u6Lzr8udQNz
+e6yGNTPILlf9kzcv1jOt9odPwQxV/COH7qdL2Zl4pT890X+ZEDSeXuYFRDOjt7Agp/xvmN7OzA1F
+d8SPuP6G7tSN4vcVT/nH6/jj1vULJiA08AkX3MbAU+583XtDjvED3H73W9nS9ZHk+hbOXjTwzzzK
+eEuzmLfKfnPSJdD866vYSiii5eW3b+aGZvUjNf8AVbf3z0DFmGo00MkTe2/vGxdXD0TIKy3lUqMw
+ZRdrjmyPSQEhFerHkcwpjimylJG3M6JEzsQFUEsT0AHXNrpMRtwM03pX5N6dNaeTXvZl4SaxdS3y
+KRRhCwWOKvzWPkPnnMe0eqGTU8ET9AEfjzP318E6SHpvvRv5h6hHY+T9YuHbjS1lRD0+ORSif8Mw
+x7IxGWaA/pD7N3Kyy4ccj5Pl7y3EZNbtQP2WLH5KpOeg9pz4dPL3fe6XSC8gZX5ljrbwyd1cr/wQ
+r/xrnK6M7kO8yMezYNTPm1CCDTkvJmorRqwHdiy1AHzzQjEZT4R3u4OQCPEWD3dzJdXMlxJ9qQ1p
+4DsPoGbvHARiAHUzkZGy9U/5xu8qSar54OsSJWy0SMy8iNjcSgpEv0Dk/wBGbDQ4+Kd9ziaqdRrv
+fQn5j+RrLzp5XuNInIiuR+9sLkivpTqDxb/VNeLex8c2efCMkacDFkMJW+MNb0TVND1W40vVLdra
++tW4SxN+BB7qw3BHUZoZwMTR5u3jIEWEBkUp75UX/Spm8EA+8/2Zg64+kNuLmy2PNUW9i3n2wae1
+W6QVa1Pxf6jgV+40zfdhagQyGB/i+8OB2ji4oCQ6Iv8AJTzXHpetSaTcvwt9S4+gx2CzrsB/sxt8
+6Zm+0OiOXFxx+qH3fscTs7MIy4Tyl976EgnO1TXPK9XiIO7uSHnHnT8ttQivp9a8sRiZLljLfaTy
+Cn1Du0tuT8NW6sh+jwzq+xPaSAgMOp2A2jPy7pfr+fe6zPpSDxQ+Tzy+1YWrmC+jlsrgfaguY3ic
+fQ4GdUNNDMOLGROPeDbQNQY7HZJ4bLVtbvTDotnNfOxHL0EZlUnuz/ZUe5OVZceLTi8s4wHmfuHM
+/Bn+YMtoi3s35ZflYPLjjWdaKTa2ykQRIeUdsrCjUP7UhBoT0HQeOcT2528NQPBw7Yep6y/Z3D4l
+yMGnIPFL6vuZ1c3HEFi3EDcmtM02jxEy2c8B8wfmZ5qHmPzRPcQvzsbUfV7M9mRCav8A7NiT8qZ7
+B2Rozp8AifqO5/Hk8/q83iTscg9N8iaK2k+V7WGVeNxPW4nU9Q0lKA+4UAHOL7Z1QzaiRHKPpHw/
+a9BoMPh4gDzO6v5kufquiX89aMkEhU/5RUhfxzE7PxceohH+kG3Uz4ccj5PL/wAuZ/R8w+melxC8
+YHuKP/xpna+0OPi01/zZA/o/S6HsudZa7x+16BrVguoaZc2bbeshVSezdVP0NnH6POcOWM+4/wBr
+vc+PjgY97yfy3rN15c8x2uoBWElnLxni6EoapIn/AAJOeh6nDHUYTG9pDY/cXl8UzjmD3PqjStTt
+7+zgvLaUS286CSKQHYqwqM8k7S0s4yIlzD0gIkLHIpD+Yv5d2nnCzjmgkW11m0Ui1uWHwuvX0pab
+8a7g9sl2J23LRSMZDixS5ju8x5/e4ep03HuPqDwrVPL/AJj8vXPpazYTWyKaevxLQsP8mVaofvzv
+sGTT6oXhmJeXX5c3C8WUNpBH2Gs28jLDCWnnbZIYVaR2PgFUE5b+RGMXKogdTsp1N8mdeWvy31nX
+Zo7jX4X03REIZrKQ8bm5puFdRvEnjX4j7dc0PantJiwRMNOePL/O/hj7v5x+z7mePTymbltF607p
+EixxARxoAqIooFUCgAA6DOCxRlKV3v3u0jF4v+e3m1DDD5ct5OUrss99Q/ZUbxof9Y/F9A8c9J9l
+9AReaXuj+k/o+bru0cwA4B8WA+SbEmSa+YfCB6UZ9zux/Vmy7dz7DGPef0NfZ2Pcy+Cd6+tdOY/y
+sp/Gn8c0mlPrdlPkxfNm0q093cTrGkrlkiUJGvYACmQjjEbrqylMnn0ROiaJqmuarb6Xpdu1zfXT
+cIol/Ek9lUbknoMthAyNDmwlIAWX2f8Alx5GsvJfle30iAiW5P72/uQKerOwHJv9UU4r7DxzfYMI
+xxp1GXIZytk+XNbDPzI/Kvy955slF3W11SBStpqUQBdAd+Drt6iV34n6CKnMfPp45BvzbsWYw9z4
+11GylsdQubGX+9tZXgkp/NGxU9fcZopCjTtQbFpv5TI9W4Hfiv6zmv13IN2NlUZzVluQ9zGkjSxy
+KGRxxZT0IIoRlsJGNEcwyoEUXm2u6NPpN9xFfRY8reUdaDtXxXO30GtjqMd/xDmHnNVpzilXTo9d
+/Lf827a8hi0rXphDfqAkN45pHN2HMn7L/gfnnL9tez53niFx7u79jstJrhIcM+fe9YiudhQ7Zwmb
+RkHZ2BCIW6zDOE9zHhc11iMJPReFCXd9FDE800ixxRgs8jkKqgdSSdgMztPoZTIFJoB4h+Z35sJq
+EUui6DITaPVLy+FR6g7xx/5J7t3+XX0XsTsHwayZR6ug7vM+bqdZrbHDDl1LHPy48nPqt+mpXiU0
+y1bkAw2lkXcKK9VH7X3Zm9t9pjBDgif3kvsHf+pj2fpPElxH6R9r2OV84El6Rhv5kXno+XWhB+K6
+kSOneinmf+I5vPZzDx6ni/mgn9H6XXdqZKxV3l5voNx9T1mzuSaKkq8z/kseLfgc7TX4fEwTj3xP
+z6Oh00+DJE+b1yU55sHrHnnn3y6wkbVrVaq3+9aDsegf+udd2F2iK8Gf+b+r9TpO0tLv4kfj+tEf
+lp+ZkvlyQadqJaXRpGqCoq8DN1ZR3U/tL9I8DldsdjjUjijtk+9xtHrPD9Mvp+59AaXrFjqFrHd2
+U6XFvIKpLGQyn7u/tnmus7NnjkQRRd3EiQsbhMkus1UsEh0QYrjdZDwj3I4VGS598yMelkWQi89/
+MD81NM8vxSWdky3msEFRCDVIj/NKR4fy9flnYdjez0stSl6cf3+79bianWRx7DeTwL/chrGpPLK7
+TXVw5knmffcndjnfZMkNPj7ojkHTQhLLPzL0KxtIrS0it4hRIxQeJPUk/M5xOfNLJMylzLv8eMQi
+IhDa9/xzJPmv/Ehk9L9YTPkxXNo0Mx8k/lR5z83yxtp9k0Onsfi1O5BjtwOh4sRWQ+yA+9Mvxaec
++Q2asmaMeb6g/Lf8q/L/AJGsmFoDdarOoW81KQAOw68EXfgld6Dr3J2zb4NPHGNubrsuYz9zNMyG
+l2KuxV8NfmDbG289+YoDX4NSuwtaVKmdip28RnO5hUz7y7nEbiPcpeVXpeyp/NHX7iP65rtaPSPe
+34+bLIzmpLepT7TH3AyceTMKV3Y2l/bNb3SB42+8HsQexyzDnnilxQNFjkxRnGpMF1ryfqNgWlgU
+3Vp1DqKuo/ylH6xnW6LtjHm2l6Z/Z8C6LUaCePcbxVvL/wCYvm7QlSKzvWktU2FrcD1YwB2FfiUf
+6pGXarsrT595R37xsfx72rFq8kNgdmX2/wCf+sqlLjS4JH7tHI8Y+4+p+vNPP2Vwk7SP2fscsdpy
+6xCy8/P3X5EItNOtoGIpykZ5ae4AMeSx+y2AH1SkfkP1ol2nLoAwnXfN/mjzHIseo3klwpP7u1Qc
+Y69qRoACfc75utNocGnFwiI+f7XDyZ8mTmbT7yt+Wd7dulzrINraChFt0lf2P8g/HNP2j7Q48YMc
+Xql39B+v7nO0vZkpbz2H2vVbaK3tbdLe2jWKCIcY40FFA9hnFZM0pyMpGyXfxiIihsHPJlbJ5f8A
+mBqyX2qJaxNyhswVYjoZG+191AM732e0RxYeOX1T+7o852nnE58I5R+9i3p5v3WvTdB1VdQ0uKQm
+s0YEc478lFK/7Lrnn3aWjODMR/Cdx7v2cnp9Jn8TGD1HNFS0YEEVB2IPSmYkdnILBfMHkr42udLA
+od2tTtT/AFCf1HOo7P7b24c3+m/X+t0+q7O/ih8v1JFpWveYvL10zWF1NZSg/vIv2WI/njaqt9Iz
+eZdPh1EfUBMfjq62GSeI7WCzfT/z58ywKFvLO2uqftrzic/OhZfuXNLm9mMEvpMo/a5ke05jmAUX
+L/zkDqpQiLSYVfszSsw+4Kv68pj7KYr3kfkyPah/msX1381vOesI0TXYs7d9mhtAY6jwLktJ/wAN
+m00vYmmw7iPEfPf9jjZdbkn1r3MesdIvb1gwUpEesr9Po8cydTrseLbnLu/HJjh0s8nkO9lWn6dB
+ZxiKIVLEc3PVjnNanVSyyuTu8OCOMUE+zWtiWeYWpp9P5nUfrP8ADMnSD1sJ8mMZs2l9wflnam2/
+Lzy3ERxb9HWzstKULxK5qPGrb50OAVCPudPlPrPvZLlrW7FXYq7FXxz+e+mmx/NLWgBRLlorlD4+
+rChb/h+WaLWRrIXbaY3AMR8vy+nqkVejhlP0io/EZrtVG4FyYHdmiHNMXIC25+0reIphgyDUbYkM
+0TG2VkJQ155f0S/Ja6tEZz1kWqP9LLQnMjD2hnxbQka+Y+1pyaXHP6glzflz5ekaoe4jHgjrT/hl
+bM4e0OoHSJ+B/W4x7LxHvV7b8t/LcbAv68w8HkAH/CKmVz9otSeXCPh+slMey8Q7yyHTNF0fTf8A
+eG0jhbp6gFXp/rmrfjmp1OtzZvrkT93y5OZi0+PH9IATIS5h03tmXGlY75u8xjTLP0YG/wBOnBEd
+OqL3c/wzd9jdmePk4pf3cefn5frcDX6vw40PqLzVVJNTuT1Od880uMe2Ko3RdVk0y8EoqYH+GdB3
+XxHuMwO0dENRjr+Icj+O9ydLqDinfTqzsTxyxrJGwaNwGVh0IOcNKBiSDsQ9KJAiwoyNkgFS+9tL
+S6XjcQpKB05AEj5HtmThzTxm4khqyY4z+oWklx5V0diSivF7I3/NXLNpj7XzjmQfh+qnDl2fiPeE
+MfKumqa85W9iy/wUZd/LGY9I/b+th/J2PvP4+CIt9G02AgpCCw/af4j+OY2XXZp85fob4aTHHkEb
+mI5CpbrymUe9fu3yMzspTHMdiknmaT4IIvElj9G38czdGNyWvIkcMUk0qQxKXkkYIijqWY0AzPAa
+n31ptmljp1rZR04WsMcKU2FI1Cj9WdLEUKdITZtEYUOxV2KuxV82/wDOVGimLXdG1pR8F3bPaSEd
+OVu/Na+5E34Zqu0IeoF2GjlsQ8Qt5TDPHKOsbBh9BrmslGwQ5oLPYnDKGU1BFQfY5opByQvnHKEn
+uu+RjzZBRRsmQzRCPlZCUQj5WQlWR8gQyVlkyBCqqyZEhK8SYKVB6tq9vptlJdTHZdkTuzHoozJ0
+ejlnyCEf7A1Z84xxMi8tvL+4v7yS7uG5SSGp8AOwHsM9F0+njhgIR5B5bLlM5GR5l0dMua1RqUwJ
+UJMKE58t636Dixnb9y5/csf2WPb5H9eaLtjs/jHix+oc/Mfsdn2fquE8EuR5Mlds5gB3SHkbLAEI
+aRstAYqDGpyYVbhQ7FUVZJuz+GwyrIUFF5Uhi+vz+pqBUdIlC/T1P682eljUPe0zO6eflNop1n8x
+tBs6ckW6W5lHbhbVnYH2IjpmfpocWQBx80qgX2vnQOodirsVdirsVea/85BeWzrP5cXc8acrnSHS
++jp14JVJfoEblvozE1sOLH7nI006n73yJmkdoy7QLv1rBFJ+OH4D8h0/DNTqoVP3t8DYTdSKUPQ5
+iFsCFIKOVPbp8st5hmFVHyBCVdHyBCVZXyBCVVZMgQlUEmCkuadUQu7BVUEsx2AA6nEQJNBBNbl5
+p5k8wPq18ShItIarAvj4sfc53vZfZ40+Pf6zz/U81rNT4sv6I5JajZsnEV1fAq8yYqpO2KqDthVl
+Wgaz9bt/Qmb/AEiEdT+0vSvz8c5PtTQ+FPij9EvsLvdDqeOPCfqCYu2a0BzkO7ZYAhSySHYq7FUx
+gj4Rhe/U/PMeRsobmlWKJ5W+ygLH6MEY2aQSwuWRpZXkb7TksfmTXN1EUKcd7p/ziz5bMuqat5jl
+X93axrZWxI2MkpDyEe6qij/ZZsuz8e5k4OsnsA+js2rgOxV2KuxV2Kqd1bQXVtLa3CCS3nRopo26
+Mjjiyn5g4CL2UGnwx5z8tXHlnzRqWhz1JspmSNyKF4j8UT/7JCDnPZcfBIh3WOfFEFQ0G8+r3oRj
+SOb4T/rfs5harHxRvubYGiy1GzUkN624XkvMdV6/LDA9GQKkj5IhkrI+QISrLJkSEqiyZEhKoJMj
+S2xXzzrbRQJp0LUeccpyOoSuw/2RzoOwdEJSOWXKPL3/ALHV9p6ihwDrzYhaW8k7Hjsq/abOsdGq
+Sp6UnGtdqjArg+KV3qYqsZ8VVVtGli5KfiO4HY4UIaC6mtLpJk2kjbcHv4g5VnwjJAxPItmPIYSE
+h0ZrFdJcQJMh+CRQw+ntnF5MRhIxPMPSwmJRBHVaxqcQlrFXYqr2kXJ+R+yv68hOVBSjsoYpN5iv
+OES2yn4pPif/AFR0+85maTHZ4mvIejH+uwzYNT7V/KbykfK3kTTdNlTheyJ9avx39eajMpp3QUT/
+AGOb/TY+CADqM0+KRLL8vanYq7FXYq7FXYq8H/5yc8jNcWVr5ws0q9mFtNSAG/pM37mT/YuxU/6w
+8M1uvw2OMObpMm/C+cs1bnsu0fUPrVqCx/ep8Mg9+x+nNTqMXDLyb4SsJmrZjEM0LMhjbb7J6ZZE
+2zBcr4kJVVkyJCVQSZGlXiTBSbeZeYLtrnWruRjUCQov+qnwj9Wd32diEMEAO6/nu8xq58WWR802
+0qBRpkbDrJVmPvWn8MzXHQeqQspWQDYbN/DAqAD4q36mKXLydgq7k9MUJ7aw0VR4ADFUm1xFjv3A
+25BWPzphVOPLFwz2LxH/AHU54/JhX9dc5rtfGBlB7w7vs6dwI7im+apz3YquRGdgq9TgJpUxjRUQ
+KOgzHJti1NNHDE0shoiCpOGMSTQUmmHXdy9zcPM/VzsPAdhm3hARFOOTb0P8hvI/+JvOsVzcR8tL
+0bjd3VRVWkB/cxn/AFmHL5KczdHi45+QcbU5OGPmX15m8dW7FXYq7FXYq7FXYqh9S06z1LT7nT72
+MTWl3G8NxEejI44sPuOCUQRRSDRsPib8wvJV95O80XWjXNXhU+pZXBH97bsTwf57Ub/KBzn82Iwl
+Tt8WQTjaSafevZ3AlXdTs6+K5i5cYmKbommXwTpJGskZ5IwqpzUyiQaLeCrHi6lW3ByHJKEkVomo
+dx2OWg2yBbWTEhK8SZGkrhJgpXnnmKze21a4qPglYyxnsQ5r+B2ztezcwyYY1zAr5POazGY5D57p
+h5dv42gNlIaSKSYq9wdyB71zPcVHXUIIIIqD2xVI7u0VKsh4gb0PTFUJEObha0riqb2dsidBue/f
+FU0Dw28RlmYIi9ScVYnf3bXd3JORQMfhHgo2H4YqyPy3avDYGRxQzNyA/wAkCgzmO1cwnlofw7O8
+7PxmMLPVNs1jnNqrMwVRUnEmlR8EIiXxY9TmPKVoVcihjeual68n1eI/uoz8RH7Tf0GbHTYeEWeb
+VOVpfaWlzeXUNpaxtNc3DrFBCgqzu54qoHiSczALNBrJp9o/lb5Eg8l+U7fTPhe/l/f6lMu/KdwK
+gH+VAOK/KvfN9p8Phxrq6jNk45Wy7L2p2KuxV2KuxV2KuxV2KsE/N78tbfzv5dMcIVNbsQ0mmTna
+pI+KFz/LJT6DQ+OY2pweJHzDdgy8B8nx1dWtzaXMtrdRNBcwO0c0MgKujqaMrA9CDmjIrYu2BtG6
+RqhtH9OU1t3O/wDknxGYufBxixzZxlTJ0cMoZSCpFQR0IzWENy48XXiwqMHJNoWWJ49xunjlsZWy
+tYJMlSrhJgpNoTU9PtdQt/SnFCN45B9pTmTpdTPDK4/Ed7RnwRyCixC80LULUl0X14h0li3+8DcZ
+02n7SxZOZ4ZdxdJl0c4eY8lJdY1FV4GUsB/MAT95Fc2Dioea6nmNZHr7dB9wxVT5HFVePULyMUSU
+ge9D+vFXVvr6QD453HQCpp/TK8mWMBcjTKEJSNAWnGm+XKOJLwgld/QBr/wRzTavtWxWP5/qdnp9
+Bvc/kyEAAUGwHQZonar44nkNFHzPbAZAKjoYViG27dzlEpWhUyKEl1rVwga1t2+M7SuO3sPfMzT4
+L9Ra5y6MfzYNT6K/5x2/KxrdI/OesxUmkU/oa3cfZRhRrgjxYGie1T3GbTRaevWfg4Gqzfwh73my
+cJ2KuxV2KuxV2KuxV2KuxV2KvHPzy/Jo+Y4n8xaBCP07Cv8Apdsu31uNRsV/4tUDb+YbeGYOr0vF
+6o83L0+fh2PJ8vujxuyOpV1JVlYUII2IIOad2KYaXq8loRFJV7cnp3X3H9Mxs+AT3HNnGVMliljl
+jEkbBkboRmtlEg0W4FfgVQltlbdPhPh2yYn3ptDOskZ+IU9+2WggptC6jI31KXj1oPuqK5biHqDG
+fJIoppYm5RsVPtmbKIPNxwaW3CwXDmSSJebfbIFOR8duhyeOcoCoku30evw1wajHHJHoaFj8e9R+
+p6eRuhSnUkkfidsyBrMw6/c7nH2Z2Pn3AiD/AFpR+y2hZaaTs3I+HKv6sfzubv8Asbpez3ZURZAr
++uf1rltLHkCI9h41/UchLVZT/F+Pg67UHsvTisWOM5fGQ+cr+xFm6dYhDCBDEOipt95zG4LNncvP
+5Mxmb5e5fpodr2MLUkk7D5HI5vpLCHNksVmeshp/kjNbLJ3N9opVVRRRQeGVEobxVI9V1wAGC0ap
+6PKO3sv9czcGm6ya5T7khzPanr/5H/k5J5luo9f12Er5ft2rBC4p9bkU9N/91KR8R7/Z8aZ2k0vG
+eKX0/e4uoz8Ow5vqVVVVCqAqqKKo2AA7DNw61vFXYq7FXYq7FXYq7FXYq7FXYq7FXjv5yfkbD5j9
+bX/LqLBrtOVza7LHd07g9Fl9+jd/HMHVaTj9Uebl4NRw7Hk+Yru0urO5ltbuF4LmBik0MilXRlNC
+rKdwRmoII2LsQbX2WoXFm/KJvhP2kP2TlOTEJjdkJUyOx1e1uwFB9OX/AH238D3zX5cEoe5tEgUd
+lDJ1K9cVQ89lBNGyEceQIJHvlkchBUsRuIJIJnhkFGQ0+fvm2hISFhxyKU8kh2KuxV2KuxVO/Ltm
+xdrthRQCsfue5zC1eTbhbMY6p/mA2qF1e21qnKZwvgvUn5DJwxmR2QTTHdR1qe6rGn7uD+Xufmf4
+ZsMWnEdzuWqU7S7Mlg9m/J/8iLrXmh13zNE9vomz21kapLdDsxpRki9+rdtt8z9NozL1S5OJn1PD
+tHm+m7e3gtoI7e3jWGCFQkUSAKqqooqqo2AAzbAU64lUwq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
+wX8yfyi8ued4DNKPqWtIvGDU4lBag6LKu3qL9NR2OY2fTRye9uxZzD3Plrzt+XfmjybfG31i1IgZ
+iLe/iq1vL/qvQUP+S1D7Zp8uCWM7uyx5YzGzGcqbExtNcvIKK59aMdm6/Q2Y+TTRl5MxMhOLbXbC
+agZvSbwfp9/TMOemmPNsEwmCurqGUhlPQg1GUEUyQmo6ZDeoK/BKv2JB+o+2W4cxgfJjKNsau9Ou
+7Un1UPHs43U/TmxhljLk1GJCGy1i7FW1VmYKoLMegG5wE0qbWGgzSESXX7uLrw/aP9MxcuqA2juW
+yMO9NpdS060QJ6i/CKCNNyPbbMSOGczdMzIBKrvzFO9Vtk9Jf523b+gzKx6QD6t2ByJTJJJI5eRi
+zHqxNTmWAByYIzRtE1bWtQj0/SbSS8vZT8EMS8jTxPZVHcnYZOMDI0GMpACy+kPyw/5x507RWi1b
+zT6eoaotHisR8VtC3UFq/wB64/4Ee/XNrp9EI7y3Lr82qJ2jyezgACg6ZnuI7FXYq7FXYq7FXYq7
+FXYq7FXYq7FXYq7FXYq7FUPf6fY6jZy2V/bx3VpOvGaCZQ6MPAqdsEogiikEjcPEvPP/ADjJp900
+l55RuhZTGrHTbos0B9o5fidPkwb5jNfm0AO8XMx6sj6nhnmbyR5r8sT+jremzWgJok5XlC5/yJVq
+jfQc12TFKHMOZDJGXIpFlbNfHNLEaxuyHxUkfqyJiDzTaMj1vUk29XkPBgD+PXKjpoHoy4yrr5kv
+KUaONvoI/jlZ0ce8p8QqUmqW8hq9lFU9SKg/hTJjCR/EUcXkpG9t61WziHzLn/jbJeGf5xRfk2NX
+ukFIRHAD19NFH664PAiedleIqE13dTf3srOPAk0+7LI44jkEElRyaEZpekarq12tpplnNe3T/Zhg
+RpG+dFB298MYmRoIMgOb2PyR/wA4y61elLrzXc/oy26/UbcrJcsPBn+KOP8A4Y+wzPxaAneWziZN
+WB9O733yv5N8teVrH6nodjHaRkD1ZAOUshHeSQ1Zj8zt2zZY8UYCgHCnkMjunOWMHYq7FXYq7FXY
+q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVk8EFxC8M8azQyDjJFIoZWHgVOxwEWoLzzzH+QP5ba0
+XkSxfS7h9zLYP6Qr/wAYmDxD6FGY09Hjl0r3ORDUzHm831v/AJxW1aMs+ia3BcL1WK8jaEj25x+s
+G/4EZiT7OPQt8dYOoYZqX5A/mlZFiukrdxr/ALstp4Xr8lZlk/4XKJaPIOjcNTA9Ugufy0/MK2JE
+vlvUtq1ZLWWRRTr8SKwyo4Jj+Esxlh3hB/4M84f9WLUP+kWf/mjI+FPuLLxI94V7f8vvPlxT0fLm
+puCachZz8a+7cKYRhmeh+SDlj3hObD8kfzSvSPT0GWJTuWuHigoDTtI6nv4ZYNJkPRgdRAdWXaP/
+AM4uecbghtU1GzsIz1EfO4kH+xAjT/h8vj2fM8yA1S1kegeh+Xv+cafIenFJNUkudYmXcrK3owkj
+/iuKj/e5zKhoIDnu0S1cjy2em6RoWjaNai10mxgsLfvFbxrGCfE8QKn3OZcYCIoCnHlInmjskxdi
+rsVdirsVdirsVdirsVdirsVf/9k=
+
+
+
+
+
+ uuid:0A41642859EE11DA9346CE657E5F1B06
+ uuid:0A41642A59EE11DA9346CE657E5F1B06
+
+ uuid:0A41642759EE11DA9346CE657E5F1B06
+ uuid:0A41642659EE11DA9346CE657E5F1B06
+
+
+ image/svg+xmlAVAHIizo@aucuneid.nettango avahi freedesktop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+ YV+s3Z4kfvsxl06fN/mb09+xc6cn0ttpsbgfduoU9qV81DpynevNWab84dapyt/xb3nnTk/jy+X6
++8Y27pSL2Od697Y7dO30Ltp477t1esbfx/cVp065CHSrbd4f5AYFxwV+ipXfXDv9XiqfKi6dvmT4
+18rHJu4UwZhtrue3rZ+tyHnMsdO3t4d31057jf3op1OnXAR1e8Z/aKeq8wIXb4Wlh9HVkVOng8F5
+fJl2WpXWbUizmd/QJNwpFxEqS6OSlSptDcZSgUedxiY6vdiN/r5pDwfQ6e7A1unmVu8xRjv9yW+Y
+nQJNRt1Gms9LhJWLyqNwZCUQ17x2/lxCncYneep5fKUrbV9Cp4WRfXk17TuBO0X7EtViNlK4Vt+k
+VEla2SqcWElhY3M3d/6MOk1OdpqJLO0+Hh5Cp8dhs1PoBYtTonLxRea6v3WVsC3wd29X+yGd7t+e
+nlqXFzb2+6uHeSqIUJcF9qk46oTFrW6VSheTz8erYane6Ts/Ta0D0uylG25PBwAMZy3zqZVaph4v
+eW3nQiPPJ2SBp13+9EAS8dNJAv+0z59e76ecnmJIfpL50/pJxu3tAn8Wvs+5PdX4i9rRtdvTd/6m
+8DOiKzb5/DnG39wth92eJvjywVnU7Wmavw3/JsynNv4S5gfjcJY+n6RUh/zdbXiXPLWjWer5mL/7
+jO87PcUr9nzK36/kZbe3z/n7jFZwe3rJPy1nX92efvOvD08b+opNPH/J8q/fzZjb0zz/drstuT09
+4N8z73nzqQ3G3gdCYuMk7fJ2JSrkP8Qzl6fVZUFZfSu6rlj1Rji5Wjp2ebvWE85/xJbL0/q68Ph9
+sur8NP3WLW/vjJ7cVkwcXobX1o/P8VNxbS96aH0qhxOHB3vkqZ3yiaN2eCd1/G0+hRUrxDeuWQ0s
+Wi9vKr3hOSF7RD9Lxa4RVVIA7hqKk+pJlct9IXKQHK0ViuX97KP6VHwsq0/aToJD3/LFgpKoFQpK
+8mST1d8+Im00Hon0x/S8LK1hVRBTOaTnPJuUL3mW667xyd2HMcKMZyCOzR1z98+Wk63d6jpg0ZI2
+zF1tXVoI7mBJXNu9jBM2gvQchsqznaa/kJ7Ts3WK9gV3m3p8wN06drpUrCddO8V6jkunINqCnlNh
+5TF2rqnHD49OS/E802l9dXXZ7BRL/0anEu4Ua0l0gZH0v613WmpbFngpynaaulkxOwV9UEi4doql
+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
+V3P/C7HLWtI=
+
+
+
\ No newline at end of file
diff --git a/docs/zeroconf-stack-de.dia b/docs/zeroconf-stack-de.dia
new file mode 100644
index 0000000..988c57c
--- /dev/null
+++ b/docs/zeroconf-stack-de.dia
@@ -0,0 +1,563 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ #A4#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Namensauflösung
+ohne zentrale Instanz#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Automatische Addressenvergabe
+für IPv4 ohne zentrale Instanz#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Suche und Registrierung von
+Netzwerkdiensten ohne zentrale Instanz#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #ZeroConf-Stack: Theorie ...#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #nss-mdns#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #zeroconf#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #Avahi#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #... und Praxis#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--
cgit
From a895ea80c64a0dc1969bf4e3a2fecf7483a69c3f Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 19:25:29 +0000
Subject: add findstatic.pl script from Andrew Tridgell to SVN. It's useful for
finding symbols that are exported but shouldn't. It's not intended for
shipping in the tarballs. However it needed a safe place to live, since I use
it regularly during Avahi development.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1281 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/findstatic.pl | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100755 avahi-core/findstatic.pl
diff --git a/avahi-core/findstatic.pl b/avahi-core/findstatic.pl
new file mode 100755
index 0000000..43a4916
--- /dev/null
+++ b/avahi-core/findstatic.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -w
+# find a list of fns and variables in the code that could be static
+# usually called with something like this:
+# findstatic.pl `find . -name "*.o"`
+# Andrew Tridgell
+
+use strict;
+
+# use nm to find the symbols
+my($saved_delim) = $/;
+undef $/;
+my($syms) = `nm -o @ARGV`;
+$/ = $saved_delim;
+
+my(@lines) = split(/\n/s, $syms);
+
+my(%def);
+my(%undef);
+my(%stype);
+
+my(%typemap) = (
+ "T" => "function",
+ "C" => "uninitialised variable",
+ "D" => "initialised variable"
+ );
+
+
+# parse the symbols into defined and undefined
+for (my($i)=0; $i <= $#{@lines}; $i++) {
+ my($line) = $lines[$i];
+ if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $3;
+ push(@{$def{$fname}}, $symbol);
+ $stype{$symbol} = $2;
+ }
+ if ($line =~ /(.*):\s* U (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $2;
+ push(@{$undef{$fname}}, $symbol);
+ }
+}
+
+# look for defined symbols that are never referenced outside the place they
+# are defined
+foreach my $f (keys %def) {
+ print "Checking $f\n";
+ my($found_one) = 0;
+ foreach my $s (@{$def{$f}}) {
+ my($found) = 0;
+ foreach my $f2 (keys %undef) {
+ if ($f2 ne $f) {
+ foreach my $s2 (@{$undef{$f2}}) {
+ if ($s2 eq $s) {
+ $found = 1;
+ $found_one = 1;
+ }
+ }
+ }
+ }
+ if ($found == 0) {
+ my($t) = $typemap{$stype{$s}};
+ print " '$s' is unique to $f ($t)\n";
+ }
+ }
+ if ($found_one == 0) {
+ print " all symbols in '$f' are unused (main program?)\n";
+ }
+}
+
--
cgit
From 5fdbded556961276a149f2221a964b070279d4f6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 25 Aug 2006 19:38:38 +0000
Subject: update "homepage" target to install the tarball on the web site
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1282 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
Makefile.am | 1 +
1 file changed, 1 insertion(+)
diff --git a/Makefile.am b/Makefile.am
index d09f75a..0dc9042 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -229,6 +229,7 @@ homepage:
scp avahi-daemon/*.introspect avahi-daemon/introspect.dtd avahi-daemon/introspect.xsl\
man/*.xml man/xmltoman.dtd man/xmltoman.xsl \
tango:www/avahi.org/tree/download/
+ scp avahi-$(PACKAGE_VERSION).tar.gz tango:www/avahi.org/tree/download/
rm -rf doxygen
$(MAKE) doxygen-run
ssh tango rm -rf www/avahi.org/tree/download/doxygen
--
cgit
From e848713151f76c41b5b12246b4ed29bee3eb3b5e Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 30 Aug 2006 14:05:14 +0000
Subject: fix random seed initialization
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1284 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index 096d0aa..ca9c034 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -1023,7 +1023,7 @@ static void init_rand_seed(void) {
}
/* If the initialization failed by some reason, we add the time to the seed*/
- seed |= (unsigned) time(NULL);
+ seed ^= (unsigned) time(NULL);
srand(seed);
}
--
cgit
From c91361ef06f6b94996a78b4c20345c76384b2440 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 30 Aug 2006 15:31:54 +0000
Subject: fix a few invalid calls to avahi_s_entry_group_xxx()
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1285 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/static-hosts.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/avahi-daemon/static-hosts.c b/avahi-daemon/static-hosts.c
index 17b268b..9941a51 100644
--- a/avahi-daemon/static-hosts.c
+++ b/avahi-daemon/static-hosts.c
@@ -98,7 +98,8 @@ static void static_host_free(StaticHost *s) {
AVAHI_LLIST_REMOVE(StaticHost, hosts, hosts, s);
- avahi_s_entry_group_free (s->group);
+ if (s->group)
+ avahi_s_entry_group_free (s->group);
avahi_free(s->host);
avahi_free(s->ip);
@@ -112,7 +113,10 @@ static void add_static_host_to_server(StaticHost *h)
int err;
if (!h->group)
- h->group = avahi_s_entry_group_new (avahi_server, entry_group_callback, h);
+ if (!(h->group = avahi_s_entry_group_new (avahi_server, entry_group_callback, h))) {
+ avahi_log_error("avahi_s_entry_group_new() failed: %s", avahi_strerror(err));
+ return;
+ }
if (!avahi_address_parse (h->ip, AVAHI_PROTO_UNSPEC, &a)) {
avahi_log_error("Static host name %s: avahi_address_parse failed", h->host);
@@ -129,7 +133,8 @@ static void add_static_host_to_server(StaticHost *h)
static void remove_static_host_from_server(StaticHost *h)
{
- avahi_s_entry_group_reset (h->group);
+ if (h->group)
+ avahi_s_entry_group_reset (h->group);
}
void static_hosts_add_to_server(void) {
--
cgit
From 60d86f779725e78ae3d295380f15208859cc06eb Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 30 Aug 2006 16:07:26 +0000
Subject: add initial version of a RFC3927/IPv4LL implementation
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1286 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
Makefile.am | 4 +-
avahi-autoipd/Makefile.am | 36 +++
avahi-autoipd/main.c | 628 ++++++++++++++++++++++++++++++++++++++++++++++
configure.ac | 16 ++
4 files changed, 682 insertions(+), 2 deletions(-)
create mode 100644 avahi-autoipd/Makefile.am
create mode 100644 avahi-autoipd/main.c
diff --git a/Makefile.am b/Makefile.am
index 0dc9042..1b8e350 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -67,8 +67,8 @@ SUBDIRS = \
tests \
service-type-database \
avahi-compat-libdns_sd \
- avahi-compat-howl
-
+ avahi-compat-howl \
+ avahi-autoipd
DX_INPUT = \
$(srcdir)/avahi-common/address.h \
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
new file mode 100644
index 0000000..33df1f5
--- /dev/null
+++ b/avahi-autoipd/Makefile.am
@@ -0,0 +1,36 @@
+# $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 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 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.
+
+if ENABLE_AUTOIPD
+if HAVE_LIBDAEMON
+
+AM_CFLAGS= \
+ -I$(top_srcdir)
+
+# This cool debug trap works on i386/gcc only
+AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
+
+sbin_PROGRAMS = avahi-autoipd
+
+avahi_autoipd_SOURCES = main.c
+avahi_autoipd_CFLAGS = $(AM_CFLAGS) $(LIBDAEMON_CFLAGS)
+avahi_autoipd_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la $(LIBDAEMON_LIBS)
+
+endif
+endif
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
new file mode 100644
index 0000000..8ce7da1
--- /dev/null
+++ b/avahi-autoipd/main.c
@@ -0,0 +1,628 @@
+/* $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 HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef __linux__
+#error "avahi-autoipd is only available on Linux for now"
+#endif
+
+/* An implementation of RFC 3927 */
+
+/* Constants from the RFC */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#define IPV4LL_NETWORK 0xA9FE0000L
+#define IPV4LL_NETMASK 0xFFFF0000L
+#define IPV4LL_HOSTMASK 0x0000FFFFL
+
+#define ETHER_ADDRLEN 6
+#define ARP_PACKET_SIZE (8+4+4+2*ETHER_ADDRLEN)
+
+typedef enum State {
+ STATE_INVALID,
+ STATE_WAITING_PROBE,
+ STATE_PROBING,
+ STATE_WAITING_ANNOUNCE,
+ STATE_ANNOUNCING,
+ STATE_RUNNING,
+ STATE_SLEEPING,
+ STATE_MAX
+} State;
+
+typedef enum ArpOperation {
+ ARP_REQUEST = 1,
+ ARP_RESPONSE = 2
+} ArpOperation;
+
+typedef struct ArpPacketInfo {
+ ArpOperation operation;
+
+ uint32_t sender_ip_address, target_ip_address;
+ uint8_t sender_hw_address[ETHER_ADDRLEN], target_hw_address[ETHER_ADDRLEN];
+} ArpPacketInfo;
+
+static State state = STATE_INVALID;
+static int n_iteration = 0;
+static int n_conflict = 0;
+
+#define RANDOM_DEVICE "/dev/urandom"
+
+static void init_rand_seed(void) {
+ int fd;
+ unsigned seed = 0;
+
+ /* Try to initialize seed from /dev/urandom, to make it a little
+ * less predictable, and to make sure that multiple machines
+ * booted at the same time choose different random seeds. */
+ if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
+ read(fd, &seed, sizeof(seed));
+ close(fd);
+ }
+
+ /* If the initialization failed by some reason, we add the time to the seed */
+ seed ^= (unsigned) time(NULL);
+
+ srand(seed);
+}
+
+static uint32_t pick_addr(uint32_t old_addr) {
+ uint32_t addr;
+
+ do {
+ unsigned r = (unsigned) rand();
+
+ /* Reduce to 16 bits */
+ while (r > 0xFFFF)
+ r = (r >> 16) ^ (r & 0xFFFF);
+
+ addr = htonl(IPV4LL_NETWORK | (uint32_t) r);
+
+ } while (addr == old_addr);
+
+ return addr;
+}
+
+static void* packet_new(const ArpPacketInfo *info, size_t *packet_len) {
+ uint8_t *r;
+
+ assert(info);
+ assert(packet_len);
+ assert(info->operation == ARP_REQUEST || info->operation == ARP_RESPONSE);
+
+ *packet_len = ARP_PACKET_SIZE;
+ r = avahi_new0(uint8_t, *packet_len);
+
+ r[1] = 1; /* HTYPE */
+ r[2] = 8; /* PTYPE */
+ r[4] = ETHER_ADDRLEN; /* HLEN */
+ r[5] = 4; /* PLEN */
+ r[7] = (uint8_t) info->operation;
+
+ memcpy(r+8, info->sender_hw_address, ETHER_ADDRLEN);
+ memcpy(r+14, &info->sender_ip_address, 4);
+ memcpy(r+18, info->target_hw_address, ETHER_ADDRLEN);
+ memcpy(r+24, &info->target_ip_address, 4);
+
+ return r;
+}
+
+static void *packet_new_probe(uint32_t ip_address, const uint8_t*hw_address, size_t *packet_len) {
+ ArpPacketInfo info;
+
+ memset(&info, 0, sizeof(info));
+ info.operation = ARP_REQUEST;
+ memcpy(info.sender_hw_address, hw_address, ETHER_ADDRLEN);
+ info.target_ip_address = ip_address;
+
+ return packet_new(&info, packet_len);
+}
+
+static void *packet_new_announcement(uint32_t ip_address, const uint8_t* hw_address, size_t *packet_len) {
+ ArpPacketInfo info;
+
+ memset(&info, 0, sizeof(info));
+ info.operation = ARP_REQUEST;
+ memcpy(info.sender_hw_address, hw_address, ETHER_ADDRLEN);
+ info.target_ip_address = ip_address;
+ info.sender_ip_address = ip_address;
+
+ return packet_new(&info, packet_len);
+}
+
+static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info) {
+ const uint8_t *p = data;
+
+ assert(data);
+
+ if (packet_len < ARP_PACKET_SIZE)
+ return -1;
+
+ /* Check HTYPE and PTYPE */
+ if (p[0] != 0 || p[1] != 1 || p[2] != 8 || p[3] != 0)
+ return -1;
+
+ /* Check HLEN, PLEN, OPERATION */
+ if (p[4] != ETHER_ADDRLEN || p[5] != 4 || p[6] != 0 || (p[7] != 1 && p[7] != 2))
+ return -1;
+
+ info->operation = p[7];
+ memcpy(info->sender_hw_address, p+8, ETHER_ADDRLEN);
+ memcpy(&info->sender_ip_address, p+14, 4);
+ memcpy(info->target_hw_address, p+18, ETHER_ADDRLEN);
+ memcpy(&info->target_ip_address, p+24, 4);
+
+ return 0;
+}
+
+static void set_state(State st, int reset_counter) {
+ const char* const state_table[] = {
+ [STATE_INVALID] = "INVALID",
+ [STATE_WAITING_PROBE] = "WAITING_PROBE",
+ [STATE_PROBING] = "PROBING",
+ [STATE_WAITING_ANNOUNCE] = "WAITING_ANNOUNCE",
+ [STATE_ANNOUNCING] = "ANNOUNCING",
+ [STATE_RUNNING] = "RUNNING",
+ [STATE_SLEEPING] = "SLEEPING"
+ };
+
+ assert(st < STATE_MAX);
+
+ if (st == state && !reset_counter) {
+ n_iteration++;
+ daemon_log(LOG_DEBUG, "State iteration %s-%i", state_table[state], n_iteration);
+ } else {
+ daemon_log(LOG_DEBUG, "State transition %s-%i -> %s-0", state_table[state], n_iteration, state_table[st]);
+ state = st;
+ n_iteration = 0;
+ }
+}
+
+static int add_address(int iface, uint32_t addr) {
+ char buf[64];
+
+ daemon_log(LOG_INFO, "Selected address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ return 0;
+}
+
+static int remove_address(int iface, uint32_t addr) {
+ char buf[64];
+
+ daemon_log(LOG_INFO, "Removing address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ return 0;
+}
+
+static int open_socket(int iface, uint8_t *hw_address) {
+ int fd = -1;
+ struct sockaddr_ll sa;
+ socklen_t sa_len;
+
+ if ((fd = socket(PF_PACKET, SOCK_DGRAM, 0)) < 0) {
+ daemon_log(LOG_ERR, "socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sll_family = AF_PACKET;
+ sa.sll_protocol = htons(ETH_P_ARP);
+ sa.sll_ifindex = iface;
+
+ if (bind(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+ daemon_log(LOG_ERR, "bind() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ sa_len = sizeof(sa);
+ if (getsockname(fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+ daemon_log(LOG_ERR, "getsockname() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (sa.sll_halen != ETHER_ADDRLEN) {
+ daemon_log(LOG_ERR, "getsockname() returned invalid hardware address.");
+ goto fail;
+ }
+
+ memcpy(hw_address, sa.sll_addr, ETHER_ADDRLEN);
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+static int send_packet(int fd, int iface, void *packet, size_t packet_len) {
+ struct sockaddr_ll sa;
+
+ assert(fd >= 0);
+ assert(packet);
+ assert(packet_len > 0);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sll_family = AF_PACKET;
+ sa.sll_protocol = htons(ETH_P_ARP);
+ sa.sll_ifindex = iface;
+ sa.sll_halen = ETHER_ADDRLEN;
+ memset(sa.sll_addr, 0xFF, ETHER_ADDRLEN);
+
+ if (sendto(fd, packet, packet_len, 0, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+ daemon_log(LOG_ERR, "sendto() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int recv_packet(int fd, void **packet, size_t *packet_len) {
+ int s;
+ struct sockaddr_ll sa;
+ socklen_t sa_len;
+
+ assert(fd >= 0);
+ assert(packet);
+ assert(packet_len);
+
+ *packet = NULL;
+
+ if (ioctl(fd, FIONREAD, &s) < 0) {
+ daemon_log(LOG_ERR, "FIONREAD failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ assert(s > 0);
+
+ *packet_len = (size_t) s;
+ *packet = avahi_new(uint8_t, s);
+
+ sa_len = sizeof(sa);
+ if (recvfrom(fd, *packet, s, 0, (struct sockaddr*) &sa, &sa_len) < 0) {
+ daemon_log(LOG_ERR, "recvfrom() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (*packet)
+ avahi_free(*packet);
+
+ return -1;
+}
+
+static int is_ll_address(uint32_t addr) {
+ return (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK;
+}
+
+static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned jitter) {
+ assert(tv);
+
+ gettimeofday(tv, NULL);
+
+ if (msec)
+ avahi_timeval_add(tv, (AvahiUsec) msec*1000);
+
+ if (jitter)
+ avahi_timeval_add(tv, (AvahiUsec) (jitter*1000.0*rand()/(RAND_MAX+1.0)));
+
+ return tv;
+}
+
+static int loop(int iface, uint32_t addr) {
+ int fd = -1, ret = -1;
+ struct timeval next_wakeup;
+ int next_wakeup_valid = 0;
+ char buf[64];
+ void *in_packet = NULL;
+ size_t in_packet_len;
+ void *out_packet = NULL;
+ size_t out_packet_len;
+ uint8_t hw_address[ETHER_ADDRLEN];
+ struct pollfd pollfds[1];
+
+ enum {
+ EVENT_NULL,
+ EVENT_PACKET,
+ EVENT_TIMEOUT
+ } event = EVENT_NULL;
+
+ if ((fd = open_socket(iface, hw_address)) < 0)
+ goto fail;
+
+ if (addr && !is_ll_address(addr)) {
+ daemon_log(LOG_WARNING, "Requested address %s is not from IPv4LL range 169.254/16, ignoring.", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ addr = 0;
+ }
+
+ if (!addr) {
+ int i;
+ uint32_t a = 1;
+
+ for (i = 0; i < ETHER_ADDRLEN; i++)
+ a += hw_address[i]*i;
+
+ addr = htonl(IPV4LL_NETWORK | (uint32_t) a);
+ }
+
+ daemon_log(LOG_INFO, "Starting with address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+
+ memset(pollfds, 0, sizeof(pollfds));
+ pollfds[0].fd = fd;
+ pollfds[0].events = POLLIN;
+
+ for (;;) {
+ int r, timeout;
+ AvahiUsec usec;
+
+ if (state == STATE_INVALID) {
+
+ /* First, wait a random time */
+ set_state(STATE_WAITING_PROBE, 1);
+
+ elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
+ next_wakeup_valid = 1;
+
+ } else if ((state == STATE_WAITING_PROBE && event == EVENT_TIMEOUT) ||
+ (state == STATE_PROBING && event == EVENT_TIMEOUT && n_iteration < PROBE_NUM-2)) {
+
+ /* Send a probe */
+ out_packet = packet_new_probe(addr, hw_address, &out_packet_len);
+ set_state(STATE_PROBING, 0);
+
+ elapse_time(&next_wakeup, PROBE_MIN*1000, (PROBE_MAX-PROBE_MIN)*1000);
+ next_wakeup_valid = 1;
+
+ } else if (state == STATE_PROBING && event == EVENT_TIMEOUT && n_iteration >= PROBE_NUM-2) {
+
+ /* Send the last probe */
+ out_packet = packet_new_probe(addr, hw_address, &out_packet_len);
+ set_state(STATE_WAITING_ANNOUNCE, 1);
+
+ elapse_time(&next_wakeup, ANNOUNCE_WAIT*1000, 0);
+ next_wakeup_valid = 1;
+
+ } else if ((state == STATE_WAITING_ANNOUNCE && event == EVENT_TIMEOUT) ||
+ (state == STATE_ANNOUNCING && event == EVENT_TIMEOUT && n_iteration < ANNOUNCE_NUM-1)) {
+
+ /* Send announcement packet */
+ out_packet = packet_new_announcement(addr, hw_address, &out_packet_len);
+ set_state(STATE_ANNOUNCING, 0);
+
+ elapse_time(&next_wakeup, ANNOUNCE_INTERVAL*1000, 0);
+ next_wakeup_valid = 1;
+
+ if (n_iteration == 0) {
+ add_address(iface, addr);
+ n_conflict = 0;
+ }
+
+ } else if ((state == STATE_ANNOUNCING && event == EVENT_TIMEOUT && n_iteration >= ANNOUNCE_NUM-1)) {
+
+ daemon_log(LOG_INFO, "Successfully claimed IP address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ set_state(STATE_RUNNING, 0);
+
+ } else if (event == EVENT_PACKET) {
+ ArpPacketInfo info;
+
+ assert(in_packet);
+
+ if (packet_parse(in_packet, in_packet_len, &info) < 0)
+ daemon_log(LOG_WARNING, "Failed to parse incoming ARP packet.");
+ else {
+ int conflict = 0;
+
+ if (info.sender_ip_address == addr) {
+ /* Normal conflict */
+ conflict = 1;
+ daemon_log(LOG_INFO, "Recieved conflicting normal ARP packet.");
+ } else if (state == STATE_WAITING_PROBE || state == STATE_PROBING || state == STATE_WAITING_ANNOUNCE) {
+ /* Probe conflict */
+ conflict = info.target_ip_address == addr && memcmp(hw_address, info.sender_hw_address, ETHER_ADDRLEN);
+ daemon_log(LOG_INFO, "Recieved conflicting probe ARP packet.");
+ }
+
+ if (conflict) {
+
+ if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
+ remove_address(iface, addr);
+
+ /* Pick a new address */
+ addr = pick_addr(addr);
+
+ daemon_log(LOG_INFO, "Trying address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+
+ set_state(STATE_WAITING_PROBE, 1);
+
+ n_conflict++;
+
+ if (n_conflict >= MAX_CONFLICTS) {
+ daemon_log(LOG_WARNING, "Got too many conflicts, rate limiting new probes.");
+ elapse_time(&next_wakeup, RATE_LIMIT_INTERVAL*1000, PROBE_WAIT*1000);
+ } else
+ elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
+
+ next_wakeup_valid = 1;
+ } else
+ daemon_log(LOG_DEBUG, "Ignoring ARP packet.");
+ }
+ }
+
+ if (out_packet) {
+ daemon_log(LOG_DEBUG, "sending...");
+
+ if (send_packet(fd, iface, out_packet, out_packet_len) < 0)
+ goto fail;
+
+ avahi_free(out_packet);
+ out_packet = NULL;
+ }
+
+ if (in_packet) {
+ avahi_free(in_packet);
+ in_packet = NULL;
+ }
+
+ timeout = -1;
+
+ if (next_wakeup_valid) {
+ usec = avahi_age(&next_wakeup);
+ timeout = usec < 0 ? (int) (-usec/1000) : 0;
+ }
+
+ daemon_log(LOG_DEBUG, "sleeping %ims", timeout);
+
+ while ((r = poll(pollfds, 1, timeout)) < 0 && errno == EINTR)
+ ;
+
+ if (r < 0) {
+ daemon_log(LOG_ERR, "poll() failed: %s", strerror(r));
+ break;
+ } else if (r == 0) {
+ event = EVENT_TIMEOUT;
+ next_wakeup_valid = 0;
+ } else {
+ assert(pollfds[0].revents == POLLIN);
+
+ if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
+ goto fail;
+
+ if (in_packet)
+ event = EVENT_PACKET;
+ }
+ }
+
+ ret = 0;
+
+fail:
+
+ avahi_free(out_packet);
+ avahi_free(in_packet);
+
+ if (fd >= 0)
+ close(fd);
+
+ return ret;
+}
+
+static int get_ifindex(const char *name) {
+ int fd = -1;
+ struct ifreq ifreq;
+
+ if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ daemon_log(LOG_ERR, "socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ strncpy(ifreq.ifr_name, name, IFNAMSIZ-1);
+ ifreq.ifr_name[IFNAMSIZ-1] = 0;
+
+ if (ioctl(fd, SIOCGIFINDEX, &ifreq) < 0) {
+ daemon_log(LOG_ERR, "SIOCGIFINDEX failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return ifreq.ifr_ifindex;
+
+fail:
+
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+int main(int argc, char*argv[]) {
+ int ret = 1;
+ int ifindex;
+ uint32_t addr = 0;
+
+ init_rand_seed();
+
+ if ((ifindex = get_ifindex(argc >= 2 ? argv[1] : "eth0")) < 0)
+ goto fail;
+
+ if (argc >= 3)
+ addr = inet_addr(argv[2]);
+
+ if (loop(ifindex, addr) < 0)
+ goto fail;
+
+ ret = 0;
+
+
+fail:
+
+ return ret;
+}
+
+/* TODO:
+
+- netlink
+- man page
+- user script
+- chroot/drop privs/caps
+- daemonize
+- defend
+- signals
+- store last used address
+- cmdline
+
+*/
diff --git a/configure.ac b/configure.ac
index 6b21c30..8c2b1b5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -659,6 +659,20 @@ if test "x$HAVE_MONO" = "xyes" && test "x$HAVE_MONODOC" = "xyes" ; then
fi
AM_CONDITIONAL(HAVE_MONODOC, test "x$HAVE_MONODOC" = "xyes")
+#
+# Build autoipd?
+#
+AC_ARG_ENABLE(autoipd,
+ AS_HELP_STRING([--disable-autoipd],[Disable building of avahi-autoipd]),
+ [case "${enableval}" in
+ yes) ENABLE_AUTOIPD=yes ;;
+ no) ENABLE_AUTOIPD=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-autoipd) ;;
+ esac],
+ [ENABLE_AUTOIPD=yes])
+
+AM_CONDITIONAL(ENABLE_AUTOIPD, test "x$ENABLE_AUTOIPD" = "xyes")
+
#
# Defining Avahi User and Group.
#
@@ -844,6 +858,7 @@ avahi-sharp/Makefile
avahi-compat-libdns_sd/Makefile
avahi-compat-howl/Makefile
avahi-compat-howl/samples/Makefile
+avahi-autoipd/Makefile
])
AC_OUTPUT
@@ -923,4 +938,5 @@ echo "\
Building avahi-compat-howl: ${ENABLE_COMPAT_HOWL}
Building tests: ${ENABLE_TESTS}
Building avahi-core documentation: ${ENABLE_CORE_DOCS}
+ Building avahi-autoipd: ${ENABLE_AUTOIPD}
"
--
cgit
From 12874f5d761b4b80ac27c1fc758a93b69d92c34c Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 30 Aug 2006 19:51:54 +0000
Subject: add netlink support
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1287 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 2 +-
avahi-autoipd/iface-linux.c | 297 ++++++++++++++++++++++++++++++++++++++++++++
avahi-autoipd/iface.h | 35 ++++++
avahi-autoipd/main.c | 105 +++++++++++-----
avahi-autoipd/main.h | 46 +++++++
5 files changed, 452 insertions(+), 33 deletions(-)
create mode 100644 avahi-autoipd/iface-linux.c
create mode 100644 avahi-autoipd/iface.h
create mode 100644 avahi-autoipd/main.h
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 33df1f5..adfe70d 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -28,7 +28,7 @@ AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
sbin_PROGRAMS = avahi-autoipd
-avahi_autoipd_SOURCES = main.c
+avahi_autoipd_SOURCES = main.c ../avahi-daemon/setproctitle.c iface.h main.h iface-linux.c
avahi_autoipd_CFLAGS = $(AM_CFLAGS) $(LIBDAEMON_CFLAGS)
avahi_autoipd_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la $(LIBDAEMON_LIBS)
diff --git a/avahi-autoipd/iface-linux.c b/avahi-autoipd/iface-linux.c
new file mode 100644
index 0000000..c2f24df
--- /dev/null
+++ b/avahi-autoipd/iface-linux.c
@@ -0,0 +1,297 @@
+/* $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 HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include "iface.h"
+
+static int fd = -1;
+static int ifindex = -1;
+
+typedef struct Address Address;
+
+struct Address {
+ uint32_t address;
+ AVAHI_LLIST_FIELDS(Address, addresses);
+};
+
+AVAHI_LLIST_HEAD(Address, addresses) = NULL;
+
+int iface_init(int i) {
+ struct sockaddr_nl addr;
+
+ if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+ daemon_log(LOG_ERR, "socket(PF_NETLINK): %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR;
+ addr.nl_pid = getpid();
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ daemon_log(LOG_ERR, "bind(): %s", strerror(errno));
+ goto fail;
+ }
+
+ ifindex = i;
+
+ return fd;
+
+fail:
+ if (fd >= 0) {
+ close(fd);
+ fd = -1;
+ }
+
+ return -1;
+}
+
+static int process_nlmsg(struct nlmsghdr *n) {
+ assert(n);
+
+ if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
+ /* A link appeared or was removed */
+
+ struct ifinfomsg *ifi;
+ ifi = NLMSG_DATA(n);
+
+ if (ifi->ifi_family != AF_UNSPEC || ifi->ifi_index != ifindex)
+ return 0;
+
+ if (n->nlmsg_type == RTM_DELLINK) {
+ daemon_log(LOG_ERR, "Interface vanished.");
+ return -1;
+ }
+
+ assert(n->nlmsg_type == RTM_NEWLINK);
+
+ if ((ifi->ifi_flags & IFF_LOOPBACK) ||
+ (ifi->ifi_flags & IFF_NOARP) ||
+ !(ifi->ifi_flags & IFF_UP) ||
+ ifi->ifi_type != ARPHRD_ETHER) {
+ daemon_log(LOG_ERR, "Interface not suitable.");
+ return -1;
+ }
+
+ } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
+
+ /* An address was added or removed */
+
+ struct rtattr *a = NULL;
+ struct ifaddrmsg *ifa;
+ int l;
+ uint32_t address = 0;
+ Address *i;
+ char buf[32];
+
+ ifa = NLMSG_DATA(n);
+
+ if (ifa->ifa_family != AF_INET || ifa->ifa_index != ifindex)
+ return 0;
+
+ l = NLMSG_PAYLOAD(n, sizeof(*ifa));
+ a = IFLA_RTA(ifa);
+
+ while(RTA_OK(a, l)) {
+
+ switch(a->rta_type) {
+ case IFA_LOCAL:
+ case IFA_ADDRESS:
+ assert(RTA_PAYLOAD(a) == 4);
+ memcpy(&address, RTA_DATA(a), sizeof(uint32_t));
+ break;
+ }
+
+ a = RTA_NEXT(a, l);
+ }
+
+ daemon_log(LOG_INFO, "%s", inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+
+ if (!address || is_ll_address(address))
+ return 0;
+
+ for (i = addresses; i; i = i->addresses_next)
+ if (i->address == address)
+ break;
+
+ if (n->nlmsg_type == RTM_DELADDR && i) {
+ AVAHI_LLIST_REMOVE(Address, addresses, addresses, i);
+ avahi_free(i);
+ } if (n->nlmsg_type == RTM_NEWADDR && !i) {
+ i = avahi_new(Address, 1);
+ i->address = address;
+ AVAHI_LLIST_PREPEND(Address, addresses, addresses, i);
+ }
+ }
+
+ return 0;
+}
+
+static int process_response(int wait_for_done, unsigned seq) {
+ assert(fd >= 0);
+
+ do {
+ size_t bytes;
+ ssize_t r;
+ char replybuf[2048];
+ struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
+
+ if ((r = recv(fd, replybuf, sizeof(replybuf), 0)) < 0) {
+ daemon_log(LOG_ERR, "recv() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ bytes = (size_t) r;
+
+ for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+
+ if (!NLMSG_OK(p, bytes) || bytes < sizeof(struct nlmsghdr) || bytes < p->nlmsg_len) {
+ daemon_log(LOG_ERR, "Netlink packet too small.");
+ return -1;
+ }
+
+ if (p->nlmsg_type == NLMSG_DONE && wait_for_done && p->nlmsg_seq == seq)
+ return 0;
+
+ if (p->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *e = (struct nlmsgerr *) NLMSG_DATA (p);
+
+ if (e->error) {
+ daemon_log(LOG_ERR, "Netlink error: %s", strerror(-e->error));
+ return -1;
+ }
+ }
+
+ if ((pid_t) p->nlmsg_pid != getpid())
+ continue;
+
+ if (process_nlmsg(p) < 0)
+ return -1;
+ }
+ } while (wait_for_done);
+
+ return 0;
+}
+
+int iface_get_initial_state(State *state) {
+ struct nlmsghdr *n;
+ struct ifinfomsg *ifi;
+ struct ifaddrmsg *ifa;
+ uint8_t req[1024];
+ int seq = 0;
+
+ assert(fd >= 0);
+ assert(state);
+
+ memset(&req, 0, sizeof(req));
+ n = (struct nlmsghdr*) req;
+ n->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+ n->nlmsg_type = RTM_GETLINK;
+ n->nlmsg_seq = seq;
+ n->nlmsg_flags = NLM_F_MATCH|NLM_F_REQUEST|NLM_F_ACK;
+ n->nlmsg_pid = 0;
+
+ ifi = NLMSG_DATA(n);
+ ifi->ifi_family = AF_UNSPEC;
+ ifi->ifi_change = -1;
+
+ if (send(fd, n, n->nlmsg_len, 0) < 0) {
+ daemon_log(LOG_ERR, "send(): %s", strerror(errno));
+ return -1;
+ }
+
+ if (process_response(1, 0) < 0)
+ return -1;
+
+ n->nlmsg_type = RTM_GETADDR;
+ n->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa));
+ n->nlmsg_seq = ++seq;
+
+ ifa = NLMSG_DATA(n);
+ ifa->ifa_family = AF_INET;
+ ifa->ifa_index = ifindex;
+
+ if (send(fd, n, n->nlmsg_len, 0) < 0) {
+ daemon_log(LOG_ERR, "send(): %s", strerror(errno));
+ return -1;
+ }
+
+ if (process_response(1, seq) < 0)
+ return -1;
+
+ *state = addresses ? STATE_SLEEPING : STATE_START;
+
+ return 0;
+}
+
+int iface_process(Event *event) {
+ int b;
+ assert(fd >= 0);
+
+ b = !!addresses;
+
+ if (process_response(0, 0) < 0)
+ return -1;
+
+ if (b && !!addresses)
+ *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
+ else if (!b && addresses)
+ *event = EVENT_ROUTABLE_ADDR_CONFIGURED;
+
+ return 0;
+}
+
+void iface_done(void) {
+ Address *a;
+
+ if (fd >= 0) {
+ close(fd);
+ fd = -1;
+ }
+
+ while ((a = addresses)) {
+ AVAHI_LLIST_REMOVE(Address, addresses, addresses, a);
+ avahi_free(a);
+ }
+}
+
+
diff --git a/avahi-autoipd/iface.h b/avahi-autoipd/iface.h
new file mode 100644
index 0000000..4480c97
--- /dev/null
+++ b/avahi-autoipd/iface.h
@@ -0,0 +1,35 @@
+#ifndef fooavahiifacehfoo
+#define fooavahiifacehfoo
+
+/* $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.
+***/
+
+#include
+
+#include "main.h"
+
+int iface_init(int ifindex);
+int iface_process(Event *event);
+void iface_done(void);
+
+int iface_get_initial_state(State *state);
+
+#endif
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 8ce7da1..200cab0 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -43,12 +43,17 @@
#include
#include
+#include
+
#include
#include
#include
#include
#include
+#include "main.h"
+#include "iface.h"
+
#ifndef __linux__
#error "avahi-autoipd is only available on Linux for now"
#endif
@@ -74,17 +79,6 @@
#define ETHER_ADDRLEN 6
#define ARP_PACKET_SIZE (8+4+4+2*ETHER_ADDRLEN)
-typedef enum State {
- STATE_INVALID,
- STATE_WAITING_PROBE,
- STATE_PROBING,
- STATE_WAITING_ANNOUNCE,
- STATE_ANNOUNCING,
- STATE_RUNNING,
- STATE_SLEEPING,
- STATE_MAX
-} State;
-
typedef enum ArpOperation {
ARP_REQUEST = 1,
ARP_RESPONSE = 2
@@ -97,7 +91,7 @@ typedef struct ArpPacketInfo {
uint8_t sender_hw_address[ETHER_ADDRLEN], target_hw_address[ETHER_ADDRLEN];
} ArpPacketInfo;
-static State state = STATE_INVALID;
+static State state = STATE_START;
static int n_iteration = 0;
static int n_conflict = 0;
@@ -212,7 +206,7 @@ static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info
static void set_state(State st, int reset_counter) {
const char* const state_table[] = {
- [STATE_INVALID] = "INVALID",
+ [STATE_START] = "START",
[STATE_WAITING_PROBE] = "WAITING_PROBE",
[STATE_PROBING] = "PROBING",
[STATE_WAITING_ANNOUNCE] = "WAITING_ANNOUNCE",
@@ -347,7 +341,7 @@ fail:
return -1;
}
-static int is_ll_address(uint32_t addr) {
+int is_ll_address(uint32_t addr) {
return (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK;
}
@@ -366,6 +360,12 @@ static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned j
}
static int loop(int iface, uint32_t addr) {
+ enum {
+ FD_ARP,
+ FD_IFACE,
+ FD_MAX
+ };
+
int fd = -1, ret = -1;
struct timeval next_wakeup;
int next_wakeup_valid = 0;
@@ -375,17 +375,19 @@ static int loop(int iface, uint32_t addr) {
void *out_packet = NULL;
size_t out_packet_len;
uint8_t hw_address[ETHER_ADDRLEN];
- struct pollfd pollfds[1];
+ struct pollfd pollfds[FD_MAX];
+ int iface_fd;
+ Event event = EVENT_NULL;
- enum {
- EVENT_NULL,
- EVENT_PACKET,
- EVENT_TIMEOUT
- } event = EVENT_NULL;
-
if ((fd = open_socket(iface, hw_address)) < 0)
goto fail;
+ if ((iface_fd = iface_init(iface)) < 0)
+ goto fail;
+
+ if (iface_get_initial_state(&state) < 0)
+ goto fail;
+
if (addr && !is_ll_address(addr)) {
daemon_log(LOG_WARNING, "Requested address %s is not from IPv4LL range 169.254/16, ignoring.", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
addr = 0;
@@ -403,15 +405,20 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_INFO, "Starting with address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ if (state == STATE_SLEEPING)
+ daemon_log(LOG_INFO, "Routable address already assigned, sleeping.");
+
memset(pollfds, 0, sizeof(pollfds));
- pollfds[0].fd = fd;
- pollfds[0].events = POLLIN;
+ pollfds[FD_ARP].fd = fd;
+ pollfds[FD_ARP].events = POLLIN;
+ pollfds[FD_IFACE].fd = iface_fd;
+ pollfds[FD_IFACE].events = POLLIN;
for (;;) {
int r, timeout;
AvahiUsec usec;
- if (state == STATE_INVALID) {
+ if (state == STATE_START) {
/* First, wait a random time */
set_state(STATE_WAITING_PROBE, 1);
@@ -488,10 +495,10 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_INFO, "Trying address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
- set_state(STATE_WAITING_PROBE, 1);
-
n_conflict++;
+ set_state(STATE_WAITING_PROBE, 1);
+
if (n_conflict >= MAX_CONFLICTS) {
daemon_log(LOG_WARNING, "Got too many conflicts, rate limiting new probes.");
elapse_time(&next_wakeup, RATE_LIMIT_INTERVAL*1000, PROBE_WAIT*1000);
@@ -502,6 +509,25 @@ static int loop(int iface, uint32_t addr) {
} else
daemon_log(LOG_DEBUG, "Ignoring ARP packet.");
}
+
+ } else if (event == EVENT_ROUTABLE_ADDR_CONFIGURED) {
+
+ daemon_log(LOG_INFO, "A routable address has been configured.");
+
+ set_state(STATE_SLEEPING, 1);
+
+ if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
+ remove_address(iface, addr);
+
+ } else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING) {
+
+ daemon_log(LOG_INFO, "No longer a routable address configured, restarting probe process.");
+
+ set_state(STATE_WAITING_PROBE, 1);
+
+ elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
+ next_wakeup_valid = 1;
+
}
if (out_packet) {
@@ -519,6 +545,7 @@ static int loop(int iface, uint32_t addr) {
in_packet = NULL;
}
+ event = EVENT_NULL;
timeout = -1;
if (next_wakeup_valid) {
@@ -528,7 +555,7 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_DEBUG, "sleeping %ims", timeout);
- while ((r = poll(pollfds, 1, timeout)) < 0 && errno == EINTR)
+ while ((r = poll(pollfds, FD_MAX, timeout)) < 0 && errno == EINTR)
;
if (r < 0) {
@@ -538,13 +565,21 @@ static int loop(int iface, uint32_t addr) {
event = EVENT_TIMEOUT;
next_wakeup_valid = 0;
} else {
- assert(pollfds[0].revents == POLLIN);
- if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
- goto fail;
+ if (pollfds[FD_ARP].revents == POLLIN) {
+ if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
+ goto fail;
+
+ if (in_packet)
+ event = EVENT_PACKET;
+ }
- if (in_packet)
- event = EVENT_PACKET;
+ if (event == EVENT_NULL &&
+ pollfds[FD_IFACE].revents == POLLIN) {
+
+ if (iface_process(&event) < 0)
+ goto fail;
+ }
}
}
@@ -557,6 +592,9 @@ fail:
if (fd >= 0)
close(fd);
+
+ if (iface_fd >= 0)
+ iface_done();
return ret;
}
@@ -594,6 +632,8 @@ int main(int argc, char*argv[]) {
int ifindex;
uint32_t addr = 0;
+ avahi_init_proc_title(argc, argv);
+
init_rand_seed();
if ((ifindex = get_ifindex(argc >= 2 ? argv[1] : "eth0")) < 0)
@@ -624,5 +664,6 @@ fail:
- signals
- store last used address
- cmdline
+- setproctitle
*/
diff --git a/avahi-autoipd/main.h b/avahi-autoipd/main.h
new file mode 100644
index 0000000..61d5ed6
--- /dev/null
+++ b/avahi-autoipd/main.h
@@ -0,0 +1,46 @@
+#ifndef fooavahimainhfoo
+#define fooavahimainhfoo
+
+/* $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.
+***/
+
+typedef enum Event {
+ EVENT_NULL,
+ EVENT_PACKET,
+ EVENT_TIMEOUT,
+ EVENT_ROUTABLE_ADDR_CONFIGURED,
+ EVENT_ROUTABLE_ADDR_UNCONFIGURED,
+} Event;
+
+typedef enum State {
+ STATE_START,
+ STATE_WAITING_PROBE,
+ STATE_PROBING,
+ STATE_WAITING_ANNOUNCE,
+ STATE_ANNOUNCING,
+ STATE_RUNNING,
+ STATE_SLEEPING,
+ STATE_MAX
+} State;
+
+int is_ll_address(uint32_t addr);
+
+#endif
--
cgit
From 834cc833bb7bb608f778c32a1f3b5bee2f09f7c0 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 30 Aug 2006 21:18:14 +0000
Subject: document iface.h to ease porting
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1288 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/iface.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/avahi-autoipd/iface.h b/avahi-autoipd/iface.h
index 4480c97..017c90d 100644
--- a/avahi-autoipd/iface.h
+++ b/avahi-autoipd/iface.h
@@ -26,10 +26,22 @@
#include "main.h"
+/* Subscribe to network configuration changes. The only events we are
+ * interested in are when routable addresses are removed/added to the
+ * monitored interface and when our monitored interface disappears. */
+
+
+/* Return a valid fd that we listen on for events */
int iface_init(int ifindex);
+
+/* Process events */
int iface_process(Event *event);
void iface_done(void);
+/* Deduce the initial state of our state machine. If a routable
+ * address is configured for the interface, *state should be set to
+ * STATE_SLEEPING., otherwide STATE_START */
+
int iface_get_initial_state(State *state);
#endif
--
cgit
From fee9053f6a8a22ff53d59fc7865230ad41fdf760 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 30 Aug 2006 21:20:53 +0000
Subject: a few fixes in netlink handling
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1289 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/iface-linux.c | 10 ++--------
avahi-autoipd/main.c | 12 +++++++-----
2 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/avahi-autoipd/iface-linux.c b/avahi-autoipd/iface-linux.c
index c2f24df..5e2996a 100644
--- a/avahi-autoipd/iface-linux.c
+++ b/avahi-autoipd/iface-linux.c
@@ -121,7 +121,6 @@ static int process_nlmsg(struct nlmsghdr *n) {
int l;
uint32_t address = 0;
Address *i;
- char buf[32];
ifa = NLMSG_DATA(n);
@@ -144,8 +143,6 @@ static int process_nlmsg(struct nlmsghdr *n) {
a = RTA_NEXT(a, l);
}
- daemon_log(LOG_INFO, "%s", inet_ntop(AF_INET, &address, buf, sizeof(buf)));
-
if (!address || is_ll_address(address))
return 0;
@@ -189,7 +186,7 @@ static int process_response(int wait_for_done, unsigned seq) {
return -1;
}
- if (p->nlmsg_type == NLMSG_DONE && wait_for_done && p->nlmsg_seq == seq)
+ if (p->nlmsg_type == NLMSG_DONE && wait_for_done && p->nlmsg_seq == seq && (pid_t) p->nlmsg_pid == getpid())
return 0;
if (p->nlmsg_type == NLMSG_ERROR) {
@@ -201,9 +198,6 @@ static int process_response(int wait_for_done, unsigned seq) {
}
}
- if ((pid_t) p->nlmsg_pid != getpid())
- continue;
-
if (process_nlmsg(p) < 0)
return -1;
}
@@ -272,7 +266,7 @@ int iface_process(Event *event) {
if (process_response(0, 0) < 0)
return -1;
- if (b && !!addresses)
+ if (b && !addresses)
*event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
else if (!b && addresses)
*event = EVENT_ROUTABLE_ADDR_CONFIGURED;
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 200cab0..feb88ee 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -230,14 +230,14 @@ static void set_state(State st, int reset_counter) {
static int add_address(int iface, uint32_t addr) {
char buf[64];
- daemon_log(LOG_INFO, "Selected address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ daemon_log(LOG_INFO, "Configuring address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
return 0;
}
static int remove_address(int iface, uint32_t addr) {
char buf[64];
- daemon_log(LOG_INFO, "Removing address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
+ daemon_log(LOG_INFO, "Unconfiguring address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
return 0;
}
@@ -464,6 +464,8 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_INFO, "Successfully claimed IP address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
set_state(STATE_RUNNING, 0);
+
+ next_wakeup_valid = 0;
} else if (event == EVENT_PACKET) {
ArpPacketInfo info;
@@ -514,10 +516,11 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_INFO, "A routable address has been configured.");
- set_state(STATE_SLEEPING, 1);
-
if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
remove_address(iface, addr);
+
+ set_state(STATE_SLEEPING, 1);
+ next_wakeup_valid = 0;
} else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING) {
@@ -655,7 +658,6 @@ fail:
/* TODO:
-- netlink
- man page
- user script
- chroot/drop privs/caps
--
cgit
From 5ea98f8caf0e163ad1f51039b8a2d13e3fe0e86c Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 00:16:27 +0000
Subject: implement command line parsing, signal handling, daemonization
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1290 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 4 +-
avahi-autoipd/main.c | 438 ++++++++++++++++++++++++++++++++++++++--------
avahi-autoipd/main.h | 1 +
3 files changed, 370 insertions(+), 73 deletions(-)
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index adfe70d..c553470 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -24,7 +24,9 @@ AM_CFLAGS= \
-I$(top_srcdir)
# This cool debug trap works on i386/gcc only
-AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
+AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' \
+ -DAVAHI_RUNTIME_DIR=\"$(avahi_runtime_dir)/\" \
+ -DAVAHI_DNSCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\"
sbin_PROGRAMS = avahi-autoipd
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index feb88ee..04c3111 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -39,6 +39,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
#include
#include
@@ -95,8 +99,41 @@ static State state = STATE_START;
static int n_iteration = 0;
static int n_conflict = 0;
+static char *interface_name = NULL;
+static char *pid_file_name = NULL;
+static uint32_t start_address = 0;
+static char *argv0 = NULL;
+static int daemonize = 0;
+static int wait_for_address = 0;
+static int use_syslog = 0;
+static int debug = 0;
+static int modify_proc_title = 1;
+
+static enum {
+ DAEMON_RUN,
+ DAEMON_KILL,
+ DAEMON_REFRESH,
+ DAEMON_VERSION,
+ DAEMON_HELP,
+ DAEMON_CHECK
+} command = DAEMON_RUN;
+
+typedef enum CalloutEvent {
+ CALLOUT_BIND,
+ CALLOUT_CONFLICT,
+ CALLOUT_UNBIND,
+ CALLOUT_STOP,
+ CALLOUT_MAX
+} CalloutEvent;
+
#define RANDOM_DEVICE "/dev/urandom"
+#define DEBUG(x) do {\
+if (debug) { \
+ x; \
+} \
+} while (0)
+
static void init_rand_seed(void) {
int fd;
unsigned seed = 0;
@@ -204,7 +241,7 @@ static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info
return 0;
}
-static void set_state(State st, int reset_counter) {
+static void set_state(State st, int reset_counter, uint32_t address) {
const char* const state_table[] = {
[STATE_START] = "START",
[STATE_WAITING_PROBE] = "WAITING_PROBE",
@@ -214,30 +251,45 @@ static void set_state(State st, int reset_counter) {
[STATE_RUNNING] = "RUNNING",
[STATE_SLEEPING] = "SLEEPING"
};
+ char buf[64];
assert(st < STATE_MAX);
if (st == state && !reset_counter) {
n_iteration++;
- daemon_log(LOG_DEBUG, "State iteration %s-%i", state_table[state], n_iteration);
+ DEBUG(daemon_log(LOG_DEBUG, "State iteration %s-%i", state_table[state], n_iteration));
} else {
- daemon_log(LOG_DEBUG, "State transition %s-%i -> %s-0", state_table[state], n_iteration, state_table[st]);
+ DEBUG(daemon_log(LOG_DEBUG, "State transition %s-%i -> %s-0", state_table[state], n_iteration, state_table[st]));
state = st;
n_iteration = 0;
}
-}
-static int add_address(int iface, uint32_t addr) {
- char buf[64];
-
- daemon_log(LOG_INFO, "Configuring address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
- return 0;
+ if (modify_proc_title) {
+ if (state == STATE_SLEEPING)
+ avahi_set_proc_title("%s: sleeping", argv0);
+ else if (state == STATE_ANNOUNCING)
+ avahi_set_proc_title("%s: announcing %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ else if (state == STATE_RUNNING)
+ avahi_set_proc_title("%s: bound %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ else
+ avahi_set_proc_title("%s: probing %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ }
}
-static int remove_address(int iface, uint32_t addr) {
- char buf[64];
+static int do_callout(CalloutEvent event, int iface, uint32_t addr) {
+ char buf[64], ifname[IFNAMSIZ];
+ const char * const event_table[CALLOUT_MAX] = {
+ [CALLOUT_BIND] = "BIND",
+ [CALLOUT_CONFLICT] = "CONFLICT",
+ [CALLOUT_UNBIND] = "UNBIND",
+ [CALLOUT_STOP] = "STOP"
+ };
+
+ daemon_log(LOG_INFO, "Callout %s, address %s on interface %s",
+ event_table[event],
+ inet_ntop(AF_INET, &addr, buf, sizeof(buf)),
+ if_indextoname(iface, ifname));
- daemon_log(LOG_INFO, "Unconfiguring address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
return 0;
}
@@ -363,7 +415,8 @@ static int loop(int iface, uint32_t addr) {
enum {
FD_ARP,
FD_IFACE,
- FD_MAX
+ FD_SIGNAL,
+ FD_MAX,
};
int fd = -1, ret = -1;
@@ -378,6 +431,10 @@ static int loop(int iface, uint32_t addr) {
struct pollfd pollfds[FD_MAX];
int iface_fd;
Event event = EVENT_NULL;
+ int retval_sent = !daemonize;
+ State st;
+
+ daemon_signal_init(SIGINT, SIGTERM, SIGCHLD, SIGHUP,0);
if ((fd = open_socket(iface, hw_address)) < 0)
goto fail;
@@ -385,9 +442,9 @@ static int loop(int iface, uint32_t addr) {
if ((iface_fd = iface_init(iface)) < 0)
goto fail;
- if (iface_get_initial_state(&state) < 0)
+ if (iface_get_initial_state(&st) < 0)
goto fail;
-
+
if (addr && !is_ll_address(addr)) {
daemon_log(LOG_WARNING, "Requested address %s is not from IPv4LL range 169.254/16, ignoring.", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
addr = 0;
@@ -403,16 +460,25 @@ static int loop(int iface, uint32_t addr) {
addr = htonl(IPV4LL_NETWORK | (uint32_t) a);
}
+ set_state(st, 1, addr);
+
daemon_log(LOG_INFO, "Starting with address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
if (state == STATE_SLEEPING)
daemon_log(LOG_INFO, "Routable address already assigned, sleeping.");
+ if (!retval_sent && (!wait_for_address || state == STATE_SLEEPING)) {
+ daemon_retval_send(0);
+ retval_sent = 1;
+ }
+
memset(pollfds, 0, sizeof(pollfds));
pollfds[FD_ARP].fd = fd;
pollfds[FD_ARP].events = POLLIN;
pollfds[FD_IFACE].fd = iface_fd;
pollfds[FD_IFACE].events = POLLIN;
+ pollfds[FD_SIGNAL].fd = daemon_signal_fd();
+ pollfds[FD_SIGNAL].events = POLLIN;
for (;;) {
int r, timeout;
@@ -421,7 +487,7 @@ static int loop(int iface, uint32_t addr) {
if (state == STATE_START) {
/* First, wait a random time */
- set_state(STATE_WAITING_PROBE, 1);
+ set_state(STATE_WAITING_PROBE, 1, addr);
elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
next_wakeup_valid = 1;
@@ -431,7 +497,7 @@ static int loop(int iface, uint32_t addr) {
/* Send a probe */
out_packet = packet_new_probe(addr, hw_address, &out_packet_len);
- set_state(STATE_PROBING, 0);
+ set_state(STATE_PROBING, 0, addr);
elapse_time(&next_wakeup, PROBE_MIN*1000, (PROBE_MAX-PROBE_MIN)*1000);
next_wakeup_valid = 1;
@@ -440,7 +506,7 @@ static int loop(int iface, uint32_t addr) {
/* Send the last probe */
out_packet = packet_new_probe(addr, hw_address, &out_packet_len);
- set_state(STATE_WAITING_ANNOUNCE, 1);
+ set_state(STATE_WAITING_ANNOUNCE, 1, addr);
elapse_time(&next_wakeup, ANNOUNCE_WAIT*1000, 0);
next_wakeup_valid = 1;
@@ -450,20 +516,25 @@ static int loop(int iface, uint32_t addr) {
/* Send announcement packet */
out_packet = packet_new_announcement(addr, hw_address, &out_packet_len);
- set_state(STATE_ANNOUNCING, 0);
+ set_state(STATE_ANNOUNCING, 0, addr);
elapse_time(&next_wakeup, ANNOUNCE_INTERVAL*1000, 0);
next_wakeup_valid = 1;
if (n_iteration == 0) {
- add_address(iface, addr);
+ do_callout(CALLOUT_BIND, iface, addr);
n_conflict = 0;
+
+ if (!retval_sent) {
+ daemon_retval_send(0);
+ retval_sent = 1;
+ }
}
} else if ((state == STATE_ANNOUNCING && event == EVENT_TIMEOUT && n_iteration >= ANNOUNCE_NUM-1)) {
daemon_log(LOG_INFO, "Successfully claimed IP address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
- set_state(STATE_RUNNING, 0);
+ set_state(STATE_RUNNING, 0, addr);
next_wakeup_valid = 0;
@@ -490,7 +561,7 @@ static int loop(int iface, uint32_t addr) {
if (conflict) {
if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
- remove_address(iface, addr);
+ do_callout(CALLOUT_CONFLICT, iface, addr);
/* Pick a new address */
addr = pick_addr(addr);
@@ -499,7 +570,7 @@ static int loop(int iface, uint32_t addr) {
n_conflict++;
- set_state(STATE_WAITING_PROBE, 1);
+ set_state(STATE_WAITING_PROBE, 1, addr);
if (n_conflict >= MAX_CONFLICTS) {
daemon_log(LOG_WARNING, "Got too many conflicts, rate limiting new probes.");
@@ -509,7 +580,7 @@ static int loop(int iface, uint32_t addr) {
next_wakeup_valid = 1;
} else
- daemon_log(LOG_DEBUG, "Ignoring ARP packet.");
+ DEBUG(daemon_log(LOG_DEBUG, "Ignoring irrelevant ARP packet."));
}
} else if (event == EVENT_ROUTABLE_ADDR_CONFIGURED) {
@@ -517,24 +588,40 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_INFO, "A routable address has been configured.");
if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
- remove_address(iface, addr);
+ do_callout(CALLOUT_UNBIND, iface, addr);
- set_state(STATE_SLEEPING, 1);
+ if (!retval_sent) {
+ daemon_retval_send(0);
+ retval_sent = 1;
+ }
+
+ set_state(STATE_SLEEPING, 1, addr);
next_wakeup_valid = 0;
} else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING) {
daemon_log(LOG_INFO, "No longer a routable address configured, restarting probe process.");
- set_state(STATE_WAITING_PROBE, 1);
+ set_state(STATE_WAITING_PROBE, 1, addr);
elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
next_wakeup_valid = 1;
+
+ } else if (event == EVENT_REFRESH_REQUEST && state == STATE_RUNNING) {
+
+ /* The user requested a reannouncing of the address by a SIGHUP */
+ daemon_log(LOG_INFO, "Reannouncing address.");
+ /* Send announcement packet */
+ out_packet = packet_new_announcement(addr, hw_address, &out_packet_len);
+ set_state(STATE_ANNOUNCING, 1, addr);
+
+ elapse_time(&next_wakeup, ANNOUNCE_INTERVAL*1000, 0);
+ next_wakeup_valid = 1;
}
if (out_packet) {
- daemon_log(LOG_DEBUG, "sending...");
+ DEBUG(daemon_log(LOG_DEBUG, "sending..."));
if (send_packet(fd, iface, out_packet, out_packet_len) < 0)
goto fail;
@@ -556,14 +643,14 @@ static int loop(int iface, uint32_t addr) {
timeout = usec < 0 ? (int) (-usec/1000) : 0;
}
- daemon_log(LOG_DEBUG, "sleeping %ims", timeout);
+ DEBUG(daemon_log(LOG_DEBUG, "sleeping %ims", timeout));
while ((r = poll(pollfds, FD_MAX, timeout)) < 0 && errno == EINTR)
;
if (r < 0) {
daemon_log(LOG_ERR, "poll() failed: %s", strerror(r));
- break;
+ goto fail;
} else if (r == 0) {
event = EVENT_TIMEOUT;
next_wakeup_valid = 0;
@@ -583,6 +670,34 @@ static int loop(int iface, uint32_t addr) {
if (iface_process(&event) < 0)
goto fail;
}
+
+ if (event == EVENT_NULL &&
+ pollfds[FD_SIGNAL].revents == POLLIN) {
+
+ int sig;
+
+ if ((sig = daemon_signal_next()) <= 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next() failed");
+ goto fail;
+ }
+
+ switch(sig) {
+ case SIGINT:
+ case SIGTERM:
+ daemon_log(LOG_INFO, "Got %s, quitting.", sig == SIGINT ? "SIGINT" : "SIGTERM");
+ ret = 0;
+ goto fail;
+
+ case SIGCHLD:
+ waitpid(-1, NULL, WNOHANG);
+ break;
+
+ case SIGHUP:
+ event = EVENT_REFRESH_REQUEST;
+ break;
+ }
+
+ }
}
}
@@ -590,6 +705,9 @@ static int loop(int iface, uint32_t addr) {
fail:
+ if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
+ do_callout(CALLOUT_STOP, iface, addr);
+
avahi_free(out_packet);
avahi_free(in_packet);
@@ -598,74 +716,250 @@ fail:
if (iface_fd >= 0)
iface_done();
+
+ if (daemonize && !retval_sent)
+ daemon_retval_send(ret);
return ret;
}
-static int get_ifindex(const char *name) {
- int fd = -1;
- struct ifreq ifreq;
- if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
- daemon_log(LOG_ERR, "socket() failed: %s", strerror(errno));
- goto fail;
- }
+static void help(FILE *f, const char *a0) {
+ fprintf(f,
+ "%s [options] INTERFACE\n"
+ " -h --help Show this help\n"
+ " -D --daemonize Daemonize after startup\n"
+ " -s --syslog Write log messages to syslog(3) instead of STDERR\n"
+ " -k --kill Kill a running daemon\n"
+ " -r --refresh Request a running daemon to refresh it's IP address\n"
+ " -c --check Return 0 if a daemon is already running\n"
+ " -V --version Show version\n"
+ " -S --start=ADDRESS Start with this address from the IPv4LL range 169.254.0.0/16\n"
+ " -w --wait Wait until an address has been acquired before daemonizing\n"
+ " --no-proc-title Don't modify process title\n"
+ " --debug Increase verbosity\n",
+ a0);
+}
+
+static int parse_command_line(int argc, char *argv[]) {
+ int c;
+
+ enum {
+ OPTION_NO_PROC_TITLE = 256,
+ OPTION_DEBUG
+ };
+
+ static const struct option long_options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "daemonize", no_argument, NULL, 'D' },
+ { "syslog", no_argument, NULL, 's' },
+ { "kill", no_argument, NULL, 'k' },
+ { "refresh", no_argument, NULL, 'r' },
+ { "check", no_argument, NULL, 'c' },
+ { "version", no_argument, NULL, 'V' },
+ { "start", required_argument, NULL, 'S' },
+ { "wait", no_argument, NULL, 'w' },
+ { "no-proc-title", no_argument, NULL, OPTION_NO_PROC_TITLE },
+ { "debug", no_argument, NULL, OPTION_DEBUG },
+ { NULL, 0, NULL, 0 }
+ };
- memset(&ifreq, 0, sizeof(ifreq));
- strncpy(ifreq.ifr_name, name, IFNAMSIZ-1);
- ifreq.ifr_name[IFNAMSIZ-1] = 0;
+ opterr = 0;
+ while ((c = getopt_long(argc, argv, "hDkVrcS:", long_options, NULL)) >= 0) {
+
+ switch(c) {
+ case 's':
+ use_syslog = 1;
+ break;
+ case 'h':
+ command = DAEMON_HELP;
+ break;
+ case 'D':
+ daemonize = 1;
+ break;
+ case 'k':
+ command = DAEMON_KILL;
+ break;
+ case 'V':
+ command = DAEMON_VERSION;
+ break;
+ case 'r':
+ command = DAEMON_REFRESH;
+ break;
+ case 'c':
+ command = DAEMON_CHECK;
+ break;
+ case 'S':
+
+ if ((start_address = inet_addr(optarg)) == (uint32_t) -1) {
+ fprintf(stderr, "Failed to parse IP address '%s'.", optarg);
+ return -1;
+ }
+ break;
+ case 'w':
+ wait_for_address = 1;
+ break;
+
+ case OPTION_NO_PROC_TITLE:
+ modify_proc_title = 0;
+ break;
- if (ioctl(fd, SIOCGIFINDEX, &ifreq) < 0) {
- daemon_log(LOG_ERR, "SIOCGIFINDEX failed: %s", strerror(errno));
- goto fail;
+ case OPTION_DEBUG:
+ debug = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Invalid command line argument: %c\n", c);
+ return -1;
+ }
}
- return ifreq.ifr_ifindex;
+ if (command == DAEMON_RUN ||
+ command == DAEMON_KILL ||
+ command == DAEMON_REFRESH ||
+ command == DAEMON_CHECK) {
-fail:
+ if (optind >= argc) {
+ fprintf(stderr, "Missing interface name.\n");
+ return -1;
+ }
- if (fd >= 0)
- close(fd);
-
- return -1;
+ interface_name = argv[optind++];
+ }
+
+ if (optind != argc) {
+ fprintf(stderr, "Too many arguments\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static const char* pid_file_proc(void) {
+ return pid_file_name;
}
int main(int argc, char*argv[]) {
- int ret = 1;
- int ifindex;
- uint32_t addr = 0;
+ int r = 1;
+ int wrote_pid_file = 0;
avahi_init_proc_title(argc, argv);
-
- init_rand_seed();
- if ((ifindex = get_ifindex(argc >= 2 ? argv[1] : "eth0")) < 0)
- goto fail;
+ if ((argv0 = strrchr(argv[0], '/')))
+ argv0++;
+ else
+ argv0 = argv[0];
- if (argc >= 3)
- addr = inet_addr(argv[2]);
-
- if (loop(ifindex, addr) < 0)
- goto fail;
+ daemon_pid_file_ident = daemon_log_ident = argv0;
+ daemon_pid_file_proc = pid_file_proc;
- ret = 0;
+ if (parse_command_line(argc, argv) < 0)
+ goto finish;
+ pid_file_name = avahi_strdup_printf(AVAHI_RUNTIME_DIR"/avahi-autoipd.%s.pid", interface_name);
+
+ if (command == DAEMON_RUN) {
+ pid_t pid;
+ int ifindex;
+
+ init_rand_seed();
+
+ if ((ifindex = if_nametoindex(interface_name)) <= 0) {
+ daemon_log(LOG_ERR, "Failed to get index for interface name '%s': %s", interface_name, strerror(errno));
+ goto finish;
+ }
+
+ if (getuid() != 0) {
+ daemon_log(LOG_ERR, "This program is intended to be run as root.");
+ goto finish;
+ }
+
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ daemon_log(LOG_ERR, "Daemon already running on PID %u", pid);
+ goto finish;
+ }
+
+ if (daemonize) {
+ daemon_retval_init();
+
+ if ((pid = daemon_fork()) < 0)
+ goto finish;
+ else if (pid != 0) {
+ int ret;
+ /** Parent **/
+
+ if ((ret = daemon_retval_wait(20)) < 0) {
+ daemon_log(LOG_ERR, "Could not receive return value from daemon process.");
+ goto finish;
+ }
+
+ r = ret;
+ goto finish;
+ }
+
+ /* Child */
+ }
+
+ if (use_syslog || daemonize)
+ daemon_log_use = DAEMON_LOG_SYSLOG;
+
+ chdir("/");
+
+ if (daemon_pid_file_create() < 0) {
+ daemon_log(LOG_ERR, "Failed to create PID file: %s", strerror(errno));
+
+ if (daemonize)
+ daemon_retval_send(1);
+ goto finish;
+ } else
+ wrote_pid_file = 1;
+
+ if (loop(ifindex, start_address) < 0)
+ goto finish;
+
+ r = 0;
+ } else if (command == DAEMON_HELP) {
+ help(stdout, argv0);
+
+ r = 0;
+ } else if (command == DAEMON_VERSION) {
+ printf("%s "PACKAGE_VERSION"\n", argv0);
+
+ r = 0;
+ } else if (command == DAEMON_KILL) {
+ if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
+ daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
+ goto finish;
+ }
+
+ r = 0;
+ } else if (command == DAEMON_REFRESH) {
+ if (daemon_pid_file_kill(SIGHUP) < 0) {
+ daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
+ goto finish;
+ }
+
+ r = 0;
+ } else if (command == DAEMON_CHECK)
+ r = (daemon_pid_file_is_running() >= 0) ? 0 : 1;
+
+
+finish:
+
+ if (daemonize)
+ daemon_retval_done();
-fail:
-
- return ret;
+ if (wrote_pid_file)
+ daemon_pid_file_remove();
+
+ return r;
}
/* TODO:
-- man page
-- user script
- chroot/drop privs/caps
-- daemonize
-- defend
-- signals
+- user script
- store last used address
-- cmdline
-- setproctitle
+- man page
*/
diff --git a/avahi-autoipd/main.h b/avahi-autoipd/main.h
index 61d5ed6..adadc99 100644
--- a/avahi-autoipd/main.h
+++ b/avahi-autoipd/main.h
@@ -28,6 +28,7 @@ typedef enum Event {
EVENT_TIMEOUT,
EVENT_ROUTABLE_ADDR_CONFIGURED,
EVENT_ROUTABLE_ADDR_UNCONFIGURED,
+ EVENT_REFRESH_REQUEST
} Event;
typedef enum State {
--
cgit
From 25ae811f3393baed4930f30278a7595dd8122dfb Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 01:26:11 +0000
Subject: set the interface to IFF_UP before using it. handle POLLERR on the
packet socket properly by recreating our socket.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1291 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/iface-linux.c | 1 -
avahi-autoipd/main.c | 94 ++++++++++++++++++++++++++++++++++++++-------
2 files changed, 80 insertions(+), 15 deletions(-)
diff --git a/avahi-autoipd/iface-linux.c b/avahi-autoipd/iface-linux.c
index 5e2996a..6f2ca1f 100644
--- a/avahi-autoipd/iface-linux.c
+++ b/avahi-autoipd/iface-linux.c
@@ -106,7 +106,6 @@ static int process_nlmsg(struct nlmsghdr *n) {
if ((ifi->ifi_flags & IFF_LOOPBACK) ||
(ifi->ifi_flags & IFF_NOARP) ||
- !(ifi->ifi_flags & IFF_UP) ||
ifi->ifi_type != ARPHRD_ETHER) {
daemon_log(LOG_ERR, "Interface not suitable.");
return -1;
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 04c3111..617a547 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -276,6 +276,44 @@ static void set_state(State st, int reset_counter, uint32_t address) {
}
}
+static int interface_up(int iface) {
+ int fd = -1;
+ struct ifreq ifreq;
+
+ if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ daemon_log(LOG_ERR, "socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ if (!if_indextoname(iface, ifreq.ifr_name)) {
+ daemon_log(LOG_ERR, "if_indextoname() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifreq) < 0) {
+ daemon_log(LOG_ERR, "SIOCGIFFLAGS failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ ifreq.ifr_flags |= IFF_UP;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifreq) < 0) {
+ daemon_log(LOG_ERR, "SIOCSIFFLAGS failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ close(fd);
+
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
static int do_callout(CalloutEvent event, int iface, uint32_t addr) {
char buf[64], ifname[IFNAMSIZ];
const char * const event_table[CALLOUT_MAX] = {
@@ -297,6 +335,9 @@ static int open_socket(int iface, uint8_t *hw_address) {
int fd = -1;
struct sockaddr_ll sa;
socklen_t sa_len;
+
+ if (interface_up(iface) < 0)
+ goto fail;
if ((fd = socket(PF_PACKET, SOCK_DGRAM, 0)) < 0) {
daemon_log(LOG_ERR, "socket() failed: %s", strerror(errno));
@@ -307,18 +348,18 @@ static int open_socket(int iface, uint8_t *hw_address) {
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(ETH_P_ARP);
sa.sll_ifindex = iface;
-
+
if (bind(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
daemon_log(LOG_ERR, "bind() failed: %s", strerror(errno));
goto fail;
}
-
+
sa_len = sizeof(sa);
if (getsockname(fd, (struct sockaddr*) &sa, &sa_len) < 0) {
daemon_log(LOG_ERR, "getsockname() failed: %s", strerror(errno));
goto fail;
}
-
+
if (sa.sll_halen != ETHER_ADDRLEN) {
daemon_log(LOG_ERR, "getsockname() returned invalid hardware address.");
goto fail;
@@ -361,6 +402,7 @@ static int recv_packet(int fd, void **packet, size_t *packet_len) {
int s;
struct sockaddr_ll sa;
socklen_t sa_len;
+ ssize_t r;
assert(fd >= 0);
assert(packet);
@@ -373,22 +415,26 @@ static int recv_packet(int fd, void **packet, size_t *packet_len) {
goto fail;
}
- assert(s > 0);
+ if (s <= 0)
+ s = 4096;
- *packet_len = (size_t) s;
*packet = avahi_new(uint8_t, s);
sa_len = sizeof(sa);
- if (recvfrom(fd, *packet, s, 0, (struct sockaddr*) &sa, &sa_len) < 0) {
+ if ((r = recvfrom(fd, *packet, s, 0, (struct sockaddr*) &sa, &sa_len)) < 0) {
daemon_log(LOG_ERR, "recvfrom() failed: %s", strerror(errno));
goto fail;
}
+ *packet_len = (size_t) r;
+
return 0;
fail:
- if (*packet)
+ if (*packet) {
avahi_free(*packet);
+ *packet = NULL;
+ }
return -1;
}
@@ -655,26 +701,46 @@ static int loop(int iface, uint32_t addr) {
event = EVENT_TIMEOUT;
next_wakeup_valid = 0;
} else {
+
+
+ if (pollfds[FD_ARP].revents) {
- if (pollfds[FD_ARP].revents == POLLIN) {
- if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
- goto fail;
+ if (pollfds[FD_ARP].revents == POLLERR) {
+ /* The interface is probably down, let's recreate our socket */
+
+ close(fd);
+
+ if ((fd = open_socket(iface, hw_address)) < 0)
+ goto fail;
+
+ pollfds[FD_ARP].fd = fd;
+
+ } else {
- if (in_packet)
- event = EVENT_PACKET;
+ assert(pollfds[FD_ARP].revents == POLLIN);
+
+ if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
+ goto fail;
+
+ if (in_packet)
+ event = EVENT_PACKET;
+ }
}
if (event == EVENT_NULL &&
- pollfds[FD_IFACE].revents == POLLIN) {
+ pollfds[FD_IFACE].revents) {
+ assert(pollfds[FD_IFACE].revents == POLLIN);
+
if (iface_process(&event) < 0)
goto fail;
}
if (event == EVENT_NULL &&
- pollfds[FD_SIGNAL].revents == POLLIN) {
+ pollfds[FD_SIGNAL].revents) {
int sig;
+ assert(pollfds[FD_SIGNAL].revents == POLLIN);
if ((sig = daemon_signal_next()) <= 0) {
daemon_log(LOG_ERR, "daemon_signal_next() failed");
--
cgit
From 5b67d0bbb21412bcdf7a271ddf34823f8776dc06 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 01:56:55 +0000
Subject: add new option --force-bind
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1292 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 617a547..1b3c9ed 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -108,6 +108,7 @@ static int wait_for_address = 0;
static int use_syslog = 0;
static int debug = 0;
static int modify_proc_title = 1;
+static int force_bind = 0;
static enum {
DAEMON_RUN,
@@ -488,7 +489,9 @@ static int loop(int iface, uint32_t addr) {
if ((iface_fd = iface_init(iface)) < 0)
goto fail;
- if (iface_get_initial_state(&st) < 0)
+ if (force_bind)
+ st = STATE_START;
+ else if (iface_get_initial_state(&st) < 0)
goto fail;
if (addr && !is_ll_address(addr)) {
@@ -644,7 +647,7 @@ static int loop(int iface, uint32_t addr) {
set_state(STATE_SLEEPING, 1, addr);
next_wakeup_valid = 0;
- } else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING) {
+ } else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING && !force_bind) {
daemon_log(LOG_INFO, "No longer a routable address configured, restarting probe process.");
@@ -653,7 +656,7 @@ static int loop(int iface, uint32_t addr) {
elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
next_wakeup_valid = 1;
- } else if (event == EVENT_REFRESH_REQUEST && state == STATE_RUNNING) {
+ } else if (event == EVENT_REFRESH_REQUEST && state == STATE_RUNNING && !force_bind) {
/* The user requested a reannouncing of the address by a SIGHUP */
daemon_log(LOG_INFO, "Reannouncing address.");
@@ -800,9 +803,13 @@ static void help(FILE *f, const char *a0) {
" -r --refresh Request a running daemon to refresh it's IP address\n"
" -c --check Return 0 if a daemon is already running\n"
" -V --version Show version\n"
- " -S --start=ADDRESS Start with this address from the IPv4LL range 169.254.0.0/16\n"
- " -w --wait Wait until an address has been acquired before daemonizing\n"
+ " -S --start=ADDRESS Start with this address from the IPv4LL range\n"
+ " 169.254.0.0/16\n"
+ " -w --wait Wait until an address has been acquired before\n"
+ " daemonizing\n"
" --no-proc-title Don't modify process title\n"
+ " --force-bind Assign an IPv4LL address even if routable address\n"
+ " is already assigned\n"
" --debug Increase verbosity\n",
a0);
}
@@ -812,6 +819,7 @@ static int parse_command_line(int argc, char *argv[]) {
enum {
OPTION_NO_PROC_TITLE = 256,
+ OPTION_FORCE_BIND,
OPTION_DEBUG
};
@@ -826,6 +834,7 @@ static int parse_command_line(int argc, char *argv[]) {
{ "start", required_argument, NULL, 'S' },
{ "wait", no_argument, NULL, 'w' },
{ "no-proc-title", no_argument, NULL, OPTION_NO_PROC_TITLE },
+ { "force-bind", no_argument, NULL, OPTION_FORCE_BIND },
{ "debug", no_argument, NULL, OPTION_DEBUG },
{ NULL, 0, NULL, 0 }
};
@@ -874,6 +883,10 @@ static int parse_command_line(int argc, char *argv[]) {
debug = 1;
break;
+ case OPTION_FORCE_BIND:
+ force_bind = 1;
+ break;
+
default:
fprintf(stderr, "Invalid command line argument: %c\n", c);
return -1;
--
cgit
From 4287a521ead9fca175c5f01594288d87433c7f77 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 15:14:22 +0000
Subject: fix service type database creation on solaris
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1293 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
service-type-database/Makefile.am | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/service-type-database/Makefile.am b/service-type-database/Makefile.am
index 1692a0b..d25f1a2 100644
--- a/service-type-database/Makefile.am
+++ b/service-type-database/Makefile.am
@@ -41,13 +41,15 @@ endif
if HAVE_DBM
noinst_SCRIPTS=build-db
-pkgdata_DATA+=service-types.db.*
+pkgdata_DATA+=service-types.db.pag service-types.db.dir
build-db: build-db.in
sed -e 's,@PYTHON\@,$(PYTHON),g' \
-e 's,@DBM\@,dbm,g' $< > $@
chmod +x $@
+service-types.db.pag: service-types.db
+service-types.db.dir: service-types.db
service-types.db: service-types build-db
$(PYTHON) build-db $(srcdir)/$< $@
--
cgit
From 16ede7fb33fde566f43390d8dcf22d49f52c80e4 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 15:48:34 +0000
Subject: add service file fragment for _sftp-ssh._tcp (closes #58)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1294 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/Makefile.am | 4 +++-
avahi-daemon/sftp-ssh.service | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
create mode 100644 avahi-daemon/sftp-ssh.service
diff --git a/avahi-daemon/Makefile.am b/avahi-daemon/Makefile.am
index 15cbb57..973a2b5 100644
--- a/avahi-daemon/Makefile.am
+++ b/avahi-daemon/Makefile.am
@@ -70,7 +70,8 @@ pkgsysconf_DATA = \
hosts
service_DATA = \
- ssh.service
+ ssh.service \
+ sftp-ssh.service
pkgdata_DATA = \
avahi-service.dtd
@@ -148,6 +149,7 @@ EXTRA_DIST = \
HostNameResolver.introspect \
RecordBrowser.introspect \
ssh.service \
+ sftp-ssh.service \
hosts \
example.service \
introspect.dtd \
diff --git a/avahi-daemon/sftp-ssh.service b/avahi-daemon/sftp-ssh.service
new file mode 100644
index 0000000..c65535a
--- /dev/null
+++ b/avahi-daemon/sftp-ssh.service
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+ SFTP File Transfer on %h
+
+
+ _sftp-ssh._tcp
+ 22
+
+
+
--
cgit
From 59f3e8bef545762113c041a1ef5868e08b353045 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 17:17:18 +0000
Subject: autoconf love:
* use autoconf to check whether dbus_connection_close() is available and use it
if it is, don't do those checks based on the D-Bus version.
* Make use of dbus_bus_get_private() if it is available.
* Since D-Bus agreeed on a cononical way to write their name, use that way in
configure.ac.
* Add configure options to define the UNIX user/group for avahi-autoipd
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1295 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-client/client.c | 21 +++++++++++----
avahi-daemon/dbus-protocol.c | 8 +++---
configure.ac | 64 ++++++++++++++++++++++++++++++--------------
3 files changed, 64 insertions(+), 29 deletions(-)
diff --git a/avahi-client/client.c b/avahi-client/client.c
index 825e88f..f665613 100644
--- a/avahi-client/client.c
+++ b/avahi-client/client.c
@@ -67,7 +67,7 @@ static void client_set_state (AvahiClient *client, AvahiServerState state) {
switch (client->state) {
case AVAHI_CLIENT_FAILURE:
if (client->bus) {
-#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR >= 62)
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
dbus_connection_close(client->bus);
#else
dbus_connection_disconnect(client->bus);
@@ -441,12 +441,18 @@ static int init_server(AvahiClient *client, int *ret_error) {
}
/* This function acts like dbus_bus_get but creates a private
- * connection instead. Eventually this should be replaced by a DBUS
- * provided version. */
+ * connection instead. */
static DBusConnection* avahi_dbus_bus_get(DBusError *error) {
DBusConnection *c;
- const char *a;
+#ifdef HAVE_DBUS_BUS_GET_PRIVATE
+ if (!(c = dbus_bus_get_private(DBUS_BUS_SYSTEM, error)))
+ return NULL;
+
+ dbus_connection_set_exit_on_disconnect(c, FALSE);
+#else
+ const char *a;
+
if (!(a = getenv("DBUS_SYSTEM_BUS_ADDRESS")) || !*a)
a = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
@@ -456,10 +462,15 @@ static DBusConnection* avahi_dbus_bus_get(DBusError *error) {
dbus_connection_set_exit_on_disconnect(c, FALSE);
if (!dbus_bus_register(c, error)) {
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
dbus_connection_close(c);
+#else
+ dbus_connection_disconnect(c);
+#endif
dbus_connection_unref(c);
return NULL;
}
+#endif
return c;
}
@@ -602,7 +613,7 @@ void avahi_client_free(AvahiClient *client) {
if (client->bus)
/* Disconnect in advance, so that the free() functions won't
* issue needless server calls */
-#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR >= 62)
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
dbus_connection_close(client->bus);
#else
dbus_connection_disconnect(client->bus);
diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c
index 4fb58d2..e74caa3 100644
--- a/avahi-daemon/dbus-protocol.c
+++ b/avahi-daemon/dbus-protocol.c
@@ -1130,7 +1130,7 @@ fail:
dbus_error_free(&error);
if (server->bus) {
-#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR >= 62)
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
dbus_connection_close(server->bus);
#else
dbus_connection_disconnect(server->bus);
@@ -1151,7 +1151,7 @@ static void dbus_disconnect(void) {
assert(server->n_clients == 0);
if (server->bus) {
-#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR >= 62)
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
dbus_connection_close(server->bus);
#else
dbus_connection_disconnect(server->bus);
@@ -1180,7 +1180,7 @@ int dbus_protocol_setup(const AvahiPoll *poll_api, int _disable_user_service_pub
if (!force)
goto fail;
- avahi_log_warn("WARNING: Failed to contact D-BUS daemon, retrying in %ims.", RECONNECT_MSEC);
+ avahi_log_warn("WARNING: Failed to contact D-Bus daemon, retrying in %ims.", RECONNECT_MSEC);
avahi_elapse_time(&tv, RECONNECT_MSEC, 0);
server->reconnect_timeout = server->poll_api->timeout_new(server->poll_api, &tv, reconnect_callback, NULL);
@@ -1190,7 +1190,7 @@ int dbus_protocol_setup(const AvahiPoll *poll_api, int _disable_user_service_pub
fail:
if (server->bus) {
-#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR >= 62)
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
dbus_connection_close(server->bus);
#else
dbus_connection_disconnect(server->bus);
diff --git a/configure.ac b/configure.ac
index 8c2b1b5..7cf15b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -164,7 +164,7 @@ case $with_distro in
AC_MSG_ERROR([Linux distribution autodetection failed, you must specify the distribution to target using --with-distro=DISTRO, set DISTRO to none if your distribution is not supported.])
;;
*)
- AC_MSG_ERROR([Your distribution (${with_distro}) is not yet supported, init scripts and dbus configuration will not be installed! (patches welcome), you can specify --with-distro=none to skip this check])
+ AC_MSG_ERROR([Your distribution (${with_distro}) is not yet supported, init scripts and D-Bus configuration will not be installed! (patches welcome), you can specify --with-distro=none to skip this check])
;;
esac
@@ -372,10 +372,10 @@ fi
AM_CONDITIONAL(HAVE_GTK, test "x$HAVE_GTK" = "xyes")
#
-# D-BUS
+# D-Bus
#
AC_ARG_ENABLE(dbus,
- AS_HELP_STRING([--disable-dbus],[Disable use of D-BUS]),
+ AS_HELP_STRING([--disable-dbus],[Disable use of D-Bus]),
[case "${enableval}" in
yes) HAVE_DBUS=yes ;;
no) HAVE_DBUS=no ;;
@@ -383,8 +383,8 @@ AC_ARG_ENABLE(dbus,
esac],
[HAVE_DBUS=yes])
-AC_ARG_WITH(dbus-sys, AS_HELP_STRING([--with-dbus-sys=], [where D-BUS system.d directory is]))
-AC_ARG_WITH(dbus-system-socket, AS_HELP_STRING([--with-dbus-system-address=], [where the dbus system socket is, you probably want to put unix:path= at the start]))
+AC_ARG_WITH(dbus-sys, AS_HELP_STRING([--with-dbus-sys=], [Path to D-Bus system.d directory]))
+AC_ARG_WITH(dbus-system-socket, AS_HELP_STRING([--with-dbus-system-address=], [Path to the D-Bus system socket, you probably want to put unix:path= at the start. Only needed for very old D-Bus releases]))
DBUS_VERSION="Disabled"
DBUS_SYS_DIR="Disabled"
@@ -392,7 +392,7 @@ DBUS_SYSTEM_BUS_DEFAULT_ADDRESS="Disabled"
if test "x$HAVE_DBUS" = "xyes" ; then
PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 0.34 ])
- AC_DEFINE(HAVE_DBUS, 1, [Whether we have D-BUS or not])
+ AC_DEFINE(HAVE_DBUS, 1, [Whether we have D-Bus or not])
DBUS_VERSION=`$PKG_CONFIG dbus-1 --modversion`
DBUS_VERSION_MAJOR=`echo $DBUS_VERSION | awk -F. '{print $1}'`
@@ -409,16 +409,16 @@ if test "x$HAVE_DBUS" = "xyes" ; then
fi
if test "z$DBUS_VERSION_MAJOR" = "z0" -a "z$DBUS_VERSION_MINOR" = "z0" -a "z$DBUS_VERSION_MICRO" = "z0"; then
- echo "Error: Couldn't determine the version of your DBUS package."
+ echo "Error: Couldn't determine the version of your D-Bus package."
echo " This is probably an error in this script, please report it"
echo " along with the following information:"
- echo " Base DBUS version ='$DBUS_VERSION'"
+ echo " Base D-Buss version ='$DBUS_VERSION'"
echo " DBUS_VERSION_MAJOR='$DBUS_VERSION_MAJOR'"
echo " DBUS_VERSION_MINOR='$DBUS_VERSION_MINOR'"
echo " DBUS_VERSION_MICRO='$DBUS_VERSION_MICRO'"
exit 1
else
- echo "Your dbus version is $DBUS_VERSION_MAJOR,$DBUS_VERSION_MINOR,$DBUS_VERSION_MICRO."
+ echo "Your D-Bus version is $DBUS_VERSION_MAJOR,$DBUS_VERSION_MINOR,$DBUS_VERSION_MICRO."
DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MAJOR=$DBUS_VERSION_MAJOR"
DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MINOR=$DBUS_VERSION_MINOR"
DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MICRO=$DBUS_VERSION_MICRO"
@@ -438,11 +438,11 @@ if test "x$HAVE_DBUS" = "xyes" ; then
if ! test -z "$with_dbus_system_address" ; then
DBUS_SYSTEM_BUS_DEFAULT_ADDRESS="$with_dbus_system_address"
else
- # This is ugly, but D-BUS doesn't export this address for us
+ # This is ugly, but D-Bus doesn't export this address for us
# so we have to guess, pretty much all setups i've seen have
# it in /var/lib/dbus or /var/run/dbus, and its defaulted to
# /var/run upstream so we will try guess first then default
- # to /var/run/dbus
+ # to /var/run/dbus.
DBUS_SYSTEM_BUS_DEFAULT_ADDRESS="unix:path=/var/run/dbus/system_bus_socket"
TRY_SOCKETS="/var/lib/dbus/system_bus_socket /var/run/dbus/system_bus_socket ${localstatedir}/run/dbus/system_bus_socket ${prefix}/var/run/dbus/system_bus_socket"
@@ -454,6 +454,10 @@ if test "x$HAVE_DBUS" = "xyes" ; then
fi
AC_SUBST(DBUS_SYSTEM_BUS_DEFAULT_ADDRESS)
+ SAVED_LIBS="$LIBS"
+ LIBS="$LIBS $DBUS_LIBS"
+ AC_CHECK_FUNCS([dbus_connection_close dbus_bus_get_private])
+ LIBS="$SAVED_LIBS"
fi
AM_CONDITIONAL(HAVE_DBUS, test "x$HAVE_DBUS" = "xyes")
@@ -577,7 +581,7 @@ if test "x$HAVE_PYTHON" = "xyes" ; then
if test "x$HAVE_DBUS" = "xyes" ; then
AC_ARG_ENABLE(python-dbus,
- AS_HELP_STRING([--disable-python-dbus],[Disable use of D-BUS in Python]),
+ AS_HELP_STRING([--disable-python-dbus],[Disable use of D-Bus in Python]),
[case "${enableval}" in
yes) HAVE_PYTHON_DBUS=yes ;;
no) HAVE_PYTHON_DBUS=no ;;
@@ -674,9 +678,9 @@ AC_ARG_ENABLE(autoipd,
AM_CONDITIONAL(ENABLE_AUTOIPD, test "x$ENABLE_AUTOIPD" = "xyes")
#
-# Defining Avahi User and Group.
+# Defining users and groups
#
-AC_ARG_WITH(avahi_user, AS_HELP_STRING([--with-avahi-user=],[User for running the Avahi daemon (avahi)]))
+AC_ARG_WITH(avahi_user, AS_HELP_STRING([--with-avahi-user=],[User for running avahi-daemon (avahi)]))
if test -z "$with_avahi_user" ; then
AVAHI_USER=avahi
else
@@ -685,7 +689,7 @@ fi
AC_SUBST(AVAHI_USER)
AC_DEFINE_UNQUOTED(AVAHI_USER,"$AVAHI_USER", [User for running the Avahi daemon])
-AC_ARG_WITH(avahi_group,AS_HELP_STRING([--with-avahi-group=],[Group for Avahi (avahi)]))
+AC_ARG_WITH(avahi_group,AS_HELP_STRING([--with-avahi-group=],[Group for running avahi-daemon (avahi)]))
if test -z "$with_avahi_group" ; then
AVAHI_GROUP=avahi
else
@@ -703,6 +707,24 @@ fi
AC_SUBST(AVAHI_PRIV_ACCESS_GROUP)
AC_DEFINE_UNQUOTED(AVAHI_PRIV_ACCESS_GROUP,"$AVAHI_PRIV_ACCESS_GROUP", [Privileged access group for Avahi clients])
+AC_ARG_WITH(autoipd_user, AS_HELP_STRING([--with-autipd-user=],[User for running the avahi-autoipd daemon (avahi-autoipd)]))
+if test -z "$with_autoipd_user" ; then
+ AVAHI_AUTOIPD_USER=avahi-autoipd
+else
+ AVAHI_AUTOIPD_USER=$with_autoipd_user
+fi
+AC_SUBST(AVAHI_AUTOIPD_USER)
+AC_DEFINE_UNQUOTED(AVAHI_AUTOIPD_USER,"$AVAHI_AUTOIPD_USER", [User for running the avahi-autoipd daemon])
+
+AC_ARG_WITH(autoipd_group,AS_HELP_STRING([--with-autoipd-group=],[Group for running the avahi-autoipd daemon (avahi-autoipd)]))
+if test -z "$with_autoipd_group" ; then
+ AVAHI_AUTOIPD_GROUP=avahi-autoipd
+else
+ AVAHI_AUTOIPD_GROUP=$with_autoipd_group
+fi
+AC_SUBST(AVAHI_AUTOIPD_GROUP)
+AC_DEFINE_UNQUOTED(AVAHI_AUTOIPD_GROUP,"$AVAHI_AUTOIPD_GROUP", [Group for running the avahi-autoipd daemon])
+
#
# Avahi runtime dir
#
@@ -877,7 +899,7 @@ echo "
CFLAGS: ${CFLAGS}
Enable GLIB: ${HAVE_GLIB}
Enable GTK: ${HAVE_GTK}
- Enable D-BUS: ${HAVE_DBUS}
+ Enable D-Bus: ${HAVE_DBUS}
Enable Expat: ${HAVE_EXPAT}
Enable GDBM: ${HAVE_GDBM}
Enable DBM: ${HAVE_DBM}
@@ -890,9 +912,11 @@ echo "
Enable Mono: ${HAVE_MONO}
Enable Monodoc: ${HAVE_MONODOC}
Distribution/OS: ${with_distro}
- User for Avahi daemon: ${AVAHI_USER}
- Group for Avahi daemon: ${AVAHI_GROUP}
- Priviliged Access Group for Avahi Clients: ${AVAHI_PRIV_ACCESS_GROUP}
+ User for avahi-daemon: ${AVAHI_USER}
+ Group for avahi-daemon: ${AVAHI_GROUP}
+ Priviliged access group for Avahi clients: ${AVAHI_PRIV_ACCESS_GROUP}
+ User for avahi-autopid: ${AVAHI_AUTOIPD_USER}
+ Group for avahi-autoipd: ${AVAHI_AUTOIPD_GROUP}
Enable chroot(): ${enable_chroot}
"
@@ -908,7 +932,7 @@ if test "x$BUILD_DAEMON" = "xyes" -a "x$HAVE_DBUS" = "xyes" -a "x$HAVE_PYTHON" =
BUILD_PYTHON=yes
fi
-BUILD_CLIENT="no (You need avahi-daemon and dbus!)"
+BUILD_CLIENT="no (You need avahi-daemon and D-Bus!)"
if test "x$BUILD_DAEMON" = "xyes" -a "x$HAVE_DBUS" = "xyes" ; then
BUILD_CLIENT=yes
--
cgit
From 1c06401a7a65f270d0b3ffe8fa6f97cb85859cd2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 17:20:30 +0000
Subject: Some more s/D-BUS/D-Bus/g
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1296 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/dbus-protocol.c | 4 ++--
avahi-daemon/main.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c
index e74caa3..0c32250 100644
--- a/avahi-daemon/dbus-protocol.c
+++ b/avahi-daemon/dbus-protocol.c
@@ -189,7 +189,7 @@ static DBusHandlerResult msg_signal_filter_impl(AVAHI_GCC_UNUSED DBusConnection
struct timeval tv;
if (server->reconnect) {
- avahi_log_warn("Disconnnected from D-BUS, trying to reconnect in %ims...", RECONNECT_MSEC);
+ avahi_log_warn("Disconnnected from D-Bus, trying to reconnect in %ims...", RECONNECT_MSEC);
dbus_disconnect();
@@ -200,7 +200,7 @@ static DBusHandlerResult msg_signal_filter_impl(AVAHI_GCC_UNUSED DBusConnection
else
server->reconnect_timeout = server->poll_api->timeout_new(server->poll_api, &tv, reconnect_callback, NULL);
} else {
- avahi_log_warn("Disconnnected from D-BUS, exiting.");
+ avahi_log_warn("Disconnnected from D-Bus, exiting.");
raise(SIGQUIT);
}
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index ca9c034..b230ff7 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -756,7 +756,7 @@ static int run_server(DaemonConfig *c) {
#endif
) < 0) {
- avahi_log_warn("WARNING: Failed to contact D-BUS daemon.");
+ avahi_log_warn("WARNING: Failed to contact D-Bus daemon.");
if (c->fail_on_missing_dbus)
goto finish;
--
cgit
From 50c10f3b83474d421f78b898ce374bdafd3cd9bc Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 17:22:29 +0000
Subject: bump D-Bus API revision (forgotten in 0.6.13)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1297 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-common/dbus.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/avahi-common/dbus.h b/avahi-common/dbus.h
index f11eb17..a93988d 100644
--- a/avahi-common/dbus.h
+++ b/avahi-common/dbus.h
@@ -22,7 +22,7 @@
USA.
***/
-/** \file dbus.h Some definitions for the DBUS interface */
+/** \file dbus.h Some definitions for the D-Bus interface */
#include
#include
@@ -41,14 +41,14 @@ AVAHI_C_DECL_BEGIN
#define AVAHI_DBUS_INTERFACE_SERVICE_RESOLVER AVAHI_DBUS_NAME".ServiceResolver"
#define AVAHI_DBUS_INTERFACE_RECORD_BROWSER AVAHI_DBUS_NAME".RecordBrowser"
-/** The DBUS API version identifier. The first byte specifies the API
+/** The D-Bus API version identifier. The first byte specifies the API
release, the second byte specifies the revision. If the revision
number is increased the API has been extended but is downwards
compatible. If the release changes compatibility is lost.
Avahi 0.6 implements API version 0x0201;
Avahi 0.6.1 implements API version 0x0202 */
-#define AVAHI_DBUS_API_VERSION ((uint32_t) 0x0202)
+#define AVAHI_DBUS_API_VERSION ((uint32_t) 0x0203)
#define AVAHI_DBUS_ERR_OK "org.freedesktop.Avahi.Success"
#define AVAHI_DBUS_ERR_FAILURE "org.freedesktop.Avahi.Failure"
--
cgit
From 0456242ed7e6ad6bb2afd2f8210e86dfa80ab9d2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 17:26:29 +0000
Subject: fix a few more s/DBUS/D-Bus/
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1298 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-common/error.c | 2 +-
avahi-common/error.h | 2 +-
avahi-daemon/dbus-protocol.c | 2 +-
examples/core-browse-services.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/avahi-common/error.c b/avahi-common/error.c
index 53ff4f2..92a2234 100644
--- a/avahi-common/error.c
+++ b/avahi-common/error.c
@@ -52,7 +52,7 @@ const char *avahi_strerror(int error) {
"Access denied",
"Invalid operation",
- "An unexpected DBUS error occured",
+ "An unexpected D-Bus error occured",
"Daemon connection failed",
"Memory exhausted",
"The object passed in was not valid",
diff --git a/avahi-common/error.h b/avahi-common/error.h
index ee0626f..3e014c2 100644
--- a/avahi-common/error.h
+++ b/avahi-common/error.h
@@ -54,7 +54,7 @@ enum {
AVAHI_ERR_ACCESS_DENIED = -20, /**< Access denied */
AVAHI_ERR_INVALID_OPERATION = -21, /**< Invalid operation */
- AVAHI_ERR_DBUS_ERROR = -22, /**< An unexpected DBUS error occured */
+ AVAHI_ERR_DBUS_ERROR = -22, /**< An unexpected D-Bus error occured */
AVAHI_ERR_DISCONNECTED = -23, /**< Daemon connection failed */
AVAHI_ERR_NO_MEMORY = -24, /**< Memory exhausted */
AVAHI_ERR_INVALID_OBJECT = -25, /**< The object passed to this function was invalid */
diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c
index 0c32250..3de5845 100644
--- a/avahi-daemon/dbus-protocol.c
+++ b/avahi-daemon/dbus-protocol.c
@@ -1102,7 +1102,7 @@ static int dbus_connect(void) {
goto fail;
}
- avahi_log_error("Failed to acquire DBUS name '"AVAHI_DBUS_NAME"'");
+ avahi_log_error("Failed to acquire D-Bus name '"AVAHI_DBUS_NAME"'");
goto fail;
}
diff --git a/examples/core-browse-services.c b/examples/core-browse-services.c
index a34281e..53b18ee 100644
--- a/examples/core-browse-services.c
+++ b/examples/core-browse-services.c
@@ -5,7 +5,7 @@
* the embeddable mDNS stack for embedded applications.
*
* End user applications should *not* use this API and should use
- * the DBUS or C APIs, please see
+ * the D-Bus or C APIs, please see
* client-browse-services.c and glib-integration.c
*
* I repeat, you probably do *not* want to use this example.
--
cgit
From 2a3d0eaa15047ec792cddc1e7e821f525d718648 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 18:31:43 +0000
Subject: remove some embarrassinlgy superfluous code
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1299 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/dbus-protocol.c | 24 ------------------------
1 file changed, 24 deletions(-)
diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c
index 3de5845..dbf30f8 100644
--- a/avahi-daemon/dbus-protocol.c
+++ b/avahi-daemon/dbus-protocol.c
@@ -346,9 +346,7 @@ static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, AVAH
} else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetNetworkInterfaceNameByIndex")) {
int32_t idx;
- int fd;
char name[IF_NAMESIZE];
-
if (!(dbus_message_get_args(m, &error, DBUS_TYPE_INT32, &idx, DBUS_TYPE_INVALID))) {
avahi_log_warn("Error parsing Server::GetNetworkInterfaceNameByIndex message");
@@ -358,29 +356,17 @@ static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, AVAH
#ifdef VALGRIND_WORKAROUND
return respond_string(c, m, "blah");
#else
-
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
- if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- char txt[256];
- snprintf(txt, sizeof(txt), "OS Error: %s", strerror(errno));
- return avahi_dbus_respond_error(c, m, AVAHI_ERR_OS, txt);
- }
-
if ((!if_indextoname(idx, name))) {
char txt[256];
snprintf(txt, sizeof(txt), "OS Error: %s", strerror(errno));
- close(fd);
return avahi_dbus_respond_error(c, m, AVAHI_ERR_OS, txt);
}
-
- close(fd);
return avahi_dbus_respond_string(c, m, name);
#endif
} else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetNetworkInterfaceIndexByName")) {
char *n;
- int fd;
int32_t idx;
if (!(dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID)) || !n) {
@@ -391,21 +377,11 @@ static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, AVAH
#ifdef VALGRIND_WORKAROUND
return respond_int32(c, m, 1);
#else
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
- if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- char txt[256];
- snprintf(txt, sizeof(txt), "OS Error: %s", strerror(errno));
- return avahi_dbus_respond_error(c, m, AVAHI_ERR_OS, txt);
- }
-
if (!(idx = if_nametoindex(n))) {
char txt[256];
snprintf(txt, sizeof(txt), "OS Error: %s", strerror(errno));
- close(fd);
return avahi_dbus_respond_error(c, m, AVAHI_ERR_OS, txt);
}
-
- close(fd);
return avahi_dbus_respond_int32(c, m, idx);
#endif
--
cgit
From 9d709a294b81066a0942a7a642df1b3d4940ad26 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 18:32:27 +0000
Subject: avahi_set_proc_title(): change the process name with prctl() if
available
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1300 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 27 +++++++++++++++++++--------
avahi-daemon/chroot.c | 2 +-
avahi-daemon/main.c | 8 ++++----
avahi-daemon/setproctitle.c | 13 ++++++++++++-
avahi-daemon/setproctitle.h | 4 +++-
5 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 1b3c9ed..6d83d3a 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -267,13 +267,13 @@ static void set_state(State st, int reset_counter, uint32_t address) {
if (modify_proc_title) {
if (state == STATE_SLEEPING)
- avahi_set_proc_title("%s: sleeping", argv0);
+ avahi_set_proc_title(argv0, "%s(%s): sleeping", argv0, interface_name);
else if (state == STATE_ANNOUNCING)
- avahi_set_proc_title("%s: announcing %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ avahi_set_proc_title(argv0, "%s(%s): announcing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
else if (state == STATE_RUNNING)
- avahi_set_proc_title("%s: bound %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ avahi_set_proc_title(argv0, "%s(%s): bound %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
else
- avahi_set_proc_title("%s: probing %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ avahi_set_proc_title(argv0, "%s(%s): probing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
}
}
@@ -840,7 +840,7 @@ static int parse_command_line(int argc, char *argv[]) {
};
opterr = 0;
- while ((c = getopt_long(argc, argv, "hDkVrcS:", long_options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hDskrcVS:w", long_options, NULL)) >= 0) {
switch(c) {
case 's':
@@ -903,7 +903,7 @@ static int parse_command_line(int argc, char *argv[]) {
return -1;
}
- interface_name = argv[optind++];
+ interface_name = avahi_strdup(argv[optind++]);
}
if (optind != argc) {
@@ -921,6 +921,7 @@ static const char* pid_file_proc(void) {
int main(int argc, char*argv[]) {
int r = 1;
int wrote_pid_file = 0;
+ char *log_ident = NULL;
avahi_init_proc_title(argc, argv);
@@ -929,12 +930,15 @@ int main(int argc, char*argv[]) {
else
argv0 = argv[0];
- daemon_pid_file_ident = daemon_log_ident = argv0;
- daemon_pid_file_proc = pid_file_proc;
+ argv0 = avahi_strdup(argv0);
+
+ daemon_log_ident = argv0;
if (parse_command_line(argc, argv) < 0)
goto finish;
+ daemon_log_ident = log_ident = avahi_strdup_printf("%s(%s)", argv0, interface_name);
+ daemon_pid_file_proc = pid_file_proc;
pid_file_name = avahi_strdup_printf(AVAHI_RUNTIME_DIR"/avahi-autoipd.%s.pid", interface_name);
if (command == DAEMON_RUN) {
@@ -993,6 +997,8 @@ int main(int argc, char*argv[]) {
} else
wrote_pid_file = 1;
+ avahi_set_proc_title(argv0, "%s(%s): starting up", argv0, interface_name);
+
if (loop(ifindex, start_address) < 0)
goto finish;
@@ -1031,6 +1037,11 @@ finish:
if (wrote_pid_file)
daemon_pid_file_remove();
+ avahi_free(log_ident);
+ avahi_free(pid_file_name);
+ avahi_free(argv0);
+ avahi_free(interface_name);
+
return r;
}
diff --git a/avahi-daemon/chroot.c b/avahi-daemon/chroot.c
index 5ca605d..99f52fa 100644
--- a/avahi-daemon/chroot.c
+++ b/avahi-daemon/chroot.c
@@ -314,7 +314,7 @@ int avahi_chroot_helper_start(const char *argv0) {
/* Drop all remaining capabilities */
avahi_caps_drop_all();
- avahi_set_proc_title("%s: chroot helper process", argv0);
+ avahi_set_proc_title(argv0, "%s: chroot helper process", argv0);
close(sock[0]);
helper_main(sock[1]);
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index b230ff7..6d8abb1 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -267,7 +267,7 @@ static void server_callback(AvahiServer *s, AvahiServerState state, void *userda
case AVAHI_SERVER_RUNNING:
avahi_log_info("Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s));
- avahi_set_proc_title("%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s));
+ avahi_set_proc_title(argv0, "%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s));
static_service_add_to_server();
static_hosts_add_to_server();
@@ -286,7 +286,7 @@ static void server_callback(AvahiServer *s, AvahiServerState state, void *userda
case AVAHI_SERVER_COLLISION: {
char *n;
- avahi_set_proc_title("%s: collision", argv0);
+ avahi_set_proc_title(argv0, "%s: collision", argv0);
static_service_remove_from_server();
static_hosts_remove_from_server();
@@ -308,7 +308,7 @@ static void server_callback(AvahiServer *s, AvahiServerState state, void *userda
case AVAHI_SERVER_REGISTERING:
- avahi_set_proc_title("%s: registering [%s]", argv0, avahi_server_get_host_name_fqdn(s));
+ avahi_set_proc_title(argv0, "%s: registering [%s]", argv0, avahi_server_get_host_name_fqdn(s));
static_service_remove_from_server();
static_hosts_remove_from_server();
@@ -1193,7 +1193,7 @@ int main(int argc, char *argv[]) {
#endif
avahi_log_info("%s "PACKAGE_VERSION" starting up.", argv0);
- avahi_set_proc_title("%s: starting up", argv0);
+ avahi_set_proc_title(argv0, "%s: starting up", argv0);
if (run_server(&config) == 0)
r = 0;
diff --git a/avahi-daemon/setproctitle.c b/avahi-daemon/setproctitle.c
index f8d3ddb..09b7f65 100644
--- a/avahi-daemon/setproctitle.c
+++ b/avahi-daemon/setproctitle.c
@@ -29,6 +29,10 @@
#include
#include
+#ifdef HAVE_SYS_PRCTL_H
+#include
+#endif
+
#include
#include "setproctitle.h"
@@ -73,7 +77,7 @@ void avahi_init_proc_title(int argc, char **argv) {
#endif
}
-void avahi_set_proc_title(const char *fmt,...) {
+void avahi_set_proc_title(const char *name, const char *fmt,...) {
#ifdef HAVE_SETPROCTITLE
char t[256];
@@ -99,4 +103,11 @@ void avahi_set_proc_title(const char *fmt,...) {
memset(argv_buffer[0] + l, 0, argv_size - l);
argv_buffer[1] = NULL;
#endif
+
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NAME)
+
+ if (name)
+ prctl(PR_SET_NAME, (unsigned long) name, 0, 0, 0);
+
+#endif
}
diff --git a/avahi-daemon/setproctitle.h b/avahi-daemon/setproctitle.h
index 6ac7612..a8f2a1a 100644
--- a/avahi-daemon/setproctitle.h
+++ b/avahi-daemon/setproctitle.h
@@ -22,7 +22,9 @@
USA.
***/
+#include
+
void avahi_init_proc_title(int argc, char **argv);
-void avahi_set_proc_title(const char *fmt, ...);
+void avahi_set_proc_title(const char *name, const char *fmt, ...) AVAHI_GCC_PRINTF_ATTR23;
#endif
--
cgit
From 7bf6b7b95b7c3327000794a24e104242598a9f3f Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 19:48:42 +0000
Subject: add support for interface configuration with user supplied event
script
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1301 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 4 +-
avahi-autoipd/avahi-autoipd.action | 77 ++++++++++++++++
avahi-autoipd/main.c | 176 ++++++++++++++++++++++++++++++++-----
3 files changed, 233 insertions(+), 24 deletions(-)
create mode 100755 avahi-autoipd/avahi-autoipd.action
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index c553470..3a6bb2a 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -20,13 +20,15 @@
if ENABLE_AUTOIPD
if HAVE_LIBDAEMON
+pkgsysconfdir=$(sysconfdir)/avahi
+
AM_CFLAGS= \
-I$(top_srcdir)
# This cool debug trap works on i386/gcc only
AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' \
-DAVAHI_RUNTIME_DIR=\"$(avahi_runtime_dir)/\" \
- -DAVAHI_DNSCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\"
+ -DAVAHI_IPCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\"
sbin_PROGRAMS = avahi-autoipd
diff --git a/avahi-autoipd/avahi-autoipd.action b/avahi-autoipd/avahi-autoipd.action
new file mode 100755
index 0000000..45ce1b5
--- /dev/null
+++ b/avahi-autoipd/avahi-autoipd.action
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# $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 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 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.
+
+set -e
+
+# Command line arguments:
+# $1 event that happened:
+# BIND: Successfully claimed address
+# CONFLICT: An IP address conflict happened
+# UNBIND: The IP address is no longer needed
+# STOP: The daemon is terminating
+# $2 interface name
+# $3 IP adddress
+
+if [ -x /bin/ip -o -x /sbin/ip ] ; then
+
+ # We have the Linux ip tool from the iproute package
+
+ case "$1" in
+ BIND)
+ ip addr add "$3"/16 label "$2:avahi" scope link dev "$2"
+ ;;
+
+ CONFLICT|UNBIND|STOP)
+ ip addr del "$3"/16 label "$2:avahi" scope link dev "$2"
+ ;;
+
+ *)
+ echo "Unknown event $1" >&2
+ exit 1
+ ;;
+ esac
+
+elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then
+
+ # We have the old ifconfig tool
+
+ case "$1" in
+ BIND)
+ ifconfig "$2" inet "$3" netmask 255.255.0.0
+ ;;
+
+ CONFLICT|UNBIND|STOP)
+ ifconfig "$2" inet 0
+ ;;
+
+ *)
+ echo "Unknown event $1" >&2
+ exit 1
+ ;;
+ esac
+else
+
+ echo "No network configuration tool found." >&2
+ exit 1
+
+fi
+
+exit 0
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 6d83d3a..82dba88 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -127,6 +127,19 @@ typedef enum CalloutEvent {
CALLOUT_MAX
} CalloutEvent;
+static const char * const callout_event_table[CALLOUT_MAX] = {
+ [CALLOUT_BIND] = "BIND",
+ [CALLOUT_CONFLICT] = "CONFLICT",
+ [CALLOUT_UNBIND] = "UNBIND",
+ [CALLOUT_STOP] = "STOP"
+};
+
+typedef struct CalloutEventInfo {
+ CalloutEvent event;
+ uint32_t address;
+ int ifindex;
+} CalloutEventInfo;
+
#define RANDOM_DEVICE "/dev/urandom"
#define DEBUG(x) do {\
@@ -243,7 +256,7 @@ static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info
}
static void set_state(State st, int reset_counter, uint32_t address) {
- const char* const state_table[] = {
+ static const char* const state_table[] = {
[STATE_START] = "START",
[STATE_WAITING_PROBE] = "WAITING_PROBE",
[STATE_PROBING] = "PROBING",
@@ -315,23 +328,6 @@ fail:
return -1;
}
-static int do_callout(CalloutEvent event, int iface, uint32_t addr) {
- char buf[64], ifname[IFNAMSIZ];
- const char * const event_table[CALLOUT_MAX] = {
- [CALLOUT_BIND] = "BIND",
- [CALLOUT_CONFLICT] = "CONFLICT",
- [CALLOUT_UNBIND] = "UNBIND",
- [CALLOUT_STOP] = "STOP"
- };
-
- daemon_log(LOG_INFO, "Callout %s, address %s on interface %s",
- event_table[event],
- inet_ntop(AF_INET, &addr, buf, sizeof(buf)),
- if_indextoname(iface, ifname));
-
- return 0;
-}
-
static int open_socket(int iface, uint8_t *hw_address) {
int fd = -1;
struct sockaddr_ll sa;
@@ -458,6 +454,125 @@ static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned j
return tv;
}
+static FILE* fork_dispatcher(void) {
+ FILE *ret;
+ int fds[2];
+ pid_t pid;
+
+ if (pipe(fds) < 0) {
+ daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if ((pid = fork()) < 0)
+ goto fail;
+ else if (pid == 0) {
+ FILE *f = NULL;
+ int r = 1;
+
+ /* Please note that the signal pipe is not closed at this
+ * point, signals will thus be dispatched in the main
+ * process. */
+
+ daemon_retval_done();
+
+ setsid();
+
+ avahi_set_proc_title(argv0, "%s(%s): callout dispatcher", argv0, interface_name);
+
+ close(fds[1]);
+
+ if (!(f = fdopen(fds[0], "r"))) {
+ daemon_log(LOG_ERR, "fdopen() failed: %s", strerror(errno));
+ goto dispatcher_fail;
+ }
+
+ for (;;) {
+ CalloutEventInfo info;
+ char name[IFNAMSIZ], buf[64];
+ int k;
+
+ if (fread(&info, sizeof(info), 1, f) != 1) {
+ if (feof(f))
+ break;
+
+ daemon_log(LOG_ERR, "fread() failed: %s", strerror(errno));
+ goto dispatcher_fail;
+ }
+
+ assert(info.event <= CALLOUT_MAX);
+
+ if (!if_indextoname(info.ifindex, name)) {
+ daemon_log(LOG_ERR, "if_indextoname() failed: %s", strerror(errno));
+ continue;
+ }
+
+ if (daemon_exec("/", &k,
+ AVAHI_IPCONF_SCRIPT, AVAHI_IPCONF_SCRIPT,
+ callout_event_table[info.event],
+ name,
+ inet_ntop(AF_INET, &info.address, buf, sizeof(buf)), NULL) < 0) {
+
+ daemon_log(LOG_ERR, "Failed to run script: %s", strerror(errno));
+ continue;
+ }
+
+ if (k != 0)
+ daemon_log(LOG_WARNING, "Script execution failed with return value %i", k);
+ }
+
+ r = 0;
+
+ dispatcher_fail:
+
+ if (f)
+ fclose(f);
+
+ _exit(r);
+ }
+
+ /* parent */
+
+ close(fds[0]);
+ fds[0] = -1;
+
+ if (!(ret = fdopen(fds[1], "w"))) {
+ daemon_log(LOG_ERR, "fdopen() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return ret;
+
+fail:
+ if (fds[0] >= 0)
+ close(fds[0]);
+ if (fds[1] >= 0)
+ close(fds[1]);
+
+ return NULL;
+}
+
+static int do_callout(FILE *f, CalloutEvent event, int iface, uint32_t addr) {
+ CalloutEventInfo info;
+ char buf[64], ifname[IFNAMSIZ];
+
+ daemon_log(LOG_INFO, "Callout %s, address %s on interface %s",
+ callout_event_table[event],
+ inet_ntop(AF_INET, &addr, buf, sizeof(buf)),
+ if_indextoname(iface, ifname));
+
+ info.event = event;
+ info.ifindex = iface;
+ info.address = addr;
+
+ if (fwrite(&info, sizeof(info), 1, f) != 1 || fflush(f) != 0) {
+ daemon_log(LOG_ERR, "Failed to write callout event: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
static int loop(int iface, uint32_t addr) {
enum {
FD_ARP,
@@ -480,15 +595,22 @@ static int loop(int iface, uint32_t addr) {
Event event = EVENT_NULL;
int retval_sent = !daemonize;
State st;
+ FILE *dispatcher = NULL;
daemon_signal_init(SIGINT, SIGTERM, SIGCHLD, SIGHUP,0);
+ if (!(dispatcher = fork_dispatcher()))
+ goto fail;
+
if ((fd = open_socket(iface, hw_address)) < 0)
goto fail;
if ((iface_fd = iface_init(iface)) < 0)
goto fail;
+/* if (drop_privs() < 0) */
+/* goto fail; */
+
if (force_bind)
st = STATE_START;
else if (iface_get_initial_state(&st) < 0)
@@ -571,7 +693,9 @@ static int loop(int iface, uint32_t addr) {
next_wakeup_valid = 1;
if (n_iteration == 0) {
- do_callout(CALLOUT_BIND, iface, addr);
+ if (do_callout(dispatcher, CALLOUT_BIND, iface, addr) < 0)
+ goto fail;
+
n_conflict = 0;
if (!retval_sent) {
@@ -610,7 +734,8 @@ static int loop(int iface, uint32_t addr) {
if (conflict) {
if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
- do_callout(CALLOUT_CONFLICT, iface, addr);
+ if (do_callout(dispatcher, CALLOUT_CONFLICT, iface, addr) < 0)
+ goto fail;
/* Pick a new address */
addr = pick_addr(addr);
@@ -637,7 +762,8 @@ static int loop(int iface, uint32_t addr) {
daemon_log(LOG_INFO, "A routable address has been configured.");
if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
- do_callout(CALLOUT_UNBIND, iface, addr);
+ if (do_callout(dispatcher, CALLOUT_UNBIND, iface, addr) < 0)
+ goto fail;
if (!retval_sent) {
daemon_retval_send(0);
@@ -775,7 +901,7 @@ static int loop(int iface, uint32_t addr) {
fail:
if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
- do_callout(CALLOUT_STOP, iface, addr);
+ do_callout(dispatcher, CALLOUT_STOP, iface, addr);
avahi_free(out_packet);
avahi_free(in_packet);
@@ -788,6 +914,9 @@ fail:
if (daemonize && !retval_sent)
daemon_retval_send(ret);
+
+ if (dispatcher)
+ fclose(dispatcher);
return ret;
}
@@ -925,6 +1054,8 @@ int main(int argc, char*argv[]) {
avahi_init_proc_title(argc, argv);
+ signal(SIGPIPE, SIG_IGN);
+
if ((argv0 = strrchr(argv[0], '/')))
argv0++;
else
@@ -1048,7 +1179,6 @@ finish:
/* TODO:
- chroot/drop privs/caps
-- user script
- store last used address
- man page
--
cgit
From a2fd5b8ed02be912fcb71cdec1c9233692d6c615 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 19:52:01 +0000
Subject: * change process title of chroot() helper process to "chroot helper"
dropping the term "process" since everyone knows that this thing is a
process anyway.
* close the libdaemon retval pipes in the chroot helper process
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1302 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/chroot.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/avahi-daemon/chroot.c b/avahi-daemon/chroot.c
index 99f52fa..20e2f03 100644
--- a/avahi-daemon/chroot.c
+++ b/avahi-daemon/chroot.c
@@ -314,7 +314,9 @@ int avahi_chroot_helper_start(const char *argv0) {
/* Drop all remaining capabilities */
avahi_caps_drop_all();
- avahi_set_proc_title(argv0, "%s: chroot helper process", argv0);
+ avahi_set_proc_title(argv0, "%s: chroot helper", argv0);
+
+ daemon_retval_done();
close(sock[0]);
helper_main(sock[1]);
--
cgit
From 882604ef60393c0248d7be685a0654be2968492b Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 19:59:45 +0000
Subject: install avahi-autoipd.action
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1303 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 2 ++
1 file changed, 2 insertions(+)
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 3a6bb2a..68ed45a 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -36,5 +36,7 @@ avahi_autoipd_SOURCES = main.c ../avahi-daemon/setproctitle.c iface.h main.h if
avahi_autoipd_CFLAGS = $(AM_CFLAGS) $(LIBDAEMON_CFLAGS)
avahi_autoipd_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la $(LIBDAEMON_LIBS)
+pkgsysconf_SCRIPTS=avahi-autoipd.action
+
endif
endif
--
cgit
From 287f9866c923e6afe8ffbbc03b90be64acf395e1 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 20:16:54 +0000
Subject: change process title to work with pidof/killall
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1304 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 82dba88..ceec9b5 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -280,13 +280,13 @@ static void set_state(State st, int reset_counter, uint32_t address) {
if (modify_proc_title) {
if (state == STATE_SLEEPING)
- avahi_set_proc_title(argv0, "%s(%s): sleeping", argv0, interface_name);
+ avahi_set_proc_title(argv0, "%s: [%s] sleeping", argv0, interface_name);
else if (state == STATE_ANNOUNCING)
- avahi_set_proc_title(argv0, "%s(%s): announcing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ avahi_set_proc_title(argv0, "%s: [%s] announcing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
else if (state == STATE_RUNNING)
- avahi_set_proc_title(argv0, "%s(%s): bound %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ avahi_set_proc_title(argv0, "%s: [%s] bound %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
else
- avahi_set_proc_title(argv0, "%s(%s): probing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ avahi_set_proc_title(argv0, "%s: [%s] probing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
}
}
@@ -478,7 +478,7 @@ static FILE* fork_dispatcher(void) {
setsid();
- avahi_set_proc_title(argv0, "%s(%s): callout dispatcher", argv0, interface_name);
+ avahi_set_proc_title(argv0, "%s: [%s] callout dispatcher", argv0, interface_name);
close(fds[1]);
@@ -1128,7 +1128,7 @@ int main(int argc, char*argv[]) {
} else
wrote_pid_file = 1;
- avahi_set_proc_title(argv0, "%s(%s): starting up", argv0, interface_name);
+ avahi_set_proc_title(argv0, "%s: [%s] starting up", argv0, interface_name);
if (loop(ifindex, start_address) < 0)
goto finish;
--
cgit
From 9ce608107192e885eb8de93290f7faf0bf9176a2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 21:22:54 +0000
Subject: implement chroot()ing and dropping of privileges
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1305 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 3 +-
avahi-autoipd/main.c | 198 ++++++++++++++++++++++++++++++++++++++++------
2 files changed, 177 insertions(+), 24 deletions(-)
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 68ed45a..2483851 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -28,7 +28,8 @@ AM_CFLAGS= \
# This cool debug trap works on i386/gcc only
AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' \
-DAVAHI_RUNTIME_DIR=\"$(avahi_runtime_dir)/\" \
- -DAVAHI_IPCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\"
+ -DAVAHI_IPCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\" \
+ -DAVAHI_IPDATA_DIR=\"$(localstatedir)/lib/avahi-autoipd\"
sbin_PROGRAMS = avahi-autoipd
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index ceec9b5..4c3b0eb 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -43,6 +43,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -109,6 +111,11 @@ static int use_syslog = 0;
static int debug = 0;
static int modify_proc_title = 1;
static int force_bind = 0;
+#ifdef HAVE_CHROOT
+static int no_chroot = 0;
+#endif
+static int no_drop_root = 0;
+static int wrote_pid_file = 0;
static enum {
DAEMON_RUN,
@@ -278,16 +285,14 @@ static void set_state(State st, int reset_counter, uint32_t address) {
n_iteration = 0;
}
- if (modify_proc_title) {
- if (state == STATE_SLEEPING)
- avahi_set_proc_title(argv0, "%s: [%s] sleeping", argv0, interface_name);
- else if (state == STATE_ANNOUNCING)
- avahi_set_proc_title(argv0, "%s: [%s] announcing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
- else if (state == STATE_RUNNING)
- avahi_set_proc_title(argv0, "%s: [%s] bound %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
- else
- avahi_set_proc_title(argv0, "%s: [%s] probing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
- }
+ if (state == STATE_SLEEPING)
+ avahi_set_proc_title(argv0, "%s: [%s] sleeping", argv0, interface_name);
+ else if (state == STATE_ANNOUNCING)
+ avahi_set_proc_title(argv0, "%s: [%s] announcing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ else if (state == STATE_RUNNING)
+ avahi_set_proc_title(argv0, "%s: [%s] bound %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+ else
+ avahi_set_proc_title(argv0, "%s: [%s] probing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
}
static int interface_up(int iface) {
@@ -527,6 +532,14 @@ static FILE* fork_dispatcher(void) {
if (f)
fclose(f);
+
+#ifdef HAVE_CHROOT
+ /* If the main process is trapped inside a chroot() we have to
+ * remove the PID file for it */
+
+ if (!no_chroot && wrote_pid_file)
+ daemon_pid_file_remove();
+#endif
_exit(r);
}
@@ -573,6 +586,126 @@ static int do_callout(FILE *f, CalloutEvent event, int iface, uint32_t addr) {
return 0;
}
+#define set_env(key, value) putenv(avahi_strdup_printf("%s=%s", (key), (value)))
+
+static int drop_privs(void) {
+ struct passwd *pw;
+ struct group * gr;
+ int r;
+ mode_t u;
+
+ /* Get user/group ID */
+
+ if (!no_drop_root) {
+
+ if (!(pw = getpwnam(AVAHI_AUTOIPD_USER))) {
+ daemon_log(LOG_ERR, "Failed to find user '"AVAHI_AUTOIPD_USER"'.");
+ return -1;
+ }
+
+ if (!(gr = getgrnam(AVAHI_AUTOIPD_GROUP))) {
+ daemon_log(LOG_ERR, "Failed to find group '"AVAHI_AUTOIPD_GROUP"'.");
+ return -1;
+ }
+
+ daemon_log(LOG_INFO, "Found user '"AVAHI_AUTOIPD_USER"' (UID %lu) and group '"AVAHI_AUTOIPD_GROUP"' (GID %lu).", (unsigned long) pw->pw_uid, (unsigned long) gr->gr_gid);
+ }
+
+ /* Create directory */
+ u = umask(0000);
+ r = mkdir(AVAHI_IPDATA_DIR, 0755);
+ umask(u);
+
+ if (r < 0 && errno != EEXIST) {
+ daemon_log(LOG_ERR, "mkdir(\""AVAHI_IPDATA_DIR"\"): %s", strerror(errno));
+ return -1;
+ }
+
+ /* Convey working directory */
+
+ if (!no_drop_root) {
+ struct stat st;
+
+ chown(AVAHI_IPDATA_DIR, pw->pw_uid, gr->gr_gid);
+
+ if (stat(AVAHI_IPDATA_DIR, &st) < 0) {
+ daemon_log(LOG_ERR, "stat(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
+ daemon_log(LOG_ERR, "Failed to create runtime directory "AVAHI_IPDATA_DIR".");
+ return -1;
+ }
+ }
+
+#ifdef HAVE_CHROOT
+
+ if (!no_chroot) {
+ if (chroot(AVAHI_IPDATA_DIR) < 0) {
+ daemon_log(LOG_ERR, "Failed to chroot(): %s", strerror(errno));
+ return -1;
+ }
+
+ daemon_log(LOG_INFO, "Successfully called chroot().");
+ chdir("/");
+
+ /* Since we are now trapped inside a chroot we cannot remove
+ * the pid file anymore, the helper process will do that for us. */
+ wrote_pid_file = 0;
+ }
+
+#endif
+
+ if (!no_drop_root) {
+
+ if (initgroups(AVAHI_AUTOIPD_USER, gr->gr_gid) != 0) {
+ daemon_log(LOG_ERR, "Failed to change group list: %s", strerror(errno));
+ return -1;
+ }
+
+#if defined(HAVE_SETRESGID)
+ r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
+#elif defined(HAVE_SETEGID)
+ if ((r = setgid(gr->gr_gid)) >= 0)
+ r = setegid(gr->gr_gid);
+#elif defined(HAVE_SETREGID)
+ r = setregid(gr->gr_gid, gr->gr_gid);
+#else
+#error "No API to drop priviliges"
+#endif
+
+ if (r < 0) {
+ daemon_log(LOG_ERR, "Failed to change GID: %s", strerror(errno));
+ return -1;
+ }
+
+#if defined(HAVE_SETRESUID)
+ r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETEUID)
+ if ((r = setuid(pw->pw_uid)) >= 0)
+ r = seteuid(pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+ r = setreuid(pw->pw_uid, pw->pw_uid);
+#else
+#error "No API to drop priviliges"
+#endif
+
+ if (r < 0) {
+ daemon_log(LOG_ERR, "Failed to change UID: %s", strerror(errno));
+ return -1;
+ }
+
+ set_env("USER", pw->pw_name);
+ set_env("LOGNAME", pw->pw_name);
+ set_env("HOME", pw->pw_dir);
+
+ daemon_log(LOG_ERR, "Successfully dropped root privileges.");
+ }
+
+ return 0;
+}
+
static int loop(int iface, uint32_t addr) {
enum {
FD_ARP,
@@ -608,8 +741,8 @@ static int loop(int iface, uint32_t addr) {
if ((iface_fd = iface_init(iface)) < 0)
goto fail;
-/* if (drop_privs() < 0) */
-/* goto fail; */
+ if (drop_privs() < 0)
+ goto fail;
if (force_bind)
st = STATE_START;
@@ -936,9 +1069,13 @@ static void help(FILE *f, const char *a0) {
" 169.254.0.0/16\n"
" -w --wait Wait until an address has been acquired before\n"
" daemonizing\n"
- " --no-proc-title Don't modify process title\n"
" --force-bind Assign an IPv4LL address even if routable address\n"
" is already assigned\n"
+ " --no-drop-root Don't drop privileges\n"
+#ifdef HAVE_CHROOT
+ " --no-chroot Don't chroot()\n"
+#endif
+ " --no-proc-title Don't modify process title\n"
" --debug Increase verbosity\n",
a0);
}
@@ -949,7 +1086,11 @@ static int parse_command_line(int argc, char *argv[]) {
enum {
OPTION_NO_PROC_TITLE = 256,
OPTION_FORCE_BIND,
- OPTION_DEBUG
+ OPTION_DEBUG,
+ OPTION_NO_DROP_ROOT,
+#ifdef HAVE_CHROOT
+ OPTION_NO_CHROOT
+#endif
};
static const struct option long_options[] = {
@@ -962,8 +1103,12 @@ static int parse_command_line(int argc, char *argv[]) {
{ "version", no_argument, NULL, 'V' },
{ "start", required_argument, NULL, 'S' },
{ "wait", no_argument, NULL, 'w' },
- { "no-proc-title", no_argument, NULL, OPTION_NO_PROC_TITLE },
{ "force-bind", no_argument, NULL, OPTION_FORCE_BIND },
+ { "no-drop-root", no_argument, NULL, OPTION_NO_DROP_ROOT },
+#ifdef HAVE_CHROOT
+ { "no-chroot", no_argument, NULL, OPTION_NO_CHROOT },
+#endif
+ { "no-proc-title", no_argument, NULL, OPTION_NO_PROC_TITLE },
{ "debug", no_argument, NULL, OPTION_DEBUG },
{ NULL, 0, NULL, 0 }
};
@@ -1016,6 +1161,16 @@ static int parse_command_line(int argc, char *argv[]) {
force_bind = 1;
break;
+ case OPTION_NO_DROP_ROOT:
+ no_drop_root = 1;
+ break;
+
+#ifdef HAVE_CHROOT
+ case OPTION_NO_CHROOT:
+ no_chroot = 1;
+ break;
+#endif
+
default:
fprintf(stderr, "Invalid command line argument: %c\n", c);
return -1;
@@ -1049,25 +1204,23 @@ static const char* pid_file_proc(void) {
int main(int argc, char*argv[]) {
int r = 1;
- int wrote_pid_file = 0;
char *log_ident = NULL;
- avahi_init_proc_title(argc, argv);
-
signal(SIGPIPE, SIG_IGN);
if ((argv0 = strrchr(argv[0], '/')))
- argv0++;
+ argv0 = avahi_strdup(argv0 + 1);
else
- argv0 = argv[0];
-
- argv0 = avahi_strdup(argv0);
+ argv0 = avahi_strdup(argv[0]);
daemon_log_ident = argv0;
if (parse_command_line(argc, argv) < 0)
goto finish;
+ if (modify_proc_title)
+ avahi_init_proc_title(argc, argv);
+
daemon_log_ident = log_ident = avahi_strdup_printf("%s(%s)", argv0, interface_name);
daemon_pid_file_proc = pid_file_proc;
pid_file_name = avahi_strdup_printf(AVAHI_RUNTIME_DIR"/avahi-autoipd.%s.pid", interface_name);
@@ -1178,7 +1331,6 @@ finish:
/* TODO:
-- chroot/drop privs/caps
- store last used address
- man page
--
cgit
From c09e09d8d0c17c12979d84fdf66b2b595f399051 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 31 Aug 2006 21:56:51 +0000
Subject: * don't allow 169.254.0.0 and 169.254.255.255 as valid addresses *
automaticaly save and restore addresses
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1306 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 4c3b0eb..c1a3d90 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -81,6 +81,7 @@
#define IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
#define IPV4LL_HOSTMASK 0x0000FFFFL
+#define IPV4LL_BROADCAST 0xA9FEFFFFL
#define ETHER_ADDRLEN 6
#define ARP_PACKET_SIZE (8+4+4+2*ETHER_ADDRLEN)
@@ -185,11 +186,69 @@ static uint32_t pick_addr(uint32_t old_addr) {
addr = htonl(IPV4LL_NETWORK | (uint32_t) r);
- } while (addr == old_addr);
+ } while (addr == old_addr || !is_ll_address(addr));
return addr;
}
+static int load_address(const char *fn, uint32_t *addr) {
+ FILE *f;
+ unsigned a, b, c, d;
+
+ assert(fn);
+ assert(addr);
+
+ if (!(f = fopen(fn, "r"))) {
+
+ if (errno == ENOENT) {
+ *addr = 0;
+ return 0;
+ }
+
+ daemon_log(LOG_ERR, "fopen() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (fscanf(f, "%u.%u.%u.%u\n", &a, &b, &c, &d) != 4) {
+ daemon_log(LOG_ERR, "Parse failure");
+ goto fail;
+ }
+
+ fclose(f);
+
+ *addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
+ return 0;
+
+fail:
+ if (f)
+ fclose(f);
+
+ return -1;
+}
+
+static int save_address(const char *fn, uint32_t addr) {
+ FILE *f;
+ char buf[32];
+
+ assert(fn);
+
+ if (!(f = fopen(fn, "w"))) {
+ daemon_log(LOG_ERR, "fopen() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ fprintf(f, "%s\n", inet_ntop(AF_INET, &addr, buf, sizeof (buf)));
+ fclose(f);
+
+ return 0;
+
+fail:
+ if (f)
+ fclose(f);
+
+ return -1;
+}
+
static void* packet_new(const ArpPacketInfo *info, size_t *packet_len) {
uint8_t *r;
@@ -442,7 +501,10 @@ fail:
}
int is_ll_address(uint32_t addr) {
- return (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK;
+ return
+ (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK &&
+ ntohl(addr) != IPV4LL_NETWORK &&
+ ntohl(addr) != IPV4LL_BROADCAST;
}
static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned jitter) {
@@ -729,6 +791,8 @@ static int loop(int iface, uint32_t addr) {
int retval_sent = !daemonize;
State st;
FILE *dispatcher = NULL;
+ char *address_fn = NULL;
+ const char *p;
daemon_signal_init(SIGINT, SIGTERM, SIGCHLD, SIGHUP,0);
@@ -749,6 +813,22 @@ static int loop(int iface, uint32_t addr) {
else if (iface_get_initial_state(&st) < 0)
goto fail;
+#ifdef HAVE_CHROOT
+ if (!no_chroot)
+ p = "";
+ else
+#endif
+ p = AVAHI_IPDATA_DIR;
+
+ address_fn = avahi_strdup_printf(
+ "%s/%02x:%02x:%02x:%02x:%02x:%02x", p,
+ hw_address[0], hw_address[1],
+ hw_address[2], hw_address[3],
+ hw_address[4], hw_address[5]);
+
+ if (!addr)
+ load_address(address_fn, &addr);
+
if (addr && !is_ll_address(addr)) {
daemon_log(LOG_WARNING, "Requested address %s is not from IPv4LL range 169.254/16, ignoring.", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
addr = 0;
@@ -830,11 +910,6 @@ static int loop(int iface, uint32_t addr) {
goto fail;
n_conflict = 0;
-
- if (!retval_sent) {
- daemon_retval_send(0);
- retval_sent = 1;
- }
}
} else if ((state == STATE_ANNOUNCING && event == EVENT_TIMEOUT && n_iteration >= ANNOUNCE_NUM-1)) {
@@ -843,7 +918,14 @@ static int loop(int iface, uint32_t addr) {
set_state(STATE_RUNNING, 0, addr);
next_wakeup_valid = 0;
+
+ save_address(address_fn, addr);
+ if (!retval_sent) {
+ daemon_retval_send(0);
+ retval_sent = 1;
+ }
+
} else if (event == EVENT_PACKET) {
ArpPacketInfo info;
@@ -1050,6 +1132,9 @@ fail:
if (dispatcher)
fclose(dispatcher);
+
+ if (address_fn)
+ avahi_free(address_fn);
return ret;
}
@@ -1331,7 +1416,6 @@ finish:
/* TODO:
-- store last used address
- man page
*/
--
cgit
From f479f120fbb067bb1d8259047a9406bf564b31f0 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 1 Sep 2006 00:54:50 +0000
Subject: remove remnants of a todo list
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1307 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index c1a3d90..72ee904 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -1413,9 +1413,3 @@ finish:
return r;
}
-
-/* TODO:
-
-- man page
-
-*/
--
cgit
From 9a49fb52b41852a9cb201b23326fde95efcb1a2c Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 1 Sep 2006 22:28:26 +0000
Subject: * ship avahi-autopid.action in the tarball * remove dependency on
libavahi-common
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1308 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 2483851..2c55241 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -33,11 +33,19 @@ AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' \
sbin_PROGRAMS = avahi-autoipd
-avahi_autoipd_SOURCES = main.c ../avahi-daemon/setproctitle.c iface.h main.h iface-linux.c
+avahi_autoipd_SOURCES = \
+ main.c main.h \
+ ../avahi-daemon/setproctitle.c ../avahi-daemon/setproctitle.h \
+ iface.h iface-linux.c \
+ ../avahi-common/malloc.h ../avahi-common/malloc.c \
+ ../avahi-common/timeval.h ../avahi-common/timeval.c
+
avahi_autoipd_CFLAGS = $(AM_CFLAGS) $(LIBDAEMON_CFLAGS)
-avahi_autoipd_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la $(LIBDAEMON_LIBS)
+avahi_autoipd_LDADD = $(AM_LDADD) $(LIBDAEMON_LIBS)
pkgsysconf_SCRIPTS=avahi-autoipd.action
endif
endif
+
+EXTRA_DIST=avahi-autoipd.action
--
cgit
From b881d65f485b6ff7d883a87a980bf2c9a5cce4bb Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 2 Sep 2006 13:48:59 +0000
Subject: lots of documentation cleanups and fixes
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1309 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
man/avahi-bookmarks.1.xml.in | 2 +-
man/avahi-browse.1.xml.in | 2 +-
man/avahi-daemon.8.xml.in | 22 +++++++++++-----------
man/avahi-daemon.conf.5.xml.in | 8 ++++----
man/avahi-discover.1.xml.in | 2 +-
man/avahi-dnsconfd.8.xml.in | 4 ++--
man/avahi-dnsconfd.action.8.xml.in | 2 +-
man/avahi-publish.1.xml.in | 2 +-
man/avahi-resolve.1.xml.in | 2 +-
man/avahi-set-host-name.1.xml.in | 2 +-
man/avahi.hosts.5.xml.in | 2 +-
man/avahi.service.5.xml.in | 2 +-
12 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/man/avahi-bookmarks.1.xml.in b/man/avahi-bookmarks.1.xml.in
index 25661f6..9bebd76 100644
--- a/man/avahi-bookmarks.1.xml.in
+++ b/man/avahi-bookmarks.1.xml.in
@@ -83,7 +83,7 @@
-
The avahi developers <@PACKAGE_BUGREPORT@>; avahi is
+
The Avahi Developers <@PACKAGE_BUGREPORT@>; Avahi is
available from
The avahi developers <@PACKAGE_BUGREPORT@>; avahi is
+
The Avahi Developers <@PACKAGE_BUGREPORT@>; Avahi is
available from
diff --git a/man/avahi-daemon.8.xml.in b/man/avahi-daemon.8.xml.in
index b4ad07d..e9732d6 100644
--- a/man/avahi-daemon.8.xml.in
+++ b/man/avahi-daemon.8.xml.in
@@ -42,8 +42,8 @@
exclusively by avahi-dnsconfd (a daemon which configures unicast
DNS servers using server info published via mDNS) and nss-mdns
(a libc NSS plugin, providing name resolution via mDNS). Finally
- there is the DBUS interface which provides a rich object
- oriented interface to DBUS enabled applications.
+ there is the D-Bus interface which provides a rich object
+ oriented interface to D-Bus enabled applications.
Upon startup avahi-daemon interprets its configuration file
@pkgsysconfdir@/avahi-daemon.conf and reads XML
@@ -63,17 +63,17 @@
@@ -144,15 +144,15 @@
-
SIGINT, SIGTERM, SIGQUIT: avahi-daemon will shutdown. This is issued by passing --kill to avahi-daemon.
+
SIGINT, SIGTERM, SIGQUIT: avahi-daemon will shutdown. (Same as --kill).
SIGHUP: avahi-daemon will reload unicast DNS
server data from /etc/resolv.conf and static
- service definitions from @servicedir@/.
+ service definitions from @servicedir@/. (Same as --reload)
SIGUSR1: avahi-daemon will dump local and remote cached resource record data to syslog.
-
The avahi developers <@PACKAGE_BUGREPORT@>; avahi is
+
The Avahi Developers <@PACKAGE_BUGREPORT@>; Avahi is
available from
avahi-autoipd implements IPv4LL, "Dynamic Configuration of
+ IPv4 Link-Local Addresses" (IETF RFC3927), a protocol for
+ automatic IP address configuration from the link-local
+ 169.254.0.0/16 range without the need for a central server. It
+ is primarily intended to be used in ad-hoc networks which lack a
+ DHCP server.
+
+
IPv4LL is part of the Zeroconf stack.
+
+
avahi-autoipd can be used as stand-alone address allocator
+ or as plugin for a DHCP client such as ISC's dhclient, where it
+ can be used as fallback solution if no DHCP server is found.
avahi-autoipd.action is the action script that
+ is called whenever an IP address has been acquired by
+ avahi-autoipd or when it detected an IP address conflict. The
+ script should add or remove the specified address from the
+ specified network interface.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Avahi Developers <@PACKAGE_BUGREPORT@>; Avahi is
+ available from
+
+
+
+
+ , , ,
+
+
+
+
+
This man page was written using by Oliver Kurth.
+
+
+
--
cgit
From deb30fa40d8131132d2d05b4439a0d773766f21b Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 2 Sep 2006 14:45:59 +0000
Subject: ship dhclient hook scripts for using ipv4ll as fallback solution
where DHCP is not available. Only installed on Debian for now, since only
Debian provides the necessary hook directories.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1311 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 28 +++++++++++++++++++++++++++-
avahi-autoipd/dhclient-enter-hook.in | 35 +++++++++++++++++++++++++++++++++++
avahi-autoipd/dhclient-exit-hook.in | 35 +++++++++++++++++++++++++++++++++++
3 files changed, 97 insertions(+), 1 deletion(-)
create mode 100755 avahi-autoipd/dhclient-enter-hook.in
create mode 100755 avahi-autoipd/dhclient-exit-hook.in
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 2c55241..7fda8c7 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -45,7 +45,33 @@ avahi_autoipd_LDADD = $(AM_LDADD) $(LIBDAEMON_LIBS)
pkgsysconf_SCRIPTS=avahi-autoipd.action
+if TARGET_DEBIAN
+
+noinst_SCRIPTS = dhclient-enter-hook dhclient-exit-hook
+
+dhclient-enter-hook: dhclient-enter-hook.in
+ sed -e 's,@sbindir\@,$(sbindir),g' $< > $@
+ chmod +x $@
+
+dhclient-exit-hook: dhclient-exit-hook.in
+ sed -e 's,@sbindir\@,$(sbindir),g' $< > $@
+ chmod +x $@
+
+BUILD = dhclient-exit-hook dhclient-enter-hook
+
+dhcliententerdir = $(sysconfdir)/dhcp3/dhclient-enter-hooks.d
+dhclientexitdir = $(sysconfdir)/dhcp3/dhclient-exit-hooks.d
+
+install-exec-hook: dhclient-exit-hook dhclient-enter-hook
+ $(INSTALL) $(srcdir)/dhclient-enter-hook $(DESTDIR)$(dhcliententerdir)/avahi-autoipd
+ $(INSTALL) $(srcdir)/dhclient-exit-hook $(DESTDIR)$(dhclientexitdir)/avahi-autoipd
+
endif
+
+
endif
+endif
+
+EXTRA_DIST=avahi-autoipd.action dhclient-enter-hook.in dhclient-exit-hook.in
-EXTRA_DIST=avahi-autoipd.action
+CLEANFILES=dhclient-enter-hook dhclient-exit-hook
diff --git a/avahi-autoipd/dhclient-enter-hook.in b/avahi-autoipd/dhclient-enter-hook.in
new file mode 100755
index 0000000..8efd468
--- /dev/null
+++ b/avahi-autoipd/dhclient-enter-hook.in
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# $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 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 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.
+
+set -e
+
+case "$reason" in
+ MEDIUM|ARPCHECK|ARPSEND|NBI)
+ ;;
+
+ PREINIT|BOUND|RENEW|REBIND|REBOOT|STOP)
+ @sbindir@/avahi-autoipd -k $interface 2> /dev/null
+ ;;
+
+ EXPIRE|FAIL|RELEASE|TIMEOUT)
+ # Starting avahi-autoipd is left for the exit hook
+ ;;
+esac
diff --git a/avahi-autoipd/dhclient-exit-hook.in b/avahi-autoipd/dhclient-exit-hook.in
new file mode 100755
index 0000000..a844b77
--- /dev/null
+++ b/avahi-autoipd/dhclient-exit-hook.in
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# $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 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 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.
+
+set -e
+
+case "$reason" in
+ MEDIUM|ARPCHECK|ARPSEND|NBI)
+ ;;
+
+ PREINIT|BOUND|RENEW|REBIND|REBOOT|STOP)
+ # Stoping avahi-autoipd is left for the enter hook
+ ;;
+
+ EXPIRE|FAIL|RELEASE|TIMEOUT)
+ @sbindir@/avahi-autoipd -wD $interface 2> /dev/null
+ ;;
+esac
--
cgit
From 4638c9afcb14dffca2d0f1749660c68a14f9f65d Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 2 Sep 2006 16:32:46 +0000
Subject: remove a bogus warning
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1312 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 72ee904..bc0ee26 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -943,7 +943,9 @@ static int loop(int iface, uint32_t addr) {
} else if (state == STATE_WAITING_PROBE || state == STATE_PROBING || state == STATE_WAITING_ANNOUNCE) {
/* Probe conflict */
conflict = info.target_ip_address == addr && memcmp(hw_address, info.sender_hw_address, ETHER_ADDRLEN);
- daemon_log(LOG_INFO, "Recieved conflicting probe ARP packet.");
+
+ if (conflict)
+ daemon_log(LOG_INFO, "Recieved conflicting probe ARP packet.");
}
if (conflict) {
--
cgit
From 9c881d484a3c54c6ba884e0eaeb2f8c77fc561db Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 2 Sep 2006 16:35:18 +0000
Subject: remove "set -e" from the hook scripts since this apparently breaks
stuff in the main hook script
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1313 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/dhclient-enter-hook.in | 2 --
avahi-autoipd/dhclient-exit-hook.in | 4 +---
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/avahi-autoipd/dhclient-enter-hook.in b/avahi-autoipd/dhclient-enter-hook.in
index 8efd468..0c274b9 100755
--- a/avahi-autoipd/dhclient-enter-hook.in
+++ b/avahi-autoipd/dhclient-enter-hook.in
@@ -19,8 +19,6 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-set -e
-
case "$reason" in
MEDIUM|ARPCHECK|ARPSEND|NBI)
;;
diff --git a/avahi-autoipd/dhclient-exit-hook.in b/avahi-autoipd/dhclient-exit-hook.in
index a844b77..4f7167e 100755
--- a/avahi-autoipd/dhclient-exit-hook.in
+++ b/avahi-autoipd/dhclient-exit-hook.in
@@ -19,14 +19,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-set -e
-
case "$reason" in
MEDIUM|ARPCHECK|ARPSEND|NBI)
;;
PREINIT|BOUND|RENEW|REBIND|REBOOT|STOP)
- # Stoping avahi-autoipd is left for the enter hook
+ # Stopping avahi-autoipd is left for the enter hook
;;
EXPIRE|FAIL|RELEASE|TIMEOUT)
--
cgit
From 11bc4c03170243db05425625fb1db33cd73a4d6f Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 2 Sep 2006 16:40:53 +0000
Subject: update INSTALL
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1314 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/INSTALL | 3 +++
docs/README | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/INSTALL b/docs/INSTALL
index 387235c..8feac51 100644
--- a/docs/INSTALL
+++ b/docs/INSTALL
@@ -49,4 +49,7 @@ To start the two daemons at boot time on Debian based distributions:
# update-rc.d avahi-daemon defaults 25 15
# update-rc.d avahi-dnsconfd defaults 26 14
+If you plan to use avahi-autoipd you have to create the user/group
+"avahi-autoipd" much the same way as "avahi".
+
$Id$
diff --git a/docs/README b/docs/README
index 4815ba2..6c6a586 100644
--- a/docs/README
+++ b/docs/README
@@ -1,6 +1,6 @@
Avahi is a free, LGPL mDNS/DNS-SD implementation.
-Copyright 2004, 2005 by the Avahi developers.
+Copyright 2004-2006 by the Avahi developers.
http://avahi.org/
--
cgit
From e18bf1fbd6d05fccf0699616dd58de9a61ed4cea Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 2 Sep 2006 18:34:03 +0000
Subject: fix "make distcheck"
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1315 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/Makefile.am | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 7fda8c7..11e6493 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -63,8 +63,12 @@ dhcliententerdir = $(sysconfdir)/dhcp3/dhclient-enter-hooks.d
dhclientexitdir = $(sysconfdir)/dhcp3/dhclient-exit-hooks.d
install-exec-hook: dhclient-exit-hook dhclient-enter-hook
- $(INSTALL) $(srcdir)/dhclient-enter-hook $(DESTDIR)$(dhcliententerdir)/avahi-autoipd
- $(INSTALL) $(srcdir)/dhclient-exit-hook $(DESTDIR)$(dhclientexitdir)/avahi-autoipd
+ $(mkdir_p) $(DESTDIR)$(dhcliententerdir) $(DESTDIR)$(dhclientexitdir)
+ $(INSTALL) dhclient-enter-hook $(DESTDIR)$(dhcliententerdir)/avahi-autoipd
+ $(INSTALL) dhclient-exit-hook $(DESTDIR)$(dhclientexitdir)/avahi-autoipd
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(dhcliententerdir)/avahi-autoipd $(DESTDIR)$(dhclientexitdir)/avahi-autoipd
endif
--
cgit
From b6472516dc7738d9f11854313c763cebf2747986 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 3 Sep 2006 16:14:27 +0000
Subject: configure broadcast address explicitly
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1316 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/avahi-autoipd.action | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/avahi-autoipd/avahi-autoipd.action b/avahi-autoipd/avahi-autoipd.action
index 45ce1b5..5efc255 100755
--- a/avahi-autoipd/avahi-autoipd.action
+++ b/avahi-autoipd/avahi-autoipd.action
@@ -36,11 +36,11 @@ if [ -x /bin/ip -o -x /sbin/ip ] ; then
case "$1" in
BIND)
- ip addr add "$3"/16 label "$2:avahi" scope link dev "$2"
+ ip addr add "$3"/16 brd 169.254.255.255 label "$2:avahi" scope link dev "$2"
;;
CONFLICT|UNBIND|STOP)
- ip addr del "$3"/16 label "$2:avahi" scope link dev "$2"
+ ip addr del "$3"/16 brd 169.254.255.255 label "$2:avahi" scope link dev "$2"
;;
*)
@@ -55,7 +55,7 @@ elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then
case "$1" in
BIND)
- ifconfig "$2" inet "$3" netmask 255.255.0.0
+ ifconfig "$2" inet "$3" netmask 255.255.0.0 broadcast 169.254.255.255
;;
CONFLICT|UNBIND|STOP)
--
cgit
From cf4e63caaef2289b9e6765357d51598d2c3ddcf6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 3 Sep 2006 17:26:57 +0000
Subject: do not remove the interface address if an UNBIND event is recieved.
This might accidently remove a manually configured address, hence better
don't touch it.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1317 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/avahi-autoipd.action | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/avahi-autoipd/avahi-autoipd.action b/avahi-autoipd/avahi-autoipd.action
index 5efc255..ba041aa 100755
--- a/avahi-autoipd/avahi-autoipd.action
+++ b/avahi-autoipd/avahi-autoipd.action
@@ -58,10 +58,17 @@ elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then
ifconfig "$2" inet "$3" netmask 255.255.0.0 broadcast 169.254.255.255
;;
- CONFLICT|UNBIND|STOP)
+ CONFLICT|STOP)
ifconfig "$2" inet 0
;;
+ UNBIND)
+ # This event is triggered when some other tool configured
+ # a routable address for this interface. That IP address
+ # probably overwrote ours, so let's not remove it again
+ # here.
+ ;;
+
*)
echo "Unknown event $1" >&2
exit 1
--
cgit
From d9b9d1c9964a605b9b47265c2a3fcc6c662ef28b Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Mon, 4 Sep 2006 17:43:28 +0000
Subject: describe how to communicate between IPv4LL and non-IPv4LL hosts
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1318 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
man/avahi-autoipd.8.xml.in | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/man/avahi-autoipd.8.xml.in b/man/avahi-autoipd.8.xml.in
index c40ce39..5bc6fad 100644
--- a/man/avahi-autoipd.8.xml.in
+++ b/man/avahi-autoipd.8.xml.in
@@ -45,6 +45,16 @@
avahi-autoipd can be used as stand-alone address allocator
or as plugin for a DHCP client such as ISC's dhclient, where it
can be used as fallback solution if no DHCP server is found.
+
+
To allow communication between hosts that have only an IPv4LL
+ address assigned and hosts that only have a routable IP address
+ assigned you may add the following routes to both network
+ configurations:
+
+
route add -net 169.254.0.0 netmask 255.255.0.0 dev eth0 metric 99
+
route add default dev eth0 metric 99
+
+
See http://developer.apple.com/qa/qa2004/qa1357.html for more information.
--
cgit
From 0fde74f6fd95367e3c90df899b21acbb5b010789 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Mon, 4 Sep 2006 18:10:26 +0000
Subject: use alias interfaces for network configuration if only ifconfig is
available
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1319 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/avahi-autoipd.action | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/avahi-autoipd/avahi-autoipd.action b/avahi-autoipd/avahi-autoipd.action
index ba041aa..27f00bb 100755
--- a/avahi-autoipd/avahi-autoipd.action
+++ b/avahi-autoipd/avahi-autoipd.action
@@ -55,18 +55,11 @@ elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then
case "$1" in
BIND)
- ifconfig "$2" inet "$3" netmask 255.255.0.0 broadcast 169.254.255.255
+ ifconfig "$2:3" inet "$3" netmask 255.255.0.0 broadcast 169.254.255.255 up
;;
- CONFLICT|STOP)
- ifconfig "$2" inet 0
- ;;
-
- UNBIND)
- # This event is triggered when some other tool configured
- # a routable address for this interface. That IP address
- # probably overwrote ours, so let's not remove it again
- # here.
+ CONFLICT|STOP|UNBIND)
+ ifconfig "$2:3" down
;;
*)
@@ -74,6 +67,7 @@ elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then
exit 1
;;
esac
+
else
echo "No network configuration tool found." >&2
--
cgit
From 0ab5eaa3fa41c910204b801cbe030fafc4c29ba2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Mon, 4 Sep 2006 21:50:40 +0000
Subject: fix --help
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1320 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index bc0ee26..ff2cfee 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -1156,7 +1156,7 @@ static void help(FILE *f, const char *a0) {
" 169.254.0.0/16\n"
" -w --wait Wait until an address has been acquired before\n"
" daemonizing\n"
- " --force-bind Assign an IPv4LL address even if routable address\n"
+ " --force-bind Assign an IPv4LL address even if a routable address\n"
" is already assigned\n"
" --no-drop-root Don't drop privileges\n"
#ifdef HAVE_CHROOT
--
cgit
From 461b588593cdc69f09901d2aa22b5f5f7021e706 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Tue, 12 Sep 2006 13:22:56 +0000
Subject: add pgp key server service type to database
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1321 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
service-type-database/service-types | 2 ++
1 file changed, 2 insertions(+)
diff --git a/service-type-database/service-types b/service-type-database/service-types
index adfe5c7..e3db1a6 100644
--- a/service-type-database/service-types
+++ b/service-type-database/service-types
@@ -116,3 +116,5 @@ _pulse-source._tcp:PulseAudio Sound Source
_mpd._tcp:Music Player Daemon
_smb._tcp:Microsoft Windows Network
+
+_pgpkey-hkp._tcp:GnuPG/PGP HKP Key Server
--
cgit
From 0bd9a88889c5d4c2ad0bd150e7584cc3f99d1116 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Tue, 12 Sep 2006 13:49:37 +0000
Subject: add a few additional service types to database
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1322 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
service-type-database/service-types | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/service-type-database/service-types b/service-type-database/service-types
index e3db1a6..b96385b 100644
--- a/service-type-database/service-types
+++ b/service-type-database/service-types
@@ -118,3 +118,19 @@ _mpd._tcp:Music Player Daemon
_smb._tcp:Microsoft Windows Network
_pgpkey-hkp._tcp:GnuPG/PGP HKP Key Server
+
+_ldap._tcp:LDAP Directory Server
+
+_iax._udp:Asterisk Exchange
+
+_nfs._tcp:Network File System
+
+_ntp._udp:NTP Time Server
+
+_raop._tcp:AirTunes Remote Audio
+
+_rtsp._tcp:RTSP Realtime Streaming Server
+_rtp._tcp:RTP Realtime Streaming Server
+
+_svn._tcp:Subversion Revision Control
+
--
cgit
From 6702120361ffb2efdf33d94c520643c51ef4a920 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Tue, 12 Sep 2006 13:58:17 +0000
Subject: minor rtp name fix
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1323 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
service-type-database/service-types | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/service-type-database/service-types b/service-type-database/service-types
index b96385b..e501072 100644
--- a/service-type-database/service-types
+++ b/service-type-database/service-types
@@ -130,7 +130,7 @@ _ntp._udp:NTP Time Server
_raop._tcp:AirTunes Remote Audio
_rtsp._tcp:RTSP Realtime Streaming Server
-_rtp._tcp:RTP Realtime Streaming Server
+_rtp._udp:RTP Realtime Streaming Server
_svn._tcp:Subversion Revision Control
--
cgit
From a09c6b5182720c67e150f9db459f661f5bc41893 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 13 Sep 2006 20:12:38 +0000
Subject: update NEWS file
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1324 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/NEWS | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/docs/NEWS b/docs/NEWS
index 4f5e4fc..fe561a5 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -1,3 +1,41 @@
+Avahi 0.6.14
+============
+
+This release fixes some bugs and includes a new component.
+
+Changes:
+
+ * Add new daemon "avahi-autoipd" which is an implementation of
+ IPv4LL as defined in RFC3927. While it is not the first
+ implemenatation of this technology for Free operating systems it
+ is clearly the most powerful and hopefully even the most
+ secure. (Because it chroot()s and drops priviliges and suchlike)
+ For more information, especially about packaging this new tool for
+ distributions, please make sure to read:
+ http://avahi.org/wiki/AvahiAutoipd
+ For the rationale for adding this program to the Avahi toolset
+ please read this mailing list thread:
+ http://lists.freedesktop.org/archives/avahi/2006-September/000863.html
+ * Fix a segfault in the code handling static host name registrations
+ * Add a few new entries to the service type database
+ * s/D-?BUS/D-Bus/g
+ * Documentation updates
+ * Fix service type database building on Solaris
+ * Make use of newer D-Bus APIs
+ * Fix random seed initialization
+ * Install SFTP static service file by default
+ * Other minor code cleanups
+
+This release is backwards compatible with Avahi 0.6.x with x < 14.
+
+Please note that this version doesn't compile on NetBSD, patches
+welcome.
+
+Please note that avahi-autoipd is available on Linux only for
+now. Patches welcome. It is recommended to pass --disable-autoipd to
+"configure" on non-Linux operating systems, otherwise the build will
+fail.
+
Avahi 0.6.13
============
--
cgit
From 74c06513b4690cd8715163b1b8e0e74a43dac5fa Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 13 Sep 2006 20:15:35 +0000
Subject: bump version number and sonames
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1325 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/configure.ac b/configure.ac
index 7cf15b2..ea6ef45 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,16 +21,16 @@
# USA.
AC_PREREQ(2.57)
-AC_INIT([avahi],[0.6.13],[avahi (at) lists (dot) freedesktop (dot) org])
+AC_INIT([avahi],[0.6.14],[avahi (at) lists (dot) freedesktop (dot) org])
AC_CONFIG_SRCDIR([avahi-core/server.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
AC_SUBST(PACKAGE_URL, [http://avahi.org/])
-AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [7:2:4])
+AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [7:3:4])
AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [4:4:0])
-AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:0:2])
+AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:1:2])
AC_SUBST(LIBAVAHI_GLIB_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT3_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT4_VERSION_INFO, [1:1:0])
--
cgit
From 1411a742fadc5de81fde4677ec16aae41f9e3b72 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Wed, 13 Sep 2006 21:31:45 +0000
Subject: reword NEWS file a little
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1326 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/NEWS | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/docs/NEWS b/docs/NEWS
index fe561a5..212d157 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -6,15 +6,17 @@ This release fixes some bugs and includes a new component.
Changes:
* Add new daemon "avahi-autoipd" which is an implementation of
- IPv4LL as defined in RFC3927. While it is not the first
- implemenatation of this technology for Free operating systems it
- is clearly the most powerful and hopefully even the most
- secure. (Because it chroot()s and drops priviliges and suchlike)
- For more information, especially about packaging this new tool for
- distributions, please make sure to read:
+ IPv4LL as defined in RFC3927, a technology for assigning link-local IP
+ addresses without DHCP server. The same functionality has been available on
+ Windows under the name APIPA. While it is not the first implemenatation of
+ this technology for Free operating systems it is clearly the most powerful
+ and hopefully even the most secure. (Because it chroot()s and drops
+ priviliges and suchlike) For more information, especially about packaging
+ this new tool for distributions, please make sure to read:
http://avahi.org/wiki/AvahiAutoipd
- For the rationale for adding this program to the Avahi toolset
- please read this mailing list thread:
+ and of course the man page included in the tarball. For the rationale for
+ adding this program to the Avahi toolset please read this mailing list
+ thread:
http://lists.freedesktop.org/archives/avahi/2006-September/000863.html
* Fix a segfault in the code handling static host name registrations
* Add a few new entries to the service type database
--
cgit
From 6014a9885431654d03484ae5ed0aded7268adfeb Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 21 Sep 2006 00:18:11 +0000
Subject: Fix build on NetBSD. Patch contributed by Daniel S. Haischt (closes
#59)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1328 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/iface-pfroute.c | 6 +++---
configure.ac | 16 ++++++++++++++++
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/avahi-core/iface-pfroute.c b/avahi-core/iface-pfroute.c
index a67b826..035e267 100644
--- a/avahi-core/iface-pfroute.c
+++ b/avahi-core/iface-pfroute.c
@@ -318,7 +318,7 @@ void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
}
}
-#ifndef HAVE_SYS_SYSCTL_H
+#if defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
/*
* I got this function from GNU zsbra
*/
@@ -428,7 +428,7 @@ static void if_add_interface(struct lifreq *lifreq, AvahiInterfaceMonitor *m, in
#endif
void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
-#ifdef HAVE_SYS_SYSCTL_H
+#ifndef HAVE_STRUCT_LIFCONF
size_t needed;
int mib[6];
char *buf, *lim, *next, count = 0;
@@ -473,7 +473,7 @@ void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
avahi_interface_monitor_check_relevant(m);
avahi_interface_monitor_update_rrs(m, 0);
avahi_log_info("Network interface enumeration completed.");
-#else
+#elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
int sockfd;
int ret;
int n;
diff --git a/configure.ac b/configure.ac
index ea6ef45..0acb7dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -104,10 +104,26 @@ AC_DEFINE([HAVE_SYS_SYSCTL_H],[],[Support for sys/sysctl.h])
, [], [
#include
#include
+#include
])
AM_CONDITIONAL(HAVE_SYS_SYSCTL_H, [ test x"$HAVE_SYS_SYSCTL_H" = xyes ])
+#
+# Check for lifconf struct; only present on Solaris
+#
+AC_MSG_CHECKING(for struct lifconf)
+AC_CACHE_VAL(avahi_cv_has_struct_lifconf,
+[AC_TRY_COMPILE(
+[#include
+#include
+],[sizeof (struct lifconf);],
+avahi_cv_has_struct_lifconf=yes,avahi_cv_has_struct_lifconf=no)])
+AC_MSG_RESULT($avahi_cv_has_struct_lifconf)
+if test $avahi_cv_has_struct_lifconf = yes; then
+ AC_DEFINE(HAVE_STRUCT_LIFCONF,1,[Define if there is a struct lifconf.])
+fi
+
#
# Check for struct ip_mreqn
#
--
cgit
From 012a7722a740e683cecc6476f6dcb42f96aacb21 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Mon, 25 Sep 2006 13:43:50 +0000
Subject: * Fix dbus_service_browser not setting AVAHI_LOOKUP_RESULT_OUR_OWN.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1329 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/dbus-service-browser.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/avahi-daemon/dbus-service-browser.c b/avahi-daemon/dbus-service-browser.c
index 29d79b6..1515ddf 100644
--- a/avahi-daemon/dbus-service-browser.c
+++ b/avahi-daemon/dbus-service-browser.c
@@ -105,10 +105,6 @@ void avahi_dbus_service_browser_callback(AvahiSServiceBrowser *b, AvahiIfIndex i
assert(b);
assert(i);
- i_interface = (int32_t) interface;
- i_protocol = (int32_t) protocol;
- u_flags = (uint32_t) flags;
-
m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, avahi_dbus_map_browse_signal_name(event));
if (event == AVAHI_BROWSER_NEW) {
@@ -118,6 +114,10 @@ void avahi_dbus_service_browser_callback(AvahiSServiceBrowser *b, AvahiIfIndex i
flags |= AVAHI_LOOKUP_RESULT_OUR_OWN;
}
+ i_interface = (int32_t) interface;
+ i_protocol = (int32_t) protocol;
+ u_flags = (uint32_t) flags;
+
if (event == AVAHI_BROWSER_NEW || event == AVAHI_BROWSER_REMOVE) {
assert(name);
assert(type);
--
cgit
From 25681df85706bbdc5d6ad011537b7b4f7d05cae5 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Tue, 3 Oct 2006 19:53:56 +0000
Subject: add updated specs to svn
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1330 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
specs/draft-cheshire-dnsext-dns-sd-04.txt | 2205 ++++++++++++++++
specs/draft-cheshire-dnsext-multicastdns-06.txt | 3074 +++++++++++++++++++++++
2 files changed, 5279 insertions(+)
create mode 100644 specs/draft-cheshire-dnsext-dns-sd-04.txt
create mode 100644 specs/draft-cheshire-dnsext-multicastdns-06.txt
diff --git a/specs/draft-cheshire-dnsext-dns-sd-04.txt b/specs/draft-cheshire-dnsext-dns-sd-04.txt
new file mode 100644
index 0000000..3179028
--- /dev/null
+++ b/specs/draft-cheshire-dnsext-dns-sd-04.txt
@@ -0,0 +1,2205 @@
+Document: draft-cheshire-dnsext-dns-sd-04.txt Stuart Cheshire
+Internet-Draft Marc Krochmal
+Category: Standards Track Apple Computer, Inc.
+Expires 10th February 2007 10th August 2006
+
+ DNS-Based Service Discovery
+
+
+
+Status of this Memo
+
+ By submitting this Internet-Draft, each author represents that any
+ applicable patent or other IPR claims of which he or she is aware
+ have been or will be disclosed, and any of which he or she becomes
+ aware will be disclosed, in accordance with Section 6 of BCP 79.
+ For the purposes of this document, the term "BCP 79" refers
+ exclusively to RFC 3979, "Intellectual Property Rights in IETF
+ Technology", published March 2005.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF), its areas, and its working groups. Note that
+ other groups may also distribute working documents as Internet-
+ Drafts.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ The list of current Internet-Drafts can be accessed at
+ http://www.ietf.org/1id-abstracts.html
+
+ The list of Internet-Draft Shadow Directories can be accessed at
+ http://www.ietf.org/shadow.html
+
+
+Abstract
+
+ This document describes a convention for naming and structuring DNS
+ resource records. Given a type of service that a client is looking
+ for, and a domain in which the client is looking for that service,
+ this convention allows clients to discover a list of named instances
+ of that desired service, using only standard DNS queries. In short,
+ this is referred to as DNS-based Service Discovery, or DNS-SD.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 1]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+Table of Contents
+
+ 1. Introduction...................................................3
+ 2. Conventions and Terminology Used in this Document..............4
+ 3. Design Goals...................................................4
+ 4. Service Instance Enumeration...................................5
+ 4.1 Structured Instance Names......................................5
+ 4.2 User Interface Presentation....................................7
+ 4.3 Internal Handling of Names.....................................7
+ 4.4 What You See Is What You Get...................................8
+ 4.5 Ordering of Service Instance Name Components...................9
+ 5. Service Name Resolution.......................................11
+ 6. Data Syntax for DNS-SD TXT Records............................12
+ 6.1 General Format Rules for DNS TXT Records......................12
+ 6.2 DNS TXT Record Format Rules for use in DNS-SD.................13
+ 6.3 DNS-SD TXT Record Size........................................14
+ 6.4 Rules for Names in DNS-SD Name/Value Pairs....................14
+ 6.5 Rules for Values in DNS-SD Name/Value Pairs...................16
+ 6.6 Example TXT Record............................................17
+ 6.7 Version Tag...................................................17
+ 7. Application Protocol Names....................................18
+ 7.1 Selective Instance Enumeration................................19
+ 7.2 Service Name Length Limits....................................20
+ 8. Flagship Naming...............................................22
+ 9. Service Type Enumeration......................................23
+ 10. Populating the DNS with Information...........................24
+ 11. Relationship to Multicast DNS.................................24
+ 12. Discovery of Browsing and Registration Domains................25
+ 13. DNS Additional Record Generation..............................26
+ 14. Comparison with Alternative Service Discovery Protocols.......27
+ 15. Real Examples.................................................29
+ 16. User Interface Considerations.................................30
+ 16.1 Service Advertising User-Interface Considerations.............30
+ 16.2 Client Browsing User-Interface Considerations.................31
+ 17. IPv6 Considerations...........................................34
+ 18. Security Considerations.......................................34
+ 19. IANA Considerations...........................................34
+ 20. Acknowledgments...............................................35
+ 21. Deployment History............................................35
+ 22. Copyright Notice..............................................36
+ 23. Normative References..........................................37
+ 24. Informative References........................................37
+ 25. Authors' Addresses............................................38
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 2]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+1. Introduction
+
+ This document describes a convention for naming and structuring DNS
+ resource records. Given a type of service that a client is looking
+ for, and a domain in which the client is looking for that service,
+ this convention allows clients to discover a list of named instances
+ of a that desired service, using only standard DNS queries. In short,
+ this is referred to as DNS-based Service Discovery, or DNS-SD.
+
+ This document proposes no change to the structure of DNS messages,
+ and no new operation codes, response codes, resource record types,
+ or any other new DNS protocol values. This document simply proposes
+ a convention for how existing resource record types can be named and
+ structured to facilitate service discovery.
+
+ This proposal is entirely compatible with today's existing unicast
+ DNS server and client software.
+
+ Note that the DNS-SD service does NOT have to be provided by the same
+ DNS server hardware that is currently providing an organization's
+ conventional host name lookup service (the service we traditionally
+ think of when we say "DNS"). By delegating the "_tcp" subdomain,
+ all the workload related to DNS-SD can be offloaded to a different
+ machine. This flexibility, to handle DNS-SD on the main DNS server,
+ or not, at the network administrator's discretion, is one of the
+ things that makes DNS-SD so compelling.
+
+ Even when the DNS-SD functions are delegated to a different machine,
+ the benefits of using DNS remain: It is mature technology, well
+ understood, with multiple independent implementations from different
+ vendors, a wide selection of books published on the subject, and an
+ established workforce experienced in its operation. In contrast,
+ adopting some other service discovery technology would require every
+ site in the world to install, learn, configure, operate and maintain
+ some entirely new and unfamiliar server software. Faced with these
+ obstacles, it seems unlikely that any other service discovery
+ technology could hope to compete with the ubiquitous deployment
+ that DNS already enjoys.
+
+ This proposal is also compatible with (but not dependent on) the
+ proposal outlined in "Multicast DNS" [mDNS].
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 3]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+2. Conventions and Terminology Used in this Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in "Key words for use in
+ RFCs to Indicate Requirement Levels" [RFC 2119].
+
+
+3. Design Goals
+
+ A good service discovery protocol needs to have many properties,
+ three of which are mentioned below:
+
+ (i) The ability to query for services of a certain type in a certain
+ logical domain and receive in response a list of named instances
+ (network browsing, or "Service Instance Enumeration").
+
+ (ii) Given a particular named instance, the ability to efficiently
+ resolve that instance name to the required information a client needs
+ to actually use the service, i.e. IP address and port number, at the
+ very least (Service Name Resolution).
+
+ (iii) Instance names should be relatively persistent. If a user
+ selects their default printer from a list of available choices today,
+ then tomorrow they should still be able to print on that printer --
+ even if the IP address and/or port number where the service resides
+ have changed -- without the user (or their software) having to repeat
+ the network browsing step a second time.
+
+ In addition, if it is to become successful, a service discovery
+ protocol should be so simple to implement that virtually any
+ device capable of implementing IP should not have any trouble
+ implementing the service discovery software as well.
+
+ These goals are discussed in more detail in the remainder of this
+ document. A more thorough treatment of service discovery requirements
+ may be found in "Requirements for a Protocol to Replace AppleTalk
+ NBP" [NBP]. That document draws upon examples from two decades of
+ operational experience with AppleTalk Name Binding Protocol to
+ develop a list of universal requirements which are broadly
+ applicable to any potential service discovery protocol.
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 4]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+4. Service Instance Enumeration
+
+ DNS SRV records [RFC 2782] are useful for locating instances of a
+ particular type of service when all the instances are effectively
+ indistinguishable and provide the same service to the client.
+
+ For example, SRV records with the (hypothetical) name
+ "_http._tcp.example.com." would allow a client to discover a list of
+ all servers implementing the "_http._tcp" service (i.e. Web servers)
+ for the "example.com." domain. The unstated assumption is that all
+ these servers offer an identical set of Web pages, and it doesn't
+ matter to the client which of the servers it uses, as long as it
+ selects one at random according to the weight and priority rules
+ laid out in RFC 2782.
+
+ Instances of other kinds of service are less easily interchangeable.
+ If a word processing application were to look up the (hypothetical)
+ SRV record "_ipp._tcp.example.com." to find the list of IPP printers
+ at Example Co., then picking one at random and printing on it would
+ probably not be what the user wanted.
+
+ The remainder of this section describes how SRV records may be used
+ in a slightly different way to allow a user to discover the names
+ of all available instances of a given type of service, in order to
+ select the particular instance the user desires.
+
+
+4.1 Structured Instance Names
+
+ This document borrows the logical service naming syntax and semantics
+ from DNS SRV records, but adds one level of indirection. Instead of
+ requesting records of type "SRV" with name "_ipp._tcp.example.com.",
+ the client requests records of type "PTR" (pointer from one name to
+ another in the DNS namespace).
+
+ In effect, if one thinks of the domain name "_ipp._tcp.example.com."
+ as being analogous to an absolute path to a directory in a file
+ system then the PTR lookup is akin to performing a listing of that
+ directory to find all the files it contains. (Remember that domain
+ names are expressed in reverse order compared to path names: An
+ absolute path name is read from left to right, beginning with a
+ leading slash on the left, and then the top level directory, then
+ the next level directory, and so on. A fully-qualified domain name is
+ read from right to left, beginning with the dot on the right -- the
+ root label -- and then the top level domain to the left of that, and
+ the second level domain to the left of that, and so on. If the fully-
+ qualified domain name "_ipp._tcp.example.com." were expressed as a
+ file system path name, it would be "/com/example/_tcp/_ipp".)
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 5]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ The result of this PTR lookup for the name "." is a
+ list of zero or more PTR records giving Service Instance Names of the
+ form:
+
+ Service Instance Name = . .
+
+ The portion of the Service Instance Name is a single DNS
+ label, containing arbitrary precomposed UTF-8-encoded text [RFC
+ 3629]. It is a user-friendly name, meaning that it is allowed to
+ contain any characters, without restriction, including spaces, upper
+ case, lower case, punctuation -- including dots -- accented
+ characters, non-roman text, and anything else that may be represented
+ using UTF-8. DNS recommends guidelines for allowable characters for
+ host names [RFC 1033][RFC 1034][RFC 1035], but Service Instance Names
+ are not host names. Service Instance Names are not intended to ever
+ be typed in by a normal user; the user selects a Service Instance
+ Name by selecting it from a list of choices presented on the screen.
+
+ Note that just because this protocol supports arbitrary UTF-8-encoded
+ names doesn't mean that any particular user or administrator is
+ obliged to make use of that capability. Any user is free, if they
+ wish, to continue naming their services using only letters, digits
+ and hyphens, with no spaces, capital letters, or other punctuation.
+
+ DNS labels are currently limited to 63 octets in length. UTF-8
+ encoding can require up to four octets per Unicode character, which
+ means that in the worst case, the portion of a name could
+ be limited to fifteen Unicode characters. However, the Unicode
+ characters with longer UTF-8 encodings tend to be the more obscure
+ ones, and tend to be the ones that convey greater meaning per
+ character.
+
+ Note that any character in the commonly-used 16-bit Unicode space
+ can be encoded with no more than three octets of UTF-8 encoding. This
+ means that an Instance name can contain up to 21 Kanji characters,
+ which is a sufficiently expressive name for most purposes.
+
+ The portion of the Service Instance Name consists of a pair
+ of DNS labels, following the established convention for SRV records
+ [RFC 2782], namely: the first label of the pair is the Application
+ Protocol Name, and the second label is either "_tcp" or "_udp",
+ depending on the transport protocol used by the application.
+ More details are given in Section 7, "Application Protocol Names".
+
+ The portion of the Service Instance Name specifies the DNS
+ subdomain within which the service names are registered. It may be
+ "local", meaning "link-local Multicast DNS" [mDNS], or it may be
+ a conventional unicast DNS domain name, such as "apple.com.",
+ "cs.stanford.edu.", or "eng.us.ibm.com." Because service names are
+ not host names, they are not constrained by the usual rules for host
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 6]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ names [RFC 1033][RFC 1034][RFC 1035], and rich-text service
+ subdomains are allowed and encouraged, for example:
+
+ Building 2, 1st Floor.apple.com.
+ Building 2, 2nd Floor.apple.com.
+ Building 2, 3rd Floor.apple.com.
+ Building 2, 4th Floor.apple.com.
+
+ In addition, because Service Instance Names are not constrained by
+ the limitations of host names, this document recommends that they
+ be stored in the DNS, and communicated over the wire, encoded as
+ straightforward canonical precomposed UTF-8, Unicode Normalization
+ Form C [UAX15]. In cases where the DNS server returns an NXDOMAIN
+ error for the name in question, client software MAY choose to retry
+ the query using "Punycode" [RFC 3492] encoding, if possible.
+
+
+4.2 User Interface Presentation
+
+ The names resulting from the PTR lookup are presented to the user in
+ a list for the user to select one (or more). Typically only the first
+ label is shown (the user-friendly portion of the name). In
+ the common case, the and are already known to the
+ user, these having been provided by the user in the first place, by
+ the act of indicating the service being sought, and the domain in
+ which to look for it. Note: The software handling the response
+ should be careful not to make invalid assumptions though, since it
+ *is* possible, though rare, for a service enumeration in one domain
+ to return the names of services in a different domain. Similarly,
+ when using subtypes (see "Selective Instance Enumeration") the
+ of the discovered instance my not be exactly the same as
+ the that was requested.
+
+ Having chosen the desired named instance, the Service Instance
+ Name may then be used immediately, or saved away in some persistent
+ user-preference data structure for future use, depending on what is
+ appropriate for the application in question.
+
+
+4.3 Internal Handling of Names
+
+ If the , and portions are internally
+ concatenated together into a single string, then care must be taken
+ with the portion, since it is allowed to contain any
+ characters, including dots.
+
+ Any dots in the portion should be escaped by preceding
+ them with a backslash ("." becomes "\."). Likewise, any backslashes
+ in the portion should also be escaped by preceding them
+ with a backslash ("\" becomes "\\"). Having done this, the three
+ components of the name may be safely concatenated. The backslash-
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 7]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ escaping allows literal dots in the name (escaped) to be
+ distinguished from label-separator dots (not escaped).
+
+ The resulting concatenated string may be safely passed to standard
+ DNS APIs like res_query(), which will interpret the string correctly
+ provided it has been escaped correctly, as described here.
+
+
+4.4 What You See Is What You Get
+
+ Some service discovery protocols decouple the true service identifier
+ from the name presented to the user. The true service identifier used
+ by the protocol is an opaque unique id, often represented using a
+ long string of hexadecimal digits, and should never be seen by the
+ typical user. The name presented to the user is merely one of the
+ ephemeral attributes attached to this opaque identifier.
+
+ The problem with this approach is that it decouples user perception
+ from reality:
+
+ * What happens if there are two service instances, with different
+ unique ids, but they have inadvertently been given the same
+ user-visible name? If two instances appear in an on-screen list
+ with the same name, how does the user know which is which?
+
+ * Suppose a printer breaks down, and the user replaces it with
+ another printer of the same make and model, and configures the
+ new printer with the exact same name as the one being replaced:
+ "Stuart's Printer". Now, when the user tries to print, the
+ on-screen print dialog tells them that their selected default
+ printer is "Stuart's Printer". When they browse the network to see
+ what is there, they see a printer called "Stuart's Printer", yet
+ when the user tries to print, they are told that the printer
+ "Stuart's Printer" can't be found. The hidden internal unique id
+ that the software is trying to find on the network doesn't match
+ the hidden internal unique id of the new printer, even though its
+ apparent "name" and its logical purpose for being there are the
+ same. To remedy this, the user typically has to delete the print
+ queue they have created, and then create a new (apparently
+ identical) queue for the new printer, so that the new queue will
+ contain the right hidden internal unique id. Having all this hidden
+ information that the user can't see makes for a confusing and
+ frustrating user experience, and exposing long ugly hexadecimal
+ strings to the user and forcing them to understand what they mean
+ is even worse.
+
+ * Suppose an existing printer is moved to a new department, and given
+ a new name and a new function. Changing the user-visible name of
+ that piece of hardware doesn't change its hidden internal unique
+ id. Users who had previously created print queues for that printer
+ will still be accessing the same hardware by its unique id, even
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 8]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ though the logical service that used to be offered by that hardware
+ has ceased to exist.
+
+ To solve these problems requires the user or administrator to be
+ aware of the supposedly hidden unique id, and to set its value
+ correctly as hardware is moved around, repurposed, or replaced,
+ thereby contradicting the notion that it is a hidden identifier that
+ human users never need to deal with. Requiring the user to understand
+ this expert behind-the-scenes knowledge of what is *really* going on
+ is just one more burden placed on the user when they are trying to
+ diagnose why their computers and network devices are not working as
+ expected.
+
+ These anomalies and counter-intuitive behaviors can be eliminated by
+ maintaining a tight bidirectional one-to-one mapping between what
+ the user sees on the screen and what is really happening "behind
+ the curtain". If something is configured incorrectly, then that is
+ apparent in the familiar day-to-day user interface that everyone
+ understands, not in some little-known rarely-used "expert" interface.
+
+ In summary: The user-visible name is the primary identifier for a
+ service. If the user-visible name is changed, then conceptually
+ the service being offered is a different logical service -- even
+ though the hardware offering the service stayed the same. If the
+ user-visible name doesn't change, then conceptually the service being
+ offered is the same logical service -- even if the hardware offering
+ the service is new hardware brought in to replace some old equipment.
+
+ There are certainly arguments on both sides of this debate.
+ Nonetheless, the designers of any service discovery protocol have
+ to make a choice between between having the primary identifiers be
+ hidden, or having them be visible, and these are the reasons that
+ we chose to make them visible. We're not claiming that there are no
+ disadvantages of having primary identifiers be visible. We considered
+ both alternatives, and we believe that the few disadvantages
+ of visible identifiers are far outweighed by the many problems
+ caused by use of hidden identifiers.
+
+
+4.5 Ordering of Service Instance Name Components
+
+ There have been questions about why services are named using DNS
+ Service Instance Names of the form:
+
+ Service Instance Name = . .
+
+ instead of:
+
+ Service Instance Name = . .
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 9]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ There are three reasons why it is beneficial to name service
+ instances with the parent domain as the most-significant (rightmost)
+ part of the name, then the abstract service type as the next-most
+ significant, and then the specific instance name as the
+ least-significant (leftmost) part of the name:
+
+
+4.5.1. Semantic Structure
+
+ The facility being provided by browsing ("Service Instance
+ Enumeration") is effectively enumerating the leaves of a tree
+ structure. A given domain offers zero or more services. For each
+ of those service types, there may be zero or more instances of
+ that service.
+
+ The user knows what type of service they are seeking. (If they are
+ running an FTP client, they are looking for FTP servers. If they have
+ a document to print, they are looking for entities that speak some
+ known printing protocol.) The user knows in which organizational or
+ geographical domain they wish to search. (The user does not want a
+ single flat list of every single printer on the planet, even if such
+ a thing were possible.) What the user does not know in advance is
+ whether the service they seek is offered in the given domain, or if
+ so, how many instances are offered, and the names of those instances.
+ Hence having the instance names be the leaves of the tree is
+ consistent with this semantic model.
+
+ Having the service types be the terminal leaves of the tree would
+ imply that the user knows the domain name, and already knows the
+ name of the service instance, but doesn't have any idea what the
+ service does. We would argue that this is a less useful model.
+
+
+4.5.2. Network Efficiency
+
+ When a DNS response contains multiple answers, name compression works
+ more effectively if all the names contain a common suffix. If many
+ answers in the packet have the same and , then each
+ occurrence of a Service Instance Name can be expressed using only
+ the part followed by a two-byte compression pointer
+ referencing a previous appearance of ".". This
+ efficiency would not be possible if the component appeared
+ first in each name.
+
+
+4.5.3. Operational Flexibility
+
+ This name structure allows subdomains to be delegated along logical
+ service boundaries. For example, the network administrator at Example
+ Co. could choose to delegate the "_tcp.example.com." subdomain to a
+ different machine, so that the machine handling service discovery
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 10]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ doesn't have to be the same as the machine handling other day-to-day
+ DNS operations. (It *can* be the same machine if the administrator so
+ chooses, but the point is that the administrator is free to make that
+ choice.) Furthermore, if the network administrator wishes to delegate
+ all information related to IPP printers to a machine dedicated to
+ that specific task, this is easily done by delegating the
+ "_ipp._tcp.example.com." subdomain to the desired machine. It is
+ also convenient to set security policies on a per-zone/per-subdomain
+ basis. For example, the administrator may choose to enable DNS
+ Dynamic Update [RFC 2136] [RFC 3007] for printers registering
+ in the "_ipp._tcp.example.com." subdomain, but not for other
+ zones/subdomains. This easy flexibility would not exist if the
+ component appeared first in each name.
+
+
+5. Service Name Resolution
+
+ Given a particular Service Instance Name, when a client needs to
+ contact that service, it sends a DNS query for the SRV record of
+ that name.
+
+ The result of the DNS query is a SRV record giving the port number
+ and target host where the service may be found.
+
+ The use of SRV records is very important. There are only 65535 TCP
+ port numbers available. These port numbers are being allocated
+ one-per-application-protocol at an alarming rate. Some protocols
+ like the X Window System have a block of 64 TCP ports allocated
+ (6000-6063). If we start allocating blocks of 64 TCP ports at a time,
+ we will run out even faster. Using a different TCP port for each
+ different instance of a given service on a given machine is entirely
+ sensible, but allocating large static ranges, as was done for X, is a
+ very inefficient way to manage a limited resource. On any given host,
+ most TCP ports are reserved for services that will never run on that
+ particular host. This is very poor utilization of the limited port
+ space. Using SRV records allows each host to allocate its available
+ port numbers dynamically to those services running on that host that
+ need them, and then advertise the allocated port numbers via SRV
+ records. Allocating the available listening port numbers locally
+ on a per-host basis as needed allows much better utilization of the
+ available port space than today's centralized global allocation.
+
+ In some environments there may be no compelling reason to assign
+ managed names to every host, since every available service is
+ accessible by name anyway, as a first-class entity in its own right.
+ However, the DNS packet format and record format still require a host
+ name to link the target host referenced in the SRV record to the
+ address records giving the IPv4 and/or IPv6 addresses for that
+ hardware. In the case where no natural host name is available, the
+ SRV record may give its own name as the name of the target host, and
+ then the requisite address records may be attached to that same name.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 11]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ It is perfectly permissible for a single name in the DNS hierarchy
+ to have multiple records of different type attached. (The only
+ restriction being that a given name may not have both a CNAME record
+ and other records at the same time.)
+
+ In the event that more than one SRV is returned, clients MUST
+ correctly interpret the priority and weight fields -- i.e. Lower
+ numbered priority servers should be used in preference to higher
+ numbered priority servers, and servers with equal priority should be
+ selected randomly in proportion to their relative weights. However,
+ in the overwhelmingly common case, a single advertised DNS-SD service
+ instance is described by exactly one SRV record, and in this common
+ case the priority and weight fields of the SRV record SHOULD both be
+ set to zero.
+
+
+6. Data Syntax for DNS-SD TXT Records
+
+ Some services discovered via Service Instance Enumeration may need
+ more than just an IP address and port number to properly identify the
+ service. For example, printing via the LPR protocol often specifies a
+ queue name. This queue name is typically short and cryptic, and need
+ not be shown to the user. It should be regarded the same way as the
+ IP address and port number -- it is one component of the addressing
+ information required to identify a specific instance of a service
+ being offered by some piece of hardware. Similarly, a file server may
+ have multiple volumes, each identified by its own volume name. A Web
+ server typically has multiple pages, each identified by its own URL.
+ In these cases, the necessary additional data is stored in a TXT
+ record with the same name as the SRV record. The specific nature of
+ that additional data, and how it is to be used, is service-dependent,
+ but the overall syntax of the data in the TXT record is standardized,
+ as described below. Every DNS-SD service MUST have a TXT record in
+ addition to its SRV record, with same name, even if the service has
+ no additional data to store and the TXT record contains no more than
+ a single zero byte.
+
+
+6.1 General Format Rules for DNS TXT Records
+
+ A DNS TXT record can be up to 65535 (0xFFFF) bytes long. The total
+ length is indicated by the length given in the resource record header
+ in the DNS message. There is no way to tell directly from the data
+ alone how long it is (e.g. there is no length count at the start, or
+ terminating NULL byte at the end). (Note that when using Multicast
+ DNS [mDNS] the maximum packet size is 9000 bytes, which imposes an
+ upper limit on the size of TXT records of about 8800 bytes.)
+
+ The format of the data within a DNS TXT record is one or more
+ strings, packed together in memory without any intervening gaps
+ or padding bytes for word alignment.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 12]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ The format of each constituent string within the DNS TXT record is a
+ single length byte, followed by 0-255 bytes of text data.
+
+ These format rules are defined in Section 3.3.14 of RFC 1035, and are
+ not specific to DNS-SD. DNS-SD simply specifies a usage convention
+ for what data should be stored in those constituent strings.
+
+ An empty TXT record containing zero strings is disallowed by RFC
+ 1035. DNS-SD implementations MUST NOT emit empty TXT records.
+ DNS-SD implementations receiving empty TXT records MUST treat them
+ as equivalent to a one-byte TXT record containing a single zero byte
+ (i.e. a single empty string).
+
+
+6.2 DNS TXT Record Format Rules for use in DNS-SD
+
+ DNS-SD uses DNS TXT records to store arbitrary name/value pairs
+ conveying additional information about the named service. Each
+ name/value pair is encoded as its own constituent string within the
+ DNS TXT record, in the form "name=value". Everything up to the first
+ '=' character is the name. Everything after the first '=' character
+ to the end of the string (including subsequent '=' characters, if
+ any) is the value. Specific rules governing names and values are
+ given below. Each author defining a DNS-SD profile for discovering
+ instances of a particular type of service should define the base set
+ of name/value attributes that are valid for that type of service.
+
+ Using this standardized name/value syntax within the TXT record makes
+ it easier for these base definitions to be expanded later by defining
+ additional named attributes. If an implementation sees unknown
+ attribute names in a service TXT record, it MUST silently ignore
+ them.
+
+ The TCP (or UDP) port number of the service, and target host name,
+ are given in the SRV record. This information -- target host name and
+ port number -- MUST NOT be duplicated using name/value attributes in
+ the TXT record.
+
+ The intention of DNS-SD TXT records is to convey a small amount of
+ useful additional information about a service. Ideally it SHOULD NOT
+ be necessary for a client to retrieve this additional information
+ before it can usefully establish a connection to the service. For a
+ well-designed TCP-based application protocol, it should be possible,
+ knowing only the host name and port number, to open a connection
+ to that listening process, and then perform version- or feature-
+ negotiation to determine the capabilities of the service instance.
+ For example, when connecting to an AppleShare server over TCP, the
+ client enters into a protocol exchange with the server to determine
+ which version of the AppleShare protocol the server implements, and
+ which optional features or capabilities (if any) are available. For a
+ well-designed application protocol, clients should be able to connect
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 13]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ and use the service even if there is no information at all in the TXT
+ record. In this case, the information in the TXT record should be
+ viewed as a performance optimization -- when a client discovers many
+ instances of a service, the TXT record allows the client to know some
+ rudimentary information about each instance without having to open a
+ TCP connection to each one and interrogate every service instance
+ separately. Extreme care should be taken when doing this to ensure
+ that the information in the TXT record is in agreement with the
+ information retrieved by a client connecting over TCP.
+
+ There are legacy protocols which provide no feature negotiation
+ capability, and in these cases it may be useful to convey necessary
+ information in the TXT record. For example, when printing using the
+ old Unix LPR (port 515) protocol, the LPR service provides no way
+ for the client to determine whether a particular printer accepts
+ PostScript, or what version of PostScript, etc. In this case it is
+ appropriate to embed this information in the TXT record, because the
+ alternative is worse -- passing around written instructions to the
+ users, arcane manual configuration of "/etc/printcap" files, etc.
+
+
+6.3 DNS-SD TXT Record Size
+
+ The total size of a typical DNS-SD TXT record is intended to be small
+ -- 200 bytes or less.
+
+ In cases where more data is justified (e.g. LPR printing), keeping
+ the total size under 400 bytes should allow it to fit in a single
+ standard 512-byte DNS message. (This standard DNS message size is
+ defined in RFC 1035.)
+
+ In extreme cases where even this is not enough, keeping the size of
+ the TXT record under 1300 bytes should allow it to fit in a single
+ 1500-byte Ethernet packet.
+
+ Using TXT records larger than 1300 bytes is NOT RECOMMENDED at this
+ time.
+
+
+6.4 Rules for Names in DNS-SD Name/Value Pairs
+
+ The "Name" MUST be at least one character. Strings beginning with an
+ '=' character (i.e. the name is missing) SHOULD be silently ignored.
+
+ The characters of "Name" MUST be printable US-ASCII values
+ (0x20-0x7E), excluding '=' (0x3D).
+
+ Spaces in the name are significant, whether leading, trailing, or in
+ the middle -- so don't include any spaces unless you really intend
+ that!
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 14]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ Case is ignored when interpreting a name, so "papersize=A4",
+ "PAPERSIZE=A4" and "Papersize=A4" are all identical.
+
+ If there is no '=', then it is a boolean attribute, and is simply
+ identified as being present, with no value.
+
+ A given attribute name may appear at most once in a TXT record.
+ The reason for this simplifying rule is to facilitate the creation
+ of client libraries that parse the TXT record into an internal data
+ structure, such as a hash table or dictionary object that maps from
+ names to values, and then make that abstraction available to client
+ code. The rule that a given attribute name may not appear more than
+ once simplifies these abstractions because they aren't required to
+ support the case of returning more than one value for a given key.
+
+ If a client receives a TXT record containing the same attribute name
+ more than once, then the client MUST silently ignore all but the
+ first occurrence of that attribute. For client implementations that
+ process a DNS-SD TXT record from start to end, placing name/value
+ pairs into a hash table, using the name as the hash table key, this
+ means that if the implementation attempts to add a new name/value
+ pair into the table and finds an entry with the same name already
+ present, then the new entry being added should be silently discarded
+ instead. For client implementations that retrieve name/value pairs by
+ searching the TXT record for the requested name, they should search
+ the TXT record from the start, and simply return the first matching
+ name they find.
+
+ When examining a TXT record for a given named attribute, there are
+ therefore four broad categories of results which may be returned:
+
+ * Attribute not present (Absent)
+
+ * Attribute present, with no value
+ (e.g. "Anon Allowed" -- server allows anonymous connections)
+
+ * Attribute present, with empty value (e.g. "Installed PlugIns=" --
+ server supports plugins, but none are presently installed)
+
+ * Attribute present, with non-empty value
+ (e.g. "Installed PlugIns=JPEG,MPEG2,MPEG4")
+
+ Each author defining a DNS-SD profile for discovering instances of a
+ particular type of service should define the interpretation of these
+ different kinds of result. For example, for some keys, there may be
+ a natural true/false boolean interpretation:
+
+ * Present implies 'true'
+ * Absent implies 'false'
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 15]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ For other keys it may be sensible to define other semantics, such as
+ value/no-value/unknown:
+
+ * Present with value implies that value.
+ E.g. "Color=4" for a four-color ink-jet printer,
+ or "Color=6" for a six-color ink-jet printer.
+
+ * Present with empty value implies 'false'. E.g. Not a color printer.
+
+ * Absent implies 'Unknown'. E.g. A print server connected to some
+ unknown printer where the print server doesn't actually know if the
+ printer does color or not -- which gives a very bad user experience
+ and should be avoided wherever possible.
+
+ (Note that this is a hypothetical example, not an example of actual
+ name/value keys used by DNS-SD network printers.)
+
+ As a general rule, attribute names that contain no dots are defined
+ as part of the open-standard definition written by the person or
+ group defining the DNS-SD profile for discovering that particular
+ service type. Vendor-specific extensions should be given names of the
+ form "keyname.company.com=value", using a domain name legitimately
+ registered to the person or organization creating the vendor-specific
+ key. This reduces the risk of accidental conflict if different
+ organizations each define their own vendor-specific keys.
+
+
+6.5 Rules for Values in DNS-SD Name/Value Pairs
+
+ If there is an '=', then everything after the first '=' to the end
+ of the string is the value. The value can contain any eight-bit
+ values including '='. Leading or trailing spaces are part of the
+ value, so don't put them there unless you intend them to be there.
+ Any quotation marks around the value are part of the value, so don't
+ put them there unless you intend them to be part of the value.
+
+ The value is opaque binary data. Often the value for a particular
+ attribute will be US-ASCII (or UTF-8) text, but it is legal for a
+ value to be any binary data. For example, if the value of a key is an
+ IPv4 address, that address should simply be stored as four bytes of
+ binary data, not as a variable-length 7-15 byte ASCII string giving
+ the address represented in textual dotted decimal notation.
+
+ Generic debugging tools should generally display all attribute values
+ as a hex dump, with accompanying text alongside displaying the UTF-8
+ interpretation of those bytes, except for attributes where the
+ debugging tool has embedded knowledge that the value is some other
+ kind of data.
+
+ Authors defining DNS-SD profiles SHOULD NOT convert binary attribute
+ data types into printable text (e.g. using hexadecimal, Base-64 or UU
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 16]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ encoding) merely for the sake of making the data be printable text
+ when seen in a generic debugging tool. Doing this simply bloats the
+ size of the TXT record, without actually making the data any more
+ understandable to someone looking at it in a generic debugging tool.
+
+
+6.6 Example TXT Record
+
+ The TXT record below contains three syntactically valid name/value
+ pairs. (The meaning of these name/value pairs, if any, would depend
+ on the definitions pertaining to the service in question that is
+ using them.)
+
+ ---------------------------------------------------------------
+ | 0x0A | name=value | 0x08 | paper=A4 | 0x0E | DNS-SD Is Cool |
+ ---------------------------------------------------------------
+
+
+6.7 Version Tag
+
+ It is recommended that authors defining DNS-SD profiles include an
+ attribute of the form "txtvers=xxx" in their definition, and require
+ it to be the first name/value pair in the TXT record. This
+ information in the TXT record can be useful to help clients maintain
+ backwards compatibility with older implementations if it becomes
+ necessary to change or update the specification over time. Even if
+ the profile author doesn't anticipate the need for any future
+ incompatible changes, having a version number in the specification
+ provides useful insurance should incompatible changes become
+ unavoidable. Clients SHOULD ignore TXT records with a txtvers number
+ higher (or lower) than the version(s) they know how to interpret.
+
+ Note that the version number in the txtvers tag describes the version
+ of the TXT record specification being used to create this TXT record,
+ not the version of the application protocol that will be used if the
+ client subsequently decides to contact that service. Ideally, every
+ DNS-SD TXT record specification starts at txtvers=1 and stays that
+ way forever. Improvements can be made by defining new keys that older
+ clients silently ignore. The only reason to increment the version
+ number is if the old specification is subsequently found to be so
+ horribly broken that there's no way to do a compatible forward
+ revision, so the txtvers number has to be incremented to tell all the
+ old clients they should just not even try to understand this new TXT
+ record.
+
+ If there is a need to indicate which version number(s) of the
+ application protocol the service implements, the recommended key
+ name for this is "protovers".
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 17]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+7. Application Protocol Names
+
+ The portion of a Service Instance Name consists of a pair
+ of DNS labels, following the established convention for SRV records
+ [RFC 2782], namely: the first label of the pair is an underscore
+ character followed by the Application Protocol Name, and the second
+ label is either "_tcp" or "_udp".
+
+ Application Protocol Names may be no more than fourteen characters
+ (not counting the mandatory underscore), conforming to normal DNS
+ host name rules: Only lower-case letters, digits, and hyphens; must
+ begin and end with lower-case letter or digit.
+
+ Wise selection of an Application Protocol Name is very important,
+ and the choice is not always as obvious as it may appear.
+
+ In some cases, the Application Protocol Name merely names and refers
+ to the on-the-wire message format and semantics being used. FTP is
+ "ftp", IPP printing is "ipp", and so on.
+
+ However, it is common to "borrow" an existing protocol and repurpose
+ it for a new task. This is entirely sensible and sound engineering
+ practice, but that doesn't mean that the new protocol is providing
+ the same semantic service as the old one, even if it borrows the same
+ message formats. For example, the local network music playing
+ protocol implemented by iTunes on Macintosh and Windows is little
+ more than "HTTP GET" commands. However, that does *not* mean that it
+ is sensible or useful to try to access one of these music servers by
+ connecting to it with a standard web browser. Consequently, the
+ DNS-SD service advertised (and browsed for) by iTunes is "_daap._tcp"
+ (Digital Audio Access Protocol), not "_http._tcp". Advertising
+ "_http._tcp" service would cause iTunes servers to show up in
+ conventional Web browsers (Safari, Camino, OmniWeb, Opera, Netscape,
+ Internet Explorer, etc.) which is little use since it offers no pages
+ containing human-readable content. Similarly, browsing for
+ "_http._tcp" service would cause iTunes to find generic web servers,
+ such as the embedded web servers in devices like printers, which is
+ little use since printers generally don't have much music to offer.
+
+ Similarly, NFS is built on top of SUN RPC, but that doesn't mean it
+ makes sense for an NFS server to advertise that it provides "SUN RPC"
+ service. Likewise, Microsoft SMB file service is built on top of
+ Netbios running over IP, but that doesn't mean it makes sense for
+ an SMB file server to advertise that it provides "Netbios-over-IP"
+ service. The DNS-SD name of a service needs to encapsulate both the
+ "what" (semantics) and the "how" (protocol implementation) of the
+ service, since knowledge of both is necessary for a client to
+ usefully use the service. Merely advertising that a service was
+ built on top of SUN RPC is no use if the client has no idea what
+ the service actually does.
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 18]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ Another common mistake is to assume that the service type advertised
+ by iTunes should be "_daap._http._tcp." This is also incorrect.
+ Similarly, a protocol designer implementing a network service that
+ happens to use Simple Object Access Protocol [SOAP] should not feel
+ compelled to have "_soap" appear somewhere in the Application
+ Protocol Name. Part of the confusion here is that the presence of
+ "_tcp" or "_udp" in the portion of a Service Instance Name
+ has led people to assume that the structure of a service name has to
+ reflect the internal structure of how the protocol was implemented.
+ This is not correct. All that is required is that the service be
+ identified by a unique Application Protocol Name. Making the
+ Application Protocol Name at least marginally descriptive of
+ what the service does is desirable, though not essential.
+
+ The "_tcp" or "_udp" should be regarded as little more than
+ boilerplate text, and care should be taken not to attach too much
+ importance to it. Some might argue that the "_tcp" or "_udp" should
+ not be there at all, but this format is defined by RFC 2782, and
+ that's not going to change. In addition, the presence of "_tcp" has
+ the useful side-effect that it provides a convenient delegation point
+ to hand off responsibility for service discovery to a different DNS
+ server, if so desired.
+
+
+7.1. Selective Instance Enumeration
+
+ This document does not attempt to define an arbitrary query language
+ for service discovery, nor do we believe one is necessary.
+
+ However, there are some circumstances where narrowing the list of
+ results may be useful. A hypothetical Web browser client that is able
+ to retrieve HTML documents via HTTP and display them may also be able
+ to retrieve HTML documents via FTP and display them, but only in the
+ case of FTP servers that allow anonymous login. For that Web browser,
+ discovering all FTP servers on the network is not useful. The Web
+ browser only wants to discover FTP servers that it is able to talk
+ to. In this case, a subtype of "_ftp._tcp" could be defined. Instead
+ of issuing a query for "_ftp._tcp.", the Web browser issues a
+ query for "_anon._sub._ftp._tcp.", where "_anon" is a defined
+ subtype of "_ftp._tcp". The response to this query only includes the
+ names of SRV records for FTP servers that are willing to allow
+ anonymous login.
+
+ Note that the FTP server's Service Instance Name is unchanged -- it
+ is still something of the form "The Server._ftp._tcp.example.com."
+ The subdomain in which FTP server SRV records are registered defines
+ the namespace within which FTP server names are unique. Additional
+ subtypes (e.g. "_anon") of the basic service type (e.g. "_ftp._tcp")
+ serve to narrow the list of results, not to create more namespace.
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 19]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ Subtypes are appropriate when it is desirable for different kinds
+ of clients to be able to browse for services at two levels of
+ granularity. In the example above, we hypothesize two classes of
+ ftp client: clients that can provide username and password when
+ connecting, and clients that can only do anonymous login. The set of
+ ftp servers on the network is the same in both cases; the difference
+ is that the more capable client wants to discover all of them,
+ whereas the more limited client only wants to find the subset of
+ those ftp servers that it can talk to. Subtypes are only appropriate
+ in two-level scenarios such as this one, where some clients want to
+ find the full set of services of a given type, and at the same time
+ other clients only want to find some subset. Generally speaking, if
+ there is no client that wants to find the entire set, then it's
+ neither necessary nor desirable to use the subtype mechanism. If all
+ clients are browsing for some particular subtype, and no client
+ exists that browses for the parent type, then an Application Protocol
+ Name representing the logical service should be defined, and software
+ should simply advertise and browse for that particular service type
+ directly. In particular, just because a particular network service
+ happens to be implemented in terms of some other underlying protocol,
+ like HTTP, Sun RPC, or SOAP, doesn't mean that it's sensible for that
+ service to be defined as a subtype of "_http", "_sunrpc", or "_soap".
+ That would only be useful if there were some class of client for
+ which it is sensible to say, "I want to discover a service on the
+ network, and I don't care what it does, as long as it does it using
+ the SOAP XML RPC mechanism."
+
+ As with the TXT record name/value pairs, the list of possible
+ subtypes, if any, are defined and specified separately for each basic
+ service type. Note that the example given here using "_ftp" is a
+ hypothetical one. The "_ftp" service type does not (currently) have
+ any subtypes defined. Subtypes are currently a little-used feature
+ of DNS-SD, and experience will show whether or not they ultimately
+ prove to have broad applicability.
+
+
+7.2 Service Name Length Limits
+
+ As described above, application protocol names are allowed to be up
+ to fourteen characters long. The reason for this limit is to leave
+ as many bytes of the domain name as possible available for use
+ by both the network administrator (choosing service domain names)
+ and the end user (choosing instance names).
+
+ A domain name may be up to 255 bytes long, including the final
+ terminating root label at the end. Domain names used by DNS-SD
+ take the following forms:
+
+ .._tcp...
+ ._sub.._tcp...
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 20]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ The first example shows a service instance name, i.e. the name of the
+ service's SRV and TXT records. The second shows a subtype browsing
+ name, i.e. the name of a PTR record pointing to service instance
+ names (see "Selective Instance Enumeration").
+
+ The instance name may be up to 63 bytes. Including the
+ length byte used by the DNS format when the name is stored in a
+ packet, that makes 64 bytes.
+
+ When using subtypes, the subtype identifier is allowed to be up to
+ 63 bytes, plus the length byte, making 64. Including the "_sub"
+ and its length byte, this makes 69 bytes.
+
+ The application protocol name may be up to 14 bytes, plus the
+ underscore and length byte, making 16. Including the "_udp" or "_tcp"
+ and its length byte, this makes 21 bytes.
+
+ Typically, DNS-SD service records are placed into subdomains of their
+ own beneath a company's existing domain name. Since these subdomains
+ are intended to be accessed through graphical user interfaces, not
+ typed on a command-line they are frequently long and descriptive.
+ Including the length byte, the user-visible service domain may be up
+ to 64 bytes.
+
+ The terminating root label at the end counts as one byte.
+
+ Of our available 255 bytes, we have now accounted for 69+21+64+1 =
+ 155 bytes. This leaves 100 bytes to accommodate the organization's
+ existing domain name . When used with Multicast DNS,
+ is "local", which easily fits. When used with parent
+ domains of 100 bytes or less, the full functionality of DNS-SD is
+ available without restriction. When used with parent domains longer
+ than 100 bytes, the protocol risks exceeding the maximum possible
+ length of domain names, causing failures. In this case, careful
+ choice of short names can help avoid overflows.
+ If the and are too long, then service
+ instances with long instance names will not be discoverable or
+ resolvable, and applications making use of long subtype names
+ may fail.
+
+ Because of this constraint, we choose to limit Application Protocol
+ Names to 14 characters or less. Allowing more characters would not
+ add to the expressive power of the protocol, and would needlessly
+ lower the limit on the maximum length that may be
+ safely used.
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 21]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+8. Flagship Naming
+
+ In some cases, there may be several network protocols available
+ which all perform roughly the same logical function. For example,
+ the printing world has the LPR protocol, and the Internet Printing
+ Protocol (IPP), both of which cause printed sheets to be emitted
+ from printers in much the same way. In addition, many printer vendors
+ send their own proprietary page description language (PDL) data
+ over a TCP connection to TCP port 9100, herein referred to as the
+ "pdl-datastream" protocol. In an ideal world we would have only one
+ network printing protocol, and it would be sufficiently good that no
+ one felt a compelling need to invent a different one. However, in
+ practice, multiple legacy protocols do exist, and a service discovery
+ protocol has to accommodate that.
+
+ Many printers implement all three printing protocols: LPR, IPP, and
+ pdl-datastream. For the benefit of clients that may speak only one of
+ those protocols, all three are advertised.
+
+ However, some clients may implement two, or all three of those
+ printing protocols. When a client looks for all three service types
+ on the network, it will find three distinct services -- an LPR
+ service, an IPP service, and a pdl-datastream service -- all of which
+ cause printed sheets to be emitted from the same physical printer.
+
+ In the case of multiple protocols like this that all perform
+ effectively the same function, the client should suppress duplicate
+ names and display each name only once. When the user prints to a
+ given named printer, the printing client is responsible for choosing
+ the protocol which will best achieve the desired effect, without, for
+ example, requiring the user to make a manual choice between LPR and
+ IPP.
+
+ As described so far, this all works very well. However, consider some
+ future printer that only supports IPP printing, and some other future
+ printer that only supports pdl-datastream printing. The name spaces
+ for different service types are intentionally disjoint -- it is
+ acceptable and desirable to be able to have both a file server called
+ "Sales Department" and a printer called "Sales Department". However,
+ it is not desirable, in the common case, to have two different
+ printers both called "Sales Department", just because those printers
+ are implementing different protocols.
+
+ To help guard against this, when there are two or more network
+ protocols which perform roughly the same logical function, one of
+ the protocols is declared the "flagship" of the fleet of related
+ protocols. Typically the flagship protocol is the oldest and/or
+ best-known protocol of the set.
+
+ If a device does not implement the flagship protocol, then it instead
+ creates a placeholder SRV record (priority=0, weight=0, port=0,
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 22]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ target host = hostname of device) with that name. If, when it
+ attempts to create this SRV record, it finds that a record with the
+ same name already exists, then it knows that this name is already
+ taken by some entity implementing at least one of the protocols from
+ the class, and it must choose another. If no SRV record already
+ exists, then the act of creating it stakes a claim to that name so
+ that future devices in the same class will detect a conflict when
+ they try to use it. The SRV record needs to contain the target host
+ name in order for the conflict detection rules to operate. If two
+ different devices were to create placeholder SRV records both using a
+ null target host name (just the root label), then the two SRV records
+ would be seen to be in agreement so no conflict would be registered.
+
+ By defining a common well-known flagship protocol for the class,
+ future devices that may not even know about each other's protocols
+ establish a common ground where they can coordinate to verify
+ uniqueness of names.
+
+ No PTR record is created advertising the presence of empty flagship
+ SRV records, since they do not represent a real service being
+ advertised.
+
+
+9. Service Type Enumeration
+
+ In general, clients are not interested in finding *every* service on
+ the network, just the services that the client knows how to talk to.
+ (Software designers may *think* there's some value to finding *every*
+ service on the network, but that's just wooly thinking.)
+
+ However, for problem diagnosis and network management tools, it may
+ be useful for network administrators to find the list of advertised
+ service types on the network, even if those service names are just
+ opaque identifiers and not particularly informative in isolation.
+
+ For this reason, a special meta-query is defined. A DNS query for
+ PTR records with the name "_services._dns-sd._udp." yields
+ a list of PTR records, where the rdata of each PTR record is the
+ name of a service type. A subsequent query for PTR records with
+ one of those names yields a list of instances of that service type.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 23]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+10. Populating the DNS with Information
+
+ How the SRV and PTR records that describe services and allow them to
+ be enumerated make their way into the DNS is outside the scope of
+ this document. However, it can happen easily in any of a number of
+ ways, for example:
+
+ On some networks, the administrator might manually enter the records
+ into the name server's configuration file.
+
+ A network monitoring tool could output a standard zone file to be
+ read into a conventional DNS server. For example, a tool that can
+ find Apple LaserWriters using AppleTalk NBP could find the list
+ of printers, communicate with each one to find its IP address,
+ PostScript version, installed options, etc., and then write out a
+ DNS zone file describing those printers and their capabilities using
+ DNS resource records. That information would then be available to
+ DNS-SD clients that don't implement AppleTalk NBP, and don't want to.
+
+ Future IP printers could use Dynamic DNS Update [RFC 2136] to
+ automatically register their own SRV and PTR records with the DNS
+ server.
+
+ A printer manager device which has knowledge of printers on the
+ network through some other management protocol could also use Dynamic
+ DNS Update [RFC 2136].
+
+ Alternatively, a printer manager device could implement enough of
+ the DNS protocol that it is able to answer DNS queries directly,
+ and Example Co.'s main DNS server could delegate the
+ _ipp._tcp.example.com subdomain to the printer manager device.
+
+ Zeroconf printers answer Multicast DNS queries on the local link
+ for appropriate PTR and SRV names ending with ".local." [mDNS]
+
+
+11. Relationship to Multicast DNS
+
+ DNS-Based Service Discovery is only peripherally related to Multicast
+ DNS, in that the standard unicast DNS queries used by DNS-SD may also
+ be performed using multicast when appropriate, which is particularly
+ beneficial in Zeroconf environments [ZC].
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 24]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+12. Discovery of Browsing and Registration Domains (Domain Enumeration)
+
+ One of the main reasons for DNS-Based Service Discovery is so that
+ when a visiting client (e.g. a laptop computer) arrives at a new
+ network, it can discover what services are available on that network
+ without manual configuration. This logic that applies to discovering
+ services without manual configuration also applies to discovering the
+ domains in which services are registered without requiring manual
+ configuration.
+
+ This discovery is performed recursively, using Unicast or Multicast
+ DNS. Five special RR names are reserved for this purpose:
+
+ b._dns-sd._udp..
+ db._dns-sd._udp..
+ r._dns-sd._udp..
+ dr._dns-sd._udp..
+ lb._dns-sd._udp..
+
+ By performing PTR queries for these names, a client can learn,
+ respectively:
+
+ o A list of domains recommended for browsing
+
+ o A single recommended default domain for browsing
+
+ o A list of domains recommended for registering services using
+ Dynamic Update
+
+ o A single recommended default domain for registering services.
+
+ o The final query shown yields the "legacy browsing" domain.
+ Sophisticated client applications that care to present choices
+ of domain to the user, use the answers learned from the previous
+ four queries to discover those domains to present. In contrast,
+ many current applications browse without specifying an explicit
+ domain, allowing the operating system to automatically select an
+ appropriate domain on their behalf. It is for this class of
+ application that the "legacy browsing" query is provided, to allow
+ the network administrator to communicate to the client operating
+ systems which domain should be used for these applications.
+
+ These domains are purely advisory. The client or user is free to
+ browse and/or register services in any domains. The purpose of these
+ special queries is to allow software to create a user-interface that
+ displays a useful list of suggested choices to the user, from which
+ they may make a suitable selection, or ignore the offered suggestions
+ and manually enter their own choice.
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 25]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ The part of the name may be "local" (meaning "perform the
+ query using link-local multicast) or it may be learned through some
+ other mechanism, such as the DHCP "Domain" option (option code 15)
+ [RFC 2132] or the DHCP "Domain Search" option (option code 119)
+ [RFC 3397].
+
+ The part of the name may also be derived from the host's IP
+ address. The host takes its IP address, and calculates the logical
+ AND of that address and its subnet mask, to derive the 'base' address
+ of the subnet. It then constructs the conventional DNS "reverse
+ mapping" name corresponding to that base address, and uses that
+ as the part of the name for the queries described above.
+ For example, if a host has address 192.168.12.34, with subnet mask
+ 255.255.0.0, then the 'base' address of the subnet is 192.168.0.0,
+ and to discover the recommended legacy browsing domain for devices
+ on this subnet, the host issues a DNS PTR query for the name
+ "lb._dns-sd._udp.0.0.168.192.in-addr.arpa."
+
+ Sophisticated clients may perform domain enumeration queries both in
+ "local" and in one or more unicast domains, and then present the user
+ with an aggregate result, combining the information received from all
+ sources.
+
+
+13. DNS Additional Record Generation
+
+ DNS has an efficiency feature whereby a DNS server may place
+ additional records in the Additional Section of the DNS Message.
+ These additional records are typically records that the client did
+ not explicitly request, but the server has reasonable grounds to
+ expect that the client might request them shortly.
+
+ This section recommends which additional records should be generated
+ to improve network efficiency for both unicast and multicast DNS-SD
+ responses.
+
+
+13.1 PTR Records
+
+ When including a PTR record in a response packet, the
+ server/responder SHOULD include the following additional records:
+
+ o The SRV record(s) named in the PTR rdata.
+ o The TXT record(s) named in the PTR rdata.
+ o All address records (type "A" and "AAAA") named in the SRV rdata.
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 26]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+13.2 SRV Records
+
+ When including an SVR record in a response packet, the
+ server/responder SHOULD include the following additional records:
+
+ o All address records (type "A" and "AAAA") named in the SRV rdata.
+
+
+13.3 TXT Records
+
+ When including a TXT record in a response packet, no additional
+ records are required.
+
+
+13.4 Other Record Types
+
+ In response to address queries, or other record types, no additional
+ records are required by this document.
+
+
+14. Comparison with Alternative Service Discovery Protocols
+
+ Over the years there have been many proposed ways to do network
+ service discovery with IP, but none achieved ubiquity in the
+ marketplace. Certainly none has achieved anything close to the
+ ubiquity of today's deployment of DNS servers, clients, and other
+ infrastructure.
+
+ The advantage of using DNS as the basis for service discovery is
+ that it makes use of those existing servers, clients, protocols,
+ infrastructure, and expertise. Existing network analyzer tools
+ already know how to decode and display DNS packets for network
+ debugging.
+
+ For ad-hoc networks such as Zeroconf environments, peer-to-peer
+ multicast protocols are appropriate. The Zeroconf host profile [ZCHP]
+ requires the use of a DNS-like protocol over IP Multicast for host
+ name resolution in the absence of DNS servers. Given that Zeroconf
+ hosts will have to implement this Multicast-based DNS-like protocol
+ anyway, it makes sense for them to also perform service discovery
+ using that same Multicast-based DNS-like software, instead of also
+ having to implement an entirely different service discovery protocol.
+
+ In larger networks, a high volume of enterprise-wide IP multicast
+ traffic may not be desirable, so any credible service discovery
+ protocol intended for larger networks has to provide some facility to
+ aggregate registrations and lookups at a central server (or servers)
+ instead of working exclusively using multicast. This requires some
+ service discovery aggregation server software to be written,
+ debugged, deployed, and maintained. This also requires some service
+ discovery registration protocol to be implemented and deployed for
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 27]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ clients to register with the central aggregation server. Virtually
+ every company with an IP network already runs a DNS server, and DNS
+ already has a dynamic registration protocol [RFC 2136]. Given that
+ virtually every company already has to operate and maintain a DNS
+ server anyway, it makes sense to take advantage of this instead of
+ also having to learn, operate and maintain a different service
+ registration server. It should be stressed again that using the
+ same software and protocols doesn't necessarily mean using the same
+ physical piece of hardware. The DNS-SD service discovery functions
+ do not have to be provided by the same piece of hardware that
+ is currently providing the company's DNS name service. The
+ "_tcp." subdomain may be delegated to a different piece of
+ hardware. However, even when the DNS-SD service is being provided
+ by a different piece of hardware, it is still the same familiar DNS
+ server software that is running, with the same configuration file
+ syntax, the same log file format, and so forth.
+
+ Service discovery needs to be able to provide appropriate security.
+ DNS already has existing mechanisms for security [RFC 2535].
+
+ In summary:
+
+ Service discovery requires a central aggregation server.
+ DNS already has one: It's called a DNS server.
+
+ Service discovery requires a service registration protocol.
+ DNS already has one: It's called DNS Dynamic Update.
+
+ Service discovery requires a query protocol
+ DNS already has one: It's called DNS.
+
+ Service discovery requires security mechanisms.
+ DNS already has security mechanisms: DNSSEC.
+
+ Service discovery requires a multicast mode for ad-hoc networks.
+ Zeroconf environments already require a multicast-based DNS-like
+ name lookup protocol for mapping host names to addresses, so it
+ makes sense to let one multicast-based protocol do both jobs.
+
+ It makes more sense to use the existing software that every network
+ needs already, instead of deploying an entire parallel system just
+ for service discovery.
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 28]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+15. Real Examples
+
+ The following examples were prepared using standard unmodified
+ nslookup and standard unmodified BIND running on GNU/Linux.
+
+ Note: In real products, this information is obtained and presented to
+ the user using graphical network browser software, not command-line
+ tools, but if you wish you can try these examples for yourself as you
+ read along, using the command-line tools already available on your
+ own Unix machine.
+
+15.1 Question: What FTP servers are being advertised from dns-sd.org?
+
+ nslookup -q=ptr _ftp._tcp.dns-sd.org.
+ _ftp._tcp.dns-sd.org
+ name = Apple\032QuickTime\032Files._ftp._tcp.dns-sd.org
+ _ftp._tcp.dns-sd.org
+ name = Microsoft\032Developer\032Files._ftp._tcp.dns-sd.org
+ _ftp._tcp.dns-sd.org
+ name = Registered\032Users'\032Only._ftp._tcp.dns-sd.org
+
+ Answer: There are three, called "Apple QuickTime Files",
+ "Microsoft Developer Files" and "Registered Users' Only".
+
+ Note that nslookup escapes spaces as "\032" for display purposes,
+ but a graphical DNS-SD browser does not.
+
+15.2 Question: What FTP servers allow anonymous access?
+
+ nslookup -q=ptr _anon._sub._ftp._tcp.dns-sd.org
+ _anon._sub._ftp._tcp.dns-sd.org
+ name = Apple\032QuickTime\032Files._ftp._tcp.dns-sd.org
+ _anon._sub._ftp._tcp.dns-sd.org
+ name = Microsoft\032Developer\032Files._ftp._tcp.dns-sd.org
+
+ Answer: Only "Apple QuickTime Files" and "Microsoft Developer Files"
+ allow anonymous access.
+
+15.3 Question: How do I access "Apple QuickTime Files"?
+
+ nslookup -q=any "Apple\032QuickTime\032Files._ftp._tcp.dns-sd.org."
+ Apple\032QuickTime\032Files._ftp._tcp.dns-sd.org
+ text = "path=/quicktime"
+ Apple\032QuickTime\032Files._ftp._tcp.dns-sd.org
+ priority = 0, weight = 0, port= 21 host = ftp.apple.com
+ ftp.apple.com internet address = 17.254.0.27
+ ftp.apple.com internet address = 17.254.0.31
+ ftp.apple.com internet address = 17.254.0.26
+
+ Answer: You need to connect to ftp.apple.com, port 21, path
+ "/quicktime". The addresses for ftp.apple.com are also given.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 29]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+16. User Interface Considerations
+
+ DNS-Based Service Discovery was designed by first giving careful
+ consideration to what constitutes a good user experience for service
+ discovery, and then designing a protocol with the features necessary
+ to enable that good user experience. This section covers two issues
+ in particular: Choice of factory-default names (and automatic
+ renaming behavior) for devices advertising services, and the
+ "continuous live update" user-experience model for clients
+ browsing to discover services.
+
+
+16.1 Service Advertising User-Interface Considerations
+
+ When a DNS-SD service is advertised using Multicast DNS [mDNS],
+ automatic name conflict and resolution will occur if there is already
+ another service of the same type advertising with the same name.
+ As described in the Multicast DNS specification [mDNS], upon a
+ conflict, the service should:
+
+ 1. Automatically select a new name (typically by appending
+ or incrementing a digit at the end of the name),
+ 2. try advertising with the new name, and
+ 3. upon success, record the new name in persistent storage.
+
+ This renaming behavior is very important, because it is the key
+ to providing user-friendly service names in the out-of-the-box
+ factory-default configuration. Some product developers may not
+ have realized this, because there are some products today where
+ the factory-default name is distinctly unfriendly, containing
+ random-looking strings of characters, like the device's Ethernet
+ address in hexadecimal. This is unnecessary, and undesirable, because
+ the point of the user-visible name is that it should be friendly and
+ useful to human users. If the name is not unique on the local network
+ the protocol will rememdy this as necessary. It is ironic that many
+ of the devices with this mistake are network printers, given that
+ these same printers also simultaneously support AppleTalk-over-
+ Ethernet, with nice user-friendly default names (and automatic
+ conflict detection and renaming). Examples of good factory-default
+ names are as follows:
+
+ Brother 5070N
+ Canon W2200 [ Apologies to makers of ]
+ HP LaserJet 4600 [ DNS-SD/mDNS printers ]
+ Lexmark W840 [ not listed. Email ]
+ Okidata C5300 [ the authors and we'll ]
+ Ricoh Aficio CL7100 [ add you to the list. ]
+ Xerox Phaser 6200DX
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 30]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ To complete the case for why adding long ugly serial numbers to
+ the end of names is neither necessary nor desirable, consider
+ the cases where the user has (a) only one network printer,
+ (b) two network printers, and (c) many network printers.
+
+ (a) In the case where the user has only one network printer, a simple
+ name like (to use a vendor-neutral example) "Printer" is more
+ user-friendly than an ugly name like "Printer 0001E68C74FB".
+ Appending ugly hexadecimal goop to the end of the name to make
+ sure the name is unique is irrelevant to a user who only has one
+ printer anyway.
+
+ (b) In the case where the user gets a second network printer,
+ having it detect that the name "Printer" is already in use
+ and automatically instead name itself "Printer (2)" provides a
+ good user experience. For the users, remembering that the old
+ printer is "Printer" and the new one is "Printer (2)" is easy
+ and intuitive. Seeing two printers called "Printer 0001E68C74FB"
+ and "Printer 00306EC3FD1C" is a lot less helpful.
+
+ (c) In the case of a network with ten network printers, seeing a
+ list of ten names all of the form "Printer xxxxxxxxxxxx" has
+ effectively taken what was supposed to be a list of user-friendly
+ rich-text names (supporting mixed case, spaces, punctuation,
+ non-Roman characters and other symbols) and turned it into
+ just about the worst user-interface imaginable: a list of
+ incomprehensible random-looking strings of letters and digits.
+ In a network with a lot of printers, it would be desirable for
+ the people setting up the printers to take a moment to give each
+ one a descriptive name, but in the event they don't, presenting
+ the users with a list of sequentially-numbered printers is a much
+ more desirable default user experience than showing a list of raw
+ Ethernet addresses.
+
+
+16.2 Client Browsing User-Interface Considerations
+
+ Of particular concern in the design of DNS-SD was the dynamic nature
+ of service discovery in a changing network environment. Other service
+ discovery protocols have been designed with an implicit unstated
+ assumption that the usage model is:
+
+ (a) client calls the service discovery code
+ (b) client gets list of discovered services
+ as of a particular instant in time, and then
+ (c) client displays list for user to select from
+
+ Superficially this usage model seems reasonable, but the problem is
+ that it's too optimistic. It only considers the success case, where
+ the user successfully finds the service they're looking for. In the
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 31]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ case where the user is looking for (say) a particular printer, and
+ that printer's not turned on or not connected, the user first has
+ to attempt to remedy the problem, and then has to click a "refresh"
+ button to retry the service discovery (or, worse, dismiss the
+ browsing window entirely, and open a new one to initiate a new
+ network search attempt) to find out whether they were successful.
+ Because nothing happens instantaneously in networking, and packets
+ can be lost, necessitating some number of retransmissions, a service
+ discovery search typically takes a few seconds. A fairly typical user
+ experience model is:
+
+ (a) display an empty window,
+ (b) display some animation like a searchlight
+ sweeping back and forth for ten seconds, and then
+ (c) at the end of the ten-second search, display
+ a static list showing what was discovered.
+
+ Every time the user clicks the "refresh" button they have to endure
+ another ten-second wait, and every time the discovered list is
+ finally shown at the end of the ten-second wait, the moment it's
+ displayed on the screen it's already beginning to get stale and
+ out-of-date.
+
+ The service discovery user experience that the DNS-SD designers had
+ in mind has some rather different properties:
+
+ 1. Displaying a list of discovered services should be effectively
+ instantaneous -- i.e. typically 1/10 second, not 10 seconds.
+
+ 2. The list of discovered services should not be getting stale
+ and out-of-date from the moment it's displayed. The list
+ should be 'live' and should continue to update as new services
+ are discovered. Because of the delays, packet losses, and
+ retransmissions inherent in networking, it is to be expected
+ that sometimes, after the initial list is displayed showing
+ the majority of discovered services, a few remaining stragglers
+ may continue to trickle in during the subsequent few seconds.
+ Even after this initial stable list has been built and displayed,
+ the list should remain 'live' and should continue to update.
+ At any future time, be it minutes, hours, or even days later,
+ if a new service of the desired type is discovered, it should be
+ displayed in the list automatically, without the user having to
+ click a "refresh" button or take any other explicit action to
+ update the display.
+
+ 3. With users getting to be in the habit of leaving service discovery
+ windows open, and coming to expect to be able to rely on them
+ to show a continuous 'live' view of current network reality,
+ this creates a new requirement for us: deletion of stale services.
+ When a service discovery list shows just a static snapshot at a
+ moment in time, then the situation is simple: either a service was
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 32]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ discovered and appears in the list, or it was not, and does not.
+ However, when our list is live and updates continuously with the
+ discovery of new services, then this implies the corollary: when
+ a service goes away, it needs to *disappear* from the service
+ discovery list. Otherwise, the result would be unacceptable: the
+ service discovery list would simply grow monotonically over time,
+ and would require a periodic "refresh" (or complete dismissal and
+ recreation) to clear out old stale data.
+
+ 4. With users getting to be in the habit of leaving service discovery
+ windows open, these windows need to update not only in response
+ to services coming and going, but also in response to changes
+ in configuration and connectivity of the client machine itself.
+ For example, if a user opens a service discovery window when no
+ Ethernet cable is connected to the client machine, and the window
+ appears empty with no discovered services, then when the user
+ connects the cable the window should automatically populate with
+ discovered services without requiring any explicit user action.
+ If the user disconnects the Ethernet cable, all the services
+ discovered via that network interface should automatically
+ disappear. If the user switches from one 802.11 wireless base
+ station to another, the service discovery window should
+ automatically update to remove all the services discovered
+ via the old wireless base station, and add all the services
+ discovered via the new one.
+
+ If these requirements seem to be setting an arbitrary and
+ unreasonably high standard for service discovery, bear in mind that
+ while it may have seemed that way to some, back in the 1990s when
+ these ideas were first proposed, in the years since then Apple and
+ other companies have shipped multiple implementations of DNS-SD/mDNS
+ that meet and exceed these requirements. In the years since Apple
+ shipped Mac OS X 10.2 Jaguar with the Open Source mDNSResponder
+ daemon, this service discovery "live browsing" paradigm has been
+ adopted and implemented in a wide range of Apple and third-party
+ applications, including printer discovery, Safari discovery of
+ devices with embedded web servers (for status and configuration),
+ iTunes music sharing, iPhoto photo sharing, the iChat Bonjour buddy
+ list, SubEthaEdit multi-user document editing, etc.
+
+ With so many different applications demonstrating that the "live
+ browsing" paradigm is clearly achievable, these four requirements
+ should not be regarded as idealistic unattainable goals, but
+ instead as the bare minimum baseline functionality that any
+ credible service discovery protocol needs to achieve.
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 33]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+17. IPv6 Considerations
+
+ IPv6 has no significant differences, except that the address of the
+ SRV record's target host is given by the appropriate IPv6 address
+ records instead of the IPv4 "A" record.
+
+
+18. Security Considerations
+
+ DNSSEC [RFC 2535] should be used where the authenticity of
+ information is important. Since DNS-SD is just a naming and usage
+ convention for records in the existing DNS system, it has no specific
+ additional security requirements over and above those that already
+ apply to DNS queries and DNS updates.
+
+
+19. IANA Considerations
+
+ This protocol builds on DNS SRV records [RFC 2782], and similarly
+ requires IANA to assign unique application protocol names.
+ Unfortunately, the "IANA Considerations" section of RFC 2782 says
+ simply, "The IANA has assigned RR type value 33 to the SRV RR.
+ No other IANA services are required by this document."
+ Due to this oversight, IANA is currently prevented from carrying
+ out the necessary function of assigning these unique identifiers.
+
+ This document proposes the following IANA allocation policy for
+ unique application protocol names:
+
+ Allowable names:
+ * Must be no more than fourteen characters long
+ * Must consist only of:
+ - lower-case letters 'a' - 'z'
+ - digits '0' - '9'
+ - the hyphen character '-'
+ * Must begin and end with a lower-case letter or digit.
+ * Must not already be assigned to some other protocol in the
+ existing IANA "list of assigned application protocol names
+ and port numbers" [ports].
+
+ These identifiers are allocated on a First Come First Served basis.
+ In the event of abuse (e.g. automated mass registrations, etc.),
+ the policy may be changed without notice to Expert Review [RFC 2434].
+
+ The textual nature of service/protocol names means that there are
+ almost infinitely many more of them available than the finite set of
+ 65535 possible port numbers. This means that developers can produce
+ experimental implementations using unregistered service names with
+ little chance of accidental collision, providing service names are
+ chosen with appropriate care. However, this document strongly
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 34]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ advocates that on or before the date a product ships, developers
+ should properly register their service names.
+
+ Some developers have expressed concern that publicly registering
+ their service names (and port numbers today) with IANA before a
+ product ships may give away clues about that product to competitors.
+ For this reason, IANA should consider allowing service name
+ applications to remain secret for some period of time, much as US
+ patent applications remain secret for two years after the date of
+ filing.
+
+ This proposed IANA allocation policy is not in force until this
+ document is published as an RFC. In the meantime, unique application
+ protocol names may be registered according to the instructions at
+ . As of August 2006, there
+ are roughly 300 application protocols in currently shipping products
+ that have been so registered as using DNS-SD for service discovery.
+
+
+20. Acknowledgments
+
+ The concepts described in this document have been explored, developed
+ and implemented with help from Richard Brown, Erik Guttman, Paul
+ Vixie, and Bill Woodcock.
+
+ Special thanks go to Bob Bradley, Josh Graessley, Scott Herscher,
+ Roger Pantos and Kiren Sekar for their significant contributions.
+
+
+21. Deployment History
+
+ The first implementations of DNS-Based Service Discovery and
+ Multicast DNS were initially developed during the late 1990s,
+ but the event that put them into the media spotlight was Steve Jobs
+ demonstrating it live on stage in his keynote presentation opening
+ Apple's annual Worldwide Developers Conference in May 2002, and
+ announcing Apple's adoption of the technology throughout its hardware
+ and software product line. Three months later, in August 2002, Apple
+ shipped Mac OS X 10.2 Jaguar, and millions of end-users got their
+ first exposure to Zero Configuration Networking with DNS-SD/mDNS
+ in applications like Safari, iChat, and printer setup. A month later,
+ in September 2002, Apple released the entire source code for the
+ mDNS Responder daemon under its Darwin Open Source project, with
+ code not just for Mac OS X, but also for a range of other platforms
+ including Windows, VxWorks, Linux, Solaris, FreeBSD, etc.
+
+ Many hardware makers were quick to see the benefits of Zero
+ Configuration Networking. Printer makers especially were enthusiastic
+ early adopters, and within a year every major printer manufacturer
+ was shipping DNS-SD/mDNS-enabled network printers. If you've bought
+ any network printer at all in the last few years, it was probably one
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 35]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ that supports DNS-SD/mDNS, even if you didn't know that at the time.
+ For Mac OS X users, telling if you have DNS-SD/mDNS printers on your
+ network is easy because they automatically appear in the "Bonjour"
+ submenu in the "Print" dialog of every Mac application. Microsoft
+ Windows users can get a similar experience by installing Bonjour for
+ Windows (takes about 90 seconds, no restart required) and running the
+ Bonjour for Windows Printer Setup Wizard [B4W].
+
+ The Open Source community has produced several independent
+ implementations of DNS-Based Service Discovery and Multicast DNS,
+ some in C like Apple's mDNSResponder daemon, and others in a variety
+ of different languages including Java, Python, Perl, and C#/Mono.
+
+
+22. Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights. For the purposes of this document,
+ the term "BCP 78" refers exclusively to RFC 3978, "IETF Rights
+ in Contributions", published March 2005.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 36]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+23. Normative References
+
+ [ports] IANA list of assigned application protocol names and port
+ numbers
+
+ [RFC 1033] Lottor, M., "Domain Administrators Operations Guide",
+ RFC 1033, November 1987.
+
+ [RFC 1034] Mockapetris, P., "Domain Names - Concepts and
+ Facilities", STD 13, RFC 1034, November 1987.
+
+ [RFC 1035] Mockapetris, P., "Domain Names - Implementation and
+ Specifications", STD 13, RFC 1035, November 1987.
+
+ [RFC 2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, March 1997.
+
+ [RFC 2782] Gulbrandsen, A., et al., "A DNS RR for specifying the
+ location of services (DNS SRV)", RFC 2782, February 2000.
+
+ [RFC 3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", RFC 3629, November 2003.
+
+ [UAX15] "Unicode Normalization Forms"
+ http://www.unicode.org/reports/tr15/
+
+
+24. Informative References
+
+ [B4W] Bonjour for Windows
+
+ [mDNS] Cheshire, S., and M. Krochmal, "Multicast DNS",
+ Internet-Draft (work in progress),
+ draft-cheshire-dnsext-multicastdns-06.txt, August 2006.
+
+ [NBP] Cheshire, S., and M. Krochmal,
+ "Requirements for a Protocol to Replace AppleTalk NBP",
+ Internet-Draft (work in progress),
+ draft-cheshire-dnsext-nbp-05.txt, August 2006.
+
+ [RFC 2132] Alexander, S., and Droms, R., "DHCP Options and BOOTP
+ Vendor Extensions", RFC 2132, March 1997.
+
+ [RFC 2136] Vixie, P., et al., "Dynamic Updates in the Domain Name
+ System (DNS UPDATE)", RFC 2136, April 1997.
+
+ [RFC 2434] Narten, T., and H. Alvestrand, "Guidelines for Writing
+ an IANA Considerations Section in RFCs", RFC 2434,
+ October 1998.
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 37]
+
+Internet Draft DNS-Based Service Discovery 10th August 2006
+
+
+ [RFC 2535] Eastlake, D., "Domain Name System Security Extensions",
+ RFC 2535, March 1999.
+
+ [RFC 3007] Wellington, B., et al., "Secure Domain Name System (DNS)
+ Dynamic Update", RFC 3007, November 2000.
+
+ [RFC 3397] Aboba, B., and Cheshire, S., "Dynamic Host Configuration
+ Protocol (DHCP) Domain Search Option", RFC 3397, November
+ 2002.
+
+ [SOAP] Nilo Mitra, "SOAP Version 1.2 Part 0: Primer",
+ W3C Proposed Recommendation, 24 June 2003
+ http://www.w3.org/TR/2003/REC-soap12-part0-20030624
+
+ [ZC] Williams, A., "Requirements for Automatic Configuration
+ of IP Hosts", Internet-Draft (work in progress),
+ draft-ietf-zeroconf-reqts-12.txt, September 2002.
+
+ [ZCHP] Guttman, E., "Zeroconf Host Profile Applicability
+ Statement", Internet-Draft (work in progress),
+ draft-ietf-zeroconf-host-prof-01.txt, July 2001.
+
+
+25. Authors' Addresses
+
+ Stuart Cheshire
+ Apple Computer, Inc.
+ 1 Infinite Loop
+ Cupertino
+ California 95014
+ USA
+
+ Phone: +1 408 974 3207
+ EMail: rfc [at] stuartcheshire [dot] org
+
+
+ Marc Krochmal
+ Apple Computer, Inc.
+ 1 Infinite Loop
+ Cupertino
+ California 95014
+ USA
+
+ Phone: +1 408 974 4368
+ EMail: marc [at] apple [dot] com
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 38]
diff --git a/specs/draft-cheshire-dnsext-multicastdns-06.txt b/specs/draft-cheshire-dnsext-multicastdns-06.txt
new file mode 100644
index 0000000..1dbe8b6
--- /dev/null
+++ b/specs/draft-cheshire-dnsext-multicastdns-06.txt
@@ -0,0 +1,3074 @@
+Document: draft-cheshire-dnsext-multicastdns-06.txt Stuart Cheshire
+Internet-Draft Marc Krochmal
+Category: Standards Track Apple Computer, Inc.
+Expires 10th February 2007 10th August 2006
+
+ Multicast DNS
+
+
+
+Status of this Memo
+
+ By submitting this Internet-Draft, each author represents that any
+ applicable patent or other IPR claims of which he or she is aware
+ have been or will be disclosed, and any of which he or she becomes
+ aware will be disclosed, in accordance with Section 6 of BCP 79.
+ For the purposes of this document, the term "BCP 79" refers
+ exclusively to RFC 3979, "Intellectual Property Rights in IETF
+ Technology", published March 2005.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF), its areas, and its working groups. Note that
+ other groups may also distribute working documents as Internet-
+ Drafts.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ The list of current Internet-Drafts can be accessed at
+ http://www.ietf.org/1id-abstracts.html
+
+ The list of Internet-Draft Shadow Directories can be accessed at
+ http://www.ietf.org/shadow.html
+
+Abstract
+
+ As networked devices become smaller, more portable, and
+ more ubiquitous, the ability to operate with less configured
+ infrastructure is increasingly important. In particular,
+ the ability to look up DNS resource record data types
+ (including, but not limited to, host names) in the absence
+ of a conventional managed DNS server, is becoming essential.
+
+ Multicast DNS (mDNS) provides the ability to do DNS-like operations
+ on the local link in the absence of any conventional unicast DNS
+ server. In addition, mDNS designates a portion of the DNS namespace
+ to be free for local use, without the need to pay any annual fee, and
+ without the need to set up delegations or otherwise configure a
+ conventional DNS server to answer for those names.
+
+ The primary benefits of mDNS names are that (i) they require little
+ or no administration or configuration to set them up, (ii) they work
+ when no infrastructure is present, and (iii) they work during
+ infrastructure failures.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 1]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+Table of Contents
+
+ 1. Introduction....................................................3
+ 2. Conventions and Terminology Used in this Document...............3
+ 3. Multicast DNS Names.............................................4
+ 4. Source Address Check............................................8
+ 5. Reverse Address Mapping.........................................9
+ 6. Querying.......................................................10
+ 7. Duplicate Suppression..........................................15
+ 8. Responding.....................................................17
+ 9. Probing and Announcing on Startup..............................20
+ 10. Conflict Resolution............................................26
+ 11. Resource Record TTL Values and Cache Coherency.................28
+ 12. Special Characteristics of Multicast DNS Domains...............33
+ 13. Multicast DNS for Service Discovery............................34
+ 14. Enabling and Disabling Multicast DNS...........................34
+ 15. Considerations for Multiple Interfaces.........................35
+ 16. Considerations for Multiple Responders on the Same Machine.....36
+ 17. Multicast DNS and Power Management.............................38
+ 18. Multicast DNS Character Set....................................39
+ 19. Multicast DNS Message Size.....................................41
+ 20. Multicast DNS Message Format...................................42
+ 21. Choice of UDP Port Number......................................45
+ 22. Summary of Differences Between Multicast DNS and Unicast DNS...46
+ 23. Benefits of Multicast Responses................................47
+ 24. IPv6 Considerations............................................48
+ 25. Security Considerations........................................49
+ 26. IANA Considerations............................................50
+ 27. Acknowledgments................................................50
+ 28. Deployment History.............................................50
+ 29. Copyright Notice...............................................51
+ 30. Normative References...........................................51
+ 31. Informative References.........................................52
+ 32. Authors' Addresses.............................................53
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 2]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+1. Introduction
+
+ When reading this document, familiarity with the concepts of Zero
+ Configuration Networking [ZC] and automatic link-local addressing
+ [RFC 2462] [RFC 3927] is helpful.
+
+ This document proposes no change to the structure of DNS messages,
+ and no new operation codes, response codes, or resource record types.
+ This document simply discusses what needs to happen if DNS clients
+ start sending DNS queries to a multicast address, and how a
+ collection of hosts can cooperate to collectively answer those
+ queries in a useful manner.
+
+ There has been discussion of how much burden Multicast DNS might
+ impose on a network. It should be remembered that whenever IPv4 hosts
+ communicate, they broadcast ARP packets on the network on a regular
+ basis, and this is not disastrous. The approximate amount of
+ multicast traffic generated by hosts making conventional use of
+ Multicast DNS is anticipated to be roughly the same order of
+ magnitude as the amount of broadcast ARP traffic those hosts already
+ generate.
+
+ New applications making new use of Multicast DNS capabilities for
+ unconventional purposes may generate more traffic. If some of those
+ new applications are "chatty", then work will be needed to help them
+ become less chatty. When performing any analysis, it is important
+ to make a distinction between the application behavior and the
+ underlying protocol behavior. If a chatty application uses UDP,
+ that doesn't mean that UDP is chatty, or that IP is chatty, or that
+ Ethernet is chatty. What it means is that the application is chatty.
+ The same applies to any future applications that may decide to layer
+ increasing portions of their functionality over Multicast DNS.
+
+
+2. Conventions and Terminology Used in this Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in "Key words for use in
+ RFCs to Indicate Requirement Levels" [RFC 2119].
+
+ This document uses the term "host name" in the strict sense to mean
+ a fully qualified domain name that has an address record. It does
+ not use the term "host name" in the commonly used but incorrect
+ sense to mean just the first DNS label of a host's fully qualified
+ domain name.
+
+ A DNS (or mDNS) packet contains an IP TTL in the IP header, which
+ is effectively a hop-count limit for the packet, to guard against
+ routing loops. Each Resource Record also contains a TTL, which is
+ the number of seconds for which the Resource Record may be cached.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 3]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ In any place where there may be potential confusion between these two
+ types of TTL, the term "IP TTL" is used to refer to the IP header TTL
+ (hop limit), and the term "RR TTL" is used to refer to the Resource
+ Record TTL (cache lifetime).
+
+ When this document uses the term "Multicast DNS", it should be taken
+ to mean: "Clients performing DNS-like queries for DNS-like resource
+ records by sending DNS-like UDP query and response packets over IP
+ Multicast to UDP port 5353."
+
+ This document uses the terms "shared" and "unique" when referring to
+ resource record sets.
+
+ A "shared" resource record set is one where several Multicast DNS
+ responders may have records with that name, rrtype, and rrclass, and
+ several responders may respond to a particular query.
+
+ A "unique" resource record set is one where all the records with
+ that name, rrtype, and rrclass are conceptually under the control
+ or ownership of a single responder, and it is expected that at most
+ one responder should respond to a query for that name, rrtype, and
+ rrclass. Before claiming ownership of a unique resource record set,
+ a responder MUST probe to verify that no other responder already
+ claims ownership of that set, as described in Section 9.1 "Probing".
+ For fault-tolerance and other reasons it is permitted sometimes to
+ have more than one responder answering for a particular "unique"
+ resource record set, but such cooperating responders MUST give
+ answers containing identical rdata for these records or the
+ answers will be perceived to be in conflict with each other.
+
+ Strictly speaking the terms "shared" and "unique" apply to resource
+ record sets, not to individual resource records, but it is sometimes
+ convenient to talk of "shared resource records" and "unique resource
+ records". When used this way, the terms should be understood to mean
+ a record that is a member of a "shared" or "unique" resource record
+ set, respectively.
+
+
+3. Multicast DNS Names
+
+ This document proposes that the DNS top-level domain ".local." be
+ designated a special domain with special semantics, namely that any
+ fully-qualified name ending in ".local." is link-local, and names
+ within this domain are meaningful only on the link where they
+ originate. This is analogous to IPv4 addresses in the 169.254/16
+ prefix, which are link-local and meaningful only on the link where
+ they originate.
+
+ Any DNS query for a name ending with ".local." MUST be sent
+ to the mDNS multicast address (224.0.0.251 or its IPv6 equivalent
+ FF02::FB).
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 4]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ It is unimportant whether a name ending with ".local." occurred
+ because the user explicitly typed in a fully qualified domain name
+ ending in ".local.", or because the user entered an unqualified
+ domain name and the host software appended the suffix ".local."
+ because that suffix appears in the user's search list. The ".local."
+ suffix could appear in the search list because the user manually
+ configured it, or because it was received in a DHCP option, or via
+ any other valid mechanism for configuring the DNS search list. In
+ this respect the ".local." suffix is treated no differently to any
+ other search domain that might appear in the DNS search list.
+
+ DNS queries for names that do not end with ".local." MAY be sent to
+ the mDNS multicast address, if no other conventional DNS server is
+ available. This can allow hosts on the same link to continue
+ communicating using each other's globally unique DNS names during
+ network outages which disrupt communication with the greater
+ Internet. When resolving global names via local multicast, it is even
+ more important to use DNSSEC or other security mechanisms to ensure
+ that the response is trustworthy. Resolving global names via local
+ multicast is a contentious issue, and this document does not discuss
+ it in detail, instead concentrating on the issue of resolving local
+ names using DNS packets sent to a multicast address.
+
+ A host that belongs to an organization or individual who has control
+ over some portion of the DNS namespace can be assigned a globally
+ unique name within that portion of the DNS namespace, for example,
+ "cheshire.apple.com." For those of us who have this luxury, this
+ works very well. However, the majority of home customers do not have
+ easy access to any portion of the global DNS namespace within which
+ they have the authority to create names as they wish. This leaves the
+ majority of home computers effectively anonymous for practical
+ purposes.
+
+ To remedy this problem, this document allows any computer user to
+ elect to give their computers link-local Multicast DNS host names of
+ the form: "single-dns-label.local." For example, a laptop computer
+ may answer to the name "cheshire.local." Any computer user is granted
+ the authority to name their computer this way, provided that the
+ chosen host name is not already in use on that link. Having named
+ their computer this way, the user has the authority to continue using
+ that name until such time as a name conflict occurs on the link which
+ is not resolved in the user's favour. If this happens, the computer
+ (or its human user) SHOULD cease using the name, and may choose to
+ attempt to allocate a new unique name for use on that link. These
+ conflicts are expected to be relatively rare for people who choose
+ reasonably imaginative names, but it is still important to have a
+ mechanism in place to handle them when they happen.
+
+ The point made in the previous paragraph is very important and bears
+ repeating. It is easy for those of us in the IETF community who run
+ our own name servers at home to forget that the majority of computer
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 5]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ users do not run their own name server and have no easy way to create
+ their own host names. When these users wish to transfer files between
+ two laptop computers, they are frequently reduced to typing in
+ dotted-decimal IP addresses because they simply have no other way for
+ one host to refer to the other by name. This is a sorry state of
+ affairs. What is worse, most users don't even bother trying to use
+ dotted-decimal IP addresses. Most users still move data between
+ machines by burning it onto CD-R, copying it onto a USB "keychain"
+ flash drive, or similar removable media.
+
+ In a world of gigabit Ethernet and ubiquitous wireless networking it
+ is a sad indictment of the networking community that most users still
+ prefer sneakernet.
+
+ Allowing ad-hoc allocation of single-label names in a single flat
+ ".local." namespace may seem to invite chaos. However, operational
+ experience with AppleTalk NBP names [NBP], which on any given link
+ are also effectively single-label names in a flat namespace, shows
+ that in practice name collisions happen extremely rarely and are not
+ a problem. Groups of computer users from disparate organizations
+ bring Macintosh laptop computers to events such as IETF Meetings, the
+ Mac Hack conference, the Apple World Wide Developer Conference, etc.,
+ and complaints at these events about users suffering conflicts and
+ being forced to rename their machines have never been an issue.
+
+ This document advocates a single flat namespace for dot-local host
+ names, (i.e. the names of DNS address records), but other DNS record
+ types (such as those used by DNS Service Discovery [DNS-SD]) may
+ contain as many labels as appropriate for the desired usage, subject
+ to the 255-byte name length limit specified below in Section 3.3
+ "Maximum Multicast DNS Name Length".
+
+ Enforcing uniqueness of host names (i.e. the names of DNS address
+ records mapping names to IP addresses) is probably desirable in the
+ common case, but this document does not mandate that. It is
+ permissible for a collection of coordinated hosts to agree to
+ maintain multiple DNS address records with the same name, possibly
+ for load balancing or fault-tolerance reasons. This document does not
+ take a position on whether that is sensible. It is important that
+ both modes of operation are supported. The Multicast DNS protocol
+ allows hosts to verify and maintain unique names for resource records
+ where that behavior is desired, and it also allows hosts to maintain
+ multiple resource records with a single shared name where that
+ behavior is desired. This consideration applies to all resource
+ records, not just address records (host names). In summary: It is
+ required that the protocol have the ability to detect and handle name
+ conflicts, but it is not required that this ability be used for every
+ record.
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 6]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+3.1 Governing Standards Body
+
+ Note that this use of the ".local." suffix falls under IETF/IANA
+ jurisdiction, not ICANN jurisdiction. DNS is an IETF network
+ protocol, governed by protocol rules defined by the IETF. These IETF
+ protocol rules dictate character set, maximum name length, packet
+ format, etc. ICANN determines additional rules that apply when the
+ IETF's DNS protocol is used on the public Internet. In contrast,
+ private uses of the DNS protocol on isolated private networks are not
+ governed by ICANN. Since this proposed change is a change to the core
+ DNS protocol rules, it affects everyone, not just those machines
+ using the ICANN-governed Internet. Hence this change falls into the
+ category of an IETF protocol rule, not an ICANN usage rule.
+
+ This allocation of responsibility is formally established in
+ "Memorandum of Understanding Concerning the Technical Work of the
+ Internet Assigned Numbers Authority" [RFC 2860]. Exception (a) of
+ clause 4.3 states that the IETF has the authority to instruct IANA
+ to reserve pseudo-TLDs as required for protocol design purposes.
+ For example, "Reserved Top Level DNS Names" [RFC 2606] defines
+ the following pseudo-TLDs:
+
+ .test
+ .example
+ .invalid
+ .localhost
+
+
+3.2 Private DNS Namespaces
+
+ Note also that the special treatment of names ending in ".local." has
+ been implemented in Macintosh computers since the days of Mac OS 9,
+ and continues today in Mac OS X. There are also implementations for
+ Linux and other platforms [dotlocal]. Operators setting up private
+ internal networks ("intranets") are advised that their lives may be
+ easier if they avoid using the suffix ".local." in names in their
+ private internal DNS server. Alternative possibilities include:
+
+ .intranet
+ .internal
+ .private
+ .corp
+ .home
+ .lan
+
+ Another alternative naming scheme, advocated by Professor D. J.
+ Bernstein, is to use a numerical suffix, such as ".6." [djbdl].
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 7]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+3.3 Maximum Multicast DNS Name Length
+
+ RFC 1034 says:
+
+ "the total number of octets that represent a domain name (i.e.,
+ the sum of all label octets and label lengths) is limited to 255."
+
+ This text implies that the final root label at the end of every name
+ is included in this count (a name can't be represented without it),
+ but the text does not explicitly state that. Implementations of
+ Multicast DNS MUST include the label length byte of the final root
+ label at the end of every name when enforcing the rule that no name
+ may be longer than 255 bytes. For example, the length of the name
+ "apple.com." is considered to be 11, which is the number of bytes it
+ takes to represent that name in a packet without using name
+ compression:
+
+ ------------------------------------------------------
+ | 0x05 | a | p | p | l | e | 0x03 | c | o | m | 0x00 |
+ ------------------------------------------------------
+
+
+4. Source Address Check
+
+ All Multicast DNS responses (including responses sent via unicast)
+ SHOULD be sent with IP TTL set to 255. This is recommended to provide
+ backwards-compatibility with older Multicast DNS clients that check
+ the IP TTL on reception to determine whether the packet originated
+ on the local link. These older clients discard all packets with TTLs
+ other than 255.
+
+ A host sending Multicast DNS queries to a link-local destination
+ address (including the 224.0.0.251 link-local multicast address)
+ MUST only accept responses to that query that originate from the
+ local link, and silently discard any other response packets. Without
+ this check, it could be possible for remote rogue hosts to send
+ spoof answer packets (perhaps unicast to the victim host) which the
+ receiving machine could misinterpret as having originated on the
+ local link.
+
+ The test for whether a response originated on the local link
+ is done in two ways:
+
+ * All responses sent to the link-local multicast address 224.0.0.251
+ are necessarily deemed to have originated on the local link,
+ regardless of source IP address. This is essential to allow devices
+ to work correctly and reliably in unusual configurations, such as
+ multiple logical IP subnets overlayed on a single link, or in cases
+ of severe misconfiguration, where devices are physically connected
+ to the same link, but are currently misconfigured with completely
+ unrelated IP addresses and subnet masks.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 8]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ * For responses sent to a unicast destination address, the source IP
+ address in the packet is checked to see if it is an address on a
+ local subnet. An address is determined to be on a local subnet if,
+ for (one of) the address(es) configured on the interface receiving
+ the packet, (I & M) == (P & M), where I and M are the interface
+ address and subnet mask respectively, P is the source IP address
+ from the packet, '&' represents the bitwise logical 'and'
+ operation, and '==' represents a bitwise equality test.
+
+ Since queriers will ignore responses apparently originating outside
+ the local subnet, responders SHOULD avoid generating responses that
+ it can reasonably predict will be ignored. This applies particularly
+ in the case of overlayed subnets. If a responder receives a query
+ addressed to the link-local multicast address 224.0.0.251, from a
+ source address not apparently on the same subnet as the responder,
+ then even if the query indicates that a unicast response is preferred
+ (see Section 6.5, "Questions Requesting Unicast Responses"), the
+ responder SHOULD elect to respond by multicast anyway, since it can
+ reasonably predict that a unicast response with an apparently
+ non-local source address will probably be ignored.
+
+
+5. Reverse Address Mapping
+
+ Like ".local.", the IPv4 and IPv6 reverse mapping domains are also
+ defined to be link-local.
+
+ Any DNS query for a name ending with "254.169.in-addr.arpa." MUST
+ be sent to the mDNS multicast address 224.0.0.251. Since names under
+ this domain correspond to IPv4 link-local addresses, it is logical
+ that the local link is the best place to find information pertaining
+ to those names.
+
+ Likewise, any DNS query for a name within the reverse mapping domains
+ for IPv6 Link-Local addresses ("8.e.f.ip6.arpa.", "9.e.f.ip6.arpa.",
+ "a.e.f.ip6.arpa.", and "b.e.f.ip6.arpa.") MUST be sent to the IPv6
+ mDNS link-local multicast address FF02::FB.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 9]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+6. Querying
+
+ There are three kinds of Multicast DNS Queries, one-shot queries of
+ the kind made by today's conventional DNS clients, one-shot queries
+ accumulating multiple responses made by multicast-aware DNS clients,
+ and continuous ongoing Multicast DNS Queries used by IP network
+ browser software.
+
+ A Multicast DNS Responder that is offering records that are intended
+ to be unique on the local link MUST also implement a Multicast DNS
+ Querier so that it can first verify the uniqueness of those records
+ before it begins answering queries for them.
+
+
+6.1 One-Shot Multicast DNS Queries
+
+ An unsophisticated DNS client may simply send its DNS queries blindly
+ to 224.0.0.251:5353, without necessarily even being aware what a
+ multicast address is. This change can typically be implemented with
+ just a few lines of code in an existing DNS resolver library. Any
+ time the name being queried for falls within one of the reserved
+ mDNS domains (see Section 12 "Special Characteristics of Multicast
+ DNS Domains") the query is sent to 224.0.0.251:5353 instead of the
+ configured unicast DNS server address that would otherwise be used.
+ Typically the timeout would also be shortened to two or three
+ seconds, but it's possible to make a minimal mDNS client with no
+ other changes apart from these.
+
+ A simple DNS client like this will typically just take the first
+ response it receives. It will not listen for additional UDP
+ responses, but in many instances this may not be a serious problem.
+ If a user types "http://cheshire.local." into their Web browser and
+ gets to see the page they were hoping for, then the protocol has met
+ the user's needs in this case.
+
+ While an unsophisticated DNS client like this is perfectly adequate
+ for simple hostname lookup, it may not get ideal behavior in
+ other cases. Additional refinements that may be adopted by more
+ sophisticated clients are described below.
+
+
+6.2 One-Shot Queries, Accumulating Multiple Responses
+
+ A more sophisticated DNS client should understand that Multicast DNS
+ is not exactly the same as unicast DNS, and should modify its
+ behavior in some simple ways.
+
+ As described above, there are some cases, such as looking up the
+ address associated with a unique host name, where a single response
+ is sufficient, and moreover may be all that is expected. However,
+ there are other DNS queries where more than one response is
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 10]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ possible, and for these queries a more sophisticated Multicast DNS
+ client should include the ability to wait for an appropriate period
+ of time to collect multiple responses.
+
+ A naive DNS client retransmits its query only so long as it has
+ received no response. A more sophisticated Multicast DNS client is
+ aware that having received one response is not necessarily an
+ indication that it might not receive others, and has the ability to
+ retransmit its query an appropriate number of times at appropriate
+ intervals until it is satisfied with the collection of responses it
+ has gathered.
+
+ A more sophisticated Multicast DNS client that is retransmitting
+ a query for which it has already received some responses, MUST
+ implement Known Answer Suppression, as described below in Section 7.1
+ "Known Answer Suppression". This indicates to responders who have
+ already replied that their responses have been received, and they
+ don't need to send them again in response to this repeated query. In
+ addition, when retransmitting queries, the interval between the first
+ two queries SHOULD be one second, and the intervals between
+ subsequent queries SHOULD double.
+
+
+6.3 Continuous Multicast DNS Querying
+
+ In One-Shot Queries, with either a single or multiple responses,
+ the underlying assumption is that the transaction begins when the
+ application issues a query, and ends when all the desired responses
+ have been received. There is another type of operation which is more
+ akin to continuous monitoring.
+
+ iTunes users are accustomed to seeing a list of shared network music
+ libraries in the sidebar of the iTunes window. There is no "refresh"
+ button for the user to click because the list is always accurate,
+ always reflecting the currently available libraries. When a new
+ library becomes available it promptly appears in the list, and when
+ a library becomes unavailable it promptly disappears. It is vitally
+ important that this responsive user interface be achieved without
+ naive polling that would place an unreasonable burden on the network.
+
+ Therefore, when retransmitting mDNS queries to implement this kind
+ of continuous monitoring, the interval between the first two queries
+ SHOULD be one second, the intervals between the subsequent queries
+ SHOULD double, and the querier MUST implement Known Answer
+ Suppression, as described below in Section 7.1. When the interval
+ between queries reaches or exceeds 60 minutes, a querier MAY cap the
+ interval to a maximum of 60 minutes, and perform subsequent queries
+ at a steady-state rate of one query per hour. To avoid accidental
+ synchronization when for some reason multiple clients begin querying
+ at exactly the same moment (e.g. because of some common external
+ trigger event), a Multicast DNS Querier SHOULD also delay the first
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 11]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ query of the series by a randomly-chosen amount in the range
+ 20-120ms.
+
+ When a Multicast DNS Querier receives an answer, the answer contains
+ a TTL value that indicates for how many seconds this answer is valid.
+ After this interval has passed, the answer will no longer be valid
+ and SHOULD be deleted from the cache. Before this time is reached,
+ a Multicast DNS Querier which has clients with an active interest in
+ the state of that record (e.g. a network browsing window displaying
+ a list of discovered services to the user) SHOULD re-issue its query
+ to determine whether the record is still valid.
+
+ To perform this cache maintenance, a Multicast DNS Querier should
+ plan to re-query for records after at least 50% of the record
+ lifetime has elapsed. This document recommends the following
+ specific strategy:
+
+ The Querier should plan to issue a query at 80% of the record
+ lifetime, and then if no answer is received, at 85%, 90% and 95%.
+ If an answer is received, then the remaining TTL is reset to the
+ value given in the answer, and this process repeats for as long as
+ the Multicast DNS Querier has an ongoing interest in the record.
+ If after four queries no answer is received, the record is deleted
+ when it reaches 100% of its lifetime. A Multicast DNS Querier MUST
+ NOT perform this cache maintenance for records for which it has no
+ clients with an active interest. If the expiry of a particular record
+ from the cache would result in no net effect to any client software
+ running on the Querier device, and no visible effect to the human
+ user, then there is no reason for the Multicast DNS Querier to
+ waste network bandwidth checking whether the record remains valid.
+
+ To avoid the case where multiple Multicast DNS Queriers on a network
+ all issue their queries simultaneously, a random variation of 2% of
+ the record TTL should be added, so that queries are scheduled to be
+ performed at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
+
+
+6.4 Multiple Questions per Query
+
+ Multicast DNS allows a querier to place multiple questions in the
+ Question Section of a single Multicast DNS query packet.
+
+ The semantics of a Multicast DNS query packet containing multiple
+ questions is identical to a series of individual DNS query packets
+ containing one question each. Combining multiple questions into a
+ single packet is purely an efficiency optimization, and has no other
+ semantic significance.
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 12]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+6.5 Questions Requesting Unicast Responses
+
+ Sending Multicast DNS responses via multicast has the benefit that
+ all the other hosts on the network get to see those responses, and
+ can keep their caches up to date, and detect conflicting responses.
+
+ However, there are situations where all the other hosts on the
+ network don't need to see every response. Some examples are a laptop
+ computer waking from sleep, or the Ethernet cable being connected to
+ a running machine, or a previously inactive interface being activated
+ through a configuration change. At the instant of wake-up or link
+ activation, the machine is a brand new participant on a new network.
+ Its Multicast DNS cache for that interface is empty, and it has no
+ knowledge of its peers on that link. It may have a significant number
+ of questions that it wants answered right away to discover
+ information about its new surroundings and present that information
+ to the user. As a new participant on the network, it has no idea
+ whether the exact same questions may have been asked and answered
+ just seconds ago. In this case, triggering a large sudden flood of
+ multicast responses may impose an unreasonable burden on the network.
+
+ To avoid large floods of potentially unnecessary responses in these
+ cases, Multicast DNS defines the top bit in the class field of a DNS
+ question as the "unicast response" bit. When this bit is set in a
+ question, it indicates that the Querier is willing to accept unicast
+ responses instead of the usual multicast responses. These questions
+ requesting unicast responses are referred to as "QU" questions, to
+ distinguish them from the more usual questions requesting multicast
+ responses ("QM" questions). A Multicast DNS Querier sending its
+ initial batch of questions immediately on wake from sleep or
+ interface activation SHOULD set the "QU" bit in those questions.
+
+ When a question is retransmitted (as described in Section 6.3
+ "Continuous Multicast DNS Querying") the "QU" bit SHOULD NOT be set
+ in subsequent retransmissions of that question. Subsequent
+ retransmissions SHOULD be usual "QM" questions. After the first
+ question has received its responses, the querier should have a large
+ known-answer list (see "Known Answer Suppression" below) so that
+ subsequent queries should elicit few, if any, further responses.
+ Reverting to multicast responses as soon as possible is important
+ because of the benefits that multicast responses provide (see
+ "Benefits of Multicast Responses" below). In addition, the "QU" bit
+ SHOULD be set only for questions that are active and ready to be sent
+ the moment of wake from sleep or interface activation. New questions
+ issued by clients afterwards should be treated as normal "QM"
+ questions and SHOULD NOT have the "QU" bit set on the first question
+ of the series.
+
+ When receiving a question with the "unicast response" bit set, a
+ responder SHOULD usually respond with a unicast packet directed back
+ to the querier. If the responder has not multicast that record
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 13]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ recently (within one quarter of its TTL), then the responder SHOULD
+ instead multicast the response so as to keep all the peer caches up
+ to date, and to permit passive conflict detection. In the case of
+ answering a probe question with the "unicast response" bit set, the
+ responder should always generate the requested unicast response, but
+ may also send a multicast announcement too if the time since the last
+ multicast announcement of that record is more than a quarter of its
+ TTL.
+
+ Except when defending a unique name against a probe from another host
+ unicast replies are subject to all the same packet generation rules
+ as multicast replies, including the cache flush bit (see Section
+ 11.3, "Announcements to Flush Outdated Cache Entries") and randomized
+ delays to reduce network collisions (see Section 8, "Responding").
+
+
+6.6 Delaying Initial Query
+
+ If a query is issued for which there already exist one or more
+ records in the local cache, and those record(s) were received with
+ the cache flush bit set (see Section 11.3, "Announcements to Flush
+ Outdated Cache Entries"), indicating that they form a unique RRSet,
+ then the host SHOULD delay its initial query by imposing a random
+ delay from 500-1000ms. This is to avoid the situation where a group
+ of hosts are synchronized by some external event and all perform
+ the same query simultaneously. This means that when the first host
+ (selected randomly by this algorithm) transmits its query, all the
+ other hosts that were about to transmit the same query can suppress
+ their superfluous queries, as described in "Duplicate Question
+ Suppression" below.
+
+
+6.7 Direct Unicast Queries to port 5353
+
+ In specialized applications there may be rare situations where it
+ makes sense for a Multicast DNS Querier to send its query via unicast
+ to a specific machine. When a Multicast DNS Responder receives a
+ query via direct unicast, it SHOULD respond as it would for a
+ "QU" query, as described above in Section 6.5 "Questions Requesting
+ Unicast Responses". Since it is possible for a unicast query to be
+ received from a machine outside the local link, Responders SHOULD
+ check that the source address in the query packet matches the local
+ subnet for that link, and silently ignore the packet if not.
+
+ There may be specialized situations, outside the scope of this
+ document, where it is intended and desirable to create a Responder
+ that does answer queries originating outside the local link. Such
+ a Responder would need to ensure that these non-local queries are
+ always answered via unicast back to the Querier, since an answer sent
+ via link-local multicast would not reach a Querier outside the local
+ link.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 14]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+7. Duplicate Suppression
+
+ A variety of techniques are used to reduce the amount of redundant
+ traffic on the network.
+
+7.1 Known Answer Suppression
+
+ When a Multicast DNS Querier sends a query to which it already knows
+ some answers, it populates the Answer Section of the DNS message with
+ those answers.
+
+ A Multicast DNS Responder SHOULD NOT answer a Multicast DNS Query if
+ the answer it would give is already included in the Answer Section
+ with an RR TTL at least half the correct value. If the RR TTL of the
+ answer as given in the Answer Section is less than half of the true
+ RR TTL as known by the Multicast DNS Responder, the responder MUST
+ send an answer so as to update the Querier's cache before the record
+ becomes in danger of expiration.
+
+ Because a Multicast DNS Responder will respond if the remaining TTL
+ given in the known answer list is less than half the true TTL, it is
+ superfluous for the Querier to include such records in the known
+ answer list. Therefore a Multicast DNS Querier SHOULD NOT include
+ records in the known answer list whose remaining TTL is less than
+ half their original TTL. Doing so would simply consume space in the
+ packet without achieving the goal of suppressing responses, and would
+ therefore be a pointless waste of network bandwidth.
+
+ A Multicast DNS Querier MUST NOT cache resource records observed in
+ the Known Answer Section of other Multicast DNS Queries. The Answer
+ Section of Multicast DNS Queries is not authoritative. By placing
+ information in the Answer Section of a Multicast DNS Query the
+ querier is stating that it *believes* the information to be true.
+ It is not asserting that the information *is* true. Some of those
+ records may have come from other hosts that are no longer on the
+ network. Propagating that stale information to other Multicast DNS
+ Queriers on the network would not be helpful.
+
+
+7.2 Multi-Packet Known Answer Suppression
+
+ Sometimes a Multicast DNS Querier will already have too many answers
+ to fit in the Known Answer Section of its query packets. In this
+ case, it should issue a Multicast DNS Query containing a question and
+ as many Known Answer records as will fit. It MUST then set the TC
+ (Truncated) bit in the header before sending the Query. It MUST then
+ immediately follow the packet with another query packet containing no
+ questions, and as many more Known Answer records as will fit. If
+ there are still too many records remaining to fit in the packet, it
+ again sets the TC bit and continues until all the Known Answer
+ records have been sent.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 15]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ A Multicast DNS Responder seeing a Multicast DNS Query with the TC
+ bit set defers its response for a time period randomly selected in
+ the interval 400-500ms. This gives the Multicast DNS Querier time to
+ send additional Known Answer packets before the Responder responds.
+ If the Responder sees any of its answers listed in the Known Answer
+ lists of subsequent packets from the querying host, it SHOULD delete
+ that answer from the list of answers it is planning to give, provided
+ that no other host on the network is also waiting to receive the same
+ answer record.
+
+ If the Responder receives additional Known Answer packets with the TC
+ bit set, it SHOULD extend the delay as necessary to ensure a pause of
+ 400-500ms after the last such packet before it sends its answer. This
+ opens the potential risk that a continuous stream of Known Answer
+ packets could, theoretically, prevent a Responder from answering
+ indefinitely. In practice answers are never actually delayed
+ significantly, and should a situation arise where significant delays
+ did happen, that would be a scenario where the network is so
+ overloaded that it would be desirable to err on the side of caution.
+ The consequence of delaying an answer may be that it takes a user
+ longer than usual to discover all the services on the local network;
+ in contrast the consequence of incorrectly answering before all the
+ Known Answer packets have been received would be wasting bandwidth
+ sending unnecessary answers on an already overloaded network. In this
+ (rare) situation, sacrificing speed to preserve reliable network
+ operation is the right trade-off.
+
+
+7.3 Duplicate Question Suppression
+
+ If a host is planning to send a query, and it sees another host on
+ the network send a query containing the same question, and the Known
+ Answer Section of that query does not contain any records which this
+ host would not also put in its own Known Answer Section, then this
+ host should treat its own query as having been sent. When multiple
+ clients on the network are querying for the same resource records,
+ there is no need for them to all be repeatedly asking the same
+ question.
+
+
+7.4 Duplicate Answer Suppression
+
+ If a host is planning to send an answer, and it sees another host on
+ the network send a response packet containing the same answer record,
+ and the TTL in that record is not less than the TTL this host would
+ have given, then this host should treat its own answer as having been
+ sent. When multiple responders on the network have the same data,
+ there is no need for all of them to respond.
+
+ This feature is particularly useful when multiple Sleep Proxy Servers
+ are deployed (see Section 17, "Multicast DNS and Power Management").
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 16]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ In the future it is possible that every general-purpose OS (Mac,
+ Windows, Linux, etc.) will implement Sleep Proxy Service as a matter
+ of course. In this case there could be a large number of Sleep Proxy
+ Servers on any given network, which is good for reliability and
+ fault-tolerance, but would be bad for the network if every Sleep
+ Proxy Server were to answer every query.
+
+8. Responding
+
+ When a Multicast DNS Responder constructs and sends a Multicast DNS
+ response packet, the Answer Section of that packet must contain only
+ records for which that Responder is explicitly authoritative. These
+ answers may be generated because the record answers a question
+ received in a Multicast DNS query packet, or at certain other times
+ that the responder determines than an unsolicited announcement is
+ warranted. A Multicast DNS Responder MUST NOT place records from its
+ cache, which have been learned from other responders on the network,
+ in the Answer Section of outgoing response packets. Only an
+ authoritative source for a given record is allowed to issue responses
+ containing that record.
+
+ The determination of whether a given record answers a given question
+ is done using the standard DNS rules: The record name must match the
+ question name, the record rrtype must match the question qtype
+ (unless the qtype is "ANY"), and the record rrclass must match the
+ question qclass (unless the qclass is "ANY").
+
+ A Multicast DNS Responder MUST only respond when it has a positive
+ non-null response to send. Error responses must never be sent. The
+ non-existence of any name in a Multicast DNS Domain is ascertained by
+ the failure of any machine to respond to the Multicast DNS query, not
+ by NXDOMAIN errors.
+
+ Multicast DNS Responses MUST NOT contain any questions in the
+ Question Section. Any questions in the Question Section of a received
+ Multicast DNS Response MUST be silently ignored. Multicast DNS
+ Queriers receiving Multicast DNS Responses do not care what question
+ elicited the response; they care only that the information in the
+ response is true and accurate.
+
+ A Multicast DNS Responder on Ethernet [IEEE802] and similar shared
+ multiple access networks SHOULD have the capability of delaying its
+ responses by up to 500ms, as determined by the rules described below.
+ If a large number of Multicast DNS Responders were all to respond
+ immediately to a particular query, a collision would be virtually
+ guaranteed. By imposing a small random delay, the number of
+ collisions is dramatically reduced. On a full-sized Ethernet using
+ the maximum cable lengths allowed and the maximum number of repeaters
+ allowed, an Ethernet frame is vulnerable to collisions during the
+ transmission of its first 256 bits. On 10Mb/s Ethernet, this equates
+ to a vulnerable time window of 25.6us. On higher-speed variants of
+ Ethernet, the vulnerable time window is shorter.
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 17]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ In the case where a Multicast DNS Responder has good reason to
+ believe that it will be the only responder on the link with a
+ positive non-null response (i.e. because it is able to answer every
+ question in the query packet, and for all of those answer records it
+ has previously verified that the name, rrtype and rrclass are unique
+ on the link) it SHOULD NOT impose any random delay before responding,
+ and SHOULD normally generate its response within at most 10ms.
+ In particular, this applies to responding to probe queries with the
+ "unicast response" bit set. Since receiving a probe query gives a
+ clear indication that some other Responder is planning to start using
+ this name in the very near future, answering such probe queries
+ to defend a unique record is a high priority and needs to be done
+ immediately, without delay. A probe query can be distinguished from
+ a normal query by the fact that a probe query contains a proposed
+ record in the Authority Section which answers the question in the
+ Question Section (for more details, see Section 9.1, "Probing").
+
+ Responding immediately without delay is appropriate for records like
+ the address record for a particular host name, when the host name has
+ been previously verified unique. Responding immediately without delay
+ is *not* appropriate for things like looking up PTR records used for
+ DNS Service Discovery [DNS-SD], where a large number of responses may
+ be anticipated.
+
+ In any case where there may be multiple responses, such as queries
+ where the answer is a member of a shared resource record set, each
+ responder SHOULD delay its response by a random amount of time
+ selected with uniform random distribution in the range 20-120ms.
+ The reason for requiring that the delay be at least 20ms is to
+ accommodate the situation where two or more query packets are sent
+ back-to-back, because in that case we want a Responder with answers
+ to more than one of those queries to have the opportunity to
+ aggregate all of its answers into a single response packet.
+
+ In the case where the query has the TC (truncated) bit set,
+ indicating that subsequent known answer packets will follow,
+ responders SHOULD delay their responses by a random amount of time
+ selected with uniform random distribution in the range 400-500ms,
+ to allow enough time for all the known answer packets to arrive,
+ as described in Section 7.2 "Multi-Packet Known Answer Suppression".
+
+ Except when a unicast response has been explicitly requested via the
+ "unicast response" bit, Multicast DNS Responses MUST be sent to UDP
+ port 5353 (the well-known port assigned to mDNS) on the 224.0.0.251
+ multicast address (or its IPv6 equivalent FF02::FB). Operating in a
+ Zeroconf environment requires constant vigilance. Just because a name
+ has been previously verified unique does not mean it will continue
+ to be so indefinitely. By allowing all Multicast DNS Responders to
+ constantly monitor their peers' responses, conflicts arising out
+ of network topology changes can be promptly detected and resolved.
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 18]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ Sending all responses by multicast also facilitates opportunistic
+ caching by other hosts on the network.
+
+ To protect the network against excessive packet flooding due to
+ software bugs or malicious attack, a Multicast DNS Responder MUST NOT
+ (except in the one special case of answering probe queries) multicast
+ a record on a given interface until at least one second has elapsed
+ since the last time that record was multicast on that particular
+ interface. A legitimate client on the network should have seen the
+ previous transmission and cached it. A client that did not receive
+ and cache the previous transmission will retry its request and
+ receive a subsequent response. In the special case of answering probe
+ queries, because of the limited time before the probing host will
+ make its decision about whether or not to use the name, a Multicast
+ DNS Responder MUST respond quickly. In this special case only, when
+ responding via multicast to a probe, a Multicast DNS Responder is
+ only required to delay its transmission as necessary to ensure an
+ interval of at least 250ms since the last time the record was
+ multicast on that interface.
+
+
+8.2 Multi-Question Queries
+
+ Multicast DNS Responders MUST correctly handle DNS query packets
+ containing more than one question, by answering any or all of the
+ questions to which they have answers. Any (non-defensive) answers
+ generated in response to query packets containing more than one
+ question SHOULD be randomly delayed in the range 20-120ms, or
+ 400-500ms if the TC (truncated) bit is set, as described above.
+ (Answers defending a name, in response to a probe for that name,
+ are not subject to this delay rule and are still sent immediately.)
+
+
+8.2 Response Aggregation
+
+ When possible, a responder SHOULD, for the sake of network
+ efficiency, aggregate as many responses as possible into a single
+ Multicast DNS response packet. For example, when a responder has
+ several responses it plans to send, each delayed by a different
+ interval, then earlier responses SHOULD be delayed by up to an
+ additional 500ms if that will permit them to be aggregated with
+ other responses scheduled to go out a little later.
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 19]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+8.3 Legacy Unicast Responses
+
+ If the source UDP port in a received Multicast DNS Query is not port
+ 5353, this indicates that the client originating the query is a
+ simple client that does not fully implement all of Multicast DNS.
+ In this case, the Multicast DNS Responder MUST send a UDP response
+ directly back to the client, via unicast, to the query packet's
+ source IP address and port. This unicast response MUST be a
+ conventional unicast response as would be generated by a conventional
+ unicast DNS server; for example, it MUST repeat the query ID and the
+ question given in the query packet.
+
+ The resource record TTL given in a legacy unicast response SHOULD NOT
+ be greater than ten seconds, even if the true TTL of the Multicast
+ DNS resource record is higher. This is because Multicast DNS
+ Responders that fully participate in the protocol use the cache
+ coherency mechanisms described in Section 11 "Resource Record TTL
+ Values and Cache Coherency" to update and invalidate stale data. Were
+ unicast responses sent to legacy clients to use the same high TTLs,
+ these legacy clients, which do not implement these cache coherency
+ mechanisms, could retain stale cached resource record data long after
+ it is no longer valid.
+
+ Having sent this unicast response, if the Responder has not sent this
+ record in any multicast response recently, it SHOULD schedule the
+ record to be sent via multicast as well, to facilitate passive
+ conflict detection. "Recently" in this context means "if the time
+ since the record was last sent via multicast is less than one quarter
+ of the record's TTL".
+
+ Note that while legacy queries usually contain exactly one question,
+ they are permitted to contain multiple questions, and responders
+ listening for multicast queries on 224.0.0.251:5353 MUST be prepared
+ to handle this correctly, responding by generating a unicast response
+ containing the list of question(s) they are answering in the Question
+ Section, and the records answering those question(s) in the Answer
+ Section.
+
+
+9. Probing and Announcing on Startup
+
+ Typically a Multicast DNS Responder should have, at the very least,
+ address records for all of its active interfaces. Creating and
+ advertising an HINFO record on each interface as well can be useful
+ to network administrators.
+
+ Whenever a Multicast DNS Responder starts up, wakes up from sleep,
+ receives an indication of an Ethernet "Link Change" event, or has any
+ other reason to believe that its network connectivity may have
+ changed in some relevant way, it MUST perform the two startup steps
+ below.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 20]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+9.1 Probing
+
+ The first startup step is that for all those resource records that a
+ Multicast DNS Responder desires to be unique on the local link, it
+ MUST send a Multicast DNS Query asking for those resource records, to
+ see if any of them are already in use. The primary example of this is
+ its address record which maps its unique host name to its unique IP
+ address. All Probe Queries SHOULD be done using the desired resource
+ record name and query type T_ANY (255), to elicit answers for all
+ types of records with that name. This allows a single question to be
+ used in place of several questions, which is more efficient on the
+ network. It also allows a host to verify exclusive ownership of a
+ name, which is desirable in most cases. It would be confusing, for
+ example, if one host owned the "A" record for "myhost.local.", but
+ a different host owned the HINFO record for that name.
+
+ The ability to place more than one question in a Multicast DNS Query
+ is useful here, because it can allow a host to use a single packet
+ for all of its resource records instead of needing a separate packet
+ for each. For example, a host can simultaneously probe for uniqueness
+ of its "A" record and all its SRV records [DNS-SD] in the same query
+ packet.
+
+ When ready to send its mDNS probe packet(s) the host should first
+ wait for a short random delay time, uniformly distributed in the
+ range 0-250ms. This random delay is to guard against the case where a
+ group of devices are powered on simultaneously, or a group of devices
+ are connected to an Ethernet hub which is then powered on, or some
+ other external event happens that might cause a group of hosts to all
+ send synchronized probes.
+
+ 250ms after the first query the host should send a second, then
+ 250ms after that a third. If, by 250ms after the third probe, no
+ conflicting Multicast DNS responses have been received, the host may
+ move to the next step, announcing. (Note that this is the one
+ exception from the normal rule that there should be at least one
+ second between repetitions of the same question, and the interval
+ between subsequent repetitions should double.)
+
+ When sending probe queries, a host MUST NOT consult its cache for
+ potential answers. Only conflicting Multicast DNS responses received
+ "live" from the network are considered valid for the purposes of
+ determining whether probing has succeeded or failed.
+
+ In order to allow services to announce their presence without
+ unreasonable delay, the time window for probing is intentionally set
+ quite short. As a result of this, from the time the first probe
+ packet is sent, another device on the network using that name has
+ just 750ms to respond to defend its name. On networks that are slow,
+ or busy, or both, it is possible for round-trip latency to account
+ for a few hundred milliseconds, and software delays in slow devices
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 21]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ can add additional delay. For this reason, it is important that when
+ a device receives a probe query for a name that it is currently using
+ for unique records, it SHOULD generate its response to defend that
+ name immediately and send it as quickly as possible. The usual rules
+ about random delays before responding, to avoid sudden bursts of
+ simultaneous answers from different hosts, do not apply here since
+ at most one host should ever respond to a given probe question. Even
+ when a single DNS query packet contains multiple probe questions,
+ it would be unusual for that packet to elicit a defensive response
+ from more than one other host. Because of the mDNS multicast rate
+ limiting rules, the first two probes SHOULD be sent as "QU" questions
+ with the "unicast response" bit set, to allow a defending host to
+ respond immediately via unicast, instead of potentially having to
+ wait before replying via multicast. At the present time, this
+ document recommends that the third probe SHOULD be sent as a standard
+ "QM" question, for backwards compatibility with the small number of
+ old devices still in use that don't implement unicast responses.
+
+ If, at any time during probing, from the beginning of the initial
+ random 0-250ms delay onward, any conflicting Multicast DNS responses
+ are received, then the probing host MUST defer to the existing host,
+ and MUST choose new names for some or all of its resource records
+ as appropriate, to avoid conflict with pre-existing hosts on the
+ network. In the case of a host probing using query type T_ANY as
+ recommended above, any answer containing a record with that name,
+ of any type, MUST be considered a conflicting response and handled
+ accordingly.
+
+ If fifteen failures occur within any ten-second period, then the host
+ MUST wait at least five seconds before each successive additional
+ probe attempt. This is to help ensure that in the event of software
+ bugs or other unanticipated problems, errant hosts do not flood the
+ network with a continuous stream of multicast traffic. For very
+ simple devices, a valid way to comply with this requirement is
+ to always wait five seconds after any failed probe attempt before
+ trying again.
+
+ If a responder knows by other means, with absolute certainty, that
+ its unique resource record set name, rrtype and rrclass cannot
+ already be in use by any other responder on the network, then it
+ MAY skip the probing step for that resource record set. For example,
+ when creating the reverse address mapping PTR records, the host can
+ reasonably assume that no other host will be trying to create those
+ same PTR records, since that would imply that the two hosts were
+ trying to use the same IP address, and if that were the case, the
+ two hosts would be suffering communication problems beyond the scope
+ of what Multicast DNS is designed to solve.
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 22]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+9.2 Simultaneous Probe Tie-Breaking
+
+ The astute reader will observe that there is a race condition
+ inherent in the previous description. If two hosts are probing for
+ the same name simultaneously, neither will receive any response to
+ the probe, and the hosts could incorrectly conclude that they may
+ both proceed to use the name. To break this symmetry, each host
+ populates the Query packets's Authority Section with the record or
+ records with the rdata that it would be proposing to use, should its
+ probing be successful. The Authority Section is being used here in a
+ way analogous to the way it is used as the "Update Section" in a DNS
+ Update packet [RFC 2136].
+
+ When a host is probing for a group of related records with the same
+ name (e.g. the SRV and TXT record describing a DNS-SD service), only
+ a single question need be placed in the Question Section, since query
+ type T_ANY (255) is used, which will elicit answers for all records
+ with that name. However, for tie-breaking to work correctly in all
+ cases, the Authority Section must contain *all* the records and
+ proposed rdata being probed for uniqueness.
+
+ When a host that is probing for a record sees another host issue a
+ query for the same record, it consults the Authority Section of that
+ query. If it finds any resource record(s) there which answers the
+ query, then it compares the data of that (those) resource record(s)
+ with its own tentative data. We consider first the simple case of a
+ host probing for a single record, receiving a simultaneous probe from
+ another host also probing for a single record. The two records are
+ compared and the lexicographically later data wins. This means that
+ if the host finds that its own data is lexicographically later, it
+ simply ignores the other host's probe. If the host finds that its own
+ data is lexicographically earlier, then it treats this exactly as if
+ it had received a positive answer to its query, and concludes that it
+ may not use the desired name.
+
+ The determination of "lexicographically later" is performed by first
+ comparing the record class, then the record type, then raw comparison
+ of the binary content of the rdata without regard for meaning or
+ structure. If the record classes differ, then the numerically greater
+ class is considered "lexicographically later". Otherwise, if the
+ record types differ, then the numerically greater type is considered
+ "lexicographically later". If the rrtype and rrclass both match then
+ the rdata is compared.
+
+ In the case of resource records containing rdata that is subject to
+ name compression, the names MUST be uncompressed before comparison.
+ (The details of how a particular name is compressed is an artifact of
+ how and where the record is written into the DNS message; it is not
+ an intrinsic property of the resource record itself.)
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 23]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ The bytes of the raw uncompressed rdata are compared in turn,
+ interpreting the bytes as eight-bit UNSIGNED values, until a byte
+ is found whose value is greater than that of its counterpart (in
+ which case the rdata whose byte has the greater value is deemed
+ lexicographically later) or one of the resource records runs out
+ of rdata (in which case the resource record which still has
+ remaining data first is deemed lexicographically later).
+
+ The following is an example of a conflict:
+
+ cheshire.local. A 169.254.99.200
+ cheshire.local. A 169.254.200.50
+
+ In this case 169.254.200.50 is lexicographically later (the third
+ byte, with value 200, is greater than its counterpart with value 99),
+ so it is deemed the winner.
+
+ Note that it is vital that the bytes are interpreted as UNSIGNED
+ values in the range 0-255, or the wrong outcome may result. In
+ the example above, if the byte with value 200 had been incorrectly
+ interpreted as a signed eight-bit value then it would be interpreted
+ as value -56, and the wrong address record would be deemed the
+ winner.
+
+
+9.2.1 Simultaneous Probe Tie-Breaking for Multiple Records
+
+ When a host is probing for a set of records with the same name, or a
+ packet is received containing multiple tie-breaker records answering
+ a given probe question in the Question Section, the host's records
+ and the tie-breaker records from the packet are each sorted into
+ order, and then compared pairwise, using the same comparison
+ technique described above, until a difference is found.
+
+ The records are sorted using the same lexicographical order as
+ described above, that is: if the record classes differ, the record
+ with the lower class number comes first. If the classes are the same
+ but the rrtypes differ, the record with the lower rrtype number comes
+ first. If the class and rrtype match, then the rdata is compared
+ bytewise until a difference is found. For example, in the common case
+ of advertising DNS-SD services with a TXT record and an SRV record,
+ the TXT record comes first (the rrtype for TXT is 16) and the SRV
+ record comes second (the rrtype for SRV is 33).
+
+ When comparing the records, if the first records match perfectly,
+ then the second records are compared, and so on. If either list of
+ records runs out of records before any difference is found, then the
+ list with records remaining is deemed to have won the tie-break. If
+ both lists run out of records at the same time without any difference
+ being found, then this indicates that two devices are advertising
+ identical sets of records, as is sometimes done for fault tolerance,
+ and there is in fact no conflict.
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 24]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+9.3 Announcing
+
+ The second startup step is that the Multicast DNS Responder MUST send
+ a gratuitous Multicast DNS Response containing, in the Answer
+ Section, all of its resource records (both shared records, and unique
+ records that have completed the probing step). If there are too many
+ resource records to fit in a single packet, multiple packets should
+ be used.
+
+ In the case of shared records (e.g. the PTR records used by DNS
+ Service Discovery [DNS-SD]), the records are simply placed as-is
+ into the Answer Section of the DNS Response.
+
+ In the case of records that have been verified to be unique in the
+ previous step, they are placed into the Answer Section of the DNS
+ Response with the most significant bit of the rrclass set to one.
+ The most significant bit of the rrclass for a record in the Answer
+ Section of a response packet is the mDNS "cache flush" bit and is
+ discussed in more detail below in Section 11.3 "Announcements to
+ Flush Outdated Cache Entries".
+
+ The Multicast DNS Responder MUST send at least two gratuitous
+ responses, one second apart. A Responder MAY send up to eight
+ gratuitous Responses, provided that the interval between gratuitous
+ responses doubles with every response sent.
+
+ A Multicast DNS Responder MUST NOT send announcements in the absence
+ of information that its network connectivity may have changed in
+ some relevant way. In particular, a Multicast DNS Responder MUST NOT
+ send regular periodic announcements as a matter of course. It is not
+ uncommon for protocol designers to encounter some problem which they
+ decide to solve using regular periodic announcements, but this is
+ generally not a wise protocol design choice. In the small scale
+ periodic announcements may seem to remedy the short-term problem,
+ but they do not scale well if the protocol becomes successful.
+ If every host on the network implements the protocol -- if multiple
+ applications on every host on the network are implementing the
+ protocol -- then even a low periodic rate of just one announcement
+ per minute per application per host can add up to multiple packets
+ per second in total. While gigabit Ethernet may be able to carry
+ a million packets per second, other network technologies cannot.
+ For example, while IEEE 802.11g wireless has a nominal data rate of
+ up to 54Mb/sec, multicasting just 100 packets per second can consume
+ the entire available bandwidth, leaving nothing for anything else.
+
+ With the increasing popularity of hand-held devices, unnecessary
+ continuous packet transmission can have bad implications for battery
+ life. It's worth pointing out the precedent that TCP was also
+ designed with this "no regular periodic idle packets" philosophy.
+ Standard TCP sends packets only when it has data to send or
+ acknowledge. If neither client nor server sends any bytes, then the
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 25]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ TCP code will send no packets, and a TCP connection can remain active
+ in this state indefinitely, with no packets being exchanged for
+ hours, days, weeks or months.
+
+ Whenever a Multicast DNS Responder receives any Multicast DNS
+ response (gratuitous or otherwise) containing a conflicting resource
+ record, the conflict MUST be resolved as described below in "Conflict
+ Resolution".
+
+
+9.4 Updating
+
+ At any time, if the rdata of any of a host's Multicast DNS records
+ changes, the host MUST repeat the Announcing step described above to
+ update neighboring caches. For example, if any of a host's IP
+ addresses change, it MUST re-announce those address records.
+
+ In the case of shared records, a host MUST send a "goodbye"
+ announcement with TTL zero (see Section 11.2 "Goodbye Packets")
+ for the old rdata, to cause it to be deleted from peer caches,
+ before announcing the new rdata. In the case of unique records,
+ a host SHOULD omit the "goodbye" announcement, since the cache
+ flush bit on the newly announced records will cause old rdata
+ to be flushed from peer caches anyway.
+
+ A host may update the contents of any of its records at any time,
+ though a host SHOULD NOT update records more frequently than ten
+ times per minute. Frequent rapid updates impose a burden on the
+ network. If a host has information to disseminate which changes more
+ frequently than ten times per minute, then it may be more appropriate
+ to design a protocol for that specific purpose.
+
+
+10. Conflict Resolution
+
+ A conflict occurs when a Multicast DNS Responder has a unique record
+ for which it is authoritative, and it receives a Multicast DNS
+ response packet containing a record with the same name, rrtype and
+ rrclass, but inconsistent rdata. What may be considered inconsistent
+ is context sensitive, except that resource records with identical
+ rdata are never considered inconsistent, even if they originate from
+ different hosts. This is to permit use of proxies and other
+ fault-tolerance mechanisms that may cause more than one responder
+ to be capable of issuing identical answers on the network.
+
+ A common example of a resource record type that is intended to be
+ unique, not shared between hosts, is the address record that maps a
+ host's name to its IP address. Should a host witness another host
+ announce an address record with the same name but a different IP
+ address, then that is considered inconsistent, and that address
+ record is considered to be in conflict.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 26]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ Whenever a Multicast DNS Responder receives any Multicast DNS
+ response (gratuitous or otherwise) containing a conflicting resource
+ record in the Answer Section, the Multicast DNS Responder MUST
+ immediately reset its conflicted unique record to probing state, and
+ go through the startup steps described above in Section 9. "Probing
+ and Announcing on Startup". The protocol used in the Probing phase
+ will determine a winner and a loser, and the loser MUST cease using
+ the name, and reconfigure.
+
+ It is very important that any host receiving a resource record that
+ conflicts with one of its own MUST take action as described above.
+ In the case of two hosts using the same host name, where one has been
+ configured to require a unique host name and the other has not, the
+ one that has not been configured to require a unique host name will
+ not perceive any conflict, and will not take any action. By reverting
+ to Probing state, the host that desires a unique host name will go
+ through the necessary steps to ensure that a unique host is obtained.
+
+ The recommended course of action after probing and failing is as
+ follows:
+
+ o Programmatically change the resource record name in an attempt to
+ find a new name that is unique. This could be done by adding some
+ further identifying information (e.g. the model name of the
+ hardware) if it is not already present in the name, appending the
+ digit "2" to the name, or incrementing a number at the end of the
+ name if one is already present.
+
+ o Probe again, and repeat until a unique name is found.
+
+ o Record this newly chosen name in persistent storage so that the
+ device will use the same name the next time it is power-cycled.
+
+ o Display a message to the user or operator informing them of the
+ name change. For example:
+
+ The name "Bob's Music" is in use by another iTunes music
+ server on the network. Your music has been renamed to
+ "Bob's Music (G4 Cube)". If you want to change this name,
+ use [describe appropriate menu item or preference dialog].
+
+ o If after one minute of probing the Multicast DNS Responder has been
+ unable to find any unused name, it should display a message to the
+ user or operator informing them of this fact. This situation should
+ never occur in normal operation. The only situations that would
+ cause this to happen would be either a deliberate denial-of-service
+ attack, or some kind of very obscure hardware or software bug that
+ acts like a deliberate denial-of-service attack.
+
+ How the user or operator is informed depends on context. A desktop
+ computer with a screen might put up a dialog box. A headless server
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 27]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ in the closet may write a message to a log file, or use whatever
+ mechanism (email, SNMP trap, etc.) it uses to inform the
+ administrator of other error conditions. On the other hand a headless
+ server in the closet may not inform the user at all -- if the user
+ cares, they will notice the name has changed, and connect to the
+ server in the usual way (e.g. via Web Browser) to configure a new
+ name.
+
+ The examples in this section focus on address records (i.e. host
+ names), but the same considerations apply to all resource records
+ where uniqueness (or maintenance of some other defined constraint)
+ is desired.
+
+
+11. Resource Record TTL Values and Cache Coherency
+
+ As a general rule, the recommended TTL value for Multicast DNS
+ resource records with a host name as the resource record's name
+ (e.g. A, AAAA, HINFO, etc.) or contained within the resource record's
+ rdata (e.g. SRV, reverse mapping PTR record, etc.) is 120 seconds.
+
+ The recommended TTL value for other Multicast DNS resource records
+ is 75 minutes.
+
+ A client with an active outstanding query will issue a query packet
+ when one or more of the resource record(s) in its cache is (are) 80%
+ of the way to expiry. If the TTL on those records is 75 minutes,
+ this ongoing cache maintenance process yields a steady-state query
+ rate of one query every 60 minutes.
+
+ Any distributed cache needs a cache coherency protocol. If Multicast
+ DNS resource records follow the recommendation and have a TTL of 75
+ minutes, that means that stale data could persist in the system for
+ a little over an hour. Making the default TTL significantly lower
+ would reduce the lifetime of stale data, but would produce too much
+ extra traffic on the network. Various techniques are available to
+ minimize the impact of such stale data.
+
+
+11.1 Cooperating Multicast DNS Responders
+
+ If a Multicast DNS Responder ("A") observes some other Multicast DNS
+ Responder ("B") send a Multicast DNS Response packet containing a
+ resource record with the same name, rrtype and rrclass as one of A's
+ resource records, but different rdata, then:
+
+ o If A's resource record is intended to be a shared resource record,
+ then this is no conflict, and no action is required.
+
+ o If A's resource record is intended to be a member of a unique
+ resource record set owned solely by that responder, then this
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 28]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ is a conflict and MUST be handled as described in Section 10
+ "Conflict Resolution".
+
+ If a Multicast DNS Responder ("A") observes some other Multicast DNS
+ Responder ("B") send a Multicast DNS Response packet containing a
+ resource record with the same name, rrtype and rrclass as one of A's
+ resource records, and identical rdata, then:
+
+ o If the TTL of B's resource record given in the packet is at least
+ half the true TTL from A's point of view, then no action is
+ required.
+
+ o If the TTL of B's resource record given in the packet is less than
+ half the true TTL from A's point of view, then A MUST mark its
+ record to be announced via multicast. Clients receiving the record
+ from B would use the TTL given by B, and hence may delete the
+ record sooner than A expects. By sending its own multicast response
+ correcting the TTL, A ensures that the record will be retained for
+ the desired time.
+
+ These rules allow multiple Multicast DNS Responders to offer the same
+ data on the network (perhaps for fault tolerance reasons) without
+ conflicting with each other.
+
+
+11.2 Goodbye Packets
+
+ In the case where a host knows that certain resource record data is
+ about to become invalid (for example when the host is undergoing a
+ clean shutdown) the host SHOULD send a gratuitous announcement mDNS
+ response packet, giving the same resource record name, rrtype,
+ rrclass and rdata, but an RR TTL of zero. This has the effect of
+ updating the TTL stored in neighboring hosts' cache entries to zero,
+ causing that cache entry to be promptly deleted.
+
+ Clients receiving a Multicast DNS Response with a TTL of zero SHOULD
+ NOT immediately delete the record from the cache, but instead record
+ a TTL of 1 and then delete the record one second later. In the case
+ of multiple Multicast DNS Responders on the network described in
+ Section 11.1 above, if one of the Responders shuts down and
+ incorrectly sends goodbye packets for its records, it gives the other
+ cooperating Responders one second to send out their own response to
+ "rescue" the records before they expire and are deleted.
+
+
+11.3 Announcements to Flush Outdated Cache Entries
+
+ Whenever a host has a resource record with potentially new data (e.g.
+ after rebooting, waking from sleep, connecting to a new network link,
+ changing IP address, etc.), the host MUST send a series of gratuitous
+ announcements to update cache entries in its neighbor hosts. In
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 29]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ these gratuitous announcements, if the record is one that is intended
+ to be unique, the host sets the most significant bit of the rrclass
+ field of the resource record. This bit, the "cache flush" bit, tells
+ neighboring hosts that this is not a shared record type. Instead of
+ merging this new record additively into the cache in addition to any
+ previous records with the same name, rrtype and rrclass, all old
+ records with that name, type and class that were received more than
+ one second ago are declared invalid, and marked to expire from the
+ cache in one second.
+
+ The semantics of the cache flush bit are as follows: Normally when a
+ resource record appears in the Answer Section of the DNS Response, it
+ means, "This is an assertion that this information is true." When a
+ resource record appears in the Answer Section of the DNS Response
+ with the "cache flush" bit set, it means, "This is an assertion that
+ this information is the truth and the whole truth, and anything you
+ may have heard more than a second ago regarding records of this
+ name/rrtype/rrclass is no longer valid".
+
+ To accommodate the case where the set of records from one host
+ constituting a single unique RRSet is too large to fit in a single
+ packet, only cache records that are more than one second old are
+ flushed. This allows the announcing host to generate a quick burst of
+ packets back-to-back on the wire containing all the members
+ of the RRSet. When receiving records with the "cache flush" bit set,
+ all records older than one second are marked to be deleted one second
+ in the future. One second after the end of the little packet burst,
+ any records not represented within that packet burst will then be
+ expired from all peer caches.
+
+ Any time a host sends a response packet containing some members of a
+ unique RRSet, it SHOULD send the entire RRSet, preferably in a single
+ packet, or if the entire RRSet will not fit in a single packet, in a
+ quick burst of packets sent as close together as possible. The host
+ SHOULD set the cache flush bit on all members of the unique RRSet.
+ In the event that for some reason the host chooses not to send the
+ entire unique RRSet in a single packet or a rapid packet burst,
+ it MUST NOT set the cache flush bit on any of those records.
+
+ The reason for waiting one second before deleting stale records from
+ the cache is to accommodate bridged networks. For example, a host's
+ address record announcement on a wireless interface may be bridged
+ onto a wired Ethernet, and cause that same host's Ethernet address
+ records to be flushed from peer caches. The one-second delay gives
+ the host the chance to see its own announcement arrive on the wired
+ Ethernet, and immediately re-announce its Ethernet interface's
+ address records so that both sets remain valid and live in peer
+ caches.
+
+ These rules apply regardless of *why* the response packet is being
+ generated. They apply to startup announcements as described in
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 30]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ Section 9.3 "Announcing", and to responses generated as a result
+ of receiving query packets.
+
+ The "cache flush" bit is only set in records in the Answer Section of
+ Multicast DNS responses sent to UDP port 5353. The "cache flush" bit
+ MUST NOT be set in any resource records in a response packet sent in
+ legacy unicast responses to UDP ports other than 5353.
+
+ The "cache flush" bit MUST NOT be set in any resource records in the
+ known-answer list of any query packet.
+
+ The "cache flush" bit MUST NOT ever be set in any shared resource
+ record. To do so would cause all the other shared versions of this
+ resource record with different rdata from different Responders to be
+ immediately deleted from all the caches on the network.
+
+ The "cache flush" bit does apply to questions listed in the Question
+ Section of a Multicast DNS packet. The top bit of the rrclass field
+ in questions is used for an entirely different purpose (see Section
+ 6.5, "Questions Requesting Unicast Responses").
+
+ Note that the "cache flush" bit is NOT part of the resource record
+ class. The "cache flush" bit is the most significant bit of the
+ second 16-bit word of a resource record in the Answer Section of
+ an mDNS packet (the field conventionally referred to as the rrclass
+ field), and the actual resource record class is the least-significant
+ fifteen bits of this field. There is no mDNS resource record class
+ 0x8001. The value 0x8001 in the rrclass field of a resource record in
+ an mDNS response packet indicates a resource record with class 1,
+ with the "cache flush" bit set. When receiving a resource record with
+ the "cache flush" bit set, implementations should take care to mask
+ off that bit before storing the resource record in memory.
+
+
+11.4 Cache Flush on Topology change
+
+ If the hardware on a given host is able to indicate physical changes
+ of connectivity, then when the hardware indicates such a change, the
+ host should take this information into account in its mDNS cache
+ management strategy. For example, a host may choose to immediately
+ flush all cache records received on a particular interface when that
+ cable is disconnected. Alternatively, a host may choose to adjust the
+ remaining TTL on all those records to a few seconds so that if the
+ cable is not reconnected quickly, those records will expire from the
+ cache.
+
+ Likewise, when a host reboots, or wakes from sleep, or undergoes some
+ other similar discontinuous state change, the cache management
+ strategy should take that information into account.
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 31]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+11.5 Cache Flush on Failure Indication
+
+ Sometimes a cache record can be determined to be stale when a client
+ attempts to use the rdata it contains, and finds that rdata to be
+ incorrect.
+
+ For example, the rdata in an address record can be determined to be
+ incorrect if attempts to contact that host fail, either because
+ ARP/ND requests for that address go unanswered (for an address on a
+ local subnet) or because a router returns an ICMP "Host Unreachable"
+ error (for an address on a remote subnet).
+
+ The rdata in an SRV record can be determined to be incorrect if
+ attempts to communicate with the indicated service at the host and
+ port number indicated are not successful.
+
+ The rdata in a DNS-SD PTR record can be determined to be incorrect if
+ attempts to look up the SRV record it references are not successful.
+
+ In any such case, the software implementing the mDNS resource record
+ cache should provide a mechanism so that clients detecting stale
+ rdata can inform the cache.
+
+ When the cache receives this hint that it should reconfirm some
+ record, it MUST issue two or more queries for the resource record in
+ question. If no response is received in a reasonable amount of time,
+ then, even though its TTL may indicate that it is not yet due to
+ expire, that record SHOULD be promptly flushed from the cache.
+
+ The end result of this is that if a printer suffers a sudden power
+ failure or other abrupt disconnection from the network, its name
+ may continue to appear in DNS-SD browser lists displayed on users'
+ screens. Eventually that entry will expire from the cache naturally,
+ but if a user tries to access the printer before that happens, the
+ failure to successfully contact the printer will trigger the more
+ hasty demise of its cache entries. This is a sensible trade-off
+ between good user-experience and good network efficiency. If we were
+ to insist that printers should disappear from the printer list within
+ 30 seconds of becoming unavailable, for all failure modes, the only
+ way to achieve this would be for the client to poll the printer at
+ least every 30 seconds, or for the printer to announce its presence
+ at least every 30 seconds, both of which would be an unreasonable
+ burden on most networks.
+
+
+11.6 Passive Observation of Failures
+
+ A host observes the multicast queries issued by the other hosts on
+ the network. One of the major benefits of also sending responses
+ using multicast is that it allows all hosts to see the responses (or
+ lack thereof) to those queries.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 32]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ If a host sees queries, for which a record in its cache would be
+ expected to be given as an answer in a multicast response, but no
+ such answer is seen, then the host may take this as an indication
+ that the record may no longer be valid.
+
+ After seeing two or more of these queries, and seeing no multicast
+ response containing the expected answer within a reasonable amount of
+ time, then even though its TTL may indicate that it is not yet due to
+ expire, that record MAY be flushed from the cache. The host SHOULD
+ NOT perform its own queries to re-confirm that the record is truly
+ gone. If every host on a large network were to do this, it would
+ cause a lot of unnecessary multicast traffic. If host A sends
+ multicast queries that remain unanswered, then there is no reason
+ to suppose that host B or any other host is likely to be any more
+ successful.
+
+ The previous section, "Cache Flush on Failure Indication", describes
+ a situation where a user trying to print discovers that the printer
+ is no longer available. By implementing the passive observation
+ described here, when one user fails to contact the printer, all
+ hosts on the network observe that failure and update their caches
+ accordingly.
+
+
+12. Special Characteristics of Multicast DNS Domains
+
+ Unlike conventional DNS names, names that end in ".local." or
+ "254.169.in-addr.arpa." have only local significance. The same is
+ true of names within the IPv6 Link-Local reverse mapping domains.
+
+ Conventional Unicast DNS seeks to provide a single unified namespace,
+ where a given DNS query yields the same answer no matter where on the
+ planet it is performed or to which recursive DNS server the query is
+ sent. In contrast, each IP link has its own private ".local.",
+ "254.169.in-addr.arpa." and IPv6 Link-Local reverse mapping
+ namespaces, and the answer to any query for a name within those
+ domains depends on where that query is asked. (This characteristic is
+ not unique to Multicast DNS. Although the original concept of DNS was
+ a single global namespace, in recent years split views, firewalls,
+ intranets, and the like have increasingly meant that the answer to a
+ given DNS query has become dependent on the location of the querier.)
+
+ Multicast DNS Domains are not delegated from their parent domain via
+ use of NS records. There are no NS records anywhere in Multicast DNS
+ Domains. Instead, all Multicast DNS Domains are delegated to the IP
+ addresses 224.0.0.251 and FF02::FB by virtue of the individual
+ organizations producing DNS client software deciding how to handle
+ those names. It would be extremely valuable for the industry if this
+ special handling were ratified and recorded by IANA, since otherwise
+ the special handling provided by each vendor is likely to be
+ inconsistent.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 33]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ The IPv4 name server for a Multicast DNS Domain is 224.0.0.251. The
+ IPv6 name server for a Multicast DNS Domain is FF02::FB. These are
+ multicast addresses; therefore they identify not a single host but a
+ collection of hosts, working in cooperation to maintain some
+ reasonable facsimile of a competently managed DNS zone. Conceptually
+ a Multicast DNS Domain is a single DNS zone, however its server is
+ implemented as a distributed process running on a cluster of loosely
+ cooperating CPUs rather than as a single process running on a single
+ CPU.
+
+ No delegation is performed within Multicast DNS Domains. Because the
+ cluster of loosely coordinated CPUs is cooperating to administer a
+ single zone, delegation is neither necessary nor desirable. Just
+ because a particular host on the network may answer queries for a
+ particular record type with the name "example.local." does not imply
+ anything about whether that host will answer for the name
+ "child.example.local.", or indeed for other record types with the
+ name "example.local."
+
+ Multicast DNS Zones have no SOA record. A conventional DNS zone's
+ SOA record contains information such as the email address of the zone
+ administrator and the monotonically increasing serial number of the
+ last zone modification. There is no single human administrator for
+ any given Multicast DNS Zone, so there is no email address. Because
+ the hosts managing any given Multicast DNS Zone are only loosely
+ coordinated, there is no readily available monotonically increasing
+ serial number to determine whether or not the zone contents have
+ changed. A host holding part of the shared zone could crash or be
+ disconnected from the network at any time without informing the other
+ hosts. There is no reliable way to provide a zone serial number that
+ would, whenever such a crash or disconnection occurred, immediately
+ change to indicate that the contents of the shared zone had changed.
+
+ Zone transfers are not possible for any Multicast DNS Zone.
+
+
+13. Multicast DNS for Service Discovery
+
+ This document does not describe using Multicast DNS for network
+ browsing or service discovery. However, the mechanisms this document
+ describes are compatible with (and support) the browsing and service
+ discovery mechanisms proposed in "DNS-Based Service Discovery"
+ [DNS-SD].
+
+
+14. Enabling and Disabling Multicast DNS
+
+ The option to fail-over to Multicast DNS for names not ending
+ in ".local." SHOULD be a user-configured option, and SHOULD
+ be disabled by default because of the possible security issues
+ related to unintended local resolution of apparently global names.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 34]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ The option to lookup unqualified (relative) names by appending
+ ".local." (or not) is controlled by whether ".local." appears
+ (or not) in the client's DNS search list.
+
+ No special control is needed for enabling and disabling Multicast DNS
+ for names explicitly ending with ".local." as entered by the user.
+ The user doesn't need a way to disable Multicast DNS for names ending
+ with ".local.", because if the user doesn't want to use Multicast
+ DNS, they can achieve this by simply not using those names. If a user
+ *does* enter a name ending in ".local.", then we can safely assume
+ the user's intention was probably that it should work. Having user
+ configuration options that can be (intentionally or unintentionally)
+ set so that local names don't work is just one more way of
+ frustrating the user's ability to perform the tasks they want,
+ perpetuating the view that, "IP networking is too complicated to
+ configure and too hard to use." This in turn perpetuates the
+ continued use of protocols like AppleTalk. If we want to retire
+ AppleTalk, NetBIOS, etc., we need to offer users equivalent IP
+ functionality that they can rely on to, "always work, like
+ AppleTalk." A little Multicast DNS traffic may be a burden on the
+ network, but it is an insignificant burden compared to continued
+ widespread use of AppleTalk.
+
+
+15. Considerations for Multiple Interfaces
+
+ A host SHOULD defend its host name (FQDN) on all active interfaces on
+ which it is answering Multicast DNS queries.
+
+ In the event of a name conflict on *any* interface, a host should
+ configure a new host name, if it wishes to maintain uniqueness of its
+ host name.
+
+ A host may choose to use the same name for all of its address records
+ on all interfaces, or it may choose to manage its Multicast DNS host
+ name(s) independently on each interface, potentially answering to
+ different names on different interfaces.
+
+ When answering a Multicast DNS query, a multi-homed host with a
+ link-local address (or addresses) SHOULD take care to ensure that
+ any address going out in a Multicast DNS response is valid for use
+ on the interface on which the response is going out.
+
+ Just as the same link-local IP address may validly be in use
+ simultaneously on different links by different hosts, the same
+ link-local host name may validly be in use simultaneously on
+ different links, and this is not an error. A multi-homed host with
+ connections to two different links may be able to communicate with
+ two different hosts that are validly using the same name. While this
+ kind of name duplication should be rare, it means that a host that
+ wants to fully support this case needs network programming APIs that
+ allow applications to specify on what interface to perform a
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 35]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ link-local Multicast DNS query, and to discover on what interface a
+ Multicast DNS response was received.
+
+ There is one other special precaution that multi-homed hosts need to
+ take. It's common with today's laptop computers to have an Ethernet
+ connection and an 802.11 wireless connection active at the same time.
+ What the software on the laptop computer can't easily tell is whether
+ the wireless connection is in fact bridged onto the same network
+ segment as its Ethernet connection. If the two networks are bridged
+ together, then packets the host sends on one interface will arrive on
+ the other interface a few milliseconds later, and care must be taken
+ to ensure that this bridging does not cause problems:
+
+ When the host announces its host name (i.e. its address records) on
+ its wireless interface, those announcement records are sent with the
+ cache-flush bit set, so when they arrive on the Ethernet segment,
+ they will cause all the peers on the Ethernet to flush the host's
+ Ethernet address records from their caches. The mDNS protocol has a
+ safeguard to protect against this situation: when records are
+ received with the cache-flush bit set, other records are not deleted
+ from peer caches immediately, but are marked for deletion in one
+ second. When the host sees its own wireless address records arrive on
+ its Ethernet interface, with the cache-flush bit set, this one-second
+ grace period gives the host time to respond and re-announce its
+ Ethernet address records, to reinstate those records in peer caches
+ before they are deleted.
+
+ As described, this solves one problem, but creates another, because
+ when those Ethernet announcement records arrive back on the wireless
+ interface, the host would again respond defensively to reinstate its
+ wireless records, and this process would continue forever,
+ continuously flooding the network with traffic. The mDNS protocol has
+ a second safeguard, to solve this problem: the cache-flush bit does
+ not apply to records received very recently, within the last second.
+ This means that when the host sees its own Ethernet address records
+ arrive on its wireless interface, with the cache-flush bit set, it
+ knows there's no need to re-announce its wireless address records
+ again because it already sent them less than a second ago, and this
+ makes them immune from deletion from peer caches.
+
+16. Considerations for Multiple Responders on the Same Machine
+
+ It is possible to have more than one Multicast DNS Responder and/or
+ Querier implementation coexist on the same machine, but there are
+ some known issues.
+
+16.1 Receiving Unicast Responses
+
+ In most operating systems, incoming multicast packets can be
+ delivered to *all* open sockets bound to the right port number,
+ provided that the clients take the appropriate steps to allow this.
+ For this reason, all Multicast DNS implementations SHOULD use the
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 36]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ SO_REUSEPORT and/or SO_REUSEADDR options (or equivalent as
+ appropriate for the operating system in question) so they will all be
+ able to bind to UDP port 5353 and receive incoming multicast packets
+ addressed to that port. However, incoming unicast UDP packets are
+ typically delivered only to the first socket to bind to that port.
+ This means that "QU" responses and other packets sent via unicast
+ will be received only by the first Multicast DNS Responder and/or
+ Querier on a system. This limitation can be partially mitigated if
+ Multicast DNS implementations detect when they are not the first
+ to bind to port 5353, and in that case they do not request "QU"
+ responses. One way to detect if there is another Multicast DNS
+ implementation already running is to attempt binding to port 5353
+ without using SO_REUSEPORT and/or SO_REUSEADDR, and if that fails
+ it indicates that some other socket is already bound to this port.
+
+
+16.2 Multi-Packet Known-Answer lists
+
+ When a Multicast DNS Querier issues a query with too many known
+ answers to fit into a single packet, it divides the known answer list
+ into two or more packets. Multicast DNS Responders associate the
+ initial truncated query with its continuation packets by examining
+ the source IP address in each packet. Since two independent Multicast
+ DNS Queriers running on the same machine will be sending packets with
+ the same source IP address, from an outside perspective they appear
+ to be a single entity. If both Queriers happened to send the same
+ multi-packet query at the same time, with different known answer
+ lists, then they could each end up suppressing answers that the other
+ needs.
+
+
+16.3 Efficiency
+
+ If different clients on a machine were to each have their own
+ separate independent Multicast DNS implementation, they would lose
+ certain efficiency benefits. Apart from the unnecessary code
+ duplication, memory usage, and CPU load, the clients wouldn't get the
+ benefit of a shared system-wide cache, and they would not be able to
+ aggregate separate queries into single packets to reduce network
+ traffic.
+
+
+16.4 Recommendation
+
+ Because of these issues, this document encourages implementers
+ to design systems with a single Multicast DNS implementation that
+ provides Multicast DNS services shared by all clients on that
+ machine. Due to engineering constraints, there may be situations
+ where embedding a Multicast DNS implementation in the client is the
+ most expedient solution, and while this will work in practice,
+ implementers should be aware of the issues outlined in this section.
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 37]
+
+Internet Draft Multicast DNS 10th August 2006
+
+17. Multicast DNS and Power Management
+
+ Many modern network devices have the ability to go into a low-power
+ mode where only a small part of the Ethernet hardware remains
+ powered, and the device can be woken up by sending a specially
+ formatted Ethernet frame which the device's power-management hardware
+ recognizes.
+
+ To make use of this in conjunction with Multicast DNS, we propose a
+ network power management service called Sleep Proxy Service. A device
+ that wishes to enter low-power mode first uses DNS-SD to determine if
+ Sleep Proxy Service is available on the local network. In some
+ networks there may be more than one piece of hardware implementing
+ Sleep Proxy Service, for fault-tolerance reasons.
+
+ If the device finds the network has Sleep Proxy Service, the device
+ transmits two or more gratuitous mDNS announcements setting the TTL
+ of its relevant resource records to zero, to delete them from
+ neighboring caches. The relevant resource records include address
+ records and SRV records, and other resource records as may apply to a
+ particular device. The device then communicates all of its remaining
+ active records, plus the names, rrtypes and rrclasses of the deleted
+ records, to the Sleep Proxy Service(s), along with a copy of the
+ specific "magic packet" required to wake the device up.
+
+ When a Sleep Proxy Service sees an mDNS query for one of the
+ device's active records (e.g. a DNS-SD PTR record), it answers on
+ behalf of the device without waking it up. When a Sleep Proxy Service
+ sees an mDNS query for one of the device's deleted resource
+ records, it deduces that some client on the network needs to make an
+ active connection to the device, and sends the specified "magic
+ packet" to wake the device up. The device then wakes up, reactivates
+ its deleted resource records, and re-announces them to the network.
+ The client waiting to connect sees the announcements, learns the
+ current IP address and port number of the desired service on the
+ device, and proceeds to connect to it.
+
+ The connecting client does not need to be aware of how Sleep Proxy
+ Service works. Only devices that implement low power mode and wish to
+ make use of Sleep Proxy Service need to be aware of how that protocol
+ works.
+
+ The reason that a device using a Sleep Proxy Service should send more
+ than one goodbye packet is to ensure deletion of the resource records
+ from all peer caches. If resource records were to inadvertently
+ remain in some peer caches, then those peers may not issue any query
+ packets for those records when attempting to access the sleeping
+ device, so the Sleep Proxy Service would not receive any queries for
+ the device's SRV and/or address records, and the necessary wake-up
+ message would not be triggered.
+
+ The full specification of mDNS / DNS-SD Sleep Proxy Service
+ is described in another document [not yet published].
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 38]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+18. Multicast DNS Character Set
+
+ Unicast DNS has been plagued by the lack of any support for non-US
+ characters. Indeed, conventional DNS is usually limited to just
+ letters, digits and hyphens, with no spaces or other punctuation.
+ Attempts to remedy this for unicast DNS have been badly constrained
+ by the need to accommodate old buggy legacy DNS implementations.
+ In reality, the DNS specification actually imposes no limits on what
+ characters may be used in names, and good DNS implementations handle
+ any arbitrary eight-bit data without trouble. However, the old rules
+ for ARPANET host names back in the 1980s required names to be just
+ letters, digits, and hyphens [RFC 1034], and since the predominant
+ use of DNS is to store host address records, many have assumed that
+ the DNS protocol itself suffers from the same limitation. It would be
+ more accurate to say that certain bad implementations may not handle
+ eight-bit data correctly, not that the protocol doesn't support it.
+
+ Multicast DNS is a new protocol and doesn't (yet) have old buggy
+ legacy implementations to constrain the design choices. Accordingly,
+ it adopts the simple obvious elegant solution: all names in Multicast
+ DNS are encoded using precomposed UTF-8 [RFC 3629]. The characters
+ SHOULD conform to Unicode Normalization Form C (NFC) [UAX15]: Use
+ precomposed characters instead of combining sequences where possible,
+ e.g. use U+00C4 ("Latin capital letter A with diaeresis") instead of
+ U+0041 U+0308 ("Latin capital letter A", "combining diaeresis").
+
+ Some users of 16-bit Unicode have taken to stuffing a "zero-width
+ non-breaking space" character (U+FEFF) at the start of each UTF-16
+ file, as a hint to identify whether the data is big-endian or
+ little-endian, and calling it a "Byte Order Mark" (BOM). Since there
+ is only one possible byte order for UTF-8 data, a BOM is neither
+ necessary nor permitted. Multicast DNS names MUST NOT contain a "Byte
+ Order Mark". Any occurrence of the Unicode character U+FEFF at the
+ start or anywhere else in a Multicast DNS name MUST be interpreted as
+ being an actual intended part of the name, representing (just as for
+ any other legal unicode value) an actual literal instance of that
+ character (in this case a zero-width non-breaking space character).
+
+ For names that are restricted to letters, digits and hyphens, the
+ UTF-8 encoding is identical to the US-ASCII encoding, so this is
+ entirely compatible with existing host names. For characters outside
+ the US-ASCII range, UTF-8 encoding is used.
+
+ Multicast DNS implementations MUST NOT use any other encodings apart
+ from precomposed UTF-8 (US-ASCII being considered a compatible subset
+ of UTF-8).
+
+ This point bears repeating: After many years of debate, as a
+ result of the need to accommodate certain DNS implementations that
+ apparently couldn't handle any character that's not a letter, digit
+ or hyphen (and apparently never will be updated to remedy this
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 39]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ limitation) the unicast DNS community settled on an extremely baroque
+ encoding called "Punycode" [RFC 3492]. Punycode is a remarkably
+ ingenious encoding solution, but it is complicated, hard to
+ understand, and hard to implement, using sophisticated techniques
+ including insertion unsort coding, generalized variable-length
+ integers, and bias adaptation. The resulting encoding is remarkably
+ compact given the constraints, but it's still not as good as simple
+ straightforward UTF-8, and it's hard even to predict whether a given
+ input string will encode to a Punycode string that fits within DNS's
+ 63-byte limit, except by simply trying the encoding and seeing
+ whether it fits. Indeed, the encoded size depends not only on the
+ input characters, but on the order they appear, so the same set of
+ characters may or may not encode to a legal Punycode string that fits
+ within DNS's 63-byte limit, depending on the order the characters
+ appear. This is extremely hard to present in a user interface that
+ explains to users why one name is allowed, but another name
+ containing the exact same characters is not. Neither Punycode nor any
+ other of the "Ascii Compatible Encodings" proposed for Unicast DNS
+ may be used in Multicast DNS packets. Any text being represented
+ internally in some other representation MUST be converted to
+ canonical precomposed UTF-8 before being placed in any Multicast DNS
+ packet.
+
+ The simple rules for case-insensitivity in Unicast DNS also apply in
+ Multicast DNS; that is to say, in name comparisons, the lower-case
+ letters "a" to "z" (0x61 to 0x7A) match their upper-case equivalents
+ "A" to "Z" (0x41 to 0x5A). Hence, if a client issues a query for an
+ address record with the name "cheshire.local", then a responder
+ having an address record with the name "Cheshire.local" should
+ issue a response. No other automatic equivalences should be assumed.
+ In particular all UTF-8 multi-byte characters (codes 0x80 and higher)
+ are compared by simple binary comparison of the raw byte values.
+ Accented characters are *not* defined to be automatically equivalent
+ to their unaccented counterparts. Where automatic equivalences are
+ desired, this may be achieved through the use of programmatically-
+ generated CNAME records. For example, if a responder has an address
+ record for an accented name Y, and a client issues a query for a name
+ X, where X is the same as Y with all the accents removed, then the
+ responder may issue a response containing two resource records:
+ A CNAME record "X CNAME Y", asserting that the requested name X
+ (unaccented) is an alias for the true (accented) name Y, followed
+ by the address record for Y.
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 40]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+19. Multicast DNS Message Size
+
+ RFC 1035 restricts DNS Messages carried by UDP to no more than 512
+ bytes (not counting the IP or UDP headers) [RFC 1035]. For UDP
+ packets carried over the wide-area Internet in 1987, this was
+ appropriate. For link-local multicast packets on today's networks,
+ there is no reason to retain this restriction. Given that the packets
+ are by definition link-local, there are no Path MTU issues to
+ consider.
+
+ Multicast DNS Messages carried by UDP may be up to the IP MTU of the
+ physical interface, less the space required for the IP header (20
+ bytes for IPv4; 40 bytes for IPv6) and the UDP header (8 bytes).
+
+ In the case of a single mDNS Resource Record which is too large to
+ fit in a single MTU-sized multicast response packet, a Multicast DNS
+ Responder SHOULD send the Resource Record alone, in a single IP
+ datagram, sent using multiple IP fragments. Resource Records this
+ large SHOULD be avoided, except in the very rare cases where they
+ really are the appropriate solution to the problem at hand.
+ Implementers should be aware that many simple devices do not
+ re-assemble fragmented IP datagrams, so large Resource Records
+ SHOULD NOT be used except in specialized cases where the implementer
+ knows that all receivers implement reassembly.
+
+ A Multicast DNS packet larger than the interface MTU, which is sent
+ using fragments, MUST NOT contain more than one Resource Record.
+
+ Even when fragmentation is used, a Multicast DNS packet, including IP
+ and UDP headers, MUST NOT exceed 9000 bytes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 41]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+20. Multicast DNS Message Format
+
+ This section describes specific restrictions on the allowable
+ values for the header fields of a Multicast DNS message.
+
+
+20.1 ID (Query Identifier)
+
+ Multicast DNS clients SHOULD listen for gratuitous responses
+ issued by hosts booting up (or waking up from sleep or otherwise
+ joining the network). Since these gratuitous responses may contain a
+ useful answer to a question for which the client is currently
+ awaiting an answer, Multicast DNS clients SHOULD examine all received
+ Multicast DNS response messages for useful answers, without regard to
+ the contents of the ID field or the Question Section. In Multicast
+ DNS, knowing which particular query message (if any) is responsible
+ for eliciting a particular response message is less interesting than
+ knowing whether the response message contains useful information.
+
+ Multicast DNS clients MAY cache any or all Multicast DNS response
+ messages they receive, for possible future use, provided of course
+ that normal TTL aging is performed on these cached resource records.
+
+ In multicast query messages, the Query ID SHOULD be set to zero on
+ transmission.
+
+ In multicast responses, including gratuitous multicast responses, the
+ Query ID MUST be set to zero on transmission, and MUST be ignored on
+ reception.
+
+ In unicast response messages generated specifically in response to a
+ particular (unicast or multicast) query, the Query ID MUST match the
+ ID from the query message.
+
+
+20.2 QR (Query/Response) Bit
+
+ In query messages, MUST be zero.
+ In response messages, MUST be one.
+
+
+20.3 OPCODE
+
+ In both multicast query and multicast response messages, MUST be zero
+ (only standard queries are currently supported over multicast, unless
+ other queries are allowed by future IETF Standards Action).
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 42]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+20.4 AA (Authoritative Answer) Bit
+
+ In query messages, the Authoritative Answer bit MUST be zero on
+ transmission, and MUST be ignored on reception.
+
+ In response messages for Multicast Domains, the Authoritative Answer
+ bit MUST be set to one (not setting this bit implies there's some
+ other place where "better" information may be found) and MUST be
+ ignored on reception.
+
+
+20.5 TC (Truncated) Bit
+
+ In query messages, if the TC bit is set, it means that additional
+ Known Answer records may be following shortly. A responder MAY choose
+ to record this fact, and wait for those additional Known Answer
+ records, before deciding whether to respond. If the TC bit is clear,
+ it means that the querying host has no additional Known Answers.
+
+ In multicast response messages, the TC bit MUST be zero on
+ transmission, and MUST be ignored on reception.
+
+ In legacy unicast response messages, the TC bit has the same meaning
+ as in conventional unicast DNS: it means that the response was too
+ large to fit in a single packet, so the client SHOULD re-issue its
+ query using TCP in order to receive the larger response.
+
+
+20.6 RD (Recursion Desired) Bit
+
+ In both multicast query and multicast response messages, the
+ Recursion Desired bit SHOULD be zero on transmission, and MUST be
+ ignored on reception.
+
+
+20.7 RA (Recursion Available) Bit
+
+ In both multicast query and multicast response messages, the
+ Recursion Available bit MUST be zero on transmission, and MUST be
+ ignored on reception.
+
+
+20.8 Z (Zero) Bit
+
+ In both query and response messages, the Zero bit MUST be zero on
+ transmission, and MUST be ignored on reception.
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 43]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+20.9 AD (Authentic Data) Bit [RFC 2535]
+
+ In query messages the Authentic Data bit MUST be zero on
+ transmission, and MUST be ignored on reception.
+
+ In response messages, the Authentic Data bit MAY be set. Resolvers
+ receiving response messages with the AD bit set MUST NOT trust the AD
+ bit unless they trust the source of the message and either have a
+ secure path to it or use DNS transaction security.
+
+
+20.10 CD (Checking Disabled) Bit [RFC 2535]
+
+ In query messages, a resolver willing to do cryptography SHOULD set
+ the Checking Disabled bit to permit it to impose its own policies.
+
+ In response messages, the Checking Disabled bit MUST be zero on
+ transmission, and MUST be ignored on reception.
+
+
+20.11 RCODE (Response Code)
+
+ In both multicast query and multicast response messages, the Response
+ Code MUST be zero on transmission. Multicast DNS messages received
+ with non-zero Response Codes MUST be silently ignored.
+
+
+20.12 Repurposing of top bit of qclass in Question Section
+
+ In the Question Section of a Multicast DNS Query, the top bit of the
+ qclass field is used to indicate that unicast responses are preferred
+ for this particular question.
+
+
+20.13 Repurposing of top bit of rrclass in Answer Section
+
+ In the Answer Section of a Multicast DNS Response, the top bit of the
+ rrclass field is used to indicate that the record is a member of a
+ unique RRSet, and the entire RRSet has been sent together (in the
+ same packet, or in consecutive packets if there are too many records
+ to fit in a single packet).
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 44]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+21. Choice of UDP Port Number
+
+ Arguments were made for and against using Multicast on UDP port 53.
+ The final decision was to use UDP port 5353. Some of the arguments
+ for and against are given below.
+
+
+21.1 Arguments for using UDP port 53:
+
+ * This is "just DNS", so it should be the same port.
+
+ * There is less work to be done updating old clients to do simple
+ mDNS queries. Only the destination address need be changed.
+ In some cases, this can be achieved without any code changes,
+ just by adding the address 224.0.0.251 to a configuration file.
+
+
+21.2 Arguments for using a different port (UDP port 5353):
+
+ * This is not "just DNS". This is a DNS-like protocol, but different.
+
+ * Changing client code to use a different port number is not hard.
+
+ * Using the same port number makes it hard to run an mDNS Responder
+ and a conventional unicast DNS server on the same machine. If a
+ conventional unicast DNS server wishes to implement mDNS as well,
+ it can still do that, by opening two sockets. Having two different
+ port numbers is important to allow this flexibility.
+
+ * Some VPN software hijacks all outgoing traffic to port 53 and
+ redirects it to a special DNS server set up to serve those VPN
+ clients while they are connected to the corporate network. It is
+ questionable whether this is the right thing to do, but it is
+ common, and redirecting link-local multicast DNS packets to a
+ remote server rarely produces any useful results. It does mean,
+ for example, that the user becomes unable to access their local
+ network printer sitting on their desk right next to their computer.
+ Using a different UDP port eliminates this particular problem.
+
+ * On many operating systems, unprivileged clients may not send or
+ receive packets on low-numbered ports. This means that any client
+ sending or receiving mDNS packets on port 53 would have to run
+ as "root", which is an undesirable security risk. Using a higher-
+ numbered UDP port eliminates this particular problem.
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 45]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+22. Summary of Differences Between Multicast DNS and Unicast DNS
+
+ The value of Multicast DNS is that it shares, as much as possible,
+ the familiar APIs, naming syntax, resource record types, etc., of
+ Unicast DNS. There are of course necessary differences by virtue of
+ it using Multicast, and by virtue of it operating in a community of
+ cooperating peers, rather than a precisely defined authoritarian
+ hierarchy controlled by a strict chain of formal delegations from the
+ top. These differences are listed below:
+
+ Multicast DNS...
+ * uses multicast
+ * uses UDP port 5353 instead of port 53
+ * operates in well-defined parts of the DNS namespace
+ * uses UTF-8, and only UTF-8, to encode resource record names
+ * defines a clear limit on the maximum legal domain name (255 bytes)
+ * allows larger UDP packets
+ * allows more than one question in a query packet
+ * uses the Answer Section of a query to list Known Answers
+ * uses the TC bit in a query to indicate additional Known Answers
+ * uses the Authority Section of a query for probe tie-breaking
+ * ignores the Query ID field (except for generating legacy responses)
+ * doesn't require the question to be repeated in the response packet
+ * uses gratuitous responses to announce new records to the peer group
+ * defines a "unicast response" bit in the rrclass of query questions
+ * defines a "cache flush" bit in the rrclass of response answers
+ * uses DNS TTL 0 to indicate that a record has been deleted
+ * monitors queries to perform Duplicate Question Suppression
+ * monitors responses to perform Duplicate Answer Suppression...
+ * ... and Ongoing Conflict Detection
+ * ... and Opportunistic Caching
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 46]
+
+Internet Draft Multicast DNS 10th August 2006
+
+23. Benefits of Multicast Responses
+
+ Some people have argued that sending responses via multicast is
+ inefficient on the network. In fact using multicast responses results
+ in a net lowering of overall multicast traffic, for a variety of
+ reasons, in addition to other benefits.
+
+ * One multicast response can update the cache on all machines on the
+ network. If another machine later wants to issue the same query, it
+ already has the answer in its cache, so it may not need to even
+ transmit that multicast query on the network at all.
+
+ * When more than one machine has the same ongoing long-lived query
+ running, every machine does not have to transmit its own
+ independent query. When one machine transmits a query, all the
+ other hosts see the answers, so they can suppress their own
+ queries.
+
+ * When a host sees a multicast query, but does not see the corres-
+ ponding multicast response, it can use this information to promptly
+ delete stale data from its cache. To achieve the same level of
+ user-interface quality and responsiveness without multicast
+ responses would require lower cache lifetimes and more frequent
+ network polling, resulting in a significantly higher packet rate.
+
+ * Multicast responses allow passive conflict detection. Without this
+ ability, some other conflict detection mechanism would be needed,
+ imposing its own additional burden on the network.
+
+ * When using delayed responses to reduce network collisions, clients
+ need to maintain a list recording to whom each answer should be
+ sent. The option of multicast responses allows clients with limited
+ storage, which cannot store an arbitrarily long list of response
+ addresses, to choose to fail-over to a single multicast response in
+ place of multiple unicast responses, when appropriate.
+
+ * In the case of overlayed subnets, multicast responses allow a
+ receiver to know with certainty that a response originated on the
+ local link, even when its source address may apparently suggest
+ otherwise.
+
+ * Link-local multicast transcends virtually every conceivable network
+ misconfiguration. Even if you have a collection of devices where
+ every device's IP address, subnet mask, default gateway, and DNS
+ server address are all wrong, packets sent by any of those devices
+ addressed to a link-local multicast destination address will still
+ be delivered to all peers on the local link. This can be extremely
+ helpful when diagnosing and rectifying network problems, since
+ it facilitates a direct communication channel between client and
+ server that works without reliance on ARP, IP routing tables, etc.
+ Being able to discover what IP address a device has (or thinks it
+ has) is frequently a very valuable first step in diagnosing why it
+ is unable to communicate on the local network.
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 47]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+24. IPv6 Considerations
+
+ An IPv4-only host and an IPv6-only host behave as "ships that pass in
+ the night". Even if they are on the same Ethernet, neither is aware
+ of the other's traffic. For this reason, each physical link may have
+ *two* unrelated ".local." zones, one for IPv4 and one for IPv6.
+ Since for practical purposes, a group of IPv4-only hosts and a group
+ of IPv6-only hosts on the same Ethernet act as if they were on two
+ entirely separate Ethernet segments, it is unsurprising that their
+ use of the ".local." zone should occur exactly as it would if
+ they really were on two entirely separate Ethernet segments.
+
+ A dual-stack (v4/v6) host can participate in both ".local."
+ zones, and should register its name(s) and perform its lookups both
+ using IPv4 and IPv6. This enables it to reach, and be reached by,
+ both IPv4-only and IPv6-only hosts. In effect this acts like a
+ multi-homed host, with one connection to the logical "IPv4 Ethernet
+ segment", and a connection to the logical "IPv6 Ethernet segment".
+
+
+24.1 IPv6 Multicast Addresses by Hashing
+
+ Some discovery protocols use a range of multicast addresses, and
+ determine the address to be used by a hash function of the name being
+ sought. Queries are sent via multicast to the address as indicated by
+ the hash function, and responses are returned to the querier via
+ unicast. Particularly in IPv6, where multicast addresses are
+ extremely plentiful, this approach is frequently advocated.
+
+ There are some problems with this:
+
+ * When a host has a large number of records with different names, the
+ host may have to join a large number of multicast groups. This can
+ place undue burden on the Ethernet hardware, which typically
+ supports a limited number of multicast addresses efficiently. When
+ this number is exceeded, the Ethernet hardware may have to resort
+ to receiving all multicasts and passing them up to the host
+ software for filtering, thereby defeating the point of using a
+ multicast address range in the first place.
+
+ * Multiple questions cannot be placed in one packet if they don't all
+ hash to the same multicast address.
+
+ * Duplicate Question Suppression doesn't work if queriers are not
+ seeing each other's queries.
+
+ * Duplicate Answer Suppression doesn't work if responders are not
+ seeing each other's responses.
+
+ * Opportunistic Caching doesn't work.
+
+ * Ongoing Conflict Detection doesn't work.
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 48]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+25. Security Considerations
+
+ The algorithm for detecting and resolving name conflicts is, by its
+ very nature, an algorithm that assumes cooperating participants. Its
+ purpose is to allow a group of hosts to arrive at a mutually disjoint
+ set of host names and other DNS resource record names, in the absence
+ of any central authority to coordinate this or mediate disputes. In
+ the absence of any higher authority to resolve disputes, the only
+ alternative is that the participants must work together cooperatively
+ to arrive at a resolution.
+
+ In an environment where the participants are mutually antagonistic
+ and unwilling to cooperate, other mechanisms are appropriate, like
+ manually administered DNS.
+
+ In an environment where there is a group of cooperating participants,
+ but there may be other antagonistic participants on the same physical
+ link, the cooperating participants need to use IPSEC signatures
+ and/or DNSSEC [RFC 2535] signatures so that they can distinguish mDNS
+ messages from trusted participants (which they process as usual) from
+ mDNS messages from untrusted participants (which they silently
+ discard).
+
+ When DNS queries for *global* DNS names are sent to the mDNS
+ multicast address (during network outages which disrupt communication
+ with the greater Internet) it is *especially* important to use
+ DNSSEC, because the user may have the impression that he or she is
+ communicating with some authentic host, when in fact he or she is
+ really communicating with some local host that is merely masquerading
+ as that name. This is less critical for names ending with ".local.",
+ because the user should be aware that those names have only local
+ significance and no global authority is implied.
+
+ Most computer users neglect to type the trailing dot at the end of a
+ fully qualified domain name, making it a relative domain name (e.g.
+ "www.example.com"). In the event of network outage, attempts to
+ positively resolve the name as entered will fail, resulting in
+ application of the search list, including ".local.", if present.
+ A malicious host could masquerade as "www.example.com" by answering
+ the resulting Multicast DNS query for "www.example.com.local."
+ To avoid this, a host MUST NOT append the search suffix
+ ".local.", if present, to any relative (partially qualified)
+ host name containing two or more labels. Appending ".local." to
+ single-label relative host names is acceptable, since the user
+ should have no expectation that a single-label host name will
+ resolve as-is.
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 49]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+26. IANA Considerations
+
+ IANA has allocated the IPv4 link-local multicast address 224.0.0.251
+ for the use described in this document.
+
+ IANA has allocated the IPv6 multicast address set FF0X::FB for the
+ use described in this document. Only address FF02::FB (Link-Local
+ Scope) is currently in use by deployed software, but it is possible
+ that in future implementers may experiment with Multicast DNS using
+ larger-scoped addresses, such as FF05::FB (Site-Local Scope).
+
+ When this document is published, IANA should designate a list of
+ domains which are deemed to have only link-local significance, as
+ described in Section 12 of this document ("Special Characteristics of
+ Multicast DNS Domains").
+
+ The re-use of the top bit of the rrclass field in the Question and
+ Answer Sections means that Multicast DNS can only carry DNS records
+ with classes in the range 0-32767. Classes in the range 32768 to
+ 65535 are incompatible with Multicast DNS. However, since to-date
+ only three DNS classes have been assigned by IANA (1, 3 and 4),
+ and only one (1, "Internet") is actually in widespread use, this
+ limitation is likely to remain a purely theoretical one.
+
+ No other IANA services are required by this document.
+
+
+27. Acknowledgments
+
+ The concepts described in this document have been explored, developed
+ and implemented with help from Freek Dijkstra, Erik Guttman, Paul
+ Vixie, Bill Woodcock, and others.
+
+ Special thanks go to Bob Bradley, Josh Graessley, Scott Herscher,
+ Roger Pantos and Kiren Sekar for their significant contributions.
+
+
+28. Deployment History
+
+ Multicast DNS client software first became available to the public
+ in Mac OS 9 in 2001. Multicast DNS Responder software first began
+ shipping to end users in large volumes (i.e. millions) with the
+ launch of Mac OS X 10.2 Jaguar in August 2002, and became available
+ for Microsoft Windows users with the launch of Apple's "Rendezvous
+ for Windows" (now "Bonjour for Windows") in June 2004.
+
+ Apple released the source code for the mDNSResponder daemon as Open
+ Source in September 2002, first under Apple's standard Apple Public
+ Source License, and then later, in August 2006, under the Apache
+ License, Version 2.0.
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 50]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+ In addition to desktop and laptop computers running Mac OS X and
+ Microsoft Windows, Multicast DNS is implemented in a wide range of
+ hardware devices, such as Apple's "AirPort Extreme" and "AirPort
+ Express" wireless base stations, home gateways from other vendors,
+ network printers, network cameras, TiVo DVRs, etc.
+
+ The Open Source community has produced many independent
+ implementations of Multicast DNS, some in C like Apple's
+ mDNSResponder daemon, and others in a variety of different languages
+ including Java, Python, Perl, and C#/Mono.
+
+
+29. Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights. For the purposes of this document,
+ the term "BCP 78" refers exclusively to RFC 3978, "IETF Rights
+ in Contributions", published March 2005.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+30. Normative References
+
+ [RFC 1034] Mockapetris, P., "Domain Names - Concepts and
+ Facilities", STD 13, RFC 1034, November 1987.
+
+ [RFC 1035] Mockapetris, P., "Domain Names - Implementation and
+ Specifications", STD 13, RFC 1035, November 1987.
+
+ [RFC 2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, March 1997.
+
+ [RFC 3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", RFC 3629, November 2003.
+
+ [UAX15] "Unicode Normalization Forms"
+ http://www.unicode.org/reports/tr15/
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 51]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+31. Informative References
+
+ [dotlocal]
+
+ [djbdl]
+
+ [DNS-SD] Cheshire, S., and M. Krochmal, "DNS-Based Service
+ Discovery", Internet-Draft (work in progress),
+ draft-cheshire-dnsext-dns-sd-04.txt, August 2006.
+
+ [IEEE802] IEEE Standards for Local and Metropolitan Area Networks:
+ Overview and Architecture.
+ Institute of Electrical and Electronic Engineers,
+ IEEE Standard 802, 1990.
+
+ [NBP] Cheshire, S., and M. Krochmal,
+ "Requirements for a Protocol to Replace AppleTalk NBP",
+ Internet-Draft (work in progress),
+ draft-cheshire-dnsext-nbp-05.txt, August 2006.
+
+ [RFC 2136] Vixie, P., et al., "Dynamic Updates in the Domain Name
+ System (DNS UPDATE)", RFC 2136, April 1997.
+
+ [RFC 2462] S. Thomson and T. Narten, "IPv6 Stateless Address
+ Autoconfiguration", RFC 2462, December 1998.
+
+ [RFC 2535] Eastlake, D., "Domain Name System Security Extensions",
+ RFC 2535, March 1999.
+
+ [RFC 2606] Eastlake, D., and A. Panitz, "Reserved Top Level DNS
+ Names", RFC 2606, June 1999.
+
+ [RFC 2860] Carpenter, B., Baker, F. and M. Roberts, "Memorandum
+ of Understanding Concerning the Technical Work of the
+ Internet Assigned Numbers Authority", RFC 2860, June
+ 2000.
+
+ [RFC 3492] Costello, A., "Punycode: A Bootstring encoding of
+ Unicode for use with Internationalized Domain Names
+ in Applications (IDNA)", RFC 3492, March 2003.
+
+ [RFC 3927] Cheshire, S., B. Aboba, and E. Guttman,
+ "Dynamic Configuration of IPv4 Link-Local Addresses",
+ RFC 3927, May 2005.
+
+ [ZC] Williams, A., "Requirements for Automatic Configuration
+ of IP Hosts", Internet-Draft (work in progress),
+ draft-ietf-zeroconf-reqts-12.txt, September 2002.
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 52]
+
+Internet Draft Multicast DNS 10th August 2006
+
+
+32. Authors' Addresses
+
+ Stuart Cheshire
+ Apple Computer, Inc.
+ 1 Infinite Loop
+ Cupertino
+ California 95014
+ USA
+
+ Phone: +1 408 974 3207
+ EMail: rfc [at] stuartcheshire [dot] org
+
+
+ Marc Krochmal
+ Apple Computer, Inc.
+ 1 Infinite Loop
+ Cupertino
+ California 95014
+ USA
+
+ Phone: +1 408 974 4368
+ EMail: marc [at] apple [dot] com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expires 10th February 2007 Cheshire & Krochmal [Page 53]
--
cgit
From 71ef4b773ae4cc737dde645b76b8ffa0ace227e5 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Mon, 6 Nov 2006 14:01:51 +0000
Subject: Fix a bug where Avahi does not verify the source of netlink messages
(Closes #69) Update NEWS/configure for 0.6.15
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1331 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/netlink.c | 6 ++++++
configure.ac | 2 +-
docs/NEWS | 12 ++++++++++++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/avahi-core/netlink.c b/avahi-core/netlink.c
index b2a6684..893295d 100644
--- a/avahi-core/netlink.c
+++ b/avahi-core/netlink.c
@@ -62,6 +62,12 @@ int avahi_netlink_work(AvahiNetlink *nl, int block) {
p = (struct nlmsghdr *) nl->buffer;
+ /* Check that this message originated from the kernel,
+ or a request from avahi itself, and not another process */
+ if ((p->nlmsg_pid != 0) && (p->nlmsg_pid != getpid())) {
+ return -1;
+ }
+
assert(nl->callback);
for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
diff --git a/configure.ac b/configure.ac
index 0acb7dc..fe26066 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,7 +21,7 @@
# USA.
AC_PREREQ(2.57)
-AC_INIT([avahi],[0.6.14],[avahi (at) lists (dot) freedesktop (dot) org])
+AC_INIT([avahi],[0.6.15],[avahi (at) lists (dot) freedesktop (dot) org])
AC_CONFIG_SRCDIR([avahi-core/server.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
diff --git a/docs/NEWS b/docs/NEWS
index 212d157..f6d04c6 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -1,3 +1,15 @@
+Avahi 0.6.15
+============
+
+This is a bugfix release, this bug is potentially security sensitive
+
+ * Check that netlink messages actually originate from the kernel
+ and not another process.
+ * Fix build on NetBSD (thanks to Daniel S. Haischt)
+ * Fix dbus_service_browser not setting AVAHI_LOOKUP_RESULT_OUR_OWN.
+
+This release is backwards compatible with Avahi 0.6.x with x < 15.
+
Avahi 0.6.14
============
--
cgit
From 2d332e78d4e78eee19f03fb6ec4db2983591c022 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Sat, 11 Nov 2006 00:00:20 +0000
Subject: Make d-bus version detection work for >= 1.0 (Closes: #71) Dont
dbus_connection_close on shared dbus connections (Closes: #68)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1333 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/dbus-protocol.c | 21 +++------------------
1 file changed, 3 insertions(+), 18 deletions(-)
diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c
index dbf30f8..caa2e19 100644
--- a/avahi-daemon/dbus-protocol.c
+++ b/avahi-daemon/dbus-protocol.c
@@ -1067,10 +1067,10 @@ static int dbus_connect(void) {
if (dbus_bus_request_name(
server->bus,
AVAHI_DBUS_NAME,
-#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR >= 60)
- DBUS_NAME_FLAG_DO_NOT_QUEUE,
-#else
+#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 60)
DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT,
+#else
+ DBUS_NAME_FLAG_DO_NOT_QUEUE,
#endif
&error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
if (dbus_error_is_set(&error)) {
@@ -1106,11 +1106,6 @@ fail:
dbus_error_free(&error);
if (server->bus) {
-#ifdef HAVE_DBUS_CONNECTION_CLOSE
- dbus_connection_close(server->bus);
-#else
- dbus_connection_disconnect(server->bus);
-#endif
dbus_connection_unref(server->bus);
server->bus = NULL;
}
@@ -1127,11 +1122,6 @@ static void dbus_disconnect(void) {
assert(server->n_clients == 0);
if (server->bus) {
-#ifdef HAVE_DBUS_CONNECTION_CLOSE
- dbus_connection_close(server->bus);
-#else
- dbus_connection_disconnect(server->bus);
-#endif
dbus_connection_unref(server->bus);
server->bus = NULL;
}
@@ -1166,11 +1156,6 @@ int dbus_protocol_setup(const AvahiPoll *poll_api, int _disable_user_service_pub
fail:
if (server->bus) {
-#ifdef HAVE_DBUS_CONNECTION_CLOSE
- dbus_connection_close(server->bus);
-#else
- dbus_connection_disconnect(server->bus);
-#endif
dbus_connection_unref(server->bus);
}
--
cgit
From d69dab67631a912244d2de9a3fd2eb5715f01185 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Fri, 17 Nov 2006 16:28:10 +0000
Subject: Add big note to avahi-discover.in pointing out the import dbus.glib
(as this has been a common problem for people to miss when using
avahi-discover as an example)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1334 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-python/avahi-discover.in | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/avahi-python/avahi-discover.in b/avahi-python/avahi-discover.in
index 5174b5a..53bb2ce 100755
--- a/avahi-python/avahi-discover.in
+++ b/avahi-python/avahi-discover.in
@@ -28,6 +28,10 @@ except ImportError:
print "Sorry, to use this tool you need to install Avahi, pygtk and python-dbus."
sys.exit(1)
+
+## !!NOTE!! ##
+# It's really important to do this, else you won't see any events
+##
try:
import dbus.glib
except ImportError, e:
--
cgit
From a3770ae01c6c0af893af0a36ceecba6ff301c6f4 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Sun, 19 Nov 2006 08:04:07 +0000
Subject: * Fix doxygen comments for avahi watch, thanks to tedp (Closes: #77)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1335 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-common/watch.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/avahi-common/watch.h b/avahi-common/watch.h
index 6d1a36e..80ed888 100644
--- a/avahi-common/watch.h
+++ b/avahi-common/watch.h
@@ -42,10 +42,10 @@ typedef struct AvahiPoll AvahiPoll;
/** Type of watch events */
typedef enum {
- AVAHI_WATCH_IN = POLLIN, /** Input event */
- AVAHI_WATCH_OUT = POLLOUT, /** Output event */
- AVAHI_WATCH_ERR = POLLERR, /** Error event */
- AVAHI_WATCH_HUP = POLLHUP /** Hangup event */
+ AVAHI_WATCH_IN = POLLIN, /**< Input event */
+ AVAHI_WATCH_OUT = POLLOUT, /**< Output event */
+ AVAHI_WATCH_ERR = POLLERR, /**< Error event */
+ AVAHI_WATCH_HUP = POLLHUP /**< Hangup event */
} AvahiWatchEvent;
/** Called whenever an I/O event happens on an I/O watch */
--
cgit
From 37b2be93e63ceff95698f24cd91cb11774eb621c Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Mon, 11 Dec 2006 09:34:00 +0000
Subject: * Revert previous patch to check nlmsg_pid as it is bogus and breaks
in many cases, notably when using NetworkManager * Replace with new
SO_PASSCRED-based check of the sending UID, which seems to work better *
Apply for for 2.6.19+ where IFA_RTA / IFLA_RTA is no longer defined * Mild
fix to some doxygen docs for avahi-common/address.h
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1336 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/iface-linux.c | 9 +++++++++
avahi-common/address.h | 6 +++---
avahi-core/iface-linux.c | 9 +++++++++
avahi-core/netlink.c | 46 ++++++++++++++++++++++++++++++++++++---------
4 files changed, 58 insertions(+), 12 deletions(-)
diff --git a/avahi-autoipd/iface-linux.c b/avahi-autoipd/iface-linux.c
index 6f2ca1f..13d2895 100644
--- a/avahi-autoipd/iface-linux.c
+++ b/avahi-autoipd/iface-linux.c
@@ -40,6 +40,15 @@
#include
#include
+#include
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+
#include "iface.h"
static int fd = -1;
diff --git a/avahi-common/address.h b/avahi-common/address.h
index 6315d31..58e6414 100644
--- a/avahi-common/address.h
+++ b/avahi-common/address.h
@@ -73,9 +73,9 @@ typedef struct AvahiAddress {
AvahiProtocol proto; /**< Address family */
union {
- AvahiIPv6Address ipv6; /** Address when IPv6 */
- AvahiIPv4Address ipv4; /** Address when IPv4 */
- uint8_t data[1]; /** Type independant data field */
+ AvahiIPv6Address ipv6; /**< Address when IPv6 */
+ AvahiIPv4Address ipv4; /**< Address when IPv4 */
+ uint8_t data[1]; /**< Type independant data field */
} data;
} AvahiAddress;
diff --git a/avahi-core/iface-linux.c b/avahi-core/iface-linux.c
index 910000d..a975976 100644
--- a/avahi-core/iface-linux.c
+++ b/avahi-core/iface-linux.c
@@ -30,6 +30,15 @@
#include
+#include
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+
#include "log.h"
#include "iface.h"
#include "iface-linux.h"
diff --git a/avahi-core/netlink.c b/avahi-core/netlink.c
index 893295d..7411c90 100644
--- a/avahi-core/netlink.c
+++ b/avahi-core/netlink.c
@@ -47,27 +47,49 @@ struct AvahiNetlink {
int avahi_netlink_work(AvahiNetlink *nl, int block) {
ssize_t bytes;
+ struct msghdr smsg;
+ struct cmsghdr *cmsg;
+ struct ucred *cred;
+ struct iovec iov;
struct nlmsghdr *p;
+ char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
assert(nl);
-
- if ((bytes = recv(nl->fd, nl->buffer, nl->buffer_length, block ? 0 : MSG_DONTWAIT)) < 0) {
-
+
+ iov.iov_base = nl->buffer;
+ iov.iov_len = nl->buffer_length;
+
+ smsg.msg_name = (void*) NULL;
+ smsg.msg_namelen = 0;
+ smsg.msg_iov = &iov;
+ smsg.msg_iovlen = 1;
+ smsg.msg_control = cred_msg;
+ smsg.msg_controllen = sizeof(cred_msg);
+ smsg.msg_flags = (block ? 0 : MSG_DONTWAIT);
+
+ if ((bytes = recvmsg(nl->fd, &smsg, 0)) < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
- avahi_log_error(__FILE__": recv() failed: %s", strerror(errno));
+ avahi_log_error(__FILE__": recvmsg() failed: %s", strerror(errno));
return -1;
}
- p = (struct nlmsghdr *) nl->buffer;
-
- /* Check that this message originated from the kernel,
- or a request from avahi itself, and not another process */
- if ((p->nlmsg_pid != 0) && (p->nlmsg_pid != getpid())) {
+ cmsg = CMSG_FIRSTHDR(&smsg);
+ cred = (struct ucred *) CMSG_DATA (cmsg);
+
+ if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ avahi_log_error("No sender credentials received, ignoring data.");
return -1;
}
+ if (cred->uid != 0) {
+ avahi_log_warn("Netlink message received from cred->uid != 0 (%d)", cred->uid);
+ return -1;
+ }
+
+ p = (struct nlmsghdr *) nl->buffer;
+
assert(nl->callback);
for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
@@ -94,6 +116,7 @@ static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent
AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, void* userdata), void* userdata) {
int fd = -1;
+ const int on = 1;
struct sockaddr_nl addr;
AvahiNetlink *nl = NULL;
@@ -115,6 +138,11 @@ AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, void
goto fail;
}
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
+ avahi_log_error(__FILE__": bind(): %s", strerror(errno));
+ goto fail;
+ }
+
if (!(nl = avahi_new(AvahiNetlink, 1))) {
avahi_log_error(__FILE__": avahi_new() failed.");
goto fail;
--
cgit
From fb09d84111eaa48462cf49879547f815dd0f08f6 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Mon, 11 Dec 2006 22:35:04 +0000
Subject: * Fix specifying priviliged access group (Closes: #85) Thanks to
delphinidae * Fix incorrect manual reference (Closes: #83) Thanks to
delphinidae * Fix incorrect assert() in client-publish-service.c (Closes:
#79) Thanks to tedp * Fix builds on < 2.6.19 (temporary fix, pending
possible "better" solution)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1337 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/iface-linux.c | 3 ++-
configure.ac | 4 ++--
examples/client-publish-service.c | 2 +-
man/avahi-dnsconfd.8.xml.in | 2 +-
4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/avahi-core/iface-linux.c b/avahi-core/iface-linux.c
index a975976..91fac65 100644
--- a/avahi-core/iface-linux.c
+++ b/avahi-core/iface-linux.c
@@ -30,12 +30,13 @@
#include
-#include
#ifndef IFLA_RTA
+#include
#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
#endif
#ifndef IFA_RTA
+#include
#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#endif
diff --git a/configure.ac b/configure.ac
index fe26066..f2dd5f2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -715,10 +715,10 @@ AC_SUBST(AVAHI_GROUP)
AC_DEFINE_UNQUOTED(AVAHI_GROUP,"$AVAHI_GROUP", [Group for Avahi])
AC_ARG_WITH(avahi_priv_access_group,AS_HELP_STRING([--with-avahi-priv-access-group=],[Priviliged access group for Avahi clients (netdev)]))
-if test -z "$with_priv_access_group" ; then
+if test -z "$with_avahi_priv_access_group" ; then
AVAHI_PRIV_ACCESS_GROUP=netdev
else
- AVAHI_PRIV_ACCESS_GROUP=$with_priv_access_group
+ AVAHI_PRIV_ACCESS_GROUP=$with_avahi_priv_access_group
fi
AC_SUBST(AVAHI_PRIV_ACCESS_GROUP)
AC_DEFINE_UNQUOTED(AVAHI_PRIV_ACCESS_GROUP,"$AVAHI_PRIV_ACCESS_GROUP", [Privileged access group for Avahi clients])
diff --git a/examples/client-publish-service.c b/examples/client-publish-service.c
index 66a59de..94dcd1e 100644
--- a/examples/client-publish-service.c
+++ b/examples/client-publish-service.c
@@ -44,7 +44,7 @@ static char *name = NULL;
static void create_services(AvahiClient *c);
static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
- assert(g == group);
+ assert(g == group || group == NULL);
/* Called whenever the entry group state changes */
diff --git a/man/avahi-dnsconfd.8.xml.in b/man/avahi-dnsconfd.8.xml.in
index 454afac..c74e1ba 100644
--- a/man/avahi-dnsconfd.8.xml.in
+++ b/man/avahi-dnsconfd.8.xml.in
@@ -92,7 +92,7 @@
- ,
+ ,
--
cgit
From ef98a3da45ebbc3bbf9601a3eef1a4eb65fb9a84 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Mon, 11 Dec 2006 22:43:57 +0000
Subject: * Also fix < 2.6.19 builds for avahi-autoipd.
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1338 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/iface-linux.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/avahi-autoipd/iface-linux.c b/avahi-autoipd/iface-linux.c
index 13d2895..2ad8a61 100644
--- a/avahi-autoipd/iface-linux.c
+++ b/avahi-autoipd/iface-linux.c
@@ -40,12 +40,13 @@
#include
#include
-#include
#ifndef IFLA_RTA
+#include
#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
#endif
#ifndef IFA_RTA
+#include
#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#endif
--
cgit
From fc2fc59b512cddcd54448144222de1d4e286e32e Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Tue, 12 Dec 2006 02:12:03 +0000
Subject: * Really actually fix build on Linux <2.6.19
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1339 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/iface-linux.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/avahi-core/iface-linux.c b/avahi-core/iface-linux.c
index 91fac65..c8ed9e0 100644
--- a/avahi-core/iface-linux.c
+++ b/avahi-core/iface-linux.c
@@ -30,6 +30,10 @@
#include
+#include "log.h"
+#include "iface.h"
+#include "iface-linux.h"
+
#ifndef IFLA_RTA
#include
#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
@@ -40,10 +44,6 @@
#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#endif
-#include "log.h"
-#include "iface.h"
-#include "iface-linux.h"
-
static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
struct nlmsghdr *n;
struct rtgenmsg *gen;
--
cgit
From 83b432c2369d7ef4142811bea0109b6588b6f313 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Sat, 16 Dec 2006 14:27:49 +0000
Subject: * Limit the number of loops in consume_labels() (Closes: #84)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1340 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/dns.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/avahi-core/dns.c b/avahi-core/dns.c
index c545c00..fec55e4 100644
--- a/avahi-core/dns.c
+++ b/avahi-core/dns.c
@@ -332,9 +332,10 @@ static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_
int ret = 0;
int compressed = 0;
int first_label = 1;
+ int i;
assert(p && ret_name && l);
- for (;;) {
+ for (i = 0; i < 127; i++) {
uint8_t n;
if (idx+1 > p->size)
--
cgit
From 451be30b300b390b46738af3fd24fc942ba3158f Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Sat, 16 Dec 2006 15:00:11 +0000
Subject: * avahi-core/dns.c: Use a '#define AVAHI_DNS_LABELS_MAX 127' instead
of the hard-coded value
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1341 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/dns.c | 2 +-
avahi-core/dns.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/avahi-core/dns.c b/avahi-core/dns.c
index fec55e4..a3ec0e6 100644
--- a/avahi-core/dns.c
+++ b/avahi-core/dns.c
@@ -335,7 +335,7 @@ static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_
int i;
assert(p && ret_name && l);
- for (i = 0; i < 127; i++) {
+ for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) {
uint8_t n;
if (idx+1 > p->size)
diff --git a/avahi-core/dns.h b/avahi-core/dns.h
index 4696e00..d1c06a5 100644
--- a/avahi-core/dns.h
+++ b/avahi-core/dns.h
@@ -28,6 +28,7 @@
#define AVAHI_DNS_PACKET_SIZE_MAX 9000
#define AVAHI_DNS_PACKET_HEADER_SIZE 12
#define AVAHI_DNS_PACKET_EXTRA_SIZE 48
+#define AVAHI_DNS_LABELS_MAX 127
typedef struct AvahiDnsPacket {
size_t size, rindex, max_size;
--
cgit
From 5ed5452823a5da7d7a7b36d5a42d3edc09f2342f Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Sat, 16 Dec 2006 15:10:48 +0000
Subject: * avahi-core/dns.c#consume_labels(): Add some additional checks on
the compressed label location
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1342 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/dns.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/avahi-core/dns.c b/avahi-core/dns.c
index a3ec0e6..b31aa20 100644
--- a/avahi-core/dns.c
+++ b/avahi-core/dns.c
@@ -332,6 +332,7 @@ static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_
int ret = 0;
int compressed = 0;
int first_label = 1;
+ unsigned label_ptr;
int i;
assert(p && ret_name && l);
@@ -385,7 +386,12 @@ static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_
if (idx+2 > p->size)
return -1;
- idx = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1];
+ label_ptr = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1];
+
+ if ((label_ptr < AVAHI_DNS_PACKET_HEADER_SIZE) || (label_ptr >= idx))
+ return -1;
+
+ idx = label_ptr;
if (!compressed)
ret += 2;
--
cgit
From 7a237191fe9521c625dee2f09accb7574edd6b4f Mon Sep 17 00:00:00 2001
From: Sebastien Estienne
Date: Sun, 17 Dec 2006 11:22:17 +0000
Subject: display a graphical error message when the deamon is not running
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1343 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-python/avahi-discover.in | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/avahi-python/avahi-discover.in b/avahi-python/avahi-discover.in
index 53bb2ce..ce7ecf3 100755
--- a/avahi-python/avahi-discover.in
+++ b/avahi-python/avahi-discover.in
@@ -24,8 +24,8 @@ import os, sys
try:
import avahi, gtk, gobject, dbus, avahi.ServiceTypeDatabase
from avahi.SimpleGladeApp import SimpleGladeApp
-except ImportError:
- print "Sorry, to use this tool you need to install Avahi, pygtk and python-dbus."
+except ImportError, e:
+ print "Sorry, to use this tool you need to install Avahi, pygtk and python-dbus.\n Error: %s" % e
sys.exit(1)
@@ -33,6 +33,8 @@ except ImportError:
# It's really important to do this, else you won't see any events
##
try:
+from dbus.dbus_bindings import DBusException
+
import dbus.glib
except ImportError, e:
pass
@@ -40,6 +42,14 @@ except ImportError, e:
service_type_browsers = {}
service_browsers = {}
+def error_msg(msg):
+ d = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL,
+ type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK)
+ d.set_markup(msg)
+ d.show_all()
+ d.run()
+ d.destroy()
+
glade_dir = "@interfacesdir@"
service_type_db = avahi.ServiceTypeDatabase.ServiceTypeDatabase()
@@ -162,8 +172,14 @@ class Main_window(SimpleGladeApp):
if self.stype is None:
print "Browsing domain '%s' on %i.%i ..." % (domain, interface, protocol)
-
- b = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.ServiceTypeBrowserNew(interface, protocol, domain, dbus.UInt32(0))), avahi.DBUS_INTERFACE_SERVICE_TYPE_BROWSER)
+
+ try:
+ b = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.ServiceTypeBrowserNew(interface, protocol, domain, dbus.UInt32(0))), avahi.DBUS_INTERFACE_SERVICE_TYPE_BROWSER)
+ except DBusException, e:
+ print e
+ error_msg("You should check that the avahi daemon is running.\n\nError : %s" % e)
+ sys.exit(0)
+
b.connect_to_signal('ItemNew', self.new_service_type)
service_type_browsers[(interface, protocol, domain)] = b
--
cgit
From c9109b4b6f4eca16ad867d91bd6683ac6355be76 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Thu, 28 Dec 2006 15:45:29 +0000
Subject: * Gracefully handle some conditions in libdns_sd that Bonjour
handles (Closes #64)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1345 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-compat-libdns_sd/compat.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/avahi-compat-libdns_sd/compat.c b/avahi-compat-libdns_sd/compat.c
index e6b6042..9dc2281 100644
--- a/avahi-compat-libdns_sd/compat.c
+++ b/avahi-compat-libdns_sd/compat.c
@@ -610,10 +610,13 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse(
struct type_info type_info;
AVAHI_WARN_LINKAGE;
-
- assert(ret_sdref);
+
+ if (!ret_sdref)
+ return kDNSServiceErr_BadParam;
+
+ *ret_sdref = NULL;
+
assert(regtype);
- assert(callback);
if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
AVAHI_WARN_UNSUPPORTED;
@@ -1091,8 +1094,14 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister (
AVAHI_WARN_LINKAGE;
- assert(ret_sdref);
- assert(regtype);
+ if (!ret_sdref)
+ return kDNSServiceErr_BadParam;
+
+ *ret_sdref = NULL;
+
+ if (!regtype)
+ return kDNSServiceErr_BadParam;
+
assert(txtRecord || txtLen == 0);
if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) {
--
cgit
From 4ac8f75c6bde11d61818664d919332111e885c0f Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Thu, 28 Dec 2006 16:17:11 +0000
Subject: * Bump SONAME version of libdns_sd * Bump SONAME version of
avahi-core (Closes #76)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1346 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac
index f2dd5f2..8d9f535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,12 +29,12 @@ AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
AC_SUBST(PACKAGE_URL, [http://avahi.org/])
AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [7:3:4])
-AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [4:4:0])
+AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [4:5:0])
AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:1:2])
AC_SUBST(LIBAVAHI_GLIB_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT3_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT4_VERSION_INFO, [1:1:0])
-AC_SUBST(LIBAVAHI_COMPAT_LIBDNS_SD_VERSION_INFO, [1:0:0])
+AC_SUBST(LIBAVAHI_COMPAT_LIBDNS_SD_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_COMPAT_HOWL_VERSION_INFO, [0:0:0])
AC_SUBST(HOWL_COMPAT_VERSION, [0.9.8])
--
cgit
From 2c34e9d4eebf3b051ed69ceb7977402af267d7c9 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Thu, 28 Dec 2006 16:24:08 +0000
Subject: * Update NEWS for 0.6.16
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1347 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
docs/NEWS | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/docs/NEWS b/docs/NEWS
index f6d04c6..fd37d42 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -1,3 +1,28 @@
+Avahi 0.6.16
+============
+
+This is a bugfix release, it fixes one DoS (100% CPU Usage) and a regression
+from the last release
+
+ * Revert previous patch to check nlmsg_pid as it is bogus and breaks in
+ many cases, notably when using NetworkManager (Closes: #72)
+ * Replace with new SO_PASSCRED-based check of the sending UID, which
+ seems to work better (Closes: #72)
+ * Handle some errors in libdns_sd more gracefully the way the real
+ libdns_sd does (Closes: #64)
+ * Apply fix for Linux 2.6.19+ where IFA_RTA / IFLA_RTA is no longer
+ defined (Closes: #86)
+ * Fix doxygen comments for avahi watch, thanks to tedp (Closes: #77)
+ * Make d-bus version detection work for >= 1.0 (Closes: #71)
+ * Dont dbus_connection_close on shared dbus connections (Closes: #68)
+ * Fix potential endless loop in dns label unpacking code (Closes: #84)
+ * Fix bogus assertion in client-publish-service.c example
+ * Mild fix to some doxygen docs for avahi-common/address.h
+ * Fix passing in custom priviledged group (previously ignored setting)
+ (Closes: #85)
+
+This release is backwards compatible with Avahi 0.6.x with x < 16.
+
Avahi 0.6.15
============
--
cgit
From 1f96e5a155d9d745e675bdc9e4233d7aa8932784 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Thu, 28 Dec 2006 16:32:11 +0000
Subject: * Bump version to 0.6.16
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1348 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 8d9f535..56ab6aa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,7 +21,7 @@
# USA.
AC_PREREQ(2.57)
-AC_INIT([avahi],[0.6.15],[avahi (at) lists (dot) freedesktop (dot) org])
+AC_INIT([avahi],[0.6.16],[avahi (at) lists (dot) freedesktop (dot) org])
AC_CONFIG_SRCDIR([avahi-core/server.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
--
cgit
From 6bdb3f092006900475384b935b91624561095412 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Thu, 28 Dec 2006 16:36:11 +0000
Subject: * Fix avahi-discover broken in an earlier commit
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1349 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-python/avahi-discover.in | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/avahi-python/avahi-discover.in b/avahi-python/avahi-discover.in
index ce7ecf3..bb02dcd 100755
--- a/avahi-python/avahi-discover.in
+++ b/avahi-python/avahi-discover.in
@@ -33,8 +33,7 @@ except ImportError, e:
# It's really important to do this, else you won't see any events
##
try:
-from dbus.dbus_bindings import DBusException
-
+ from dbus.dbus_bindings import DBusException
import dbus.glib
except ImportError, e:
pass
--
cgit
From 13c8a680f9685baba470123aa7bee015cb890438 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Thu, 28 Dec 2006 16:54:26 +0000
Subject: * Import debian init script changes from debian
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1350 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
initscript/debian/avahi-daemon.in | 9 +++++++++
initscript/debian/avahi-dnsconfd.in | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/initscript/debian/avahi-daemon.in b/initscript/debian/avahi-daemon.in
index 3a68781..553db78 100755
--- a/initscript/debian/avahi-daemon.in
+++ b/initscript/debian/avahi-daemon.in
@@ -83,10 +83,19 @@ PATH=/sbin:/bin:/usr/sbin:/usr/bin
DESC="Avahi mDNS/DNS-SD Daemon"
NAME="avahi-daemon"
DAEMON="@sbindir@/$NAME"
+SCRIPTNAME=/etc/init.d/$NAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
+# don't start if /etc/default/avahi-daemon says so.
+AVAHI_DAEMON_START=1
+test -f /etc/default/avahi-daemon && . /etc/default/avahi-daemon
+
+if [ "$AVAHI_DAEMON_START" != "1" -a "$1" != "stop" ]; then
+ exit 0
+fi
+
#
# Function that starts the daemon/service.
#
diff --git a/initscript/debian/avahi-dnsconfd.in b/initscript/debian/avahi-dnsconfd.in
index c7cbf05..ee63e06 100755
--- a/initscript/debian/avahi-dnsconfd.in
+++ b/initscript/debian/avahi-dnsconfd.in
@@ -83,10 +83,19 @@ PATH=/sbin:/bin:/usr/sbin:/usr/bin
DESC="Avahi Unicast DNS Configuration Daemon"
NAME="avahi-dnsconfd"
DAEMON="@sbindir@/$NAME"
+SCRIPTNAME=/etc/init.d/$NAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
+# don't start if /etc/default/avahi-dnsconfd says so.
+AVAHI_DNSCONFD_START=1
+test -f /etc/default/avahi-dnsconfd && . /etc/default/avahi-dnsconfd
+
+if [ "$AVAHI_DNSCONFD_START" != "1" -a "$1" != "stop" ]; then
+ exit 0
+fi
+
#
# Function that starts the daemon/service.
#
--
cgit
From 28ff2f3f8b16f779a4945a8d4548a03a942d530c Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Fri, 29 Dec 2006 00:39:11 +0000
Subject: * Correct comment reference to the priviledged access group in
avahi-dbus.conf - thanks to Olivier Blin
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1352 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/avahi-dbus.conf.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/avahi-daemon/avahi-dbus.conf.in b/avahi-daemon/avahi-dbus.conf.in
index c570754..e439e07 100644
--- a/avahi-daemon/avahi-dbus.conf.in
+++ b/avahi-daemon/avahi-dbus.conf.in
@@ -19,7 +19,7 @@
-
+
--
cgit
From 6e9695a1e6a5f6a70195b6de37448aaf9cde8e30 Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Fri, 29 Dec 2006 12:50:03 +0000
Subject: * Make sure we return -1 if we fall of the end of consume_labels()
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1353 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/dns.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/avahi-core/dns.c b/avahi-core/dns.c
index b31aa20..6817ebd 100644
--- a/avahi-core/dns.c
+++ b/avahi-core/dns.c
@@ -400,6 +400,8 @@ static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_
} else
return -1;
}
+
+ return -1;
}
int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) {
--
cgit
From b292e2016d1530df667f391e535e9b5c0599ccea Mon Sep 17 00:00:00 2001
From: Trent Lloyd
Date: Fri, 29 Dec 2006 12:55:23 +0000
Subject: * Un-bump DNS-SD soname, we are following apple's version so we
shouldn't bump it (woops)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1354 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 56ab6aa..91931ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,7 +34,7 @@ AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:1:2])
AC_SUBST(LIBAVAHI_GLIB_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT3_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT4_VERSION_INFO, [1:1:0])
-AC_SUBST(LIBAVAHI_COMPAT_LIBDNS_SD_VERSION_INFO, [1:1:0])
+AC_SUBST(LIBAVAHI_COMPAT_LIBDNS_SD_VERSION_INFO, [1:0:0])
AC_SUBST(LIBAVAHI_COMPAT_HOWL_VERSION_INFO, [0:0:0])
AC_SUBST(HOWL_COMPAT_VERSION, [0.9.8])
--
cgit
From 92cef261ed05f8f5ede25d3bd352ee88a9f18185 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 15:08:29 +0000
Subject: add a comment so that #87 is not repeated
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1355 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 2 ++
1 file changed, 2 insertions(+)
diff --git a/configure.ac b/configure.ac
index 91931ce..f3b9e41 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,8 @@ AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:1:2])
AC_SUBST(LIBAVAHI_GLIB_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT3_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT4_VERSION_INFO, [1:1:0])
+
+# Do not touch these, since they we took this version-info from upstream HOWL/Bonjour
AC_SUBST(LIBAVAHI_COMPAT_LIBDNS_SD_VERSION_INFO, [1:0:0])
AC_SUBST(LIBAVAHI_COMPAT_HOWL_VERSION_INFO, [0:0:0])
AC_SUBST(HOWL_COMPAT_VERSION, [0.9.8])
--
cgit
From 28eb71adcf582a728d6ae7d00f7afbf8bd4a38c1 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 15:44:16 +0000
Subject: Check for a working C++ compiler in configure.ac (Closes #75) Thanks,
tedp!
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1356 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/configure.ac b/configure.ac
index f3b9e41..89477e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,6 +58,16 @@ AC_PROG_GCC_TRADITIONAL
# libtool stuff
AC_PROG_LIBTOOL
+AC_CACHE_CHECK([whether the C++ compiler works], [avahi_cv_sys_cxx_works], [
+ AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([int main() { }], [avahi_cv_sys_cxx_works=yes],
+ [avahi_cv_sys_cxx_works=no])
+ AC_LANG_POP([C++])
+ ])
+[ if [ "x$avahi_cv_sys_cxx_works" = "xno" ]; then ]
+ AC_MSG_FAILURE([The C++ compiler does not work])
+[ fi ]
+
ACX_PTHREAD(,AC_MSG_ERROR([Missing POSIX Threads support]))
#
--
cgit
From 8b792d513254e334b7ead4e47dd3f37b23b06e77 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 16:39:16 +0000
Subject: Don't use dbus_message_iter_get_array_len() since it is obsolete
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1357 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/dbus-util.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/avahi-daemon/dbus-util.c b/avahi-daemon/dbus-util.c
index c9ef949..d1a7a20 100644
--- a/avahi-daemon/dbus-util.c
+++ b/avahi-daemon/dbus-util.c
@@ -341,12 +341,9 @@ int avahi_dbus_read_strlst(DBusMessage *m, int idx, AvahiStringList **l) {
dbus_message_iter_recurse(&sub, &sub2);
- if (dbus_message_iter_get_array_len(&sub2) > 0)
- dbus_message_iter_get_fixed_array(&sub2, &k, &n);
- else {
- k = (const uint8_t*) "";
- n = 0;
- }
+ k = (const uint8_t*) "";
+ n = 0;
+ dbus_message_iter_get_fixed_array(&sub2, &k, &n);
strlst = avahi_string_list_add_arbitrary(strlst, k, n);
--
cgit
From 8bc7858318a61d998527212e0948a3c469922105 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 16:52:13 +0000
Subject: Fix error message when passing an invalid command line option (Closes
#88)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1358 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-autoipd/main.c | 2 +-
avahi-daemon/main.c | 2 +-
avahi-dnsconfd/main.c | 2 +-
avahi-utils/avahi-browse.c | 2 +-
avahi-utils/avahi-publish.c | 2 +-
avahi-utils/avahi-resolve.c | 2 +-
avahi-utils/avahi-set-host-name.c | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index ff2cfee..60fc8aa 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -1259,7 +1259,7 @@ static int parse_command_line(int argc, char *argv[]) {
#endif
default:
- fprintf(stderr, "Invalid command line argument: %c\n", c);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index 6d8abb1..33f1265 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -426,7 +426,7 @@ static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
c->debug = 1;
break;
default:
- fprintf(stderr, "Invalid command line argument: %c\n", o);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
diff --git a/avahi-dnsconfd/main.c b/avahi-dnsconfd/main.c
index d2fcf71..1f3e3b8 100644
--- a/avahi-dnsconfd/main.c
+++ b/avahi-dnsconfd/main.c
@@ -419,7 +419,7 @@ static int parse_command_line(int argc, char *argv[]) {
command = DAEMON_CHECK;
break;
default:
- fprintf(stderr, "Invalid command line argument: %c\n", c);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
diff --git a/avahi-utils/avahi-browse.c b/avahi-utils/avahi-browse.c
index 248b651..faa8f8a 100644
--- a/avahi-utils/avahi-browse.c
+++ b/avahi-utils/avahi-browse.c
@@ -686,7 +686,7 @@ static int parse_command_line(Config *c, const char *argv0, int argc, char *argv
break;
#endif
default:
- fprintf(stderr, "Invalid command line argument: %c\n", o);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
diff --git a/avahi-utils/avahi-publish.c b/avahi-utils/avahi-publish.c
index 77dc422..4edca6f 100644
--- a/avahi-utils/avahi-publish.c
+++ b/avahi-utils/avahi-publish.c
@@ -290,7 +290,7 @@ static int parse_command_line(Config *c, const char *argv0, int argc, char *argv
c->subtypes = avahi_string_list_add(c->subtypes, optarg);
break;
default:
- fprintf(stderr, "Invalid command line argument: %c\n", o);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
diff --git a/avahi-utils/avahi-resolve.c b/avahi-utils/avahi-resolve.c
index 9b1d517..fe01ae5 100644
--- a/avahi-utils/avahi-resolve.c
+++ b/avahi-utils/avahi-resolve.c
@@ -212,7 +212,7 @@ static int parse_command_line(Config *c, const char *argv0, int argc, char *argv
c->proto = AVAHI_PROTO_INET6;
break;
default:
- fprintf(stderr, "Invalid command line argument: %c\n", o);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
diff --git a/avahi-utils/avahi-set-host-name.c b/avahi-utils/avahi-set-host-name.c
index 06f5322..8672e4e 100644
--- a/avahi-utils/avahi-set-host-name.c
+++ b/avahi-utils/avahi-set-host-name.c
@@ -107,7 +107,7 @@ static int parse_command_line(Config *c, int argc, char *argv[]) {
c->verbose = 1;
break;
default:
- fprintf(stderr, "Invalid command line argument: %c\n", o);
+ fprintf(stderr, "Invalid command line argument: %s\n", argv[optind-1]);
return -1;
}
}
--
cgit
From 9099211ddf545d21aca5177f94a99f9ef49707b2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 17:13:39 +0000
Subject: Fix documentation of avahi_{entry_group|client}_new(): clarify that
the specified callback function is called for the first time from withing the
avahi_xxx_new() context. (Closes #65)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1359 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-client/client.h | 2 +-
avahi-client/publish.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/avahi-client/client.h b/avahi-client/client.h
index f102f05..6a12e9a 100644
--- a/avahi-client/client.h
+++ b/avahi-client/client.h
@@ -62,7 +62,7 @@ typedef void (*AvahiClientCallback) (
AvahiClient* avahi_client_new (
const AvahiPoll *poll_api /**< The abstract event loop API to use */,
AvahiClientFlags flags /**< Some flags to modify the behaviour of the client library */,
- AvahiClientCallback callback /**< A callback that is called whenever the state of the client changes. This may be NULL */,
+ AvahiClientCallback callback /**< A callback that is called whenever the state of the client changes. This may be NULL. Please note that this function is called for the first time from within the avahi_client_new() context! Thus, in the callback you should not make use of global variables that are initialized only after your call to avahi_client_new(). A common mistake is to store the AvahiClient pointer returned by avahi_client_new() in a global variable and assume that this global variable already contains the valid pointer when the callback is called for the first time. A work-around for this is to always use the AvahiClient pointer passed to the callback function instead of the global pointer. */,
void *userdata /**< Some arbitrary user data pointer that will be passed to the callback function */,
int *error /**< If creation of the client fails, this integer will contain the error cause. May be NULL if you aren't interested in the reason why avahi_client_new() failed. */);
diff --git a/avahi-client/publish.h b/avahi-client/publish.h
index 053ea88..98f4f98 100644
--- a/avahi-client/publish.h
+++ b/avahi-client/publish.h
@@ -53,7 +53,7 @@ typedef void (*AvahiEntryGroupCallback) (
/** Create a new AvahiEntryGroup object */
AvahiEntryGroup* avahi_entry_group_new(
AvahiClient* c,
- AvahiEntryGroupCallback callback /**< This callback is called whenever the state of this entry group changes. May not be NULL. */,
+ AvahiEntryGroupCallback callback /**< This callback is called whenever the state of this entry group changes. May not be NULL. Please note that this function is called for the first time from within the avahi_entry_group_new() context! Thus, in the callback you should not make use of global variables that are initialized only after your call to avahi_entry_group_new(). A common mistake is to store the AvahiEntryGroup pointer returned by avahi_entry_group_new() in a global variable and assume that this global variable already contains the valid pointer when the callback is called for the first time. A work-around for this is to always use the AvahiEntryGroup pointer passed to the callback function instead of the global pointer. */,
void *userdata /**< This arbitrary user data pointer will be passed to the callback functon */);
/** Clean up and free an AvahiEntryGroup object */
--
cgit
From a66c19d9111dc950aac47b7becaddf1b0814f251 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 17:19:30 +0000
Subject: remove yet another occurence of dbus_message_iter_get_array_len() due
to obsolescence (similar in style to r1357)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1360 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-client/resolver.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/avahi-client/resolver.c b/avahi-client/resolver.c
index 2d9f808..4403a12 100644
--- a/avahi-client/resolver.c
+++ b/avahi-client/resolver.c
@@ -106,6 +106,8 @@ DBusHandlerResult avahi_service_resolver_event (AvahiClient *client, AvahiResolv
for (;;) {
DBusMessageIter sub2;
int at;
+ const uint8_t *k;
+ int n;
if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
break;
@@ -119,13 +121,10 @@ DBusHandlerResult avahi_service_resolver_event (AvahiClient *client, AvahiResolv
dbus_message_iter_recurse(&sub, &sub2);
- if (dbus_message_iter_get_array_len(&sub2) > 0) {
- uint8_t *k;
- int n;
-
- dbus_message_iter_get_fixed_array(&sub2, &k, &n);
+ k = NULL; n = 0;
+ dbus_message_iter_get_fixed_array(&sub2, &k, &n);
+ if (k && n > 0)
strlst = avahi_string_list_add_arbitrary(strlst, k, n);
- }
dbus_message_iter_next(&sub);
}
--
cgit
From c3ed8d543014877ec1a5cd2ab0fbef79c03c0467 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sun, 31 Dec 2006 17:33:57 +0000
Subject: Ignore EAGAIN errors on recvmsg() (Closes #60)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1361 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/socket.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/avahi-core/socket.c b/avahi-core/socket.c
index 9291f08..9ca1437 100644
--- a/avahi-core/socket.c
+++ b/avahi-core/socket.c
@@ -635,6 +635,9 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_add
goto fail;
}
+ if (ms <= 0)
+ goto fail;
+
p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
@@ -650,7 +653,14 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_add
msg.msg_flags = 0;
if ((l = recvmsg(fd, &msg, 0)) < 0) {
- avahi_log_warn("recvmsg(): %s", strerror(errno));
+ /* Linux returns EAGAIN when an invalid IP packet has been
+ recieved. We suppress warnings in this case because this might
+ create quite a bit of log traffic on machines with unstable
+ links. (See #60) */
+
+ if (errno != EAGAIN)
+ avahi_log_warn("recvmsg(): %s", strerror(errno));
+
goto fail;
}
@@ -768,6 +778,9 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_add
avahi_log_warn("ioctl(): %s", strerror(errno));
goto fail;
}
+
+ if (ms <= 0)
+ goto fail;
p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
@@ -785,7 +798,14 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_add
msg.msg_flags = 0;
if ((l = recvmsg(fd, &msg, 0)) < 0) {
- avahi_log_warn("recvmsg(): %s", strerror(errno));
+ /* Linux returns EAGAIN when an invalid IP packet has been
+ recieved. We suppress warnings in this case because this might
+ create quite a bit of log traffic on machines with unstable
+ links. (See #60) */
+
+ if (errno != EAGAIN)
+ avahi_log_warn("recvmsg(): %s", strerror(errno));
+
goto fail;
}
--
cgit
From 53759b345814d36f75e2a534113e3b1cd0120f08 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Mon, 1 Jan 2007 11:30:33 +0000
Subject: Modify C++ testing code to pass when CXXFLAGS contains -Werror.
(Closes #75) (BTW, -Werror is a call for trouble anyway due to differring gcc
versions)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1362 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 89477e3..bb44c52 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,7 +60,7 @@ AC_PROG_LIBTOOL
AC_CACHE_CHECK([whether the C++ compiler works], [avahi_cv_sys_cxx_works], [
AC_LANG_PUSH([C++])
- AC_COMPILE_IFELSE([int main() { }], [avahi_cv_sys_cxx_works=yes],
+ AC_COMPILE_IFELSE([int main() { return 0; }], [avahi_cv_sys_cxx_works=yes],
[avahi_cv_sys_cxx_works=no])
AC_LANG_POP([C++])
])
--
cgit
From 3b2233262bae52823c141b53afb2df77841aad64 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Thu, 4 Jan 2007 23:53:39 +0000
Subject: Some more ia64 fixes. (Closes #90)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1363 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/socket.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/avahi-core/socket.c b/avahi-core/socket.c
index 9ca1437..febb20c 100644
--- a/avahi-core/socket.c
+++ b/avahi-core/socket.c
@@ -475,10 +475,10 @@ int avahi_send_dns_packet_ipv4(
struct iovec io;
#ifdef IP_PKTINFO
struct cmsghdr *cmsg;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in_pktinfo))];
+ size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
#elif defined(IP_SENDSRCADDR)
struct cmsghdr *cmsg;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in_addr))];
+ size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1];
#endif
assert(fd >= 0);
@@ -565,7 +565,7 @@ int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex interface, AvahiDnsPacket *p
struct msghdr msg;
struct iovec io;
struct cmsghdr *cmsg;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
assert(fd >= 0);
assert(p);
--
cgit
From 1d4f819072e901518309aff781f9f1e7272dca87 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 5 Jan 2007 00:21:19 +0000
Subject: Rework DBus connection handling: use a private DBusConnection instead
of a shared instance. (Closes #89)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1364 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/Makefile.am | 2 +-
avahi-daemon/dbus-protocol.c | 44 +++++++++++++++++++++++++++++++++++++++++---
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/avahi-daemon/Makefile.am b/avahi-daemon/Makefile.am
index 973a2b5..02be288 100644
--- a/avahi-daemon/Makefile.am
+++ b/avahi-daemon/Makefile.am
@@ -115,7 +115,7 @@ avahi_daemon_SOURCES += \
avahi_daemon_LDADD += \
$(DBUS_LIBS)
-avahi_daemon_CFLAGS += $(DBUS_CFLAGS)
+avahi_daemon_CFLAGS += $(DBUS_CFLAGS) -DDBUS_SYSTEM_BUS_DEFAULT_ADDRESS=\"$(DBUS_SYSTEM_BUS_DEFAULT_ADDRESS)\"
dbusservice_DATA = avahi-dbus.conf
diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c
index caa2e19..9bfc151 100644
--- a/avahi-daemon/dbus-protocol.c
+++ b/avahi-daemon/dbus-protocol.c
@@ -1051,12 +1051,34 @@ static int dbus_connect(void) {
assert(!server->bus);
dbus_error_init(&error);
-
- if (!(server->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+
+#ifdef HAVE_DBUS_BUS_GET_PRIVATE
+ if (!(server->bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
assert(dbus_error_is_set(&error));
- avahi_log_error("dbus_bus_get(): %s", error.message);
+ avahi_log_error("dbus_bus_get_private(): %s", error.message);
goto fail;
}
+#else
+ {
+ const char *a;
+
+ if (!(a = getenv("DBUS_SYSTEM_BUS_ADDRESS")) || !*a)
+ a = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
+
+ if (!(server->bus = dbus_connection_open_private(a, &error))) {
+ assert(dbus_error_is_set(&error));
+ avahi_log_error("dbus_bus_open_private(): %s", error.message);
+ goto fail;
+ }
+
+ if (!dbus_bus_register(server->bus, &error)) {
+ assert(dbus_error_is_set(&error));
+ avahi_log_error("dbus_bus_register(): %s", error.message);
+ goto fail;
+ }
+ }
+#endif
+
if (avahi_dbus_connection_glue(server->bus, server->poll_api) < 0) {
avahi_log_error("avahi_dbus_connection_glue() failed");
goto fail;
@@ -1106,6 +1128,11 @@ fail:
dbus_error_free(&error);
if (server->bus) {
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
+ dbus_connection_close(server->bus);
+#else
+ dbus_connection_disconnect(server->bus);
+#endif
dbus_connection_unref(server->bus);
server->bus = NULL;
}
@@ -1122,6 +1149,11 @@ static void dbus_disconnect(void) {
assert(server->n_clients == 0);
if (server->bus) {
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
+ dbus_connection_close(server->bus);
+#else
+ dbus_connection_disconnect(server->bus);
+#endif
dbus_connection_unref(server->bus);
server->bus = NULL;
}
@@ -1156,6 +1188,12 @@ int dbus_protocol_setup(const AvahiPoll *poll_api, int _disable_user_service_pub
fail:
if (server->bus) {
+#ifdef HAVE_DBUS_CONNECTION_CLOSE
+ dbus_connection_close(server->bus);
+#else
+ dbus_connection_disconnect(server->bus);
+#endif
+
dbus_connection_unref(server->bus);
}
--
cgit
From 1c24d17c1001240306e054ea534c3aa69a45604b Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 5 Jan 2007 22:18:34 +0000
Subject: * optionally, register A RR via IPv6, AAAA RR via IPv4 (Closes #62) *
add new API function avahi_server_get_config()
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1365 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/announce.c | 2 +-
avahi-core/core.h | 5 ++++
avahi-core/iface.c | 77 ++++++++++++++++++++++++++++-----------------------
avahi-core/server.c | 14 ++++++++--
4 files changed, 59 insertions(+), 39 deletions(-)
diff --git a/avahi-core/announce.c b/avahi-core/announce.c
index 4a87e1d..a77fbb6 100644
--- a/avahi-core/announce.c
+++ b/avahi-core/announce.c
@@ -498,7 +498,7 @@ void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye
assert(i);
if (send_goodbye)
- if (avahi_interface_is_relevant(i)) {
+ if (i->announcing) {
AvahiEntry *e;
for (e = s->entries; e; e = e->entries_next)
diff --git a/avahi-core/core.h b/avahi-core/core.h
index e73abd2..c8e6fd6 100644
--- a/avahi-core/core.h
+++ b/avahi-core/core.h
@@ -63,6 +63,8 @@ typedef struct AvahiServerConfig {
AvahiStringList *browse_domains; /**< Additional browsing domains */
int disable_publishing; /**< Disable publishing of any record */
int allow_point_to_point; /**< Enable publishing on POINTOPOINT interfaces */
+ int publish_a_on_ipv6; /**< Publish an IPv4 A RR on IPv6 sockets */
+ int publish_aaaa_on_ipv4; /**< Publish an IPv4 A RR on IPv6 sockets */
} AvahiServerConfig;
/** Allocate a new mDNS responder object. */
@@ -148,6 +150,9 @@ uint32_t avahi_server_get_local_service_cookie(AvahiServer *s);
/** Set the wide area DNS servers */
int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n);
+/** Return the current configuration of the server \since 0.6.17 */
+const AvahiServerConfig* avahi_server_get_config(AvahiServer *s);
+
AVAHI_C_DECL_END
#endif
diff --git a/avahi-core/iface.c b/avahi-core/iface.c
index 5685618..9fc8cb3 100644
--- a/avahi-core/iface.c
+++ b/avahi-core/iface.c
@@ -50,9 +50,9 @@ void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs
assert(a);
m = a->monitor;
- if (a->interface->announcing &&
- m->list_complete &&
+ if (m->list_complete &&
avahi_interface_address_is_relevant(a) &&
+ avahi_interface_is_relevant(a->interface) &&
!remove_rrs &&
m->server->config.publish_addresses &&
(m->server->state == AVAHI_SERVER_RUNNING ||
@@ -67,11 +67,15 @@ void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs
if (avahi_s_entry_group_is_empty(a->entry_group)) {
char t[AVAHI_ADDRESS_STR_MAX];
- avahi_address_snprint(t, sizeof(t), &a->address);
+ AvahiProtocol p;
- avahi_log_info("Registering new address record for %s on %s.", t, a->interface->hardware->name);
+ p = (a->interface->protocol == AVAHI_PROTO_INET && m->server->config.publish_a_on_ipv6) ||
+ (a->interface->protocol == AVAHI_PROTO_INET6 && m->server->config.publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : a->interface->protocol;
+
+ avahi_address_snprint(t, sizeof(t), &a->address);
+ avahi_log_info("Registering new address record for %s on %s.%s.", t, a->interface->hardware->name, p == AVAHI_PROTO_UNSPEC ? "*" : avahi_proto_to_string(p));
- if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, a->interface->protocol, 0, NULL, &a->address) < 0) {
+ if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, p, 0, NULL, &a->address) < 0) {
avahi_log_warn(__FILE__": avahi_server_add_address() failed: %s", avahi_strerror(m->server->error));
avahi_s_entry_group_free(a->entry_group);
a->entry_group = NULL;
@@ -171,6 +175,10 @@ static int interface_mdns_mcast_join(AvahiInterface *i, int join) {
if (!!join == !!i->mcast_joined)
return 0;
+
+ if ((i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 < 0) ||
+ (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 < 0))
+ return -1;
if (join) {
AvahiInterfaceAddress *a;
@@ -186,10 +194,8 @@ static int interface_mdns_mcast_join(AvahiInterface *i, int join) {
a = i->addresses;
/* Hmm, there is no address available. */
- if (!a) {
- avahi_log_warn(__FILE__": interface_mdns_mcast_join() called but no local address available.");
+ if (!a)
return -1;
- }
i->local_mcast_address = a->address;
}
@@ -387,9 +393,9 @@ AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex
avahi_hashmap_insert(m->hashmap, &hw->index, hw);
- if (m->server->fd_ipv4 >= 0)
+ if (m->server->fd_ipv4 >= 0 || m->server->config.publish_a_on_ipv6)
avahi_interface_new(m, hw, AVAHI_PROTO_INET);
- if (m->server->fd_ipv6 >= 0)
+ if (m->server->fd_ipv6 >= 0 || m->server->config.publish_aaaa_on_ipv4)
avahi_interface_new(m, hw, AVAHI_PROTO_INET6);
return hw;
@@ -426,13 +432,16 @@ void avahi_interface_check_relevant(AvahiInterface *i) {
b = avahi_interface_is_relevant(i);
if (m->list_complete && b && !i->announcing) {
- avahi_log_info("New relevant interface %s.%s for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
-
interface_mdns_mcast_join(i, 1);
- i->announcing = 1;
- avahi_announce_interface(m->server, i);
- avahi_multicast_lookup_engine_new_interface(m->server->multicast_lookup_engine, i);
+ if (i->mcast_joined) {
+ avahi_log_info("New relevant interface %s.%s for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
+
+ i->announcing = 1;
+ avahi_announce_interface(m->server, i);
+ avahi_multicast_lookup_engine_new_interface(m->server->multicast_lookup_engine, i);
+ }
+
} else if (!b && i->announcing) {
avahi_log_info("Interface %s.%s no longer relevant for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
@@ -553,7 +562,7 @@ void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, c
assert(i);
assert(p);
- if (!avahi_interface_is_relevant(i))
+ if (!i->announcing)
return;
assert(!a || a->proto == i->protocol);
@@ -575,10 +584,10 @@ int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately
assert(i);
assert(key);
- if (avahi_interface_is_relevant(i))
- return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id);
-
- return 0;
+ if (!i->announcing)
+ return 0;
+
+ return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id);
}
int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) {
@@ -590,20 +599,20 @@ int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int fl
assert(i);
assert(record);
- if (avahi_interface_is_relevant(i))
- return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
-
- return 0;
+ if (!i->announcing)
+ return 0;
+
+ return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
}
int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, int immediately) {
assert(i);
assert(record);
- if (avahi_interface_is_relevant(i))
- return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
+ if (!i->announcing)
+ return 0;
- return 0;
+ return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
}
int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) {
@@ -625,21 +634,19 @@ int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void
int avahi_interface_is_relevant(AvahiInterface *i) {
AvahiInterfaceAddress *a;
- int relevant_address;
assert(i);
- relevant_address = 0;
+ if (!i->hardware->flags_ok)
+ return 0;
for (a = i->addresses; a; a = a->address_next)
- if (avahi_interface_address_is_relevant(a)) {
- relevant_address = 1;
- break;
- }
+ if (avahi_interface_address_is_relevant(a))
+ return 1;
- return i->hardware->flags_ok && relevant_address;
+ return 0;
}
-
+
int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) {
AvahiInterfaceAddress *b;
assert(a);
diff --git a/avahi-core/server.c b/avahi-core/server.c
index 5bfbc50..952f09e 100644
--- a/avahi-core/server.c
+++ b/avahi-core/server.c
@@ -803,7 +803,7 @@ static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
for (j = s->monitor->interfaces; j; j = j->interface_next)
- if (avahi_interface_is_relevant(j) &&
+ if (j->announcing &&
j != i &&
(s->config.reflect_ipv || j->protocol == i->protocol)) {
@@ -884,7 +884,7 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddres
assert(src_address->proto == dst_address->proto);
if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
- !avahi_interface_is_relevant(i)) {
+ !i->announcing) {
avahi_log_warn("Recieved packet from invalid interface.");
return;
}
@@ -977,7 +977,7 @@ static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
}
if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
- !avahi_interface_is_relevant(j))
+ !j->announcing)
return;
/* Patch the original ID into this response */
@@ -1560,6 +1560,8 @@ AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
c->browse_domains = NULL;
c->disable_publishing = 0;
c->allow_point_to_point = 0;
+ c->publish_aaaa_on_ipv4 = 1;
+ c->publish_a_on_ipv6 = 0;
return c;
}
@@ -1721,3 +1723,9 @@ int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, un
avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
return AVAHI_OK;
}
+
+const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
+ assert(s);
+
+ return &s->config;
+}
--
cgit
From 1438c0119533ea6374f233ee5dc9898b32f7e7c6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 5 Jan 2007 22:19:28 +0000
Subject: add new config options "publish-aaaa-on-ipv4=" and
"publish-a-on-ipv6="
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1366 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/avahi-daemon.conf | 2 ++
avahi-daemon/main.c | 10 +++++++---
avahi-daemon/static-hosts.c | 9 ++++++++-
man/avahi-daemon.conf.5.xml.in | 16 ++++++++++++++++
4 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/avahi-daemon/avahi-daemon.conf b/avahi-daemon/avahi-daemon.conf
index 1992a67..21fa166 100644
--- a/avahi-daemon/avahi-daemon.conf
+++ b/avahi-daemon/avahi-daemon.conf
@@ -45,6 +45,8 @@ enable-wide-area=yes
#publish-domain=yes
#publish-dns-servers=192.168.50.1, 192.168.50.2
#publish-resolv-conf-dns-servers=yes
+#publish-aaaa-on-ipv4=yes
+#publish-a-on-ipv6=no
[reflector]
#enable-reflector=no
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index 33f1265..0b26446 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -442,7 +442,7 @@ static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
static int is_yes(const char *s) {
assert(s);
- return *s == 'y' || *s == 'Y';
+ return *s == 'y' || *s == 'Y' || *s == '1' || *s == 't' || *s == 'T';
}
static int load_config_file(DaemonConfig *c) {
@@ -541,8 +541,12 @@ static int load_config_file(DaemonConfig *c) {
c->server_config.add_service_cookie = is_yes(p->value);
else if (strcasecmp(p->key, "publish-dns-servers") == 0) {
avahi_strfreev(c->publish_dns_servers);
- c->publish_dns_servers = avahi_split_csv(p->value);
- } else {
+ c->publish_dns_servers = avahi_split_csv(p->value);
+ } else if (strcasecmp(p->key, "publish-a-on-ipv6") == 0)
+ c->server_config.publish_a_on_ipv6 = is_yes(p->value);
+ else if (strcasecmp(p->key, "publish-aaaa-on-ipv4") == 0)
+ c->server_config.publish_aaaa_on_ipv4 = is_yes(p->value);
+ else {
avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
goto finish;
}
diff --git a/avahi-daemon/static-hosts.c b/avahi-daemon/static-hosts.c
index 9941a51..35058fc 100644
--- a/avahi-daemon/static-hosts.c
+++ b/avahi-daemon/static-hosts.c
@@ -110,7 +110,9 @@ static void static_host_free(StaticHost *s) {
static void add_static_host_to_server(StaticHost *h)
{
AvahiAddress a;
+ AvahiProtocol p;
int err;
+ const AvahiServerConfig *config;
if (!h->group)
if (!(h->group = avahi_s_entry_group_new (avahi_server, entry_group_callback, h))) {
@@ -123,7 +125,12 @@ static void add_static_host_to_server(StaticHost *h)
return;
}
- if ((err = avahi_server_add_address(avahi_server, h->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, h->host, &a))) {
+ config = avahi_server_get_config(avahi_server);
+
+ p = (a.proto == AVAHI_PROTO_INET && config->publish_a_on_ipv6) ||
+ (a.proto == AVAHI_PROTO_INET6 && config->publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : a.proto;
+
+ if ((err = avahi_server_add_address(avahi_server, h->group, AVAHI_IF_UNSPEC, p, 0, h->host, &a))) {
avahi_log_error ("Static host name %s: avahi_server_add_address failure: %s", h->host, avahi_strerror(err));
return;
}
diff --git a/man/avahi-daemon.conf.5.xml.in b/man/avahi-daemon.conf.5.xml.in
index bfbd0e5..efd1a41 100644
--- a/man/avahi-daemon.conf.5.xml.in
+++ b/man/avahi-daemon.conf.5.xml.in
@@ -223,6 +223,22 @@
SIGHUP to have it reload this file. Defaults to "no".
+
+
+
+
--
cgit
From e9a34a5760dff1e476377d6a5904905d5bc0ba86 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 5 Jan 2007 22:19:52 +0000
Subject: bump version number and SONAMES
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1367 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
configure.ac | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/configure.ac b/configure.ac
index bb44c52..b62f06a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,7 +21,7 @@
# USA.
AC_PREREQ(2.57)
-AC_INIT([avahi],[0.6.16],[avahi (at) lists (dot) freedesktop (dot) org])
+AC_INIT([avahi],[0.6.17],[avahi (at) lists (dot) freedesktop (dot) org])
AC_CONFIG_SRCDIR([avahi-core/server.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
@@ -29,8 +29,8 @@ AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
AC_SUBST(PACKAGE_URL, [http://avahi.org/])
AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [7:3:4])
-AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [4:5:0])
-AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:1:2])
+AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [5:0:0])
+AC_SUBST(LIBAVAHI_CLIENT_VERSION_INFO, [5:2:2])
AC_SUBST(LIBAVAHI_GLIB_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT3_VERSION_INFO, [1:1:0])
AC_SUBST(LIBAVAHI_QT4_VERSION_INFO, [1:1:0])
--
cgit
From 5183633d2f84c0e2a0342f49c9cab275adb43ed8 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 5 Jan 2007 22:36:12 +0000
Subject: build avahi-discover only if either dbm or gdbm is available (patch
from swegener/Gentoo)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1368 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-python/Makefile.am | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/avahi-python/Makefile.am b/avahi-python/Makefile.am
index 7f4980a..0800506 100644
--- a/avahi-python/Makefile.am
+++ b/avahi-python/Makefile.am
@@ -34,11 +34,22 @@ if HAVE_PYTHON_DBUS
if HAVE_PYGTK
pythonscripts = \
- avahi-bookmarks \
- avahi-discover
+ avahi-bookmarks
desktopdir = $(datadir)/applications
-desktop_DATA = avahi-discover.desktop
+desktop_DATA =
+
+if HAVE_GDBM
+pythonscripts += \
+ avahi-discover
+desktop_DATA += avahi-discover.desktop
+endif
+
+if HAVE_DBM
+pythonscripts += \
+ avahi-discover
+desktop_DATA += avahi-discover.desktop
+endif
avahi-discover.desktop: avahi-discover.desktop.in
sed -e 's,@bindir\@,$(bindir),g' $< > $@
--
cgit
From 64e22ecd9ab38afaaaa81e7cd9f1c901dfe03b9b Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Fri, 5 Jan 2007 22:43:03 +0000
Subject: debian init scripts: print a nice warning when not starting avahi due
to settings in /etc/default
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1369 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
initscript/debian/avahi-daemon.in | 11 +++++++++++
initscript/debian/avahi-dnsconfd.in | 11 +++++++++++
2 files changed, 22 insertions(+)
diff --git a/initscript/debian/avahi-daemon.in b/initscript/debian/avahi-daemon.in
index 553db78..30a2c2f 100755
--- a/initscript/debian/avahi-daemon.in
+++ b/initscript/debian/avahi-daemon.in
@@ -75,6 +75,16 @@ else
return $1
}
+ log_warning_msg () {
+ if log_use_fancy_output; then
+ YELLOW=`$TPUT setaf 3`
+ NORMAL=`$TPUT op`
+ echo "$YELLOW*$NORMAL $@"
+ else
+ echo "$@"
+ fi
+ }
+
fi
#set -e
@@ -93,6 +103,7 @@ AVAHI_DAEMON_START=1
test -f /etc/default/avahi-daemon && . /etc/default/avahi-daemon
if [ "$AVAHI_DAEMON_START" != "1" -a "$1" != "stop" ]; then
+ log_warning_msg "Not starting $DESC $NAME, disabled via /etc/default/$NAME"
exit 0
fi
diff --git a/initscript/debian/avahi-dnsconfd.in b/initscript/debian/avahi-dnsconfd.in
index ee63e06..ac34804 100755
--- a/initscript/debian/avahi-dnsconfd.in
+++ b/initscript/debian/avahi-dnsconfd.in
@@ -75,6 +75,16 @@ else
return $1
}
+ log_warning_msg () {
+ if log_use_fancy_output; then
+ YELLOW=`$TPUT setaf 3`
+ NORMAL=`$TPUT op`
+ echo "$YELLOW*$NORMAL $@"
+ else
+ echo "$@"
+ fi
+ }
+
fi
#set -e
@@ -93,6 +103,7 @@ AVAHI_DNSCONFD_START=1
test -f /etc/default/avahi-dnsconfd && . /etc/default/avahi-dnsconfd
if [ "$AVAHI_DNSCONFD_START" != "1" -a "$1" != "stop" ]; then
+ log_warning_msg "Not starting $DESC $NAME, disabled via /etc/default/$NAME"
exit 0
fi
--
cgit
From d39a5d3be8041cbf6fb93c274d49473c0cd1c4ea Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 6 Jan 2007 16:10:01 +0000
Subject: * set IPV6_V6ONLY for unicast DNS sockets, too * print a warning when
FIONREAD returns an invalid size
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1370 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/socket.c | 45 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 38 insertions(+), 7 deletions(-)
diff --git a/avahi-core/socket.c b/avahi-core/socket.c
index febb20c..61b5eb4 100644
--- a/avahi-core/socket.c
+++ b/avahi-core/socket.c
@@ -560,7 +560,14 @@ int avahi_send_dns_packet_ipv4(
return sendmsg_loop(fd, &msg, 0);
}
-int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex interface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port) {
+int avahi_send_dns_packet_ipv6(
+ int fd,
+ AvahiIfIndex interface,
+ AvahiDnsPacket *p,
+ const AvahiIPv6Address *src_address,
+ const AvahiIPv6Address *dst_address,
+ uint16_t dst_port) {
+
struct sockaddr_in6 sa;
struct msghdr msg;
struct iovec io;
@@ -617,7 +624,14 @@ int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex interface, AvahiDnsPacket *p
return sendmsg_loop(fd, &msg, 0);
}
-AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv4Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl) {
+AvahiDnsPacket *avahi_recv_dns_packet_ipv4(
+ int fd,
+ AvahiIPv4Address *ret_src_address,
+ uint16_t *ret_src_port,
+ AvahiIPv4Address *ret_dst_address,
+ AvahiIfIndex *ret_iface,
+ uint8_t *ret_ttl) {
+
AvahiDnsPacket *p= NULL;
struct msghdr msg;
struct iovec io;
@@ -635,8 +649,10 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_add
goto fail;
}
- if (ms <= 0)
+ if (ms < 0) {
+ avahi_log_warn("FIONREAD returned negative value.");
goto fail;
+ }
p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
@@ -761,7 +777,14 @@ fail:
return NULL;
}
-AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl) {
+AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
+ int fd,
+ AvahiIPv6Address *ret_src_address,
+ uint16_t *ret_src_port,
+ AvahiIPv6Address *ret_dst_address,
+ AvahiIfIndex *ret_iface,
+ uint8_t *ret_ttl) {
+
AvahiDnsPacket *p = NULL;
struct msghdr msg;
struct iovec io;
@@ -779,8 +802,10 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_add
goto fail;
}
- if (ms <= 0)
+ if (ms < 0) {
+ avahi_log_warn("FIONREAD returned negative value.");
goto fail;
+ }
p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
@@ -860,7 +885,7 @@ AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_add
assert(found_iface);
assert(found_ttl);
-
+
return p;
fail:
@@ -912,13 +937,19 @@ fail:
int avahi_open_unicast_socket_ipv6(void) {
struct sockaddr_in6 local;
- int fd = -1;
+ int fd = -1, yes;
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
avahi_log_warn("socket() failed: %s", strerror(errno));
goto fail;
}
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
+ goto fail;
+ }
+
memset(&local, 0, sizeof(local));
local.sin6_family = AF_INET6;
--
cgit
From 81abdde5ccc144c160791bdfca1c9e2cadeef2e5 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 6 Jan 2007 16:10:47 +0000
Subject: create unicast IPv4 socket oly if IPv4 is enabled, same for unicast
IPv6
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1371 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-core/wide-area.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/avahi-core/wide-area.c b/avahi-core/wide-area.c
index 4072f83..bf3f60d 100644
--- a/avahi-core/wide-area.c
+++ b/avahi-core/wide-area.c
@@ -579,8 +579,8 @@ AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) {
e->cleanup_dead = 0;
/* Create sockets */
- e->fd_ipv4 = avahi_open_unicast_socket_ipv4();
- e->fd_ipv6 = avahi_open_unicast_socket_ipv6();
+ e->fd_ipv4 = s->config.use_ipv4 ? avahi_open_unicast_socket_ipv4() : -1;
+ e->fd_ipv6 = s->config.use_ipv6 ? avahi_open_unicast_socket_ipv6() : -1;
if (e->fd_ipv4 < 0 && e->fd_ipv6 < 0) {
avahi_log_error(__FILE__": Failed to create wide area sockets: %s", strerror(errno));
--
cgit
From 78567b1a41841a5fe341dba81c5656d95d909399 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 6 Jan 2007 16:16:41 +0000
Subject: fix a bogus error condition
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1372 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-daemon/static-hosts.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/avahi-daemon/static-hosts.c b/avahi-daemon/static-hosts.c
index 35058fc..ebc358d 100644
--- a/avahi-daemon/static-hosts.c
+++ b/avahi-daemon/static-hosts.c
@@ -116,7 +116,7 @@ static void add_static_host_to_server(StaticHost *h)
if (!h->group)
if (!(h->group = avahi_s_entry_group_new (avahi_server, entry_group_callback, h))) {
- avahi_log_error("avahi_s_entry_group_new() failed: %s", avahi_strerror(err));
+ avahi_log_error("avahi_s_entry_group_new() failed: %s", avahi_strerror(avahi_server_errno(avahi_server)));
return;
}
@@ -130,7 +130,7 @@ static void add_static_host_to_server(StaticHost *h)
p = (a.proto == AVAHI_PROTO_INET && config->publish_a_on_ipv6) ||
(a.proto == AVAHI_PROTO_INET6 && config->publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : a.proto;
- if ((err = avahi_server_add_address(avahi_server, h->group, AVAHI_IF_UNSPEC, p, 0, h->host, &a))) {
+ if ((err = avahi_server_add_address(avahi_server, h->group, AVAHI_IF_UNSPEC, p, 0, h->host, &a)) < 0) {
avahi_log_error ("Static host name %s: avahi_server_add_address failure: %s", h->host, avahi_strerror(err));
return;
}
--
cgit
From b303e69589394fe25ff8f5fe6a17dfa6a4b07ea6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 6 Jan 2007 16:45:34 +0000
Subject: Pass AM_CFLAGS instead of CFLAGS when build the libdns_sd test tool
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1373 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-compat-libdns_sd/Makefile.am | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/avahi-compat-libdns_sd/Makefile.am b/avahi-compat-libdns_sd/Makefile.am
index f822135..a5cb202 100644
--- a/avahi-compat-libdns_sd/Makefile.am
+++ b/avahi-compat-libdns_sd/Makefile.am
@@ -66,8 +66,8 @@ null_test_LDADD = $(AM_LDADD) $(PTHREAD_LIBS) $(PTHREAD_CFLAGS) ../avahi-common/
# licensing restrictions.
libdns_sd-test: libdns_sd-test.c libdns_sd.la
- $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(PTHREAD_CFLAGS) -o libdns_sd-test.o -c libdns_sd-test.c
- $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(PTHREAD_CFLAGS) -o libdns_sd-test libdns_sd-test.o $(PTHREAD_LIBS) ../avahi-common/libavahi-common.la libdns_sd.la
+ $(LIBTOOL) --mode=compile $(CC) $(AM_CFLAGS) $(PTHREAD_CFLAGS) -o libdns_sd-test.o -c libdns_sd-test.c
+ $(LIBTOOL) --mode=link $(CC) $(AM_CFLAGS) $(PTHREAD_CFLAGS) -o libdns_sd-test libdns_sd-test.o $(PTHREAD_LIBS) ../avahi-common/libavahi-common.la libdns_sd.la
CLEANFILES = libdns_sd-test.o libdns_sd-test
--
cgit
From 5b7e4f49787da905b9ec7d01eaf76462ca3a6e28 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 6 Jan 2007 16:51:42 +0000
Subject: * handle NULL sdrefs gracefully. * fix mutex locking order in
DNSProcessResult
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1374 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
avahi-compat-libdns_sd/compat.c | 35 +++++++++++++++++++++--------------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/avahi-compat-libdns_sd/compat.c b/avahi-compat-libdns_sd/compat.c
index 9dc2281..9d8ff1c 100644
--- a/avahi-compat-libdns_sd/compat.c
+++ b/avahi-compat-libdns_sd/compat.c
@@ -469,25 +469,26 @@ static void sdref_unref(DNSServiceRef sdref) {
}
int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdref) {
- if (!sdref || sdref->n_ref <= 0)
- return -1;
AVAHI_WARN_LINKAGE;
+ if (!sdref || sdref->n_ref <= 0)
+ return -1;
+
return sdref->main_fd;
}
DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) {
DNSServiceErrorType ret = kDNSServiceErr_Unknown;
- assert(sdref);
- assert(sdref->n_ref >= 1);
-
AVAHI_WARN_LINKAGE;
- ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
-
+ if (!sdref || sdref->n_ref <= 0)
+ return kDNSServiceErr_BadParam;
+
sdref_ref(sdref);
+
+ ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
/* Cleanup notification socket */
if (read_command(sdref->main_fd) != COMMAND_POLL_DONE)
@@ -512,10 +513,10 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) {
finish:
- sdref_unref(sdref);
-
ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
+ sdref_unref(sdref);
+
return ret;
}
@@ -613,7 +614,6 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse(
if (!ret_sdref)
return kDNSServiceErr_BadParam;
-
*ret_sdref = NULL;
assert(regtype);
@@ -739,7 +739,10 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve(
AVAHI_WARN_LINKAGE;
- assert(ret_sdref);
+ if (!ret_sdref)
+ return kDNSServiceErr_BadParam;
+ *ret_sdref = NULL;
+
assert(name);
assert(regtype);
assert(domain);
@@ -853,7 +856,10 @@ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains(
AVAHI_WARN_LINKAGE;
- assert(ret_sdref);
+ if (!ret_sdref)
+ return kDNSServiceErr_BadParam;
+ *ret_sdref = NULL;
+
assert(callback);
if (interface == kDNSServiceInterfaceIndexLocalOnly ||
@@ -1096,7 +1102,6 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister (
if (!ret_sdref)
return kDNSServiceErr_BadParam;
-
*ret_sdref = NULL;
if (!regtype)
@@ -1210,10 +1215,12 @@ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord(
int ret = kDNSServiceErr_Unknown;
AvahiStringList *txt = NULL;
- assert(sdref);
AVAHI_WARN_LINKAGE;
+ if (!sdref || sdref->n_ref <= 0)
+ return kDNSServiceErr_BadParam;
+
if (flags || rref) {
AVAHI_WARN_UNSUPPORTED;
return kDNSServiceErr_Unsupported;
--
cgit
From 841dd9c0c209572fbe8d366972ff1707ebae28b6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering
Date: Sat, 6 Jan 2007 17:35:10 +0000
Subject: refer to some wiki stories from the man pages
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1375 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
---
man/avahi-autoipd.8.xml.in | 2 ++
man/avahi-daemon.8.xml.in | 2 ++
2 files changed, 4 insertions(+)
diff --git a/man/avahi-autoipd.8.xml.in b/man/avahi-autoipd.8.xml.in
index 5bc6fad..4655765 100644
--- a/man/avahi-autoipd.8.xml.in
+++ b/man/avahi-autoipd.8.xml.in
@@ -154,6 +154,8 @@
,
+
+
http://avahi.org/wiki/AvahiAutoipd documents how avahi-autoipd is best packaged and integrated into distributions.