From: Sven M. Hallberg Date: Wed, 20 Aug 2014 11:23:27 +0000 (+0200) Subject: combine avahi-core and avahi-common components into one library X-Git-Url: http://git.meshlink.io/?a=commitdiff_plain;ds=inline;h=1a5dff7d166883e99fe1dba62dc3c407a9f7e332;p=catta combine avahi-core and avahi-common components into one library --- diff --git a/Makefile.am b/Makefile.am index b49ca9b..5d06840 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,31 +37,30 @@ EXTRA_DIST = \ docs/overview.dia \ docs/server-states.dia \ docs/avahi-poll.dia \ - avahi-core.pc.in \ + avahi.pc.in \ doxygen_to_devhelp.xsl SUBDIRS = \ common \ - avahi-common \ - avahi-core \ + avahi \ examples DX_INPUT = \ - $(srcdir)/avahi-common/address.h \ - $(srcdir)/avahi-common/malloc.h \ - $(srcdir)/avahi-common/strlst.h \ - $(srcdir)/avahi-common/alternative.h \ - $(srcdir)/avahi-common/defs.h \ - $(srcdir)/avahi-common/error.h \ - $(srcdir)/avahi-common/domain.h \ - $(srcdir)/avahi-common/watch.h \ - $(srcdir)/avahi-common/simple-watch.h \ - $(srcdir)/avahi-common/thread-watch.h \ - $(srcdir)/avahi-core/core.h \ - $(srcdir)/avahi-core/lookup.h \ - $(srcdir)/avahi-core/publish.h \ - $(srcdir)/avahi-core/rr.h \ - $(srcdir)/avahi-core/log.h + $(srcdir)/avahi/address.h \ + $(srcdir)/avahi/malloc.h \ + $(srcdir)/avahi/strlst.h \ + $(srcdir)/avahi/alternative.h \ + $(srcdir)/avahi/defs.h \ + $(srcdir)/avahi/error.h \ + $(srcdir)/avahi/domain.h \ + $(srcdir)/avahi/watch.h \ + $(srcdir)/avahi/simple-watch.h \ + $(srcdir)/avahi/thread-watch.h \ + $(srcdir)/avahi/core.h \ + $(srcdir)/avahi/lookup.h \ + $(srcdir)/avahi/publish.h \ + $(srcdir)/avahi/rr.h \ + $(srcdir)/avahi/log.h DX_EXAMPLE_PATH = $(srcdir)/examples DX_EXAMPLE_PATTERNS = *.c @@ -73,8 +72,8 @@ pkgconfigdir = $(libdir)/pkgconfig -e 's,@libdir\@,$(libdir),g' \ -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' $< > $@ -pkgconfig_DATA = avahi-core.pc -CLEANFILES = avahi-core.pc +pkgconfig_DATA = avahi.pc +CLEANFILES = avahi.pc CLEANFILES += avahi.devhelp avahi.devhelp: doxygen-run diff --git a/README b/README index eba8f3c..24d9cab 100644 --- a/README +++ b/README @@ -22,6 +22,3 @@ CONFIGURE SWITCHES: --enable-tests build some test programs, including those for 'make check'. - ---enable-core-docs -include files from 'avahi-core' when building the doxygen docs. diff --git a/avahi-common/.gitignore b/avahi-common/.gitignore deleted file mode 100644 index b523810..0000000 --- a/avahi-common/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -*.o -*.lo -*.la -Makefile -Makefile.in -.deps -.libs -alternative-test -domain-test -strlst-test -timeval-test -utf8-test -watch-test -watch-test-thread diff --git a/avahi-common/Makefile.am b/avahi-common/Makefile.am deleted file mode 100644 index c78e34a..0000000 --- a/avahi-common/Makefile.am +++ /dev/null @@ -1,121 +0,0 @@ -# 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. - -AM_CFLAGS=-I$(top_srcdir) - -# This cool debug trap works on i386/gcc only -AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' - -avahi_commonincludedir=$(includedir)/avahi-common - -avahi_commoninclude_HEADERS = \ - strlst.h \ - address.h \ - alternative.h \ - domain.h \ - cdecl.h \ - defs.h \ - malloc.h \ - watch.h \ - timeval.h \ - simple-watch.h \ - thread-watch.h \ - gccmacro.h \ - error.h \ - llist.h \ - rlist.h - -if ENABLE_TESTS -noinst_PROGRAMS = \ - strlst-test \ - domain-test \ - alternative-test \ - timeval-test \ - watch-test \ - watch-test-thread \ - utf8-test -endif - -lib_LTLIBRARIES = \ - libavahi-common.la - -libavahi_common_la_SOURCES = \ - malloc.c malloc.h \ - address.c address.h \ - alternative.c alternative.h \ - error.c error.h \ - strlst.c strlst.h \ - domain.c domain.h \ - timeval.c timeval.h \ - simple-watch.c simple-watch.h \ - thread-watch.c thread-watch.h \ - watch.h gccmacro.h \ - rlist.h rlist.c \ - utf8.c utf8.h \ - i18n.c i18n.h - -libavahi_common_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) -DAVAHI_LOCALEDIR=\"$(avahilocaledir)\" -libavahi_common_la_LIBADD = $(AM_LDADD) $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) $(INTLLIBS) -libavahi_common_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBAVAHI_COMMON_VERSION_INFO) - -strlst_test_SOURCES = \ - strlst.c strlst.h \ - malloc.c malloc.h \ - strlst-test.c -strlst_test_CFLAGS = $(AM_CFLAGS) - -alternative_test_SOURCES = \ - alternative.c alternative.h \ - malloc.c malloc.h \ - domain.c domain.h \ - address.c address.h \ - alternative-test.c \ - utf8.c utf8.h -alternative_test_CFLAGS = $(AM_CFLAGS) - -domain_test_SOURCES = \ - domain.c domain.h \ - malloc.c malloc.h \ - address.c address.h \ - domain-test.c \ - utf8.c utf8.h -domain_test_CFLAGS = $(AM_CFLAGS) - -watch_test_SOURCES = \ - timeval.c timeval.h \ - simple-watch.c simple-watch.h \ - watch.h \ - malloc.c malloc.h \ - watch-test.c -watch_test_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) -watch_test_LDADD = $(AM_LDADD) $(PTHREAD_LIBS) $(PTHREAD_CFLAGS) - -watch_test_thread_SOURCES = $(watch_test_SOURCES) thread-watch.c thread-watch.h -watch_test_thread_CFLAGS = $(watch_test_CFLAGS) -DUSE_THREAD -watch_test_thread_LDADD = $(watch_test_LDADD) - -timeval_test_SOURCES = \ - timeval.c timeval.h \ - timeval-test.c -timeval_test_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) -timeval_test_LDADD = $(AM_LDADD) $(PTHREAD_LIBS) $(PTHREAD_CFLAGS) - -utf8_test_SOURCES = \ - utf8-test.c \ - utf8.c utf8.h -utf8_test_CFLAGS = $(AM_CFLAGS) -utf8_test_LDADD = $(AM_LDADD) diff --git a/avahi-common/address.c b/avahi-common/address.c deleted file mode 100644 index e8f6148..0000000 --- a/avahi-common/address.c +++ /dev/null @@ -1,155 +0,0 @@ -/*** - 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 "address.h" -#include "malloc.h" - -static size_t address_get_size(const AvahiAddress *a) { - assert(a); - - if (a->proto == AVAHI_PROTO_INET) - return 4; - else if (a->proto == AVAHI_PROTO_INET6) - return 16; - - return 0; -} - -int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) { - assert(a); - assert(b); - - if (a->proto != b->proto) - return -1; - - return memcmp(a->data.data, b->data.data, address_get_size(a)); -} - -char *avahi_address_snprint(char *s, size_t length, const AvahiAddress *a) { - assert(s); - assert(length); - assert(a); - - if (!(inet_ntop(avahi_proto_to_af(a->proto), a->data.data, s, length))) - return NULL; - - return s; -} - -char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length) { - assert(ret_s); - assert(length > 0); - assert(a); - - if (a->proto == AVAHI_PROTO_INET) { - uint32_t n = ntohl(a->data.ipv4.address); - snprintf( - ret_s, length, - "%u.%u.%u.%u.in-addr.arpa", - n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24); - } else { - assert(a->proto == AVAHI_PROTO_INET6); - - snprintf( - ret_s, length, - "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", - a->data.ipv6.address[15] & 0xF, a->data.ipv6.address[15] >> 4, - a->data.ipv6.address[14] & 0xF, a->data.ipv6.address[14] >> 4, - a->data.ipv6.address[13] & 0xF, a->data.ipv6.address[13] >> 4, - a->data.ipv6.address[12] & 0xF, a->data.ipv6.address[12] >> 4, - a->data.ipv6.address[11] & 0xF, a->data.ipv6.address[11] >> 4, - a->data.ipv6.address[10] & 0xF, a->data.ipv6.address[10] >> 4, - a->data.ipv6.address[ 9] & 0xF, a->data.ipv6.address[ 9] >> 4, - a->data.ipv6.address[ 8] & 0xF, a->data.ipv6.address[ 8] >> 4, - a->data.ipv6.address[ 7] & 0xF, a->data.ipv6.address[ 7] >> 4, - a->data.ipv6.address[ 6] & 0xF, a->data.ipv6.address[ 6] >> 4, - a->data.ipv6.address[ 5] & 0xF, a->data.ipv6.address[ 5] >> 4, - a->data.ipv6.address[ 4] & 0xF, a->data.ipv6.address[ 4] >> 4, - a->data.ipv6.address[ 3] & 0xF, a->data.ipv6.address[ 3] >> 4, - a->data.ipv6.address[ 2] & 0xF, a->data.ipv6.address[ 2] >> 4, - a->data.ipv6.address[ 1] & 0xF, a->data.ipv6.address[ 1] >> 4, - a->data.ipv6.address[ 0] & 0xF, a->data.ipv6.address[ 0] >> 4); - } - - return ret_s; -} - -AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol proto, AvahiAddress *ret_addr) { - assert(ret_addr); - assert(s); - - if (proto == AVAHI_PROTO_UNSPEC) { - if (inet_pton(AF_INET, s, ret_addr->data.data) <= 0) { - if (inet_pton(AF_INET6, s, ret_addr->data.data) <= 0) - return NULL; - else - ret_addr->proto = AVAHI_PROTO_INET6; - } else - ret_addr->proto = AVAHI_PROTO_INET; - } else { - if (inet_pton(avahi_proto_to_af(proto), s, ret_addr->data.data) <= 0) - return NULL; - - ret_addr->proto = proto; - } - - return ret_addr; -} - -int avahi_proto_to_af(AvahiProtocol proto) { - if (proto == AVAHI_PROTO_INET) - return AF_INET; - if (proto == AVAHI_PROTO_INET6) - return AF_INET6; - - assert(proto == AVAHI_PROTO_UNSPEC); - return AF_UNSPEC; -} - -AvahiProtocol avahi_af_to_proto(int af) { - if (af == AF_INET) - return AVAHI_PROTO_INET; - if (af == AF_INET6) - return AVAHI_PROTO_INET6; - - assert(af == AF_UNSPEC); - return AVAHI_PROTO_UNSPEC; -} - -const char* avahi_proto_to_string(AvahiProtocol proto) { - if (proto == AVAHI_PROTO_INET) - return "IPv4"; - if (proto == AVAHI_PROTO_INET6) - return "IPv6"; - - assert(proto == AVAHI_PROTO_UNSPEC); - return "UNSPEC"; -} diff --git a/avahi-common/address.h b/avahi-common/address.h deleted file mode 100644 index a14104f..0000000 --- a/avahi-common/address.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef fooaddresshfoo -#define fooaddresshfoo - -/*** - 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. -***/ - -/** \file address.h Definitions and functions to manipulate IP addresses. */ - -#include -#include - -#include - -AVAHI_C_DECL_BEGIN - -/** Protocol family specification, takes the values AVAHI_PROTO_INET, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC */ -typedef int AvahiProtocol; - -/** Numeric network interface index. Takes OS dependent values and the special constant AVAHI_IF_UNSPEC */ -typedef int AvahiIfIndex; - -/** Values for AvahiProtocol */ -enum { - AVAHI_PROTO_INET = 0, /**< IPv4 */ - AVAHI_PROTO_INET6 = 1, /**< IPv6 */ - AVAHI_PROTO_UNSPEC = -1 /**< Unspecified/all protocol(s) */ -}; - -/** Special values for AvahiIfIndex */ -enum { - AVAHI_IF_UNSPEC = -1 /**< Unspecified/all interface(s) */ -}; - -/** Maximum size of an address in string form */ -#define AVAHI_ADDRESS_STR_MAX 40 /* IPv6 Max = 4*8 + 7 + 1 for NUL */ - -/** Return TRUE if the specified interface index is valid */ -#define AVAHI_IF_VALID(ifindex) (((ifindex) >= 0) || ((ifindex) == AVAHI_IF_UNSPEC)) - -/** Return TRUE if the specified protocol is valid */ -#define AVAHI_PROTO_VALID(protocol) (((protocol) == AVAHI_PROTO_INET) || ((protocol) == AVAHI_PROTO_INET6) || ((protocol) == AVAHI_PROTO_UNSPEC)) - -/** An IPv4 address */ -typedef struct AvahiIPv4Address { - uint32_t address; /**< Address data in network byte order. */ -} AvahiIPv4Address; - -/** An IPv6 address */ -typedef struct AvahiIPv6Address { - uint8_t address[16]; /**< Address data */ -} AvahiIPv6Address; - -/** Protocol (address family) independent address structure */ -typedef struct AvahiAddress { - AvahiProtocol proto; /**< Address family */ - - union { - AvahiIPv6Address ipv6; /**< Address when IPv6 */ - AvahiIPv4Address ipv4; /**< Address when IPv4 */ - uint8_t data[1]; /**< Type-independent data field */ - } data; -} AvahiAddress; - -/** @{ \name Comparison */ - -/** Compare two addresses. Returns 0 when equal, a negative value when a < b, a positive value when a > b. */ -int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); - -/** @} */ - -/** @{ \name String conversion */ - -/** Convert the specified address *a to a human readable character string, use AVAHI_ADDRESS_STR_MAX to allocate an array of the right size */ -char *avahi_address_snprint(char *ret_s, size_t length, const AvahiAddress *a); - -/** Convert the specified human readable character string to an - * address structure. Set af to AVAHI_UNSPEC for automatic address - * family detection. */ -AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol af, AvahiAddress *ret_addr); - -/** @} */ - -/** \cond fulldocs */ -/** Generate the DNS reverse lookup name for an IPv4 or IPv6 address. */ -char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length); -/** \endcond */ - -/** @{ \name Protocol/address family handling */ - -/** Map AVAHI_PROTO_xxx constants to Unix AF_xxx constants */ -int avahi_proto_to_af(AvahiProtocol proto); - -/** Map Unix AF_xxx constants to AVAHI_PROTO_xxx constants */ -AvahiProtocol avahi_af_to_proto(int af); - -/** Return a textual representation of the specified protocol number. i.e. "IPv4", "IPv6" or "UNSPEC" */ -const char* avahi_proto_to_string(AvahiProtocol proto); - -/** @} */ - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/alternative-test.c b/avahi-common/alternative-test.c deleted file mode 100644 index 9255435..0000000 --- a/avahi-common/alternative-test.c +++ /dev/null @@ -1,90 +0,0 @@ -/*** - 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 "alternative.h" -#include "malloc.h" -#include "domain.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - const char* const test_strings[] = { - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXüüüüüüü", - "gurke", - "-", - " #", - "1", - "#0", - " #0", - " #1", - "#-1", - " #-1", - "-0", - "--0", - "-1", - "--1", - "-2", - "gurke1", - "gurke0", - "gurke-2", - "gurke #0", - "gurke #1", - "gurke #", - "gurke#1", - "gurke-", - "gurke---", - "gurke #", - "gurke ###", - NULL - }; - - char *r = NULL; - int i, j, k; - - for (k = 0; test_strings[k]; k++) { - - printf(">>>>>%s<<<<\n", test_strings[k]); - - for (j = 0; j < 2; j++) { - - for (i = 0; i <= 100; i++) { - char *n; - - n = i == 0 ? avahi_strdup(test_strings[k]) : (j ? avahi_alternative_service_name(r) : avahi_alternative_host_name(r)); - avahi_free(r); - r = n; - - if (j) - assert(avahi_is_valid_service_name(n)); - else - assert(avahi_is_valid_host_name(n)); - - printf("%s\n", r); - } - } - } - - avahi_free(r); - return 0; -} diff --git a/avahi-common/alternative.c b/avahi-common/alternative.c deleted file mode 100644 index b3d39f0..0000000 --- a/avahi-common/alternative.c +++ /dev/null @@ -1,182 +0,0 @@ -/*** - 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 "alternative.h" -#include "malloc.h" -#include "domain.h" -#include "utf8.h" - -static void drop_incomplete_utf8(char *c) { - char *e; - - e = strchr(c, 0) - 1; - - while (e >= c) { - - if (avahi_utf8_valid(c)) - break; - - assert(*e & 128); - *e = 0; - - e--; - } -} - -char *avahi_alternative_host_name(const char *s) { - const char *e; - char *r; - - assert(s); - - if (!avahi_is_valid_host_name(s)) - return NULL; - - if ((e = strrchr(s, '-'))) { - const char *p; - - e++; - - for (p = e; *p; p++) - if (!isdigit(*p)) { - e = NULL; - break; - } - - if (e && (*e == '0' || *e == 0)) - e = NULL; - } - - if (e) { - char *c, *m; - size_t l; - int n; - - n = atoi(e)+1; - if (!(m = avahi_strdup_printf("%i", n))) - return NULL; - - l = e-s-1; - - if (l >= AVAHI_LABEL_MAX-1-strlen(m)-1) - l = AVAHI_LABEL_MAX-1-strlen(m)-1; - - if (!(c = avahi_strndup(s, l))) { - avahi_free(m); - return NULL; - } - - drop_incomplete_utf8(c); - - r = avahi_strdup_printf("%s-%s", c, m); - avahi_free(c); - avahi_free(m); - - } else { - char *c; - - if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-2))) - return NULL; - - drop_incomplete_utf8(c); - - r = avahi_strdup_printf("%s-2", c); - avahi_free(c); - } - - assert(avahi_is_valid_host_name(r)); - - return r; -} - -char *avahi_alternative_service_name(const char *s) { - const char *e; - char *r; - - assert(s); - - if (!avahi_is_valid_service_name(s)) - return NULL; - - if ((e = strstr(s, " #"))) { - const char *n, *p; - e += 2; - - while ((n = strstr(e, " #"))) - e = n + 2; - - for (p = e; *p; p++) - if (!isdigit(*p)) { - e = NULL; - break; - } - - if (e && (*e == '0' || *e == 0)) - e = NULL; - } - - if (e) { - char *c, *m; - size_t l; - int n; - - n = atoi(e)+1; - if (!(m = avahi_strdup_printf("%i", n))) - return NULL; - - l = e-s-2; - - if (l >= AVAHI_LABEL_MAX-1-strlen(m)-2) - l = AVAHI_LABEL_MAX-1-strlen(m)-2; - - if (!(c = avahi_strndup(s, l))) { - avahi_free(m); - return NULL; - } - - drop_incomplete_utf8(c); - - r = avahi_strdup_printf("%s #%s", c, m); - avahi_free(c); - avahi_free(m); - } else { - char *c; - - if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-3))) - return NULL; - - drop_incomplete_utf8(c); - - r = avahi_strdup_printf("%s #2", c); - avahi_free(c); - } - - assert(avahi_is_valid_service_name(r)); - - return r; -} diff --git a/avahi-common/alternative.h b/avahi-common/alternative.h deleted file mode 100644 index 9b044de..0000000 --- a/avahi-common/alternative.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef fooalternativehfoo -#define fooalternativehfoo - -/*** - 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. -***/ - -/** \file alternative.h Functions to find alternative names for hosts and services in the case of name collision */ - -#include - -AVAHI_C_DECL_BEGIN - -/** Find an alternative for the specified host name. If called with an - * original host name, "-2" is appended, afterwards the number is - * increased on each call. (i.e. "foo" becomes "foo-2" becomes "foo-3" - * and so on.) avahi_free() the result. */ -char *avahi_alternative_host_name(const char *s); - -/** Find an alternative for the specified service name. If called with - * an original service name, " #2" is appended. Afterwards the number - * is increased on each call (i.e. "foo" becomes "foo #2" becomes "foo - * #3" and so on.) avahi_free() the result. */ -char *avahi_alternative_service_name(const char *s); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/cdecl.h b/avahi-common/cdecl.h deleted file mode 100644 index aef6aba..0000000 --- a/avahi-common/cdecl.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef foocdeclhfoo -#define foocdeclhfoo - -/*** - 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. -***/ - -/** \file cdecl.h C++ compatibility */ -#ifdef __cplusplus -/** If using C++ this macro enables C mode, otherwise does nothing */ -#define AVAHI_C_DECL_BEGIN extern "C" { -/** If using C++ this macro switches back to C++ mode, otherwise does nothing */ -#define AVAHI_C_DECL_END } - -#else -/** If using C++ this macro enables C mode, otherwise does nothing */ -#define AVAHI_C_DECL_BEGIN -/** If using C++ this macro switches back to C++ mode, otherwise does nothing */ -#define AVAHI_C_DECL_END - -#endif - -#endif diff --git a/avahi-common/defs.h b/avahi-common/defs.h deleted file mode 100644 index bb73a9d..0000000 --- a/avahi-common/defs.h +++ /dev/null @@ -1,356 +0,0 @@ -#ifndef foodefshfoo -#define foodefshfoo - -/*** - 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. -***/ - -/** \file defs.h Some common definitions */ - -#include - -/** \mainpage - * - * \section choose_api Choosing an API - * - * Avahi provides three programming APIs for integration of - * mDNS/DNS-SD features into your C progams: - * - * \li avahi-core: an API for embedding a complete mDNS/DNS-SD stack - * into your software. This is intended for developers of embedded - * appliances only. We dissuade from using this API in normal desktop - * applications since it is not a good idea to run multiple mDNS - * stacks simultaneously on the same host. - * \li the D-Bus API: an extensive D-Bus interface for browsing and - * registering mDNS/DNS-SD services using avahi-daemon. We recommend - * using this API for software written in any language other than - * C (e.g. Python). - * \li avahi-client: a simplifying C wrapper around the D-Bus API. We - * recommend using this API in C or C++ progams. The D-Bus internals - * are hidden completely. - * \li avahi-gobject: an object-oriented C wrapper based on - * GLib's GObject. We recommd using this API for GNOME/Gtk programs. - * - * All three APIs are very similar, however avahi-core is the most powerful. - * - * In addition to the three APIs described above Avahi supports two - * compatibility libraries: - * - * \li avahi-compat-libdns_sd: the original Bonjour API as documented - * in the header file "dns_sd.h" by Apple Computer, Inc. - * - * \li avahi-compat-howl: the HOWL API as released with HOWL 0.9.8 by - * Porchdog Software. - * - * Please note that these compatibility layers are incomplete and - * generally a waste of resources. We strongly encourage everyone to - * use our native APIs for newly written programs and to port older - * programs to avahi-client! - * - * The native APIs (avahi-client and avahi-core) can be integrated - * into external event loops. We provide adapters for the following - * event loop implementations: - * - * \li avahi-glib: The GLIB main loop as used by GTk+/GNOME - * - * \li avahi-qt: The Qt main loop as used by Qt/KDE - * - * Finally, we provide a high-level Gtk+ GUI dialog called - * avahi-ui for user-friendly browsing for services. - * - * The doxygen-generated API documentation covers avahi-client - * (including its auxiliary APIs), the event loop adapters and - * avahi-ui. For the other APIs please consult the original - * documentation (for the compatibility APIs) or the header files. - * - * Please note that the doxygen-generated API documentation of the - * native Avahi API is not complete. A few definitions that are part - * of the Avahi API have been removed from this documentation, either - * because they are only relevant in a very few low-level applications - * or because they are considered obsolete. Please consult the C header - * files for all definitions that are part of the Avahi API. Please - * note that these hidden definitions are considered part of the Avahi - * API and will stay available in the API in the future. - * - * \section error_reporting Error Reporting - * - * Some notes on the Avahi error handling: - * - * - Error codes are negative integers and defined as AVAHI_ERR_xx - * - If a function returns some kind of non-negative integer value on - * success, a failure is indicated by returning the error code - * directly. - * - If a function returns a pointer of some kind on success, a - * failure is indicated by returning NULL - * - The last error number may be retrieved by calling - * avahi_client_errno() - * - Just like the libc errno variable the Avahi errno is NOT reset to - * AVAHI_OK if a function call succeeds. - * - You may convert a numeric error code into a human readable string - * using avahi_strerror() - * - The constructor function avahi_client_new() returns the error - * code in a call-by-reference argument - * - * \section event_loop Event Loop Abstraction - * - * Avahi uses a simple event loop abstraction layer. A table AvahiPoll - * which contains function pointers for user defined timeout and I/O - * condition event source implementations needs to be passed to - * avahi_client_new(). An adapter for this abstraction layer is - * available for the GLib main loop in the object AvahiGLibPoll. A - * simple stand-alone implementation is available under the name - * AvahiSimplePoll. An adpater for the Qt main loop is available from - * avahi_qt_poll_get(). - * - * \section good_publish How to Register Services - * - * - Subscribe to server state changes. Pass a callback function - * pointer to avahi_client_new(). It will be called - * whenever the server state changes. - * - Only register your services when the server is in state - * AVAHI_SERVER_RUNNING. If you register your services in other server - * states they might not be accessible since the local host name might not necessarily - * be established. - * - Remove your services when the server enters - * AVAHI_SERVER_COLLISION or AVAHI_SERVER_REGISTERING state. Your - * services may not be reachable anymore since the local host name is - * no longer established or is currently in the process of being - * established. - * - When registering services, use the following algorithm: - * - Create a new entry group (i.e. avahi_entry_group_new()) - * - Add your service(s)/additional RRs/subtypes (e.g. avahi_entry_group_add_service()) - * - Commit the entry group (i.e. avahi_entry_group_commit()) - * - Subscribe to entry group state changes. - * - If the entry group enters AVAHI_ENTRY_GROUP_COLLISION state the - * services of the entry group are automatically removed from the - * server. You may immediately add your services back to the entry - * group (but with new names, perhaps using - * avahi_alternative_service_name()) and commit again. Please do not - * free the entry group and create a new one. This would inhibit some - * traffic limiting algorithms in mDNS. - * - When you need to modify your services (i.e. change the TXT data - * or the port number), use the AVAHI_PUBLISH_UPDATE flag. Please do - * not free the entry group and create a new one. This would inhibit - * some traffic limiting algorithms in mDNS. When changing just the - * TXT data avahi_entry_group_update_txt() is a shortcut for - * AVAHI_PUBLISH_UPDATE. Please note that you cannot use - * AVAHI_PUBLISH_UPDATE when changing the service name! Renaming a - * DNS-SD service is identical to deleting and creating a new one, and - * that's exactly what you should do in that case. First call - * avahi_entry_group_reset() to remove it and then read it normally. - * - * \section good_browse How to Browse for Services - * - * - For normal applications you need to call avahi_service_browser_new() - * for the service type you want to browse for. Use - * avahi_service_resolver_new() to acquire service data for a service - * name. - * - You can use avahi_domain_browser_new() to get a list of announced - * browsing domains. Please note that not all domains whith services - * on the LAN are mandatorily announced. - * - There is no need to subscribe to server state changes. - * - * \section daemon_dies How to Write a Client That Can Deal with Daemon Restarts - * - * With Avahi it is possible to write client applications that can - * deal with Avahi daemon restarts. To accomplish that make sure to - * pass AVAHI_CLIENT_NO_FAIL to avahi_client_new()'s flags - * parameter. That way avahi_client_new() will succeed even when the - * daemon is not running. In that case the object will enter - * AVAHI_CLIENT_CONNECTING state. As soon as the daemon becomes - * available the object will enter one of the AVAHI_CLIENT_S_xxx - * states. Make sure to not create browsers or entry groups before the - * client object has entered one of those states. As usual you will be - * informed about state changes with the callback function supplied to - * avahi_client_new(). If the client is forced to disconnect from the - * server it will enter AVAHI_CLIENT_FAILURE state with - * avahi_client_errno() == AVAHI_ERR_DISCONNECTED. Free the - * AvahiClient object in that case (and all its associated objects - * such as entry groups and browser objects prior to that) and - * reconnect to the server anew - again with passing - * AVAHI_CLIENT_NO_FAIL to avahi_client_new(). - * - * We encourage implementing this in all software where service - * discovery is not an integral part of application. e.g. use it in - * all kinds of background daemons, but not necessarily in software - * like iChat compatible IM software. - * - * For now AVAHI_CLIENT_NO_FAIL cannot deal with D-Bus daemon restarts. - * - * \section domains How to Deal Properly with Browsing Domains - * - * Due to the introduction of wide-area DNS-SD the correct handling of - * domains becomes more important for Avahi enabled applications. All - * applications that offer the user a list of services discovered with - * Avahi should offer some kind of editable drop down box where the - * user can either enter his own domain or select one of those offered - * by AvahiDomainBrowser. The default domain to browse should be the - * one returned by avahi_client_get_domain_name(). The list of domains - * returned by AvahiDomainBrowser is assembled by the browsing domains - * configured in the daemon's configuration file, the domains - * announced inside the default domain, the domains set with the - * environment variable $AVAHI_BROWSE_DOMAINS (colon-seperated) on the - * client side and the domains set in the XDG configuration file - * ~/.config/avahi/browse-domains on the client side (seperated by - * newlines). File managers offering some kind of "Network - * Neighborhood" folder should show the entries of the default domain - * right inside that and offer subfolders for the browsing domains - * returned by AvahiDomainBrowser. - */ - -AVAHI_C_DECL_BEGIN - -/** @{ \name States */ - -/** States of a server object */ -typedef enum { - AVAHI_SERVER_INVALID, /**< Invalid state (initial) */ - AVAHI_SERVER_REGISTERING, /**< Host RRs are being registered */ - AVAHI_SERVER_RUNNING, /**< All host RRs have been established */ - AVAHI_SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */ - AVAHI_SERVER_FAILURE /**< Some fatal failure happened, the server is unable to proceed */ -} AvahiServerState; - -/** States of an entry group object */ -typedef enum { - AVAHI_ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been commited, the user must still call avahi_entry_group_commit() */ - AVAHI_ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */ - AVAHI_ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */ - AVAHI_ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */ - AVAHI_ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */ -} AvahiEntryGroupState; - -/** @} */ - -/** @{ \name Flags */ - -/** Some flags for publishing functions */ -typedef enum { - AVAHI_PUBLISH_UNIQUE = 1, /**< For raw records: The RRset is intended to be unique */ - AVAHI_PUBLISH_NO_PROBE = 2, /**< For raw records: Though the RRset is intended to be unique no probes shall be sent */ - AVAHI_PUBLISH_NO_ANNOUNCE = 4, /**< For raw records: Do not announce this RR to other hosts */ - AVAHI_PUBLISH_ALLOW_MULTIPLE = 8, /**< For raw records: Allow multiple local records of this type, even if they are intended to be unique */ -/** \cond fulldocs */ - AVAHI_PUBLISH_NO_REVERSE = 16, /**< For address records: don't create a reverse (PTR) entry */ - AVAHI_PUBLISH_NO_COOKIE = 32, /**< For service records: do not implicitly add the local service cookie to TXT data */ -/** \endcond */ - AVAHI_PUBLISH_UPDATE = 64, /**< Update existing records instead of adding new ones */ -/** \cond fulldocs */ - AVAHI_PUBLISH_USE_WIDE_AREA = 128, /**< Register the record using wide area DNS (i.e. unicast DNS update) */ - AVAHI_PUBLISH_USE_MULTICAST = 256 /**< Register the record using multicast DNS */ -/** \endcond */ -} AvahiPublishFlags; - -/** Some flags for lookup functions */ -typedef enum { -/** \cond fulldocs */ - AVAHI_LOOKUP_USE_WIDE_AREA = 1, /**< Force lookup via wide area DNS */ - AVAHI_LOOKUP_USE_MULTICAST = 2, /**< Force lookup via multicast DNS */ -/** \endcond */ - AVAHI_LOOKUP_NO_TXT = 4, /**< When doing service resolving, don't lookup TXT record */ - AVAHI_LOOKUP_NO_ADDRESS = 8 /**< When doing service resolving, don't lookup A/AAAA record */ -} AvahiLookupFlags; - -/** Some flags for lookup callback functions */ -typedef enum { - AVAHI_LOOKUP_RESULT_CACHED = 1, /**< This response originates from the cache */ - AVAHI_LOOKUP_RESULT_WIDE_AREA = 2, /**< This response originates from wide area DNS */ - AVAHI_LOOKUP_RESULT_MULTICAST = 4, /**< This response originates from multicast DNS */ - AVAHI_LOOKUP_RESULT_LOCAL = 8, /**< This record/service resides on and was announced by the local host. Only available in service and record browsers and only on AVAHI_BROWSER_NEW. */ - AVAHI_LOOKUP_RESULT_OUR_OWN = 16, /**< This service belongs to the same local client as the browser object. Only available in avahi-client, and only for service browsers and only on AVAHI_BROWSER_NEW. */ - AVAHI_LOOKUP_RESULT_STATIC = 32 /**< The returned data has been defined statically by some configuration option */ -} AvahiLookupResultFlags; - -/** @} */ - -/** @{ \name Events */ - -/** Type of callback event when browsing */ -typedef enum { - AVAHI_BROWSER_NEW, /**< The object is new on the network */ - AVAHI_BROWSER_REMOVE, /**< The object has been removed from the network */ - AVAHI_BROWSER_CACHE_EXHAUSTED, /**< One-time event, to notify the user that all entries from the caches have been sent */ - AVAHI_BROWSER_ALL_FOR_NOW, /**< One-time event, to notify the user that more records will probably not show up in the near future, i.e. all cache entries have been read and all static servers been queried */ - AVAHI_BROWSER_FAILURE /**< Browsing failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ -} AvahiBrowserEvent; - -/** Type of callback event when resolving */ -typedef enum { - AVAHI_RESOLVER_FOUND, /**< RR found, resolving successful */ - AVAHI_RESOLVER_FAILURE /**< Resolving failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ -} AvahiResolverEvent; - -/** @} */ - -/** @{ \name Other definitions */ - -/** The type of domain to browse for */ -typedef enum { - AVAHI_DOMAIN_BROWSER_BROWSE, /**< Browse for a list of available browsing domains */ - AVAHI_DOMAIN_BROWSER_BROWSE_DEFAULT, /**< Browse for the default browsing domain */ - AVAHI_DOMAIN_BROWSER_REGISTER, /**< Browse for a list of available registering domains */ - AVAHI_DOMAIN_BROWSER_REGISTER_DEFAULT, /**< Browse for the default registering domain */ - AVAHI_DOMAIN_BROWSER_BROWSE_LEGACY, /**< Legacy browse domain - see DNS-SD spec for more information */ - AVAHI_DOMAIN_BROWSER_MAX -} AvahiDomainBrowserType; - -/** @} */ - -/** \cond fulldocs */ -/** For every service a special TXT item is implicitly added, which - * contains a random cookie which is private to the local daemon. This - * can be used by clients to determine if two services on two - * different subnets are effectively the same. */ -#define AVAHI_SERVICE_COOKIE "org.freedesktop.Avahi.cookie" - -/** In invalid cookie as special value */ -#define AVAHI_SERVICE_COOKIE_INVALID (0) -/** \endcond fulldocs */ - -/** @{ \name DNS RR definitions */ - -/** DNS record types, see RFC 1035 */ -enum { - AVAHI_DNS_TYPE_A = 0x01, - AVAHI_DNS_TYPE_NS = 0x02, - AVAHI_DNS_TYPE_CNAME = 0x05, - AVAHI_DNS_TYPE_SOA = 0x06, - AVAHI_DNS_TYPE_PTR = 0x0C, - AVAHI_DNS_TYPE_HINFO = 0x0D, - AVAHI_DNS_TYPE_MX = 0x0F, - AVAHI_DNS_TYPE_TXT = 0x10, - AVAHI_DNS_TYPE_AAAA = 0x1C, - AVAHI_DNS_TYPE_SRV = 0x21 -}; - -/** DNS record classes, see RFC 1035 */ -enum { - AVAHI_DNS_CLASS_IN = 0x01 /**< Probably the only class we will ever use */ -}; - -/** @} */ - -/** The default TTL for RRs which contain a host name of some kind. */ -#define AVAHI_DEFAULT_TTL_HOST_NAME (120) - -/** The default TTL for all other records. */ -#define AVAHI_DEFAULT_TTL (75*60) - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/domain-test.c b/avahi-common/domain-test.c deleted file mode 100644 index cf763ec..0000000 --- a/avahi-common/domain-test.c +++ /dev/null @@ -1,123 +0,0 @@ -/*** - 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 "domain.h" -#include "malloc.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - char *s; - char t[256], r[256]; - const char *p; - size_t size; - char name[64], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; - - printf("%s\n", s = avahi_normalize_name_strdup("foo.foo\\046.")); - avahi_free(s); - - printf("%s\n", s = avahi_normalize_name_strdup("foo.foo\\.foo.")); - avahi_free(s); - - - printf("%s\n", s = avahi_normalize_name_strdup("fo\\\\o\\..f oo.")); - avahi_free(s); - - printf("%i\n", avahi_domain_equal("\\065aa bbb\\.\\046cc.cc\\\\.dee.fff.", "Aaa BBB\\.\\.cc.cc\\\\.dee.fff")); - printf("%i\n", avahi_domain_equal("A", "a")); - - printf("%i\n", avahi_domain_equal("a", "aaa")); - - printf("%u = %u\n", avahi_domain_hash("ccc\\065aa.aa\\.b\\\\."), avahi_domain_hash("cccAaa.aa\\.b\\\\")); - - - avahi_service_name_join(t, sizeof(t), "foo.foo.foo \\.", "_http._tcp", "test.local"); - printf("<%s>\n", t); - - avahi_service_name_split(t, name, sizeof(name), type, sizeof(type), domain, sizeof(domain)); - printf("name: <%s>; type: <%s>; domain <%s>\n", name, type, domain); - - avahi_service_name_join(t, sizeof(t), NULL, "_http._tcp", "one.two\\. .local"); - printf("<%s>\n", t); - - avahi_service_name_split(t, NULL, 0, type, sizeof(type), domain, sizeof(domain)); - printf("name: <>; type: <%s>; domain <%s>\n", type, domain); - - - p = "--:---\\\\\\123\\065_äöü\\064\\.\\\\sjöödfhh.sdfjhskjdf"; - printf("unescaped: <%s>, rest: %s\n", avahi_unescape_label(&p, t, sizeof(t)), p); - - size = sizeof(r); - s = r; - - printf("escaped: <%s>\n", avahi_escape_label(t, strlen(t), &s, &size)); - - p = r; - printf("unescaped: <%s>\n", avahi_unescape_label(&p, t, sizeof(t))); - - assert(avahi_is_valid_service_type_generic("_foo._bar._waldo")); - assert(!avahi_is_valid_service_type_strict("_foo._bar._waldo")); - assert(!avahi_is_valid_service_subtype("_foo._bar._waldo")); - - assert(avahi_is_valid_service_type_generic("_foo._tcp")); - assert(avahi_is_valid_service_type_strict("_foo._tcp")); - assert(!avahi_is_valid_service_subtype("_foo._tcp")); - - assert(!avahi_is_valid_service_type_generic("_foo._bar.waldo")); - assert(!avahi_is_valid_service_type_strict("_foo._bar.waldo")); - assert(!avahi_is_valid_service_subtype("_foo._bar.waldo")); - - assert(!avahi_is_valid_service_type_generic("")); - assert(!avahi_is_valid_service_type_strict("")); - assert(!avahi_is_valid_service_subtype("")); - - assert(avahi_is_valid_service_type_generic("_foo._sub._bar._tcp")); - assert(!avahi_is_valid_service_type_strict("_foo._sub._bar._tcp")); - assert(avahi_is_valid_service_subtype("_foo._sub._bar._tcp")); - - printf("%s\n", avahi_get_type_from_subtype("_foo._sub._bar._tcp")); - - assert(!avahi_is_valid_host_name("sf.ooo.")); - assert(avahi_is_valid_host_name("sfooo.")); - assert(avahi_is_valid_host_name("sfooo")); - - assert(avahi_is_valid_domain_name(".")); - assert(avahi_is_valid_domain_name("")); - - assert(avahi_normalize_name(".", t, sizeof(t))); - assert(avahi_normalize_name("", t, sizeof(t))); - - assert(!avahi_is_valid_fqdn(".")); - assert(!avahi_is_valid_fqdn("")); - assert(!avahi_is_valid_fqdn("foo")); - assert(avahi_is_valid_fqdn("foo.bar")); - assert(avahi_is_valid_fqdn("foo.bar.")); - assert(avahi_is_valid_fqdn("gnurz.foo.bar.")); - assert(!avahi_is_valid_fqdn("192.168.50.1")); - assert(!avahi_is_valid_fqdn("::1")); - assert(!avahi_is_valid_fqdn(".192.168.50.1.")); - - return 0; -} diff --git a/avahi-common/domain.c b/avahi-common/domain.c deleted file mode 100644 index 3b1ab68..0000000 --- a/avahi-common/domain.c +++ /dev/null @@ -1,609 +0,0 @@ -/*** - 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 "domain.h" -#include "malloc.h" -#include "error.h" -#include "address.h" -#include "utf8.h" - -/* Read the first label from string *name, unescape "\" and write it to dest */ -char *avahi_unescape_label(const char **name, char *dest, size_t size) { - unsigned i = 0; - char *d; - - assert(dest); - assert(size > 0); - assert(name); - - d = dest; - - for (;;) { - if (i >= size) - return NULL; - - if (**name == '.') { - (*name)++; - break; - } - - if (**name == 0) - break; - - if (**name == '\\') { - /* Escaped character */ - - (*name) ++; - - if (**name == 0) - /* Ending NUL */ - return NULL; - - else if (**name == '\\' || **name == '.') { - /* Escaped backslash or dot */ - *(d++) = *((*name) ++); - i++; - } else if (isdigit(**name)) { - int n; - - /* Escaped literal ASCII character */ - - if (!isdigit(*(*name+1)) || !isdigit(*(*name+2))) - return NULL; - - n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0')); - - if (n > 255 || n == 0) - return NULL; - - *(d++) = (char) n; - i++; - - (*name) += 3; - } else - return NULL; - - } else { - - /* Normal character */ - - *(d++) = *((*name) ++); - i++; - } - } - - assert(i < size); - - *d = 0; - - if (!avahi_utf8_valid(dest)) - return NULL; - - return dest; -} - -/* Escape "\" and ".", append \0 */ -char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) { - char *r; - - assert(src); - assert(ret_name); - assert(*ret_name); - assert(ret_size); - assert(*ret_size > 0); - - r = *ret_name; - - while (src_length > 0) { - if (*src == '.' || *src == '\\') { - - /* Dot or backslash */ - - if (*ret_size < 3) - return NULL; - - *((*ret_name) ++) = '\\'; - *((*ret_name) ++) = *src; - (*ret_size) -= 2; - - } else if ( - *src == '_' || - *src == '-' || - (*src >= '0' && *src <= '9') || - (*src >= 'a' && *src <= 'z') || - (*src >= 'A' && *src <= 'Z')) { - - /* Proper character */ - - if (*ret_size < 2) - return NULL; - - *((*ret_name)++) = *src; - (*ret_size) --; - - } else { - - /* Everything else */ - - if (*ret_size < 5) - return NULL; - - *((*ret_name) ++) = '\\'; - *((*ret_name) ++) = '0' + (char) ((uint8_t) *src / 100); - *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10); - *((*ret_name) ++) = '0' + (char) ((uint8_t) *src % 10); - - (*ret_size) -= 4; - } - - src_length --; - src++; - } - - **ret_name = 0; - - return r; -} - -char *avahi_normalize_name(const char *s, char *ret_s, size_t size) { - int empty = 1; - char *r; - - assert(s); - assert(ret_s); - assert(size > 0); - - r = ret_s; - *ret_s = 0; - - while (*s) { - char label[AVAHI_LABEL_MAX]; - - if (!(avahi_unescape_label(&s, label, sizeof(label)))) - return NULL; - - if (label[0] == 0) { - - if (*s == 0 && empty) - return ret_s; - - return NULL; - } - - if (!empty) { - if (size < 1) - return NULL; - - *(r++) = '.'; - size--; - - } else - empty = 0; - - avahi_escape_label(label, strlen(label), &r, &size); - } - - return ret_s; -} - -char *avahi_normalize_name_strdup(const char *s) { - char t[AVAHI_DOMAIN_NAME_MAX]; - assert(s); - - if (!(avahi_normalize_name(s, t, sizeof(t)))) - return NULL; - - return avahi_strdup(t); -} - -int avahi_domain_equal(const char *a, const char *b) { - assert(a); - assert(b); - - if (a == b) - return 1; - - for (;;) { - char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r; - - r = avahi_unescape_label(&a, ca, sizeof(ca)); - assert(r); - r = avahi_unescape_label(&b, cb, sizeof(cb)); - assert(r); - - if (strcasecmp(ca, cb)) - return 0; - - if (!*a && !*b) - return 1; - } - - return 1; -} - -int avahi_is_valid_service_type_generic(const char *t) { - assert(t); - - if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) - return 0; - - do { - char label[AVAHI_LABEL_MAX]; - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return 0; - - if (strlen(label) <= 2 || label[0] != '_') - return 0; - - } while (*t); - - return 1; -} - -int avahi_is_valid_service_type_strict(const char *t) { - char label[AVAHI_LABEL_MAX]; - assert(t); - - if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) - return 0; - - /* Application name */ - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return 0; - - if (strlen(label) <= 2 || label[0] != '_') - return 0; - - if (!*t) - return 0; - - /* _tcp or _udp boilerplate */ - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return 0; - - if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) - return 0; - - if (*t) - return 0; - - return 1; -} - -const char *avahi_get_type_from_subtype(const char *t) { - char label[AVAHI_LABEL_MAX]; - const char *ret; - assert(t); - - if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) - return NULL; - - /* Subtype name */ - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return NULL; - - if (strlen(label) <= 2 || label[0] != '_') - return NULL; - - if (!*t) - return NULL; - - /* String "_sub" */ - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return NULL; - - if (strcasecmp(label, "_sub")) - return NULL; - - if (!*t) - return NULL; - - ret = t; - - /* Application name */ - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return NULL; - - if (strlen(label) <= 2 || label[0] != '_') - return NULL; - - if (!*t) - return NULL; - - /* _tcp or _udp boilerplate */ - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return NULL; - - if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) - return NULL; - - if (*t) - return NULL; - - return ret; -} - -int avahi_is_valid_service_subtype(const char *t) { - assert(t); - - return !!avahi_get_type_from_subtype(t); -} - -int avahi_is_valid_domain_name(const char *t) { - int is_first = 1; - assert(t); - - if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) - return 0; - - do { - char label[AVAHI_LABEL_MAX]; - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return 0; - - /* Explicitly allow the root domain name */ - if (is_first && label[0] == 0 && *t == 0) - return 1; - - is_first = 0; - - if (label[0] == 0) - return 0; - - } while (*t); - - return 1; -} - -int avahi_is_valid_service_name(const char *t) { - assert(t); - - if (strlen(t) >= AVAHI_LABEL_MAX || !*t) - return 0; - - return 1; -} - -int avahi_is_valid_host_name(const char *t) { - char label[AVAHI_LABEL_MAX]; - assert(t); - - if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) - return 0; - - if (!(avahi_unescape_label(&t, label, sizeof(label)))) - return 0; - - if (strlen(label) < 1) - return 0; - - if (*t) - return 0; - - return 1; -} - -unsigned avahi_domain_hash(const char *s) { - unsigned hash = 0; - - while (*s) { - char c[AVAHI_LABEL_MAX], *p, *r; - - r = avahi_unescape_label(&s, c, sizeof(c)); - assert(r); - - for (p = c; *p; p++) - hash = 31 * hash + tolower(*p); - } - - return hash; -} - -int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) { - char escaped_name[AVAHI_LABEL_MAX*4]; - char normalized_type[AVAHI_DOMAIN_NAME_MAX]; - char normalized_domain[AVAHI_DOMAIN_NAME_MAX]; - - assert(p); - - /* Validity checks */ - - if ((name && !avahi_is_valid_service_name(name))) - return AVAHI_ERR_INVALID_SERVICE_NAME; - - if (!avahi_is_valid_service_type_generic(type)) - return AVAHI_ERR_INVALID_SERVICE_TYPE; - - if (!avahi_is_valid_domain_name(domain)) - return AVAHI_ERR_INVALID_DOMAIN_NAME; - - /* Preparation */ - - if (name) { - size_t l = sizeof(escaped_name); - char *e = escaped_name, *r; - r = avahi_escape_label(name, strlen(name), &e, &l); - assert(r); - } - - if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type)))) - return AVAHI_ERR_INVALID_SERVICE_TYPE; - - if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain)))) - return AVAHI_ERR_INVALID_DOMAIN_NAME; - - /* Concatenation */ - - snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain); - - return AVAHI_OK; -} - -#ifndef HAVE_STRLCPY - -static size_t strlcpy(char *dest, const char *src, size_t n) { - assert(dest); - assert(src); - - if (n > 0) { - strncpy(dest, src, n-1); - dest[n-1] = 0; - } - - return strlen(src); -} - -#endif - -int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size) { - enum { - NAME, - TYPE, - DOMAIN - } state; - int type_empty = 1, domain_empty = 1; - - assert(p); - assert(type); - assert(type_size > 0); - assert(domain); - assert(domain_size > 0); - - if (name) { - assert(name_size > 0); - *name = 0; - state = NAME; - } else - state = TYPE; - - *type = *domain = 0; - - while (*p) { - char buf[64]; - - if (!(avahi_unescape_label(&p, buf, sizeof(buf)))) - return -1; - - switch (state) { - case NAME: - strlcpy(name, buf, name_size); - state = TYPE; - break; - - case TYPE: - - if (buf[0] == '_') { - - if (!type_empty) { - if (!type_size) - return AVAHI_ERR_NO_MEMORY; - - *(type++) = '.'; - type_size --; - - } else - type_empty = 0; - - if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size))) - return AVAHI_ERR_NO_MEMORY; - - break; - } - - state = DOMAIN; - /* fall through */ - - case DOMAIN: - - if (!domain_empty) { - if (!domain_size) - return AVAHI_ERR_NO_MEMORY; - - *(domain++) = '.'; - domain_size --; - } else - domain_empty = 0; - - if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size))) - return AVAHI_ERR_NO_MEMORY; - - break; - } - } - - return 0; -} - -int avahi_is_valid_fqdn(const char *t) { - char label[AVAHI_LABEL_MAX]; - char normalized[AVAHI_DOMAIN_NAME_MAX]; - const char *k = t; - AvahiAddress a; - assert(t); - - if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) - return 0; - - if (!avahi_is_valid_domain_name(t)) - return 0; - - /* Check if there are at least two labels*/ - if (!(avahi_unescape_label(&k, label, sizeof(label)))) - return 0; - - if (label[0] == 0 || !k) - return 0; - - if (!(avahi_unescape_label(&k, label, sizeof(label)))) - return 0; - - if (label[0] == 0 || !k) - return 0; - - /* Make sure that the name is not an IP address */ - if (!(avahi_normalize_name(t, normalized, sizeof(normalized)))) - return 0; - - if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a)) - return 0; - - return 1; -} diff --git a/avahi-common/domain.h b/avahi-common/domain.h deleted file mode 100644 index a0dcd0f..0000000 --- a/avahi-common/domain.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef foodomainhfoo -#define foodomainhfoo - -/*** - 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. -***/ - -/** \file domain.h Domain name handling functions */ - -#include -#include - -#include - -AVAHI_C_DECL_BEGIN - -/** The maximum length of a a fully escaped domain name C string. This - * is calculated like this: RFC1034 mandates maximum length of FQDNs - * is 255. The maximum label length is 63. To minimize the number of - * (non-escaped) dots, we comprise our maximum-length domain name of - * four labels á 63 characters plus three inner dots. Escaping the - * four labels quadruples their length at maximum. An escaped domain - * name has the therefore the maximum length of 63*4*4+3=1011. A - * trailing NUL and perhaps two unnecessary dots leading and trailing - * the string brings us to 1014. */ -#define AVAHI_DOMAIN_NAME_MAX 1014 - -/** Maximum size of an unescaped label */ -#define AVAHI_LABEL_MAX 64 - -/** @{ \name Normalization */ - -/** Normalize a domain name into canonical form. This drops trailing - * dots and removes useless backslash escapes. */ -char *avahi_normalize_name(const char *s, char *ret_s, size_t size); - -/** Normalize a domain name into canonical form. This drops trailing - * dots and removes useless backslash escapes. avahi_free() the - * result! */ -char *avahi_normalize_name_strdup(const char *s); - -/** @} */ - -/** @{ \name Comparison */ - -/** Return 1 when the specified domain names are equal, 0 otherwise */ -int avahi_domain_equal(const char *a, const char *b); - -/** Return some kind of hash value for the domain, useful for using domains as hash table keys. */ -unsigned avahi_domain_hash(const char *name); - -/** @} */ - -/** @{ \name Escaping */ - -/** Read the first label from the textual domain name *name, unescape - * it and write it to dest, *name is changed to point to the next label*/ -char *avahi_unescape_label(const char **name, char *dest, size_t size); - -/** Escape the domain name in *src and write it to *ret_name */ -char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size); - -/** @} */ - -/** @{ \name Validity Checks */ - -/** Return 1 when the specified string contains a valid generic DNS-SD - * service type (i.e. a series of words starting with "_"), 0 - * otherwise */ -int avahi_is_valid_service_type_generic(const char *t); - -/** Return 1 when the specified string contains a valid strict DNS-SD - * service type (i.e. consisting of only two words, the latter being - * either _udp or _tcp), 0 otherwise */ -int avahi_is_valid_service_type_strict(const char *t); - -/** Return 1 when the specified string contains a valid DNS-SD service - * subtype, 0 otherwise */ -int avahi_is_valid_service_subtype(const char *t); - -/** Return 1 when the specified string contains a valid domain name, 0 otherwise */ -int avahi_is_valid_domain_name(const char *t); - -/** Return 1 when the specified string contains a valid DNS-SD service name, 0 otherwise */ -int avahi_is_valid_service_name(const char *t); - -/** Return 1 when the specified string contains a valid non-FQDN host name (i.e. without dots), 0 otherwise */ -int avahi_is_valid_host_name(const char *t); - -/** Return 1 when the specified string contains a valid FQDN host name (i.e. with more than one label and non-numerical), 0 otherwise. \since 0.6.9 */ -int avahi_is_valid_fqdn(const char *t); - -/** @} */ - -/** @{ \name DNS-SD service name handling */ - -/** Construct a valid complete DNS-SD service name from a name, a type and a domain */ -int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain); - -/** Split a full service name into name, type and domain */ -int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size); - -/** @} */ - -/** @{ \name DNS-SD Subtype handling */ - -/** Return a pointer to the type section of a subtype i.e. _foo._sub._bar._tcp => _bar._tcp */ -const char *avahi_get_type_from_subtype(const char *t); - -/** @} */ - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/error.c b/avahi-common/error.c deleted file mode 100644 index 63c5033..0000000 --- a/avahi-common/error.c +++ /dev/null @@ -1,97 +0,0 @@ -/*** - 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 "error.h" -#include "i18n.h" - -const char *avahi_strerror(int error) { - - const char * const msg[- AVAHI_ERR_MAX] = { - N_("OK"), - N_("Operation failed"), - N_("Bad state"), - N_("Invalid host name"), - N_("Invalid domain name"), - N_("No suitable network protocol available"), - N_("Invalid DNS TTL"), - N_("Resource record key is pattern"), - N_("Local name collision"), - N_("Invalid record"), - - N_("Invalid service name"), - N_("Invalid service type"), - N_("Invalid port number"), - N_("Invalid record key"), - N_("Invalid address"), - N_("Timeout reached"), - N_("Too many clients"), - N_("Too many objects"), - N_("Too many entries"), - N_("OS Error"), - - N_("Access denied"), - N_("Invalid operation"), - N_("An unexpected D-Bus error occurred"), - N_("Daemon connection failed"), - N_("Memory exhausted"), - N_("The object passed in was not valid"), - N_("Daemon not running"), - N_("Invalid interface index"), - N_("Invalid protocol specification"), - N_("Invalid flags"), - - N_("Not found"), - N_("Invalid configuration"), - N_("Version mismatch"), - N_("Invalid service subtype"), - N_("Invalid packet"), - N_("Invalid DNS return code"), - N_("DNS failure: FORMERR"), - N_("DNS failure: SERVFAIL"), - N_("DNS failure: NXDOMAIN"), - N_("DNS failure: NOTIMP"), - - N_("DNS failure: REFUSED"), - N_("DNS failure: YXDOMAIN"), - N_("DNS failure: YXRRSET"), - N_("DNS failure: NXRRSET"), - N_("DNS failure: NOTAUTH"), - N_("DNS failure: NOTZONE"), - N_("Invalid RDATA"), - N_("Invalid DNS type"), - N_("Invalid DNS class"), - N_("Not supported"), - - N_("Not permitted"), - N_("Invalid argument"), - N_("Is empty"), - N_("The requested operation is invalid because redundant") - }; - - avahi_init_i18n(); - - if (-error < 0 || -error >= -AVAHI_ERR_MAX) - return _("Invalid Error Code"); - - return _(msg[-error]); -} diff --git a/avahi-common/error.h b/avahi-common/error.h deleted file mode 100644 index 94511c9..0000000 --- a/avahi-common/error.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef fooerrorhfoo -#define fooerrorhfoo - -/*** - 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. -***/ - -/** \file error.h Error codes and auxiliary functions */ - -#include - -AVAHI_C_DECL_BEGIN - -/** Error codes used by avahi */ -enum { - AVAHI_OK = 0, /**< OK */ - AVAHI_ERR_FAILURE = -1, /**< Generic error code */ - AVAHI_ERR_BAD_STATE = -2, /**< Object was in a bad state */ - AVAHI_ERR_INVALID_HOST_NAME = -3, /**< Invalid host name */ - AVAHI_ERR_INVALID_DOMAIN_NAME = -4, /**< Invalid domain name */ - AVAHI_ERR_NO_NETWORK = -5, /**< No suitable network protocol available */ - AVAHI_ERR_INVALID_TTL = -6, /**< Invalid DNS TTL */ - AVAHI_ERR_IS_PATTERN = -7, /**< RR key is pattern */ - AVAHI_ERR_COLLISION = -8, /**< Name collision */ - AVAHI_ERR_INVALID_RECORD = -9, /**< Invalid RR */ - - AVAHI_ERR_INVALID_SERVICE_NAME = -10, /**< Invalid service name */ - AVAHI_ERR_INVALID_SERVICE_TYPE = -11, /**< Invalid service type */ - AVAHI_ERR_INVALID_PORT = -12, /**< Invalid port number */ - AVAHI_ERR_INVALID_KEY = -13, /**< Invalid key */ - AVAHI_ERR_INVALID_ADDRESS = -14, /**< Invalid address */ - AVAHI_ERR_TIMEOUT = -15, /**< Timeout reached */ - AVAHI_ERR_TOO_MANY_CLIENTS = -16, /**< Too many clients */ - AVAHI_ERR_TOO_MANY_OBJECTS = -17, /**< Too many objects */ - AVAHI_ERR_TOO_MANY_ENTRIES = -18, /**< Too many entries */ - AVAHI_ERR_OS = -19, /**< OS error */ - - AVAHI_ERR_ACCESS_DENIED = -20, /**< Access denied */ - AVAHI_ERR_INVALID_OPERATION = -21, /**< Invalid operation */ - AVAHI_ERR_DBUS_ERROR = -22, /**< An unexpected D-Bus error occurred */ - 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 */ - AVAHI_ERR_NO_DAEMON = -26, /**< Daemon not running */ - AVAHI_ERR_INVALID_INTERFACE = -27, /**< Invalid interface */ - AVAHI_ERR_INVALID_PROTOCOL = -28, /**< Invalid protocol */ - AVAHI_ERR_INVALID_FLAGS = -29, /**< Invalid flags */ - - AVAHI_ERR_NOT_FOUND = -30, /**< Not found */ - AVAHI_ERR_INVALID_CONFIG = -31, /**< Configuration error */ - AVAHI_ERR_VERSION_MISMATCH = -32, /**< Verson mismatch */ - AVAHI_ERR_INVALID_SERVICE_SUBTYPE = -33, /**< Invalid service subtype */ - AVAHI_ERR_INVALID_PACKET = -34, /**< Invalid packet */ - AVAHI_ERR_INVALID_DNS_ERROR = -35, /**< Invlaid DNS return code */ - AVAHI_ERR_DNS_FORMERR = -36, /**< DNS Error: Form error */ - AVAHI_ERR_DNS_SERVFAIL = -37, /**< DNS Error: Server Failure */ - AVAHI_ERR_DNS_NXDOMAIN = -38, /**< DNS Error: No such domain */ - AVAHI_ERR_DNS_NOTIMP = -39, /**< DNS Error: Not implemented */ - - AVAHI_ERR_DNS_REFUSED = -40, /**< DNS Error: Operation refused */ - AVAHI_ERR_DNS_YXDOMAIN = -41, - AVAHI_ERR_DNS_YXRRSET = -42, - AVAHI_ERR_DNS_NXRRSET = -43, - AVAHI_ERR_DNS_NOTAUTH = -44, /**< DNS Error: Not authorized */ - AVAHI_ERR_DNS_NOTZONE = -45, - AVAHI_ERR_INVALID_RDATA = -46, /**< Invalid RDATA */ - AVAHI_ERR_INVALID_DNS_CLASS = -47, /**< Invalid DNS class */ - AVAHI_ERR_INVALID_DNS_TYPE = -48, /**< Invalid DNS type */ - AVAHI_ERR_NOT_SUPPORTED = -49, /**< Not supported */ - - 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 it is redundant */ - - /**** - **** IF YOU ADD A NEW ERROR CODE HERE, PLEASE DON'T FORGET TO ADD - **** IT TO THE STRING ARRAY IN avahi_strerror() IN error.c AND - **** TO THE ARRAY IN dbus.c AND FINALLY TO dbus.h! - **** - **** Also remember to update the MAX value below. - ****/ - - AVAHI_ERR_MAX = -54 -}; - -/** Return a human readable error string for the specified error code */ -const char *avahi_strerror(int error); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/gccmacro.h b/avahi-common/gccmacro.h deleted file mode 100644 index 4c97111..0000000 --- a/avahi-common/gccmacro.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef foogccmacrohfoo -#define foogccmacrohfoo - -/*** - 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. -***/ - -/** \file gccmacro.h Defines some macros for GCC extensions */ - -#include - -AVAHI_C_DECL_BEGIN - -#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) -#define AVAHI_GCC_ALLOC_SIZE(x) __attribute__ ((__alloc_size__(x))) -#define AVAHI_GCC_ALLOC_SIZE2(x,y) __attribute__ ((__alloc_size__(x,y))) -#else -/** Macro for usage of GCC's alloc_size attribute */ -#define AVAHI_GCC_ALLOC_SIZE(x) -#define AVAHI_GCC_ALLOC_SIZE2(x,y) -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define AVAHI_GCC_SENTINEL __attribute__ ((sentinel)) -#else -/** Macro for usage of GCC's sentinel compilation warnings */ -#define AVAHI_GCC_SENTINEL -#endif - -#ifdef __GNUC__ -#define AVAHI_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) -#else -/** Macro for usage of GCC's printf compilation warnings */ -#define AVAHI_GCC_PRINTF_ATTR(a,b) -#endif - -/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 1 and 2 */ -#define AVAHI_GCC_PRINTF_ATTR12 AVAHI_GCC_PRINTF_ATTR(1,2) - -/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 2 and 3 */ -#define AVAHI_GCC_PRINTF_ATTR23 AVAHI_GCC_PRINTF_ATTR(2,3) - -#ifdef __GNUC__ -#define AVAHI_GCC_NORETURN __attribute__((noreturn)) -#else -/** Macro for no-return functions */ -#define AVAHI_GCC_NORETURN -#endif - -#ifdef __GNUC__ -#define AVAHI_GCC_UNUSED __attribute__ ((unused)) -#else -/** Macro for not used parameter */ -#define AVAHI_GCC_UNUSED -#endif - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/i18n.c b/avahi-common/i18n.c deleted file mode 100644 index 509abbe..0000000 --- a/avahi-common/i18n.c +++ /dev/null @@ -1,42 +0,0 @@ -/*** - 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 "i18n.h" - -void avahi_init_i18n(void) { -#ifdef ENABLE_NLS - - /* Not really thread safe, but this doesn't matter much since - * bindtextdomain is supposed to be idempotent anyway. */ - /* FIXME: but is bindtextdomain reentrant!? */ - - static int done = 0; - - if (!done) { - bindtextdomain(GETTEXT_PACKAGE, AVAHI_LOCALEDIR); - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - done = 1; - } - -#endif /* ENABLE_NLS */ -} diff --git a/avahi-common/i18n.h b/avahi-common/i18n.h deleted file mode 100644 index f46931d..0000000 --- a/avahi-common/i18n.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef fooi18nhfoo -#define fooi18nhfoo - -/*** - 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 ENABLE_NLS - -#if !defined(GETTEXT_PACKAGE) -#error "Something is very wrong here, config.h needs to be included first" -#endif - -#include - -#define _(String) dgettext(GETTEXT_PACKAGE, String) -#ifdef gettext_noop -#define N_(String) gettext_noop(String) -#else -#define N_(String) (String) -#endif - -#else /* NLS is disabled */ - -#define _(String) (String) -#define N_(String) (String) -#define textdomain(String) (String) -#define gettext(String) (String) -#define dgettext(Domain,String) (String) -#define dcgettext(Domain,String,Type) (String) -#define bindtextdomain(Domain,Directory) (Domain) -#define bind_textdomain_codeset(Domain,Codeset) (Codeset) - -#endif /* ENABLE_NLS */ - -void avahi_init_i18n(void); - -#endif diff --git a/avahi-common/llist.h b/avahi-common/llist.h deleted file mode 100644 index e37056d..0000000 --- a/avahi-common/llist.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef foollistfoo -#define foollistfoo - -/*** - 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. -***/ - -/** \file llist.h A simple macro based linked list implementation */ - -#include - -#include - -AVAHI_C_DECL_BEGIN - -/** The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define AVAHI_LLIST_HEAD(t,name) t *name - -/** The pointers in the linked list's items. Use this in the item structure */ -#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev - -/** Initialize the list's head */ -#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0) - -/** Initialize a list item */ -#define AVAHI_LLIST_INIT(t,name,item) do { \ - t *_item = (item); \ - assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while(0) - -/** Prepend an item to the list */ -#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \ - t **_head = &(head), *_item = (item); \ - assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_head = _item; \ - } while (0) - -/** Remove an item from the list */ -#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \ - t **_head = &(head), *_item = (item); \ - assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else {\ - assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while(0) - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/malloc.c b/avahi-common/malloc.c deleted file mode 100644 index 23b13a9..0000000 --- a/avahi-common/malloc.c +++ /dev/null @@ -1,257 +0,0 @@ -/*** - 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 "malloc.h" - -#ifndef va_copy -#ifdef __va_copy -#define va_copy(DEST,SRC) __va_copy((DEST),(SRC)) -#else -#define va_copy(DEST,SRC) memcpy(&(DEST), &(SRC), sizeof(va_list)) -#endif -#endif - -static const AvahiAllocator *allocator = NULL; - -static void oom(void) AVAHI_GCC_NORETURN; - -static void oom(void) { - - static const char msg[] = "Out of memory, aborting ...\n"; - const char *n = msg; - - while (strlen(n) > 0) { - ssize_t r; - - if ((r = write(2, n, strlen(n))) < 0) - break; - - n += r; - } - - abort(); -} - -/* Default implementation for avahi_malloc() */ -static void* xmalloc(size_t size) { - void *p; - - if (size == 0) - return NULL; - - if (!(p = malloc(size))) - oom(); - - return p; -} - -/* Default implementation for avahi_realloc() */ -static void *xrealloc(void *p, size_t size) { - - if (size == 0) { - free(p); - return NULL; - } - - if (!(p = realloc(p, size))) - oom(); - - return p; -} - -/* Default implementation for avahi_calloc() */ -static void *xcalloc(size_t nmemb, size_t size) { - void *p; - - if (size == 0 || nmemb == 0) - return NULL; - - if (!(p = calloc(nmemb, size))) - oom(); - - return p; -} - -void *avahi_malloc(size_t size) { - - if (size <= 0) - return NULL; - - if (!allocator) - return xmalloc(size); - - assert(allocator->malloc); - return allocator->malloc(size); -} - -void *avahi_malloc0(size_t size) { - void *p; - - if (size <= 0) - return NULL; - - if (!allocator) - return xcalloc(1, size); - - if (allocator->calloc) - return allocator->calloc(1, size); - - assert(allocator->malloc); - if ((p = allocator->malloc(size))) - memset(p, 0, size); - - return p; -} - -void avahi_free(void *p) { - - if (!p) - return; - - if (!allocator) { - free(p); - return; - } - - assert(allocator->free); - allocator->free(p); -} - -void *avahi_realloc(void *p, size_t size) { - - if (size == 0) { - avahi_free(p); - return NULL; - } - - if (!allocator) - return xrealloc(p, size); - - assert(allocator->realloc); - return allocator->realloc(p, size); -} - -char *avahi_strdup(const char *s) { - char *r; - size_t size; - - if (!s) - return NULL; - - size = strlen(s); - if (!(r = avahi_malloc(size+1))) - return NULL; - - memcpy(r, s, size+1); - return r; -} - -char *avahi_strndup(const char *s, size_t max) { - char *r; - size_t size; - const char *p; - - if (!s) - return NULL; - - for (p = s, size = 0; - size < max && *p; - p++, size++); - - if (!(r = avahi_new(char, size+1))) - return NULL; - - memcpy(r, s, size); - r[size] = 0; - return r; -} - -/* Change the allocator */ -void avahi_set_allocator(const AvahiAllocator *a) { - allocator = a; -} - -char *avahi_strdup_vprintf(const char *fmt, va_list ap) { - size_t len = 80; - char *buf; - - assert(fmt); - - if (!(buf = avahi_malloc(len))) - return NULL; - - for (;;) { - int n; - char *nbuf; - va_list ap2; - - va_copy (ap2, ap); - n = vsnprintf(buf, len, fmt, ap2); - va_end (ap2); - - if (n >= 0 && n < (int) len) - return buf; - - if (n >= 0) - len = n+1; - else - len *= 2; - - if (!(nbuf = avahi_realloc(buf, len))) { - avahi_free(buf); - return NULL; - } - - buf = nbuf; - } -} - -char *avahi_strdup_printf(const char *fmt, ... ) { - char *s; - va_list ap; - - assert(fmt); - - va_start(ap, fmt); - s = avahi_strdup_vprintf(fmt, ap); - va_end(ap); - - return s; -} - -void *avahi_memdup(const void *s, size_t l) { - void *p; - assert(s); - - if (!(p = avahi_malloc(l))) - return NULL; - - memcpy(p, s, l); - return p; -} diff --git a/avahi-common/malloc.h b/avahi-common/malloc.h deleted file mode 100644 index ffaac3f..0000000 --- a/avahi-common/malloc.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef foomallochfoo -#define foomallochfoo - -/*** - 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. -***/ - -/** \file malloc.h Memory allocation */ - -#include -#include -#include -#include - -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Allocate some memory, just like the libc malloc() */ -void *avahi_malloc(size_t size) AVAHI_GCC_ALLOC_SIZE(1); - -/** Similar to avahi_malloc() but set the memory to zero */ -void *avahi_malloc0(size_t size) AVAHI_GCC_ALLOC_SIZE(1); - -/** Free some memory */ -void avahi_free(void *p); - -/** Similar to libc's realloc() */ -void *avahi_realloc(void *p, size_t size) AVAHI_GCC_ALLOC_SIZE(2); - -/** Internal helper for avahi_new() */ -static inline void* AVAHI_GCC_ALLOC_SIZE2(1,2) avahi_new_internal(unsigned n, size_t k) { - assert(n < INT_MAX/k); - return avahi_malloc(n*k); -} - -/** Allocate n new structures of the specified type. */ -#define avahi_new(type, n) ((type*) avahi_new_internal((n), sizeof(type))) - -/** Internal helper for avahi_new0() */ -static inline void* AVAHI_GCC_ALLOC_SIZE2(1,2) avahi_new0_internal(unsigned n, size_t k) { - assert(n < INT_MAX/k); - return avahi_malloc0(n*k); -} - -/** Same as avahi_new() but set the memory to zero */ -#define avahi_new0(type, n) ((type*) avahi_new0_internal((n), sizeof(type))) - -/** Just like libc's strdup() */ -char *avahi_strdup(const char *s); - -/** Just like libc's strndup() */ -char *avahi_strndup(const char *s, size_t l); - -/** Duplicate the given memory block into a new one allocated with avahi_malloc() */ -void *avahi_memdup(const void *s, size_t l) AVAHI_GCC_ALLOC_SIZE(2); - -/** Wraps allocator functions */ -typedef struct AvahiAllocator { - void* (*malloc)(size_t size) AVAHI_GCC_ALLOC_SIZE(1); - void (*free)(void *p); - void* (*realloc)(void *p, size_t size) AVAHI_GCC_ALLOC_SIZE(2); - void* (*calloc)(size_t nmemb, size_t size) AVAHI_GCC_ALLOC_SIZE2(1,2); /**< May be NULL */ -} AvahiAllocator; - -/** Change the allocator. May be NULL to return to default (libc) - * allocators. The structure is not copied! */ -void avahi_set_allocator(const AvahiAllocator *a); - -/** Like sprintf() but store the result in a freshly allocated buffer. Free this with avahi_free() */ -char *avahi_strdup_printf(const char *fmt, ... ) AVAHI_GCC_PRINTF_ATTR12; - -/** \cond fulldocs */ -/** Same as avahi_strdup_printf() but take a va_list instead of varargs */ -char *avahi_strdup_vprintf(const char *fmt, va_list ap); -/** \endcond */ - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/rlist.c b/avahi-common/rlist.c deleted file mode 100644 index 17dcb24..0000000 --- a/avahi-common/rlist.c +++ /dev/null @@ -1,62 +0,0 @@ -/*** - 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 "rlist.h" -#include "malloc.h" - -AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data) { - AvahiRList *n; - - if (!(n = avahi_new(AvahiRList, 1))) - return NULL; - - n->data = data; - - AVAHI_LLIST_PREPEND(AvahiRList, rlist, r, n); - return r; -} - -AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data) { - AvahiRList *n; - - for (n = r; n; n = n->rlist_next) - - if (n->data == data) { - AVAHI_LLIST_REMOVE(AvahiRList, rlist, r, n); - avahi_free(n); - break; - } - - return r; -} - -AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n) { - assert(n); - - AVAHI_LLIST_REMOVE(AvahiRList, rlist, r, n); - avahi_free(n); - - return r; -} diff --git a/avahi-common/rlist.h b/avahi-common/rlist.h deleted file mode 100644 index 9bcc1d5..0000000 --- a/avahi-common/rlist.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef foorlistfoo -#define foorlistfoo - -/*** - 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. -***/ - -/** \file rlist.h A simple linked list implementation */ - -#include "llist.h" - -AVAHI_C_DECL_BEGIN - -/** A doubly linked list type */ -typedef struct AvahiRList AvahiRList; - -/** A doubly linked list type */ -struct AvahiRList { - AVAHI_LLIST_FIELDS(AvahiRList, rlist); - void *data; -}; - -/** Prepend a new item to the beginning of the list and return the new beginning */ -AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data); - -/** Remove the first occurence of the specified item from the list and return the new beginning */ -AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data); - -/** Remove the specified item from the list and return the new beginning */ -AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/simple-watch.c b/avahi-common/simple-watch.c deleted file mode 100644 index 8df18dd..0000000 --- a/avahi-common/simple-watch.c +++ /dev/null @@ -1,649 +0,0 @@ -/*** - 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 "llist.h" -#include "malloc.h" -#include "timeval.h" -#include "simple-watch.h" - -struct AvahiWatch { - AvahiSimplePoll *simple_poll; - int dead; - - int idx; - struct pollfd pollfd; - - AvahiWatchCallback callback; - void *userdata; - - AVAHI_LLIST_FIELDS(AvahiWatch, watches); -}; - -struct AvahiTimeout { - AvahiSimplePoll *simple_poll; - int dead; - - int enabled; - struct timeval expiry; - - AvahiTimeoutCallback callback; - void *userdata; - - AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts); -}; - -struct AvahiSimplePoll { - AvahiPoll api; - AvahiPollFunc poll_func; - void *poll_func_userdata; - - struct pollfd* pollfds; - int n_pollfds, max_pollfds, rebuild_pollfds; - - int watch_req_cleanup, timeout_req_cleanup; - int quit; - int events_valid; - - int n_watches; - AVAHI_LLIST_HEAD(AvahiWatch, watches); - AVAHI_LLIST_HEAD(AvahiTimeout, timeouts); - - int wakeup_pipe[2]; - int wakeup_issued; - - int prepared_timeout; - - enum { - STATE_INIT, - STATE_PREPARING, - STATE_PREPARED, - STATE_RUNNING, - STATE_RAN, - STATE_DISPATCHING, - STATE_DISPATCHED, - STATE_QUIT, - STATE_FAILURE - } state; -}; - -void avahi_simple_poll_wakeup(AvahiSimplePoll *s) { - char c = 'W'; - assert(s); - - write(s->wakeup_pipe[1], &c, sizeof(c)); - s->wakeup_issued = 1; -} - -static void clear_wakeup(AvahiSimplePoll *s) { - char c[10]; /* Read ten at a time */ - - if (!s->wakeup_issued) - return; - - s->wakeup_issued = 0; - - for(;;) - if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c)) - break; -} - -static int set_nonblock(int fd) { - int n; - - assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFL)) < 0) - return -1; - - if (n & O_NONBLOCK) - return 0; - - return fcntl(fd, F_SETFL, n|O_NONBLOCK); -} - -static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) { - AvahiWatch *w; - AvahiSimplePoll *s; - - assert(api); - assert(fd >= 0); - assert(callback); - - s = api->userdata; - assert(s); - - if (!(w = avahi_new(AvahiWatch, 1))) - return NULL; - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(s); - - w->simple_poll = s; - w->dead = 0; - - w->pollfd.fd = fd; - w->pollfd.events = event; - w->pollfd.revents = 0; - - w->callback = callback; - w->userdata = userdata; - - w->idx = -1; - s->rebuild_pollfds = 1; - - AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w); - s->n_watches++; - - return w; -} - -static void watch_update(AvahiWatch *w, AvahiWatchEvent events) { - assert(w); - assert(!w->dead); - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(w->simple_poll); - - w->pollfd.events = events; - - if (w->idx != -1) { - assert(w->simple_poll); - w->simple_poll->pollfds[w->idx] = w->pollfd; - } else - w->simple_poll->rebuild_pollfds = 1; -} - -static AvahiWatchEvent watch_get_events(AvahiWatch *w) { - assert(w); - assert(!w->dead); - - if (w->idx != -1 && w->simple_poll->events_valid) - return w->simple_poll->pollfds[w->idx].revents; - - return 0; -} - -static void remove_pollfd(AvahiWatch *w) { - assert(w); - - if (w->idx == -1) - return; - - w->simple_poll->rebuild_pollfds = 1; -} - -static void watch_free(AvahiWatch *w) { - assert(w); - - assert(!w->dead); - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(w->simple_poll); - - remove_pollfd(w); - - w->dead = 1; - w->simple_poll->n_watches --; - w->simple_poll->watch_req_cleanup = 1; -} - -static void destroy_watch(AvahiWatch *w) { - assert(w); - - remove_pollfd(w); - AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w); - - if (!w->dead) - w->simple_poll->n_watches --; - - avahi_free(w); -} - -static void cleanup_watches(AvahiSimplePoll *s, int all) { - AvahiWatch *w, *next; - assert(s); - - for (w = s->watches; w; w = next) { - next = w->watches_next; - - if (all || w->dead) - destroy_watch(w); - } - - s->timeout_req_cleanup = 0; -} - -static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) { - AvahiTimeout *t; - AvahiSimplePoll *s; - - assert(api); - assert(callback); - - s = api->userdata; - assert(s); - - if (!(t = avahi_new(AvahiTimeout, 1))) - return NULL; - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(s); - - t->simple_poll = s; - t->dead = 0; - - if ((t->enabled = !!tv)) - t->expiry = *tv; - - t->callback = callback; - t->userdata = userdata; - - AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t); - return t; -} - -static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { - assert(t); - assert(!t->dead); - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(t->simple_poll); - - if ((t->enabled = !!tv)) - t->expiry = *tv; -} - -static void timeout_free(AvahiTimeout *t) { - assert(t); - assert(!t->dead); - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(t->simple_poll); - - t->dead = 1; - t->simple_poll->timeout_req_cleanup = 1; -} - - -static void destroy_timeout(AvahiTimeout *t) { - assert(t); - - AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t); - - avahi_free(t); -} - -static void cleanup_timeouts(AvahiSimplePoll *s, int all) { - AvahiTimeout *t, *next; - assert(s); - - for (t = s->timeouts; t; t = next) { - next = t->timeouts_next; - - if (all || t->dead) - destroy_timeout(t); - } - - s->timeout_req_cleanup = 0; -} - -AvahiSimplePoll *avahi_simple_poll_new(void) { - AvahiSimplePoll *s; - - if (!(s = avahi_new(AvahiSimplePoll, 1))) - return NULL; - - if (pipe(s->wakeup_pipe) < 0) { - avahi_free(s); - return NULL; - } - - set_nonblock(s->wakeup_pipe[0]); - set_nonblock(s->wakeup_pipe[1]); - - s->api.userdata = s; - - s->api.watch_new = watch_new; - s->api.watch_free = watch_free; - s->api.watch_update = watch_update; - s->api.watch_get_events = watch_get_events; - - s->api.timeout_new = timeout_new; - s->api.timeout_free = timeout_free; - s->api.timeout_update = timeout_update; - - s->pollfds = NULL; - s->max_pollfds = s->n_pollfds = 0; - s->rebuild_pollfds = 1; - s->quit = 0; - s->n_watches = 0; - s->events_valid = 0; - - s->watch_req_cleanup = 0; - s->timeout_req_cleanup = 0; - - s->prepared_timeout = 0; - - s->state = STATE_INIT; - - s->wakeup_issued = 0; - - avahi_simple_poll_set_func(s, NULL, NULL); - - AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches); - AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts); - - return s; -} - -void avahi_simple_poll_free(AvahiSimplePoll *s) { - assert(s); - - cleanup_timeouts(s, 1); - cleanup_watches(s, 1); - assert(s->n_watches == 0); - - avahi_free(s->pollfds); - - if (s->wakeup_pipe[0] >= 0) - close(s->wakeup_pipe[0]); - - if (s->wakeup_pipe[1] >= 0) - close(s->wakeup_pipe[1]); - - avahi_free(s); -} - -static int rebuild(AvahiSimplePoll *s) { - AvahiWatch *w; - int idx; - - assert(s); - - if (s->n_watches+1 > s->max_pollfds) { - struct pollfd *n; - - s->max_pollfds = s->n_watches + 10; - - if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds))) - return -1; - - s->pollfds = n; - } - - - s->pollfds[0].fd = s->wakeup_pipe[0]; - s->pollfds[0].events = POLLIN; - s->pollfds[0].revents = 0; - - idx = 1; - - for (w = s->watches; w; w = w->watches_next) { - - if(w->dead) - continue; - - assert(w->idx < s->max_pollfds); - s->pollfds[w->idx = idx++] = w->pollfd; - } - - s->n_pollfds = idx; - s->events_valid = 0; - s->rebuild_pollfds = 0; - - return 0; -} - -static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) { - AvahiTimeout *t, *n = NULL; - assert(s); - - for (t = s->timeouts; t; t = t->timeouts_next) { - - if (t->dead || !t->enabled) - continue; - - if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0) - n = t; - } - - return n; -} - -static void timeout_callback(AvahiTimeout *t) { - assert(t); - assert(!t->dead); - assert(t->enabled); - - t->enabled = 0; - t->callback(t, t->userdata); -} - -int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) { - AvahiTimeout *next_timeout; - - assert(s); - assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE); - s->state = STATE_PREPARING; - - /* Clear pending wakeup requests */ - clear_wakeup(s); - - /* Cleanup things first */ - if (s->watch_req_cleanup) - cleanup_watches(s, 0); - - if (s->timeout_req_cleanup) - cleanup_timeouts(s, 0); - - /* Check whether a quit was requested */ - if (s->quit) { - s->state = STATE_QUIT; - return 1; - } - - /* Do we need to rebuild our array of pollfds? */ - if (s->rebuild_pollfds) - if (rebuild(s) < 0) { - s->state = STATE_FAILURE; - return -1; - } - - /* Calculate the wakeup time */ - if ((next_timeout = find_next_timeout(s))) { - struct timeval now; - int t; - AvahiUsec usec; - - if (next_timeout->expiry.tv_sec == 0 && - next_timeout->expiry.tv_usec == 0) { - - /* Just a shortcut so that we don't need to call gettimeofday() */ - timeout = 0; - goto finish; - } - - gettimeofday(&now, NULL); - usec = avahi_timeval_diff(&next_timeout->expiry, &now); - - if (usec <= 0) { - /* Timeout elapsed */ - - timeout = 0; - goto finish; - } - - /* Calculate sleep time. We add 1ms because otherwise we'd - * wake up too early most of the time */ - t = (int) (usec / 1000) + 1; - - if (timeout < 0 || timeout > t) - timeout = t; - } - -finish: - s->prepared_timeout = timeout; - s->state = STATE_PREPARED; - return 0; -} - -int avahi_simple_poll_run(AvahiSimplePoll *s) { - assert(s); - assert(s->state == STATE_PREPARED || s->state == STATE_FAILURE); - - s->state = STATE_RUNNING; - - for (;;) { - errno = 0; - - if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) { - - if (errno == EINTR) - continue; - - s->state = STATE_FAILURE; - return -1; - } - - break; - } - - /* The poll events are now valid again */ - s->events_valid = 1; - - /* Update state */ - s->state = STATE_RAN; - return 0; -} - -int avahi_simple_poll_dispatch(AvahiSimplePoll *s) { - AvahiTimeout *next_timeout; - AvahiWatch *w; - - assert(s); - assert(s->state == STATE_RAN); - s->state = STATE_DISPATCHING; - - /* We execute only on callback in every iteration */ - - /* Check whether the wakeup time has been reached now */ - if ((next_timeout = find_next_timeout(s))) { - - if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) { - - /* Just a shortcut so that we don't need to call gettimeofday() */ - timeout_callback(next_timeout); - goto finish; - } - - if (avahi_age(&next_timeout->expiry) >= 0) { - - /* Timeout elapsed */ - timeout_callback(next_timeout); - goto finish; - } - } - - /* Look for some kind of I/O event */ - for (w = s->watches; w; w = w->watches_next) { - - if (w->dead) - continue; - - assert(w->idx >= 0); - assert(w->idx < s->n_pollfds); - - if (s->pollfds[w->idx].revents != 0) { - w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata); - goto finish; - } - } - -finish: - - s->state = STATE_DISPATCHED; - return 0; -} - -int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) { - int r; - - if ((r = avahi_simple_poll_prepare(s, timeout)) != 0) - return r; - - if ((r = avahi_simple_poll_run(s)) != 0) - return r; - - if ((r = avahi_simple_poll_dispatch(s)) != 0) - return r; - - return 0; -} - -void avahi_simple_poll_quit(AvahiSimplePoll *s) { - assert(s); - - s->quit = 1; - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(s); -} - -const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) { - assert(s); - - return &s->api; -} - -static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, AVAHI_GCC_UNUSED void *userdata) { - return poll(ufds, nfds, timeout); -} - -void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) { - assert(s); - - s->poll_func = func ? func : system_poll; - s->poll_func_userdata = func ? userdata : NULL; - - /* If there is a background thread running the poll() for us, tell it to exit the poll() */ - avahi_simple_poll_wakeup(s); -} - -int avahi_simple_poll_loop(AvahiSimplePoll *s) { - int r; - - assert(s); - - for (;;) - if ((r = avahi_simple_poll_iterate(s, -1)) != 0) - if (r >= 0 || errno != EINTR) - return r; -} diff --git a/avahi-common/simple-watch.h b/avahi-common/simple-watch.h deleted file mode 100644 index 72c1905..0000000 --- a/avahi-common/simple-watch.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef foosimplewatchhfoo -#define foosimplewatchhfoo - -/*** - 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. -***/ - -/** \file simple-watch.h Simple poll() based main loop implementation */ - -#include -#include -#include - -AVAHI_C_DECL_BEGIN - -/** A main loop object. Main loops of this type aren't very flexible - * since they only support a single wakeup type. Nevertheless it - * should suffice for small test and example applications. */ -typedef struct AvahiSimplePoll AvahiSimplePoll; - -/** Create a new main loop object */ -AvahiSimplePoll *avahi_simple_poll_new(void); - -/** Free a main loop object */ -void avahi_simple_poll_free(AvahiSimplePoll *s); - -/** Return the abstracted poll API object for this main loop - * object. The is will return the same pointer each time it is - * called. */ -const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s); - -/** Run a single main loop iteration of this main loop. If sleep_time -is < 0 this will block until any of the registered events happens, -then it will execute the attached callback function. If sleep_time is -0 the routine just checks if any event is pending. If yes the attached -callback function is called, otherwise the function returns -immediately. If sleep_time > 0 the function will block for at most the -specified time in msecs. Returns -1 on error, 0 on success and 1 if a -quit request has been scheduled. Usually this function should be called -in a loop until it returns a non-zero value*/ -int avahi_simple_poll_iterate(AvahiSimplePoll *s, int sleep_time); - -/** Request that the main loop quits. If this is called the next - call to avahi_simple_poll_iterate() will return 1 */ -void avahi_simple_poll_quit(AvahiSimplePoll *s); - -/** Prototype for a poll() type function */ -typedef int (*AvahiPollFunc)(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata); - -/** Replace the internally used poll() function. By default the system's poll() will be used */ -void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata); - -/** The first stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ -int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout); - -/** The second stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ -int avahi_simple_poll_run(AvahiSimplePoll *s); - -/** The third and final stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ -int avahi_simple_poll_dispatch(AvahiSimplePoll *s); - -/** Call avahi_simple_poll_iterate() in a loop and return if it returns non-zero */ -int avahi_simple_poll_loop(AvahiSimplePoll *s); - -/** Wakeup the main loop. (for threaded environments) */ -void avahi_simple_poll_wakeup(AvahiSimplePoll *s); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/strlst-test.c b/avahi-common/strlst-test.c deleted file mode 100644 index 0945b37..0000000 --- a/avahi-common/strlst-test.c +++ /dev/null @@ -1,127 +0,0 @@ -/*** - 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 "strlst.h" -#include "malloc.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - char *t, *v; - uint8_t data[1024]; - AvahiStringList *a = NULL, *b, *p; - size_t size, n; - int r; - - a = avahi_string_list_new("prefix", "a", "b", NULL); - - a = avahi_string_list_add(a, "start"); - a = avahi_string_list_add(a, "foo=99"); - a = avahi_string_list_add(a, "bar"); - a = avahi_string_list_add(a, ""); - a = avahi_string_list_add(a, ""); - a = avahi_string_list_add(a, "quux"); - a = avahi_string_list_add(a, ""); - a = avahi_string_list_add_arbitrary(a, (const uint8_t*) "null\0null", 9); - a = avahi_string_list_add_printf(a, "seven=%i %c", 7, 'x'); - a = avahi_string_list_add_pair(a, "blubb", "blaa"); - a = avahi_string_list_add_pair(a, "uxknurz", NULL); - a = avahi_string_list_add_pair_arbitrary(a, "uxknurz2", (const uint8_t*) "blafasel\0oerks", 14); - - a = avahi_string_list_add(a, "end"); - - t = avahi_string_list_to_string(a); - printf("--%s--\n", t); - avahi_free(t); - - n = avahi_string_list_serialize(a, NULL, 0); - size = avahi_string_list_serialize(a, data, sizeof(data)); - assert(size == n); - - printf("%zu\n", size); - - for (t = (char*) data, n = 0; n < size; n++, t++) { - if (*t <= 32) - printf("(%u)", *t); - else - printf("%c", *t); - } - - printf("\n"); - - assert(avahi_string_list_parse(data, size, &b) == 0); - - printf("equal: %i\n", avahi_string_list_equal(a, b)); - - t = avahi_string_list_to_string(b); - printf("--%s--\n", t); - avahi_free(t); - - avahi_string_list_free(b); - - b = avahi_string_list_copy(a); - - assert(avahi_string_list_equal(a, b)); - - t = avahi_string_list_to_string(b); - printf("--%s--\n", t); - avahi_free(t); - - p = avahi_string_list_find(a, "seven"); - assert(p); - - r = avahi_string_list_get_pair(p, &t, &v, NULL); - assert(r >= 0); - assert(t); - assert(v); - - printf("<%s>=<%s>\n", t, v); - avahi_free(t); - avahi_free(v); - - p = avahi_string_list_find(a, "quux"); - assert(p); - - r = avahi_string_list_get_pair(p, &t, &v, NULL); - assert(r >= 0); - assert(t); - assert(!v); - - printf("<%s>=<%s>\n", t, v); - avahi_free(t); - avahi_free(v); - - avahi_string_list_free(a); - avahi_string_list_free(b); - - n = avahi_string_list_serialize(NULL, NULL, 0); - size = avahi_string_list_serialize(NULL, data, sizeof(data)); - assert(size == 1); - assert(size == n); - - assert(avahi_string_list_parse(data, size, &a) == 0); - assert(!a); - - return 0; -} diff --git a/avahi-common/strlst.c b/avahi-common/strlst.c deleted file mode 100644 index b861cf8..0000000 --- a/avahi-common/strlst.c +++ /dev/null @@ -1,505 +0,0 @@ -/*** - 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 "strlst.h" -#include "malloc.h" -#include "defs.h" - -AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size) { - AvahiStringList *n; - - if (!(n = avahi_malloc(sizeof(AvahiStringList) + size))) - return NULL; - - n->next = l; - n->size = size; - - /* NUL terminate strings, just to make sure */ - n->text[size] = 0; - - return n; -} - -AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t*text, size_t size) { - AvahiStringList *n; - - assert(size == 0 || text); - - if (!(n = avahi_string_list_add_anonymous(l, size))) - return NULL; - - if (size > 0) - memcpy(n->text, text, size); - - return n; -} - -AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text) { - assert(text); - - return avahi_string_list_add_arbitrary(l, (const uint8_t*) text, strlen(text)); -} - -int avahi_string_list_parse(const void* data, size_t size, AvahiStringList **ret) { - const uint8_t *c; - AvahiStringList *r = NULL; - - assert(data); - assert(ret); - - c = data; - while (size > 0) { - size_t k; - - k = *(c++); - size--; - - if (k > size) - goto fail; /* Overflow */ - - if (k > 0) { /* Ignore empty strings */ - AvahiStringList *n; - - if (!(n = avahi_string_list_add_arbitrary(r, c, k))) - goto fail; /* OOM */ - - r = n; - } - - c += k; - size -= k; - } - - *ret = r; - - return 0; - -fail: - avahi_string_list_free(r); - return -1; -} - -void avahi_string_list_free(AvahiStringList *l) { - AvahiStringList *n; - - while (l) { - n = l->next; - avahi_free(l); - l = n; - } -} - -AvahiStringList* avahi_string_list_reverse(AvahiStringList *l) { - AvahiStringList *r = NULL, *n; - - while (l) { - n = l->next; - l->next = r; - r = l; - l = n; - } - - return r; -} - -char* avahi_string_list_to_string(AvahiStringList *l) { - AvahiStringList *n; - size_t s = 0; - char *t, *e; - - for (n = l; n; n = n->next) { - if (n != l) - s ++; - - s += n->size+2; - } - - if (!(t = e = avahi_new(char, s+1))) - return NULL; - - l = avahi_string_list_reverse(l); - - for (n = l; n; n = n->next) { - if (n != l) - *(e++) = ' '; - - *(e++) = '"'; - strncpy(e, (char*) n->text, n->size); - e[n->size] = 0; - e = strchr(e, 0); - *(e++) = '"'; - - assert(e); - } - - l = avahi_string_list_reverse(l); - - *e = 0; - - return t; -} - -size_t avahi_string_list_serialize(AvahiStringList *l, void *data, size_t size) { - size_t used = 0; - - if (data) { - AvahiStringList *n; - uint8_t *c; - - l = avahi_string_list_reverse(l); - c = data; - - for (n = l; size > 1 && n; n = n->next) { - size_t k; - - if ((k = n->size) == 0) - /* Skip empty strings */ - continue; - - if (k > 255) - /* Truncate strings at 255 characters */ - k = 255; - - if (k > size-1) - /* Make sure this string fits in */ - k = size-1; - - *(c++) = (uint8_t) k; - memcpy(c, n->text, k); - c += k; - - used += 1 + k; - size -= 1 + k; - } - - l = avahi_string_list_reverse(l); - - if (used == 0 && size > 0) { - - /* Empty lists are treated specially. To comply with - * section 6.1 of the DNS-SD spec, we return a single - * empty string (i.e. a NUL byte)*/ - - *(uint8_t*) data = 0; - used = 1; - } - - } else { - AvahiStringList *n; - - for (n = l; n; n = n->next) { - size_t k; - - if ((k = n->size) == 0) - continue; - - if (k > 255) - k = 255; - - used += 1+k; - } - - if (used == 0) - used = 1; - } - - return used; -} - -int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b) { - - for (;;) { - if (!a && !b) - return 1; - - if (!a || !b) - return 0; - - if (a->size != b->size) - return 0; - - if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0) - return 0; - - a = a->next; - b = b->next; - } -} - -AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) { - va_list va; - - va_start(va, r); - r = avahi_string_list_add_many_va(r, va); - va_end(va); - - return r; -} - -AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) { - const char *txt; - - while ((txt = va_arg(va, const char*))) - r = avahi_string_list_add(r, txt); - - return r; -} - -AvahiStringList *avahi_string_list_new(const char *txt, ...) { - va_list va; - AvahiStringList *r = NULL; - - if (txt) { - r = avahi_string_list_add(r, txt); - - va_start(va, txt); - r = avahi_string_list_add_many_va(r, va); - va_end(va); - } - - return r; -} - -AvahiStringList *avahi_string_list_new_va(va_list va) { - return avahi_string_list_add_many_va(NULL, va); -} - -AvahiStringList *avahi_string_list_copy(const AvahiStringList *l) { - AvahiStringList *r = NULL; - - for (; l; l = l->next) - if (!(r = avahi_string_list_add_arbitrary(r, l->text, l->size))) { - avahi_string_list_free(r); - return NULL; - } - - return avahi_string_list_reverse(r); -} - -AvahiStringList *avahi_string_list_new_from_array(const char *array[], int length) { - AvahiStringList *r = NULL; - int i; - - assert(array); - - for (i = 0; length >= 0 ? i < length : !!array[i]; i++) - r = avahi_string_list_add(r, array[i]); - - return r; -} - -unsigned avahi_string_list_length(const AvahiStringList *l) { - unsigned n = 0; - - for (; l; l = l->next) - n++; - - return n; -} - -AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va) { - size_t len = 80; - AvahiStringList *r; - - assert(format); - - if (!(r = avahi_malloc(sizeof(AvahiStringList) + len))) - return NULL; - - for (;;) { - int n; - AvahiStringList *nr; - va_list va2; - - va_copy(va2, va); - n = vsnprintf((char*) r->text, len, format, va2); - va_end(va2); - - if (n >= 0 && n < (int) len) - break; - - if (n >= 0) - len = n+1; - else - len *= 2; - - if (!(nr = avahi_realloc(r, sizeof(AvahiStringList) + len))) { - avahi_free(r); - return NULL; - } - - r = nr; - } - - r->next = l; - r->size = strlen((char*) r->text); - - return r; -} - -AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) { - va_list va; - - assert(format); - - va_start(va, format); - l = avahi_string_list_add_vprintf(l, format, va); - va_end(va); - - return l; -} - -AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key) { - size_t n; - - assert(key); - n = strlen(key); - - for (; l; l = l->next) { - if (strcasecmp((char*) l->text, key) == 0) - return l; - - if (strncasecmp((char*) l->text, key, n) == 0 && l->text[n] == '=') - return l; - } - - return NULL; -} - -AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value) { - assert(key); - - if (value) - return avahi_string_list_add_printf(l, "%s=%s", key, value); - else - return avahi_string_list_add(l, key); -} - -AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size) { - size_t n; - assert(key); - - if (!value) - return avahi_string_list_add(l, key); - - n = strlen(key); - - if (!(l = avahi_string_list_add_anonymous(l, n + 1 + size))) - return NULL; - - memcpy(l->text, key, n); - l->text[n] = '='; - memcpy(l->text + n + 1, value, size); - - return l; -} - -int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size) { - char *e; - - assert(l); - - if (!(e = memchr(l->text, '=', l->size))) { - - if (key) - if (!(*key = avahi_strdup((char*) l->text))) - return -1; - - if (value) - *value = NULL; - - if (size) - *size = 0; - - } else { - size_t n; - - if (key) - if (!(*key = avahi_strndup((char*) l->text, e - (char *) l->text))) - return -1; - - e++; /* Advance after '=' */ - - n = l->size - (e - (char*) l->text); - - if (value) { - - if (!(*value = avahi_memdup(e, n+1))) { - if (key) - avahi_free(*key); - return -1; - } - - (*value)[n] = 0; - } - - if (size) - *size = n; - } - - return 0; -} - -AvahiStringList *avahi_string_list_get_next(AvahiStringList *l) { - assert(l); - return l->next; -} - -uint8_t *avahi_string_list_get_text(AvahiStringList *l) { - assert(l); - return l->text; -} - -size_t avahi_string_list_get_size(AvahiStringList *l) { - assert(l); - return l->size; -} - -uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l) { - AvahiStringList *f; - char *value = NULL, *end = NULL; - uint32_t ret; - - if (!(f = avahi_string_list_find(l, AVAHI_SERVICE_COOKIE))) - return AVAHI_SERVICE_COOKIE_INVALID; - - if (avahi_string_list_get_pair(f, NULL, &value, NULL) < 0 || !value) - return AVAHI_SERVICE_COOKIE_INVALID; - - ret = (uint32_t) strtoll(value, &end, 0); - - if (*value && end && *end != 0) { - avahi_free(value); - return AVAHI_SERVICE_COOKIE_INVALID; - } - - avahi_free(value); - - return ret; -} diff --git a/avahi-common/strlst.h b/avahi-common/strlst.h deleted file mode 100644 index 94adcea..0000000 --- a/avahi-common/strlst.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef footxtlisthfoo -#define footxtlisthfoo - -/*** - 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. -***/ - -/** \file strlst.h Implementation of a data type to store lists of strings */ - -#include -#include -#include - -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Linked list of strings that can contain any number of binary - * characters, including NUL bytes. An empty list is created by - * assigning a NULL to a pointer to AvahiStringList. The string list - * is stored in reverse order, so that appending to the string list is - * effectively a prepending to the linked list. This object is used - * primarily for storing DNS TXT record data. */ -typedef struct AvahiStringList { - struct AvahiStringList *next; /**< Pointer to the next linked list element */ - size_t size; /**< Size of text[] */ - uint8_t text[1]; /**< Character data */ -} AvahiStringList; - -/** @{ \name Construction and destruction */ - -/** Create a new string list by taking a variable list of NUL - * terminated strings. The strings are copied using g_strdup(). The - * argument list must be terminated by a NULL pointer. */ -AvahiStringList *avahi_string_list_new(const char *txt, ...) AVAHI_GCC_SENTINEL; - -/** \cond fulldocs */ -/** Same as avahi_string_list_new() but pass a va_list structure */ -AvahiStringList *avahi_string_list_new_va(va_list va); -/** \endcond */ - -/** Create a new string list from a string array. The strings are - * copied using g_strdup(). length should contain the length of the - * array, or -1 if the array is NULL terminated*/ -AvahiStringList *avahi_string_list_new_from_array(const char **array, int length); - -/** Free a string list */ -void avahi_string_list_free(AvahiStringList *l); - -/** @} */ - -/** @{ \name Adding strings */ - -/** Append a NUL terminated string to the specified string list. The - * passed string is copied using g_strdup(). Returns the new list - * start. */ -AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text); - -/** Append a new NUL terminated formatted string to the specified string list */ -AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) AVAHI_GCC_PRINTF_ATTR23; - -/** \cond fulldocs */ -/** Append a new NUL terminated formatted string to the specified string list */ -AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va); -/** \endcond */ - -/** Append an arbitrary length byte string to the list. Returns the - * new list start. */ -AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t *text, size_t size); - -/** Append a new entry to the string list. The string is not filled -with data. The caller should fill in string data afterwards by writing -it to l->text, where l is the pointer returned by this function. This -function exists solely to optimize a few operations where otherwise -superfluous string copying would be necessary. */ -AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size); - -/** Same as avahi_string_list_add(), but takes a variable number of - * NUL terminated strings. The argument list must be terminated by a - * NULL pointer. Returns the new list start. */ -AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) AVAHI_GCC_SENTINEL; - -/** \cond fulldocs */ -/** Same as avahi_string_list_add_many(), but use a va_list - * structure. Returns the new list start. */ -AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); -/** \endcond */ - -/** @} */ - -/** @{ \name String list operations */ - -/** Convert the string list object to a single character string, - * seperated by spaces and enclosed in "". avahi_free() the result! This - * function doesn't work well with strings that contain NUL bytes. */ -char* avahi_string_list_to_string(AvahiStringList *l); - -/** \cond fulldocs */ -/** Serialize the string list object in a way that is compatible with - * the storing of DNS TXT records. Strings longer than 255 bytes are truncated. */ -size_t avahi_string_list_serialize(AvahiStringList *l, void * data, size_t size); - -/** Inverse of avahi_string_list_serialize() */ -int avahi_string_list_parse(const void *data, size_t size, AvahiStringList **ret); -/** \endcond */ - -/** Compare to string lists */ -int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b); - -/** Copy a string list */ -AvahiStringList *avahi_string_list_copy(const AvahiStringList *l); - -/** Reverse the string list. */ -AvahiStringList* avahi_string_list_reverse(AvahiStringList *l); - -/** Return the number of elements in the string list */ -unsigned avahi_string_list_length(const AvahiStringList *l); - -/** @} */ - -/** @{ \name Accessing items */ - -/** Returns the next item in the string list */ -AvahiStringList *avahi_string_list_get_next(AvahiStringList *l); - -/** Returns the text for the current item */ -uint8_t *avahi_string_list_get_text(AvahiStringList *l); - -/** Returns the size of the current text */ -size_t avahi_string_list_get_size(AvahiStringList *l); - -/** @} */ - -/** @{ \name DNS-SD TXT pair handling */ - -/** Find the string list entry for the given DNS-SD TXT key */ -AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key); - -/** Return the DNS-SD TXT key and value for the specified string list - * item. If size is not NULL it will be filled with the length of - * value. (for strings containing NUL bytes). If the entry doesn't - * contain a value *value will be set to NULL. You need to - * avahi_free() the strings returned in *key and *value. */ -int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size); - -/** Add a new DNS-SD TXT key value pair to the string list. value may - * be NULL in case you want to specify a key without a value */ -AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value); - -/** Same as avahi_string_list_add_pair() but allow strings containing NUL bytes in *value. */ -AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size); - -/** @} */ - -/** \cond fulldocs */ -/** Try to find a magic service cookie in the specified DNS-SD string - * list. Or return AVAHI_SERVICE_COOKIE_INVALID if none is found. */ -uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l); -/** \endcond */ - -AVAHI_C_DECL_END - -#endif - diff --git a/avahi-common/thread-watch.c b/avahi-common/thread-watch.c deleted file mode 100644 index c0cadeb..0000000 --- a/avahi-common/thread-watch.c +++ /dev/null @@ -1,186 +0,0 @@ -/*** - 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 "llist.h" -#include "malloc.h" -#include "timeval.h" -#include "simple-watch.h" -#include "thread-watch.h" - -struct AvahiThreadedPoll { - AvahiSimplePoll *simple_poll; - pthread_t thread_id; - pthread_mutex_t mutex; - int thread_running; - int retval; -}; - -static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { - pthread_mutex_t *mutex = userdata; - int r; - - /* Before entering poll() we unlock the mutex, so that - * avahi_simple_poll_quit() can succeed from another thread. */ - - pthread_mutex_unlock(mutex); - r = poll(ufds, nfds, timeout); - pthread_mutex_lock(mutex); - - return r; -} - -static void* thread(void *userdata){ - AvahiThreadedPoll *p = userdata; - sigset_t mask; - - /* Make sure that signals are delivered to the main thread */ - sigfillset(&mask); - pthread_sigmask(SIG_BLOCK, &mask, NULL); - - pthread_mutex_lock(&p->mutex); - p->retval = avahi_simple_poll_loop(p->simple_poll); - pthread_mutex_unlock(&p->mutex); - - return NULL; -} - -AvahiThreadedPoll *avahi_threaded_poll_new(void) { - AvahiThreadedPoll *p; - - if (!(p = avahi_new(AvahiThreadedPoll, 1))) - goto fail; /* OOM */ - - if (!(p->simple_poll = avahi_simple_poll_new())) - goto fail; - - pthread_mutex_init(&p->mutex, NULL); - - avahi_simple_poll_set_func(p->simple_poll, poll_func, &p->mutex); - - p->thread_running = 0; - - return p; - -fail: - if (p) { - if (p->simple_poll) { - avahi_simple_poll_free(p->simple_poll); - pthread_mutex_destroy(&p->mutex); - } - - avahi_free(p); - } - - return NULL; -} - -void avahi_threaded_poll_free(AvahiThreadedPoll *p) { - assert(p); - - /* Make sure that this function is not called from the helper thread */ - assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); - - if (p->thread_running) - avahi_threaded_poll_stop(p); - - if (p->simple_poll) - avahi_simple_poll_free(p->simple_poll); - - pthread_mutex_destroy(&p->mutex); - avahi_free(p); -} - -const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p) { - assert(p); - - return avahi_simple_poll_get(p->simple_poll); -} - -int avahi_threaded_poll_start(AvahiThreadedPoll *p) { - assert(p); - - assert(!p->thread_running); - - if (pthread_create(&p->thread_id, NULL, thread, p) < 0) - return -1; - - p->thread_running = 1; - - return 0; -} - -int avahi_threaded_poll_stop(AvahiThreadedPoll *p) { - assert(p); - - if (!p->thread_running) - return -1; - - /* Make sure that this function is not called from the helper thread */ - assert(!pthread_equal(pthread_self(), p->thread_id)); - - pthread_mutex_lock(&p->mutex); - avahi_simple_poll_quit(p->simple_poll); - pthread_mutex_unlock(&p->mutex); - - pthread_join(p->thread_id, NULL); - p->thread_running = 0; - - return p->retval; -} - -void avahi_threaded_poll_quit(AvahiThreadedPoll *p) { - assert(p); - - /* Make sure that this function is called from the helper thread */ - assert(pthread_equal(pthread_self(), p->thread_id)); - - avahi_simple_poll_quit(p->simple_poll); -} - -void avahi_threaded_poll_lock(AvahiThreadedPoll *p) { - assert(p); - - /* Make sure that this function is not called from the helper thread */ - assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); - - pthread_mutex_lock(&p->mutex); -} - -void avahi_threaded_poll_unlock(AvahiThreadedPoll *p) { - assert(p); - - /* Make sure that this function is not called from the helper thread */ - assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); - - pthread_mutex_unlock(&p->mutex); -} diff --git a/avahi-common/thread-watch.h b/avahi-common/thread-watch.h deleted file mode 100644 index dec0cf3..0000000 --- a/avahi-common/thread-watch.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef foothreadedwatchhfoo -#define foothreadedwatchhfoo - -/*** - 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. -***/ - -/** \file thread-watch.h Threaded poll() based main loop implementation */ - -#include -#include -#include - -AVAHI_C_DECL_BEGIN - -/** A main loop object that runs an AvahiSimplePoll in its own thread. \since 0.6.4 */ -typedef struct AvahiThreadedPoll AvahiThreadedPoll; - -/** Create a new event loop object. This will allocate the internal - * AvahiSimplePoll, but will not start the helper thread. \since 0.6.4 */ -AvahiThreadedPoll *avahi_threaded_poll_new(void); - -/** Free an event loop object. This will stop the associated event loop - * thread (if it is running). \since 0.6.4 */ -void avahi_threaded_poll_free(AvahiThreadedPoll *p); - -/** Return the abstracted poll API object for this event loop - * object. The will return the same pointer each time it is - * called. \since 0.6.4 */ -const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p); - -/** Start the event loop helper thread. After the thread has started - * you must make sure to access the event loop object - * (AvahiThreadedPoll, AvahiPoll and all its associated objects) - * synchronized, i.e. with proper locking. You may want to use - * avahi_threaded_poll_lock()/avahi_threaded_poll_unlock() for this, - * which will lock the the entire event loop. Please note that event - * loop callback functions are called from the event loop helper thread - * with that lock held, i.e. avahi_threaded_poll_lock() calls are not - * required from event callbacks. \since 0.6.4 */ -int avahi_threaded_poll_start(AvahiThreadedPoll *p); - -/** Request that the event loop quits and the associated thread - stops. Call this from outside the helper thread if you want to shut - it down. \since 0.6.4 */ -int avahi_threaded_poll_stop(AvahiThreadedPoll *p); - -/** Request that the event loop quits and the associated thread - stops. Call this from inside the helper thread if you want to shut it - down. \since 0.6.4 */ -void avahi_threaded_poll_quit(AvahiThreadedPoll *p); - -/** Lock the main loop object. Use this if you want to access the event - * loop objects (such as creating a new event source) from anything - * else but the event loop helper thread, i.e. from anything else but event - * loop callbacks \since 0.6.4 */ -void avahi_threaded_poll_lock(AvahiThreadedPoll *p); - -/** Unlock the event loop object, use this as counterpart to - * avahi_threaded_poll_lock() \since 0.6.4 */ -void avahi_threaded_poll_unlock(AvahiThreadedPoll *p); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/timeval-test.c b/avahi-common/timeval-test.c deleted file mode 100644 index 387c180..0000000 --- a/avahi-common/timeval-test.c +++ /dev/null @@ -1,43 +0,0 @@ -/*** - 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 "gccmacro.h" -#include "timeval.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - - struct timeval a = { 5, 5 }, b; - - b = a; - - printf("%li.%li\n", a.tv_sec, a.tv_usec); - avahi_timeval_add(&a, -50); - - printf("%li.%li\n", a.tv_sec, a.tv_usec); - - printf("%lli\n", (long long) avahi_timeval_diff(&a, &b)); - - return 0; -} diff --git a/avahi-common/timeval.c b/avahi-common/timeval.c deleted file mode 100644 index cdb0f09..0000000 --- a/avahi-common/timeval.c +++ /dev/null @@ -1,123 +0,0 @@ -/*** - 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 "timeval.h" - -int avahi_timeval_compare(const struct timeval *a, const struct timeval *b) { - assert(a); - assert(b); - - if (a->tv_sec < b->tv_sec) - return -1; - - if (a->tv_sec > b->tv_sec) - return 1; - - if (a->tv_usec < b->tv_usec) - return -1; - - if (a->tv_usec > b->tv_usec) - return 1; - - return 0; -} - -AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b) { - assert(a); - assert(b); - - if (avahi_timeval_compare(a, b) < 0) - return - avahi_timeval_diff(b, a); - - return ((AvahiUsec) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec; -} - -struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec) { - AvahiUsec u; - assert(a); - - u = usec + a->tv_usec; - - if (u < 0) { - a->tv_usec = (long) (1000000 + (u % 1000000)); - a->tv_sec += (long) (-1 + (u / 1000000)); - } else { - a->tv_usec = (long) (u % 1000000); - a->tv_sec += (long) (u / 1000000); - } - - return a; -} - -AvahiUsec avahi_age(const struct timeval *a) { - struct timeval now; - - assert(a); - - gettimeofday(&now, NULL); - - return avahi_timeval_diff(&now, a); -} - -struct timeval *avahi_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) { - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - static int last_rand; - static time_t timestamp = 0; - - time_t now; - int r; - - now = time(NULL); - - pthread_mutex_lock(&mutex); - if (now >= timestamp + 10) { - timestamp = now; - last_rand = rand(); - } - - r = last_rand; - - pthread_mutex_unlock(&mutex); - - /* We use the same jitter for 10 seconds. That way our - * time events elapse in bursts which has the advantage that - * packet data can be aggregated better */ - - avahi_timeval_add(tv, (AvahiUsec) (jitter*1000.0*r/(RAND_MAX+1.0))); - } - - return tv; -} - diff --git a/avahi-common/timeval.h b/avahi-common/timeval.h deleted file mode 100644 index 6470f01..0000000 --- a/avahi-common/timeval.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef footimevalhfoo -#define footimevalhfoo - -/*** - 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. -***/ - -/** \file timeval.h Functions to facilitate timeval handling */ - -#include -#include - -#include - -AVAHI_C_DECL_BEGIN - -/** A numeric data type for storing microsecond values. (signed 64bit integer) */ -typedef int64_t AvahiUsec; - -/** Compare two timeval structures and return a negative value when a < b, 0 when a == b and a positive value otherwise */ -int avahi_timeval_compare(const struct timeval *a, const struct timeval *b); - -/** Calculate the difference between two timeval structures as microsecond value */ -AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b); - -/** Add a number of microseconds to the specified timeval structure and return it. *a is modified. */ -struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec); - -/** Return the difference between the current time and *a. Positive if *a was earlier */ -AvahiUsec avahi_age(const struct timeval *a); - -/** Fill *tv with the current time plus "ms" milliseconds plus an - * extra jitter of "j" milliseconds. Pass 0 for j if you don't want - * the jitter */ -struct timeval *avahi_elapse_time(struct timeval *tv, unsigned ms, unsigned j); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/utf8-test.c b/avahi-common/utf8-test.c deleted file mode 100644 index d12ed58..0000000 --- a/avahi-common/utf8-test.c +++ /dev/null @@ -1,37 +0,0 @@ -/*** - 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 "utf8.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - - assert(avahi_utf8_valid("hallo")); - assert(!avahi_utf8_valid("üxknürz")); - assert(avahi_utf8_valid("üxknürz")); - - return 0; -} diff --git a/avahi-common/utf8.c b/avahi-common/utf8.c deleted file mode 100644 index ab10ba0..0000000 --- a/avahi-common/utf8.c +++ /dev/null @@ -1,110 +0,0 @@ -/* This file is based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. - * - * Copyright (C) 1999 Tom Tromey - * Copyright (C) 2000 Red Hat, Inc. - * - * This library 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. - * - * This library 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 this library; 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 "utf8.h" - -#define UNICODE_VALID(Char) \ - ((Char) < 0x110000 && \ - (((Char) & 0xFFFFF800) != 0xD800) && \ - ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ - ((Char) & 0xFFFE) != 0xFFFE) - - -#define CONTINUATION_CHAR \ - do { \ - if ((*(const unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */ \ - goto error; \ - val <<= 6; \ - val |= (*(const unsigned char *)p) & 0x3f; \ - } while(0) - - -const char * -avahi_utf8_valid (const char *str) - -{ - unsigned val = 0; - unsigned min = 0; - const char *p; - - for (p = str; *p; p++) - { - if (*(const unsigned char *)p < 128) - /* done */; - else - { - if ((*(const unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */ - { - if ( ((*(const unsigned char *)p & 0x1e) == 0)) - goto error; - p++; - if ( ((*(const unsigned char *)p & 0xc0) != 0x80)) /* 10xxxxxx */ - goto error; - } - else - { - if ((*(const unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */ - { - min = (1 << 11); - val = *(const unsigned char *)p & 0x0f; - goto TWO_REMAINING; - } - else if ((*(const unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */ - { - min = (1 << 16); - val = *(const unsigned char *)p & 0x07; - } - else - goto error; - - p++; - CONTINUATION_CHAR; - TWO_REMAINING: - p++; - CONTINUATION_CHAR; - p++; - CONTINUATION_CHAR; - - if ( (val < min)) - goto error; - - if ( (!UNICODE_VALID(val))) - goto error; - } - - continue; - - error: - return NULL; - } - } - - return str; -} diff --git a/avahi-common/utf8.h b/avahi-common/utf8.h deleted file mode 100644 index dc1ce4b..0000000 --- a/avahi-common/utf8.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef fooutf8hfoo -#define fooutf8hfoo - -/*** - 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 - -AVAHI_C_DECL_BEGIN - -const char *avahi_utf8_valid(const char *str); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-common/watch-test.c b/avahi-common/watch-test.c deleted file mode 100644 index 6c178ba..0000000 --- a/avahi-common/watch-test.c +++ /dev/null @@ -1,115 +0,0 @@ -/*** - 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 "watch.h" -#include "timeval.h" -#include "gccmacro.h" - -static const AvahiPoll *api = NULL; - -#ifndef USE_THREAD -#include "simple-watch.h" -static AvahiSimplePoll *simple_poll = NULL; -#else -#include "thread-watch.h" -static AvahiThreadedPoll *threaded_poll = NULL; -#endif - -static void callback(AvahiWatch *w, int fd, AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) { - - if (event & AVAHI_WATCH_IN) { - ssize_t r; - char c; - - if ((r = read(fd, &c, 1)) <= 0) { - fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); - api->watch_free(w); - return; - } - - printf("Read: %c\n", c >= 32 && c < 127 ? c : '.'); - } -} - -static void wakeup(AvahiTimeout *t, AVAHI_GCC_UNUSED void *userdata) { - static int i = 0; - struct timeval tv; - - printf("Wakeup #%i\n", i++); - - if (i > 10) { -#ifndef USE_THREAD - avahi_simple_poll_quit(simple_poll); -#else - avahi_threaded_poll_quit(threaded_poll); -#endif - } else { - avahi_elapse_time(&tv, 1000, 0); - api->timeout_update(t, &tv); - } -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - struct timeval tv; - -#ifndef USE_THREAD - simple_poll = avahi_simple_poll_new(); - assert(simple_poll); - api = avahi_simple_poll_get(simple_poll); - assert(api); -#else - threaded_poll = avahi_threaded_poll_new(); - assert(threaded_poll); - api = avahi_threaded_poll_get(threaded_poll); - assert(api); -#endif - - api->watch_new(api, 0, AVAHI_WATCH_IN, callback, NULL); - - avahi_elapse_time(&tv, 1000, 0); - api->timeout_new(api, &tv, wakeup, NULL); - -#ifndef USE_THREAD - /* Our main loop */ - avahi_simple_poll_loop(simple_poll); - avahi_simple_poll_free(simple_poll); - -#else - avahi_threaded_poll_start(threaded_poll); - - fprintf(stderr, "Now doing some stupid stuff ...\n"); - sleep(20); - fprintf(stderr, "... stupid stuff is done.\n"); - - avahi_threaded_poll_free(threaded_poll); - -#endif - - return 0; -} diff --git a/avahi-common/watch.h b/avahi-common/watch.h deleted file mode 100644 index 86e63d3..0000000 --- a/avahi-common/watch.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef foowatchhfoo -#define foowatchhfoo - -/*** - 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. -***/ - -/** \file watch.h Simplistic main loop abstraction */ - -#include -#include - -#include - -AVAHI_C_DECL_BEGIN - -/** An I/O watch object */ -typedef struct AvahiWatch AvahiWatch; - -/** A timeout watch object */ -typedef struct AvahiTimeout AvahiTimeout; - -/** An event polling abstraction object */ -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 */ -} AvahiWatchEvent; - -/** Called whenever an I/O event happens on an I/O watch */ -typedef void (*AvahiWatchCallback)(AvahiWatch *w, int fd, AvahiWatchEvent event, void *userdata); - -/** Called when the timeout is reached */ -typedef void (*AvahiTimeoutCallback)(AvahiTimeout *t, void *userdata); - -/** Defines an abstracted event polling API. This may be used to - connect Avahi to other main loops. This is loosely based on Unix - poll(2). A consumer will call watch_new() for all file descriptors it - wants to listen for events on. In addition he can call timeout_new() - to define time based events .*/ -struct AvahiPoll { - - /** Some abstract user data usable by the provider of the API */ - void* userdata; - - /** Create a new watch for the specified file descriptor and for - * the specified events. The API will call the callback function - * whenever any of the events happens. */ - AvahiWatch* (*watch_new)(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata); - - /** Update the events to wait for. It is safe to call this function from an AvahiWatchCallback */ - void (*watch_update)(AvahiWatch *w, AvahiWatchEvent event); - - /** Return the events that happened. It is safe to call this function from an AvahiWatchCallback */ - AvahiWatchEvent (*watch_get_events)(AvahiWatch *w); - - /** Free a watch. It is safe to call this function from an AvahiWatchCallback */ - void (*watch_free)(AvahiWatch *w); - - /** Set a wakeup time for the polling loop. The API will call the - callback function when the absolute time *tv is reached. If tv is - NULL, the timeout is disabled. After the timeout expired the - callback function will be called and the timeout is disabled. You - can reenable it by calling timeout_update() */ - AvahiTimeout* (*timeout_new)(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata); - - /** Update the absolute expiration time for a timeout, If tv is - * NULL, the timeout is disabled. It is safe to call this function from an AvahiTimeoutCallback */ - void (*timeout_update)(AvahiTimeout *, const struct timeval *tv); - - /** Free a timeout. It is safe to call this function from an AvahiTimeoutCallback */ - void (*timeout_free)(AvahiTimeout *t); -}; - -AVAHI_C_DECL_END - -#endif - diff --git a/avahi-core.pc.in b/avahi-core.pc.in deleted file mode 100644 index 52491b4..0000000 --- a/avahi-core.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=${prefix} -libdir=@libdir@ -includedir=${prefix}/include - -Name: avahi-core -Description: Avahi Multicast DNS Responder (Embeddable Stack) -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lavahi-common -lavahi-core -Cflags: -D_REENTRANT -I${includedir} diff --git a/avahi-core/.gitignore b/avahi-core/.gitignore deleted file mode 100644 index 847b855..0000000 --- a/avahi-core/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -*.o -*.lo -*.la -Makefile -Makefile.in -.deps -.libs -avahi-reflector -avahi-test -conformance-test -dns-spin-test -dns-test -hashmap-test -prioq-test -querier-test -timeeventq-test -update-test -*-test.log -*-test.trs -test-suite.log diff --git a/avahi-core/Makefile.am b/avahi-core/Makefile.am deleted file mode 100644 index 2f09596..0000000 --- a/avahi-core/Makefile.am +++ /dev/null @@ -1,173 +0,0 @@ -# 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. - -AM_CFLAGS=-I$(top_srcdir) - -# This cool debug trap works on i386/gcc only -AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' - -avahiincludedir=$(includedir)/avahi-core - -avahiinclude_HEADERS = \ - core.h \ - log.h \ - rr.h \ - publish.h \ - lookup.h - -lib_LTLIBRARIES = \ - libavahi-core.la - -if ENABLE_TESTS -noinst_PROGRAMS = \ - prioq-test \ - avahi-test \ - conformance-test \ - avahi-reflector \ - dns-test \ - dns-spin-test \ - timeeventq-test \ - hashmap-test \ - querier-test \ - update-test - -TESTS = \ - dns-spin-test \ - dns-test \ - hashmap-test -endif - -libavahi_core_la_SOURCES = \ - timeeventq.c timeeventq.h\ - iface.c iface.h \ - server.c internal.h entry.c \ - prioq.c prioq.h \ - cache.c cache.h \ - socket.c socket.h \ - response-sched.c response-sched.h \ - query-sched.c query-sched.h \ - probe-sched.c probe-sched.h \ - announce.c announce.h \ - browse.c browse.h \ - rrlist.c rrlist.h \ - resolve-host-name.c \ - resolve-address.c \ - browse-domain.c \ - browse-service-type.c \ - browse-service.c \ - resolve-service.c \ - dns.c dns.h \ - rr.c rr.h rr-util.h \ - core.h lookup.h publish.h \ - log.c log.h \ - browse-dns-server.c \ - fdutil.h fdutil.c \ - util.c util.h \ - hashmap.c hashmap.h \ - wide-area.c wide-area.h \ - multicast-lookup.c multicast-lookup.h \ - querier.c querier.h \ - addr-util.h addr-util.c \ - domain-util.h domain-util.c \ - dns-srv-rr.h - -if HAVE_NETLINK -libavahi_core_la_SOURCES += \ - iface-linux.c iface-linux.h \ - netlink.c netlink.h -else -if HAVE_PF_ROUTE -libavahi_core_la_SOURCES += \ - iface-pfroute.c iface-pfroute.h -else -libavahi_core_la_SOURCES += \ - iface-none.c -endif -endif - -libavahi_core_la_CFLAGS = $(AM_CFLAGS) -libavahi_core_la_LIBADD = $(AM_LDADD) ../avahi-common/libavahi-common.la -libavahi_core_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBAVAHI_CORE_VERSION_INFO) - -prioq_test_SOURCES = \ - prioq-test.c \ - prioq.c prioq.h -prioq_test_CFLAGS = $(AM_CFLAGS) -prioq_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la - -avahi_test_SOURCES = \ - avahi-test.c -avahi_test_CFLAGS = $(AM_CFLAGS) -avahi_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la - -update_test_SOURCES = \ - update-test.c -update_test_CFLAGS = $(AM_CFLAGS) -update_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la - -querier_test_SOURCES = \ - querier-test.c -querier_test_CFLAGS = $(AM_CFLAGS) -querier_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la - -conformance_test_SOURCES = \ - conformance-test.c -conformance_test_CFLAGS = $(AM_CFLAGS) -conformance_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la - -avahi_reflector_SOURCES = \ - avahi-reflector.c -avahi_reflector_CFLAGS = $(AM_CFLAGS) -avahi_reflector_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la - -dns_test_SOURCES = \ - dns.c dns.h \ - dns-test.c \ - log.c log.h \ - util.c util.h \ - rr.c rr.h \ - hashmap.c hashmap.h \ - domain-util.c domain-util.h \ - addr-util.c addr-util.h -dns_test_CFLAGS = $(AM_CFLAGS) -dns_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la - -dns_spin_test_SOURCES = \ - dns-spin-test.c -dns_spin_test_CFLAGS = $(AM_CFLAGS) -dns_spin_test_LDADD = $(AM_LDADD) libavahi-core.la - -timeeventq_test_SOURCES = \ - timeeventq-test.c \ - timeeventq.h timeeventq.c \ - prioq.h prioq.c \ - log.c log.h -timeeventq_test_CFLAGS = $(AM_CFLAGS) -timeeventq_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la - -hashmap_test_SOURCES = \ - hashmap-test.c \ - hashmap.h hashmap.c \ - util.h util.c -hashmap_test_CFLAGS = $(AM_CFLAGS) -hashmap_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la - -valgrind: avahi-test - libtool --mode=execute valgrind ./avahi-test - -gdb: avahi-test - libtool --mode=execute gdb ./avahi-test diff --git a/avahi-core/addr-util.c b/avahi-core/addr-util.c deleted file mode 100644 index 979b1b7..0000000 --- a/avahi-core/addr-util.c +++ /dev/null @@ -1,94 +0,0 @@ -/*** - 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 "addr-util.h" - -AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) { - assert(sa); - assert(ret_addr); - - assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); - - ret_addr->proto = avahi_af_to_proto(sa->sa_family); - - if (sa->sa_family == AF_INET) - memcpy(&ret_addr->data.ipv4, &((const struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4)); - else - memcpy(&ret_addr->data.ipv6, &((const struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6)); - - return ret_addr; -} - -uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa) { - assert(sa); - - assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); - - if (sa->sa_family == AF_INET) - return ntohs(((const struct sockaddr_in*) sa)->sin_port); - else - return ntohs(((const struct sockaddr_in6*) sa)->sin6_port); -} - -int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a) { - - static const uint8_t ipv4_in_ipv6[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF - }; - - assert(a); - - if (a->proto != AVAHI_PROTO_INET6) - return 0; - - return memcmp(a->data.ipv6.address, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0; -} - -#define IPV4LL_NETWORK 0xA9FE0000L -#define IPV4LL_NETMASK 0xFFFF0000L -#define IPV6LL_NETWORK 0xFE80 -#define IPV6LL_NETMASK 0xFFC0 - -int avahi_address_is_link_local(const AvahiAddress *a) { - assert(a); - - if (a->proto == AVAHI_PROTO_INET) { - uint32_t n = ntohl(a->data.ipv4.address); - return (n & IPV4LL_NETMASK) == IPV4LL_NETWORK; - } - else if (a->proto == AVAHI_PROTO_INET6) { - unsigned n = (a->data.ipv6.address[0] << 8) | (a->data.ipv6.address[1] << 0); - return (n & IPV6LL_NETMASK) == IPV6LL_NETWORK; - } - - return 0; -} diff --git a/avahi-core/addr-util.h b/avahi-core/addr-util.h deleted file mode 100644 index 66a9422..0000000 --- a/avahi-core/addr-util.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef fooaddrutilhfoo -#define fooaddrutilhfoo - -/*** - 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 - -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Make an address structture of a sockaddr structure */ -AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr); - -/** Return the port number of a sockaddr structure (either IPv4 or IPv6) */ -uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa); - -/** Check whether the specified IPv6 address is in fact an - * encapsulated IPv4 address, returns 1 if yes, 0 otherwise */ -int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a); - -/** Check whether the specified address is a link-local IPv4 or IPv6 address; - * returns 1 if yes, 0 otherwise */ -int avahi_address_is_link_local(const AvahiAddress *a); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/announce.c b/avahi-core/announce.c deleted file mode 100644 index ccdbf15..0000000 --- a/avahi-core/announce.c +++ /dev/null @@ -1,524 +0,0 @@ -/*** - 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 "announce.h" -#include "log.h" -#include "rr-util.h" - -#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250 -#define AVAHI_PROBE_JITTER_MSEC 250 -#define AVAHI_PROBE_INTERVAL_MSEC 250 - -static void remove_announcer(AvahiServer *s, AvahiAnnouncer *a) { - assert(s); - assert(a); - - if (a->time_event) - avahi_time_event_free(a->time_event); - - AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_interface, a->interface->announcers, a); - AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_entry, a->entry->announcers, a); - - avahi_free(a); -} - -static void elapse_announce(AvahiTimeEvent *e, void *userdata); - -static void set_timeout(AvahiAnnouncer *a, const struct timeval *tv) { - assert(a); - - if (!tv) { - if (a->time_event) { - avahi_time_event_free(a->time_event); - a->time_event = NULL; - } - } else { - - if (a->time_event) - avahi_time_event_update(a->time_event, tv); - else - a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a); - } -} - -static void next_state(AvahiAnnouncer *a); - -void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) { - AvahiEntry *e; - assert(g); - assert(!g->dead); - - /* Check whether all group members have been probed */ - - if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) - return; - - avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED); - - if (g->dead) - return; - - for (e = g->entries; e; e = e->by_group_next) { - AvahiAnnouncer *a; - - for (a = e->announcers; a; a = a->by_entry_next) { - - if (a->state != AVAHI_WAITING) - continue; - - a->state = AVAHI_ANNOUNCING; - - if (immediately) { - /* Shortcut */ - - a->n_iteration = 1; - next_state(a); - } else { - struct timeval tv; - a->n_iteration = 0; - avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); - set_timeout(a, &tv); - } - } - } -} - -static void next_state(AvahiAnnouncer *a) { - assert(a); - - if (a->state == AVAHI_WAITING) { - - assert(a->entry->group); - - avahi_s_entry_group_check_probed(a->entry->group, 1); - - } else if (a->state == AVAHI_PROBING) { - - if (a->n_iteration >= 4) { - /* Probing done */ - - if (a->entry->group) { - assert(a->entry->group->n_probing); - a->entry->group->n_probing--; - } - - if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING) - a->state = AVAHI_WAITING; - else { - a->state = AVAHI_ANNOUNCING; - a->n_iteration = 1; - } - - set_timeout(a, NULL); - next_state(a); - } else { - struct timeval tv; - - avahi_interface_post_probe(a->interface, a->entry->record, 0); - - avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0); - set_timeout(a, &tv); - - a->n_iteration++; - } - - } else if (a->state == AVAHI_ANNOUNCING) { - - if (a->entry->flags & AVAHI_PUBLISH_UNIQUE) - /* Send the whole rrset at once */ - avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0); - else - avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0); - - avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0); - - if (++a->n_iteration >= 4) { - /* Announcing done */ - - a->state = AVAHI_ESTABLISHED; - - set_timeout(a, NULL); - } else { - struct timeval tv; - avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC); - - if (a->n_iteration < 10) - a->sec_delay *= 2; - - set_timeout(a, &tv); - } - } -} - -static void elapse_announce(AvahiTimeEvent *e, void *userdata) { - assert(e); - - next_state(userdata); -} - -static AvahiAnnouncer *get_announcer(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncer *a; - - assert(s); - assert(e); - assert(i); - - for (a = e->announcers; a; a = a->by_entry_next) - if (a->interface == i) - return a; - - return NULL; -} - -static void go_to_initial_state(AvahiAnnouncer *a) { - AvahiEntry *e; - struct timeval tv; - - assert(a); - e = a->entry; - - if ((e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)) - a->state = AVAHI_PROBING; - else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) { - - if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED) - a->state = AVAHI_ANNOUNCING; - else - a->state = AVAHI_WAITING; - - } else - a->state = AVAHI_ESTABLISHED; - - a->n_iteration = 1; - a->sec_delay = 1; - - if (a->state == AVAHI_PROBING && e->group) - e->group->n_probing++; - - if (a->state == AVAHI_PROBING) - set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC)); - else if (a->state == AVAHI_ANNOUNCING) - set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC)); - else - set_timeout(a, NULL); -} - -static void new_announcer(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { - AvahiAnnouncer *a; - - assert(s); - assert(i); - assert(e); - assert(!e->dead); - - if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e)) - return; - - /* We don't want duplicate announcers */ - if (get_announcer(s, e, i)) - return; - - if ((!(a = avahi_new(AvahiAnnouncer, 1)))) { - avahi_log_error(__FILE__": Out of memory."); - return; - } - - a->server = s; - a->interface = i; - a->entry = e; - a->time_event = NULL; - - AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_interface, i->announcers, a); - AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_entry, e->announcers, a); - - go_to_initial_state(a); -} - -void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { - AvahiEntry *e; - - assert(s); - assert(i); - - if (!i->announcing) - return; - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead) - new_announcer(s, i, e); -} - -static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - AvahiEntry *e = userdata; - - assert(m); - assert(i); - assert(e); - assert(!e->dead); - - new_announcer(m->server, i, e); -} - -void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) { - assert(s); - assert(e); - assert(!e->dead); - - avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e); -} - -void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) { - AvahiEntry *e; - - assert(s); - assert(g); - - for (e = g->entries; e; e = e->by_group_next) - if (!e->dead) - avahi_announce_entry(s, e); -} - -int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncer *a; - - assert(s); - assert(e); - assert(i); - assert(!e->dead); - - if (!(a = get_announcer(s, e, i))) - return 0; - - return - a->state == AVAHI_ANNOUNCING || - a->state == AVAHI_ESTABLISHED || - (a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE)); -} - -int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncer *a; - - assert(s); - assert(e); - assert(i); - assert(!e->dead); - - if (!(a = get_announcer(s, e, i))) - return 0; - - return - a->state == AVAHI_PROBING || - (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE)); -} - -void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { - AvahiAnnouncer *a; - - assert(s); - assert(e); - assert(i); - - if (!(a = get_announcer(s, e, i))) - return; - - if (a->state == AVAHI_PROBING && a->entry->group) - a->entry->group->n_probing--; - - go_to_initial_state(a); -} - -static AvahiRecord *make_goodbye_record(AvahiRecord *r) { - AvahiRecord *g; - - assert(r); - - if (!(g = avahi_record_copy(r))) - return NULL; /* OOM */ - - assert(g->ref == 1); - g->ttl = 0; - - return g; -} - -static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) { - AvahiEntry *i; - - assert(s); - assert(e); - - for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) { - - if ((i == e) || (i->dead)) - continue; - - if (!avahi_record_equal_no_ttl(i->record, e->record)) - continue; - - return 1; - } - - return 0; -} - -static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - AvahiEntry *e = userdata; - AvahiRecord *g; - - assert(m); - assert(i); - assert(e); - assert(!e->dead); - - if (!avahi_interface_match(i, e->interface, e->protocol)) - return; - - if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE) - return; - - if (!avahi_entry_is_registered(m->server, e, i)) - return; - - if (is_duplicate_entry(m->server, e)) - return; - - if (!(g = make_goodbye_record(e->record))) - return; /* OOM */ - - avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1); - avahi_record_unref(g); -} - -static void reannounce(AvahiAnnouncer *a) { - AvahiEntry *e; - struct timeval tv; - - assert(a); - e = a->entry; - - /* If the group this entry belongs to is not even commited, there's nothing to reannounce */ - if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION)) - return; - - /* Because we might change state we decrease the probing counter first */ - if (a->state == AVAHI_PROBING && a->entry->group) - a->entry->group->n_probing--; - - if (a->state == AVAHI_PROBING || - (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))) - - /* We were probing or waiting after probe, so we restart probing from the beginning here */ - - a->state = AVAHI_PROBING; - else if (a->state == AVAHI_WAITING) - - /* We were waiting, but were not probing before, so we continue waiting */ - a->state = AVAHI_WAITING; - - else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE) - - /* No announcer needed */ - a->state = AVAHI_ESTABLISHED; - - else { - - /* Ok, let's restart announcing */ - a->state = AVAHI_ANNOUNCING; - } - - /* Now let's increase the probing counter again */ - if (a->state == AVAHI_PROBING && e->group) - e->group->n_probing++; - - a->n_iteration = 1; - a->sec_delay = 1; - - if (a->state == AVAHI_PROBING) - set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC)); - else if (a->state == AVAHI_ANNOUNCING) - set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC)); - else - set_timeout(a, NULL); -} - - -static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - AvahiEntry *e = userdata; - AvahiAnnouncer *a; - - assert(m); - assert(i); - assert(e); - assert(!e->dead); - - if (!(a = get_announcer(m->server, e, i))) - return; - - reannounce(a); -} - -void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) { - - assert(s); - assert(e); - assert(!e->dead); - - avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e); -} - -void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) { - assert(s); - assert(i); - - if (send_goodbye) - if (i->announcing) { - AvahiEntry *e; - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead) - send_goodbye_callback(s->monitor, i, e); - } - - if (remove) - while (i->announcers) - remove_announcer(s, i->announcers); -} - -void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) { - assert(s); - assert(e); - - if (send_goodbye) - if (!e->dead) - avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e); - - if (remove) - while (e->announcers) - remove_announcer(s, e->announcers); -} - diff --git a/avahi-core/announce.h b/avahi-core/announce.h deleted file mode 100644 index fd23d8b..0000000 --- a/avahi-core/announce.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef fooannouncehfoo -#define fooannouncehfoo - -/*** - 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 struct AvahiAnnouncer AvahiAnnouncer; - -#include -#include "iface.h" -#include "internal.h" -#include "timeeventq.h" -#include "publish.h" - -typedef enum { - AVAHI_PROBING, /* probing phase */ - AVAHI_WAITING, /* wait for other records in group */ - AVAHI_ANNOUNCING, /* announcing phase */ - AVAHI_ESTABLISHED /* we'e established */ -} AvahiAnnouncerState; - -struct AvahiAnnouncer { - AvahiServer *server; - AvahiInterface *interface; - AvahiEntry *entry; - - AvahiTimeEvent *time_event; - - AvahiAnnouncerState state; - unsigned n_iteration; - unsigned sec_delay; - - AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_interface); - AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_entry); -}; - -void avahi_announce_interface(AvahiServer *s, AvahiInterface *i); -void avahi_announce_entry(AvahiServer *s, AvahiEntry *e); -void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g); - -void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); - -void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately); - -int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); -int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); - -void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int rem); -void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int rem); - -void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e); - -#endif diff --git a/avahi-core/avahi-reflector.c b/avahi-core/avahi-reflector.c deleted file mode 100644 index dee1e93..0000000 --- a/avahi-core/avahi-reflector.c +++ /dev/null @@ -1,61 +0,0 @@ -/*** - 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 - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) { - AvahiServer *server; - AvahiServerConfig config; - int error; - AvahiSimplePoll *simple_poll; - - simple_poll = avahi_simple_poll_new(); - - avahi_server_config_init(&config); - config.publish_hinfo = 0; - config.publish_addresses = 0; - config.publish_workstation = 0; - config.publish_domain = 0; - config.use_ipv6 = 0; - config.enable_reflector = 1; - - server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error); - avahi_server_config_free(&config); - - for (;;) - if (avahi_simple_poll_iterate(simple_poll, -1) != 0) - break; - - avahi_server_free(server); - avahi_simple_poll_free(simple_poll); - - return 0; -} diff --git a/avahi-core/avahi-test.c b/avahi-core/avahi-test.c deleted file mode 100644 index 8b2376f..0000000 --- a/avahi-core/avahi-test.c +++ /dev/null @@ -1,402 +0,0 @@ -/*** - 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 - -static AvahiSEntryGroup *group = NULL; -static AvahiServer *server = NULL; -static char *service_name = NULL; - -static const AvahiPoll *poll_api; - -static void quit_timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void* userdata) { - AvahiSimplePoll *simple_poll = userdata; - - avahi_simple_poll_quit(simple_poll); -} - -static void dump_line(const char *text, AVAHI_GCC_UNUSED void* userdata) { - printf("%s\n", text); -} - -static void dump_timeout_callback(AvahiTimeout *timeout, void* userdata) { - struct timeval tv; - - AvahiServer *avahi = userdata; - avahi_server_dump(avahi, dump_line, NULL); - - avahi_elapse_time(&tv, 5000, 0); - poll_api->timeout_update(timeout, &tv); -} - -static const char *browser_event_to_string(AvahiBrowserEvent event) { - switch (event) { - case AVAHI_BROWSER_NEW : return "NEW"; - case AVAHI_BROWSER_REMOVE : return "REMOVE"; - case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED"; - case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW"; - case AVAHI_BROWSER_FAILURE : return "FAILURE"; - } - - abort(); -} - -static const char *resolver_event_to_string(AvahiResolverEvent event) { - switch (event) { - case AVAHI_RESOLVER_FOUND: return "FOUND"; - case AVAHI_RESOLVER_FAILURE: return "FAILURE"; - } - abort(); -} - -static void record_browser_callback( - AvahiSRecordBrowser *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - char *t; - - assert(r); - - if (record) { - avahi_log_debug("RB: record [%s] on %i.%i is %s", t = avahi_record_to_string(record), interface, protocol, browser_event_to_string(event)); - avahi_free(t); - } else - avahi_log_debug("RB: [%s]", browser_event_to_string(event)); - -} - -static void remove_entries(void); -static void create_entries(int new_name); - -static void entry_group_callback(AVAHI_GCC_UNUSED AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) { - avahi_log_debug("entry group state: %i", state); - - if (state == AVAHI_ENTRY_GROUP_COLLISION) { - remove_entries(); - create_entries(1); - avahi_log_debug("Service name conflict, retrying with <%s>", service_name); - } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) { - avahi_log_debug("Service established under name <%s>", service_name); - } -} - -static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) { - - server = s; - avahi_log_debug("server state: %i", state); - - if (state == AVAHI_SERVER_RUNNING) { - avahi_log_debug("Server startup complete. Host name is <%s>. Service cookie is %u", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); - create_entries(0); - } else if (state == AVAHI_SERVER_COLLISION) { - char *n; - remove_entries(); - - n = avahi_alternative_host_name(avahi_server_get_host_name(s)); - - avahi_log_debug("Host name conflict, retrying with <%s>", n); - avahi_server_set_host_name(s, n); - avahi_free(n); - } -} - -static void remove_entries(void) { - if (group) - avahi_s_entry_group_reset(group); -} - -static void create_entries(int new_name) { - AvahiAddress a; - AvahiRecord *r; - - remove_entries(); - - if (!group) - group = avahi_s_entry_group_new(server, entry_group_callback, NULL); - - assert(avahi_s_entry_group_is_empty(group)); - - if (!service_name) - service_name = avahi_strdup("Test Service"); - else if (new_name) { - char *n = avahi_alternative_service_name(service_name); - avahi_free(service_name); - service_name = n; - } - - if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, service_name, "_http._tcp", NULL, NULL, 80, "foo", NULL) < 0) { - avahi_log_error("Failed to add HTTP service"); - goto fail; - } - - if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, service_name, "_ftp._tcp", NULL, NULL, 21, "foo", NULL) < 0) { - avahi_log_error("Failed to add FTP service"); - goto fail; - } - - if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,service_name, "_webdav._tcp", NULL, NULL, 80, "foo", NULL) < 0) { - avahi_log_error("Failed to add WEBDAV service"); - goto fail; - } - - if (avahi_server_add_dns_server_address(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &a), 53) < 0) { - avahi_log_error("Failed to add new DNS Server address"); - goto fail; - } - - r = avahi_record_new_full("cname.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, AVAHI_DEFAULT_TTL); - r->data.cname.name = avahi_strdup("cocaine.local"); - - if (avahi_server_add(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, r) < 0) { - avahi_record_unref(r); - avahi_log_error("Failed to add CNAME record"); - goto fail; - } - avahi_record_unref(r); - - avahi_s_entry_group_commit(group); - return; - -fail: - if (group) - avahi_s_entry_group_free(group); - - group = NULL; -} - -static void hnr_callback( - AVAHI_GCC_UNUSED AvahiSHostNameResolver *r, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const char *hostname, - const AvahiAddress *a, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - char t[AVAHI_ADDRESS_STR_MAX]; - - if (a) - avahi_address_snprint(t, sizeof(t), a); - - avahi_log_debug("HNR: (%i.%i) <%s> -> %s [%s]", iface, protocol, hostname, a ? t : "n/a", resolver_event_to_string(event)); -} - -static void ar_callback( - AVAHI_GCC_UNUSED AvahiSAddressResolver *r, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const AvahiAddress *a, - const char *hostname, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - char t[AVAHI_ADDRESS_STR_MAX]; - - avahi_address_snprint(t, sizeof(t), a); - - avahi_log_debug("AR: (%i.%i) %s -> <%s> [%s]", iface, protocol, t, hostname ? hostname : "n/a", resolver_event_to_string(event)); -} - -static void db_callback( - AVAHI_GCC_UNUSED AvahiSDomainBrowser *b, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *domain, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - - avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain ? domain : "NULL", browser_event_to_string(event)); -} - -static void stb_callback( - AVAHI_GCC_UNUSED AvahiSServiceTypeBrowser *b, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *service_type, - const char *domain, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - - avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type ? service_type : "NULL", domain ? domain : "NULL", browser_event_to_string(event)); -} - -static void sb_callback( - AVAHI_GCC_UNUSED AvahiSServiceBrowser *b, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, - const char *service_type, - const char *domain, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name ? name : "NULL", service_type ? service_type : "NULL", domain ? domain : "NULL", browser_event_to_string(event)); -} - -static void sr_callback( - AVAHI_GCC_UNUSED AvahiSServiceResolver *r, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const char *name, - const char*service_type, - const char*domain_name, - const char*hostname, - const AvahiAddress *a, - uint16_t port, - AvahiStringList *txt, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - - if (event != AVAHI_RESOLVER_FOUND) - avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain_name, resolver_event_to_string(event)); - else { - char t[AVAHI_ADDRESS_STR_MAX], *s; - - avahi_address_snprint(t, sizeof(t), a); - - s = avahi_string_list_to_string(txt); - avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s>: %s/%s:%i (%s) [%s]", iface, protocol, name, service_type, domain_name, hostname, t, port, s, resolver_event_to_string(event)); - avahi_free(s); - } -} - -static void dsb_callback( - AVAHI_GCC_UNUSED AvahiSDNSServerBrowser *b, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char*hostname, - const AvahiAddress *a, - uint16_t port, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - - char t[AVAHI_ADDRESS_STR_MAX] = "n/a"; - - if (a) - avahi_address_snprint(t, sizeof(t), a); - - avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname ? hostname : "NULL", t, port, browser_event_to_string(event)); -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - AvahiSRecordBrowser *r; - AvahiSHostNameResolver *hnr; - AvahiSAddressResolver *ar; - AvahiKey *k; - AvahiServerConfig config; - AvahiAddress a; - AvahiSDomainBrowser *db; - AvahiSServiceTypeBrowser *stb; - AvahiSServiceBrowser *sb; - AvahiSServiceResolver *sr; - AvahiSDNSServerBrowser *dsb; - AvahiSimplePoll *simple_poll; - int error; - struct timeval tv; - - simple_poll = avahi_simple_poll_new(); - poll_api = avahi_simple_poll_get(simple_poll); - - avahi_server_config_init(&config); - - avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); - config.n_wide_area_servers = 1; - config.enable_wide_area = 1; - - server = avahi_server_new(poll_api, &config, server_callback, NULL, &error); - avahi_server_config_free(&config); - - k = avahi_key_new("_http._tcp.0pointer.de", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); - r = avahi_s_record_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, k, 0, record_browser_callback, NULL); - avahi_key_unref(k); - - hnr = avahi_s_host_name_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "cname.local", AVAHI_PROTO_UNSPEC, 0, hnr_callback, NULL); - - ar = avahi_s_address_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_address_parse("192.168.50.1", AVAHI_PROTO_INET, &a), 0, ar_callback, NULL); - - db = avahi_s_domain_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, db_callback, NULL); - - stb = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, 0, stb_callback, NULL); - - sb = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, 0, sb_callback, NULL); - - sr = avahi_s_service_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "Ecstasy HTTP", "_http._tcp", "local", AVAHI_PROTO_UNSPEC, 0, sr_callback, NULL); - - dsb = avahi_s_dns_server_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "local", AVAHI_DNS_SERVER_RESOLVE, AVAHI_PROTO_UNSPEC, 0, dsb_callback, NULL); - - avahi_elapse_time(&tv, 1000*5, 0); - poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, server); - - avahi_elapse_time(&tv, 1000*60, 0); - poll_api->timeout_new(poll_api, &tv, quit_timeout_callback, simple_poll); - - avahi_simple_poll_loop(simple_poll); - - avahi_s_record_browser_free(r); - avahi_s_host_name_resolver_free(hnr); - avahi_s_address_resolver_free(ar); - avahi_s_domain_browser_free(db); - avahi_s_service_type_browser_free(stb); - avahi_s_service_browser_free(sb); - avahi_s_service_resolver_free(sr); - avahi_s_dns_server_browser_free(dsb); - - if (group) - avahi_s_entry_group_free(group); - - if (server) - avahi_server_free(server); - - if (simple_poll) - avahi_simple_poll_free(simple_poll); - - avahi_free(service_name); - - return 0; -} diff --git a/avahi-core/browse-dns-server.c b/avahi-core/browse-dns-server.c deleted file mode 100644 index a51c38f..0000000 --- a/avahi-core/browse-dns-server.c +++ /dev/null @@ -1,322 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" -#include "rr.h" - -typedef struct AvahiDNSServerInfo AvahiDNSServerInfo; - -struct AvahiDNSServerInfo { - AvahiSDNSServerBrowser *browser; - - AvahiIfIndex interface; - AvahiProtocol protocol; - AvahiRecord *srv_record; - AvahiSHostNameResolver *host_name_resolver; - AvahiAddress address; - AvahiLookupResultFlags flags; - - AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info); -}; - -struct AvahiSDNSServerBrowser { - AvahiServer *server; - - AvahiSRecordBrowser *record_browser; - AvahiSDNSServerBrowserCallback callback; - void* userdata; - AvahiProtocol aprotocol; - AvahiLookupFlags user_flags; - - unsigned n_info; - - AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser); - AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info); -}; - -static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) { - AvahiDNSServerInfo *i; - - assert(b); - assert(r); - - for (i = b->info; i; i = i->info_next) - if (i->interface == interface && - i->protocol == protocol && - avahi_record_equal_no_ttl(r, i->srv_record)) - return i; - - return NULL; -} - -static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) { - assert(b); - assert(i); - - avahi_record_unref(i->srv_record); - if (i->host_name_resolver) - avahi_s_host_name_resolver_free(i->host_name_resolver); - - AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i); - - assert(b->n_info >= 1); - b->n_info--; - - avahi_free(i); -} - -static void host_name_resolver_callback( - AvahiSHostNameResolver *r, - AVAHI_GCC_UNUSED AvahiIfIndex interface, - AVAHI_GCC_UNUSED AvahiProtocol protocol, - AvahiResolverEvent event, - const char *host_name, - const AvahiAddress *a, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiDNSServerInfo *i = userdata; - - assert(r); - assert(host_name); - assert(i); - - switch (event) { - case AVAHI_RESOLVER_FOUND: { - i->address = *a; - - i->browser->callback( - i->browser, - i->interface, - i->protocol, - AVAHI_BROWSER_NEW, - i->srv_record->data.srv.name, - &i->address, - i->srv_record->data.srv.port, - i->flags | flags, - i->browser->userdata); - - break; - } - - case AVAHI_RESOLVER_FAILURE: - /* Ignore */ - break; - } - - avahi_s_host_name_resolver_free(i->host_name_resolver); - i->host_name_resolver = NULL; -} - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSDNSServerBrowser *b = userdata; - - assert(rr); - assert(b); - - /* Filter flags */ - flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; - - switch (event) { - case AVAHI_BROWSER_NEW: { - AvahiDNSServerInfo *i; - - assert(record); - assert(record->key->type == AVAHI_DNS_TYPE_SRV); - - if (get_server_info(b, interface, protocol, record)) - return; - - if (b->n_info >= 10) - return; - - if (!(i = avahi_new(AvahiDNSServerInfo, 1))) - return; /* OOM */ - - i->browser = b; - i->interface = interface; - i->protocol = protocol; - i->srv_record = avahi_record_ref(record); - i->host_name_resolver = avahi_s_host_name_resolver_new( - b->server, - interface, protocol, - record->data.srv.name, - b->aprotocol, - b->user_flags, - host_name_resolver_callback, i); - i->flags = flags; - - AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i); - - b->n_info++; - break; - } - - case AVAHI_BROWSER_REMOVE: { - AvahiDNSServerInfo *i; - - assert(record); - assert(record->key->type == AVAHI_DNS_TYPE_SRV); - - if (!(i = get_server_info(b, interface, protocol, record))) - return; - - if (!i->host_name_resolver) - b->callback( - b, - interface, - protocol, - event, - i->srv_record->data.srv.name, - &i->address, - i->srv_record->data.srv.port, - i->flags | flags, - b->userdata); - - server_info_free(b, i); - break; - } - - case AVAHI_BROWSER_FAILURE: - case AVAHI_BROWSER_ALL_FOR_NOW: - case AVAHI_BROWSER_CACHE_EXHAUSTED: - - b->callback( - b, - interface, - protocol, - event, - NULL, - NULL, - 0, - flags, - b->userdata); - - break; - } -} - -AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDNSServerType type, - AvahiProtocol aprotocol, - AvahiLookupFlags flags, - AvahiSDNSServerBrowserCallback callback, - void* userdata) { - - static const char * const type_table[AVAHI_DNS_SERVER_MAX] = { - "_domain._udp", - "_dns-update._udp" - }; - - AvahiSDNSServerBrowser *b; - AvahiKey *k = NULL; - char n[AVAHI_DOMAIN_NAME_MAX]; - int r; - - assert(server); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DNS_SERVER_MAX, AVAHI_ERR_INVALID_FLAGS); - - if (!domain) - domain = server->domain_name; - - if ((r = avahi_service_name_join(n, sizeof(n), NULL, type_table[type], domain)) < 0) { - avahi_server_set_errno(server, r); - return NULL; - } - - if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - b->server = server; - b->callback = callback; - b->userdata = userdata; - b->aprotocol = aprotocol; - b->n_info = 0; - b->user_flags = flags; - - AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info); - AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b); - - if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) - goto fail; - - avahi_key_unref(k); - - return b; - -fail: - - if (k) - avahi_key_unref(k); - - avahi_s_dns_server_browser_free(b); - return NULL; -} - -void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) { - assert(b); - - while (b->info) - server_info_free(b, b->info); - - AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b); - - if (b->record_browser) - avahi_s_record_browser_free(b->record_browser); - - avahi_free(b); -} - diff --git a/avahi-core/browse-domain.c b/avahi-core/browse-domain.c deleted file mode 100644 index 9705b2a..0000000 --- a/avahi-core/browse-domain.c +++ /dev/null @@ -1,235 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" - -struct AvahiSDomainBrowser { - int ref; - - AvahiServer *server; - - AvahiSRecordBrowser *record_browser; - - AvahiDomainBrowserType type; - AvahiSDomainBrowserCallback callback; - void* userdata; - - AvahiTimeEvent *defer_event; - - int all_for_now_scheduled; - - AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser); -}; - -static void inc_ref(AvahiSDomainBrowser *b) { - assert(b); - assert(b->ref >= 1); - - b->ref++; -} - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSDomainBrowser *b = userdata; - char *n = NULL; - - assert(rr); - assert(b); - - if (event == AVAHI_BROWSER_ALL_FOR_NOW && - b->defer_event) { - - b->all_for_now_scheduled = 1; - return; - } - - /* Filter flags */ - flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; - - if (record) { - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - n = record->data.ptr.name; - - if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) { - AvahiStringList *l; - - /* Filter out entries defined statically */ - - for (l = b->server->config.browse_domains; l; l = l->next) - if (avahi_domain_equal((char*) l->text, n)) - return; - } - - } - - b->callback(b, interface, protocol, event, n, flags, b->userdata); -} - -static void defer_callback(AvahiTimeEvent *e, void *userdata) { - AvahiSDomainBrowser *b = userdata; - AvahiStringList *l; - - assert(e); - assert(b); - - assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE); - - avahi_time_event_free(b->defer_event); - b->defer_event = NULL; - - /* Increase ref counter */ - inc_ref(b); - - for (l = b->server->config.browse_domains; l; l = l->next) { - - /* Check whether this object still exists outside our own - * stack frame */ - if (b->ref <= 1) - break; - - b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata); - } - - if (b->ref > 1) { - /* If the ALL_FOR_NOW event has already been scheduled, execute it now */ - - if (b->all_for_now_scheduled) - b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata); - } - - /* Decrease ref counter */ - avahi_s_domain_browser_free(b); -} - -AvahiSDomainBrowser *avahi_s_domain_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDomainBrowserType type, - AvahiLookupFlags flags, - AvahiSDomainBrowserCallback callback, - void* userdata) { - - static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = { - "b", - "db", - "r", - "dr", - "lb" - }; - - AvahiSDomainBrowser *b; - AvahiKey *k = NULL; - char n[AVAHI_DOMAIN_NAME_MAX]; - int r; - - assert(server); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - - if (!domain) - domain = server->domain_name; - - if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) { - avahi_server_set_errno(server, r); - return NULL; - } - - if (!(b = avahi_new(AvahiSDomainBrowser, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - b->ref = 1; - b->server = server; - b->callback = callback; - b->userdata = userdata; - b->record_browser = NULL; - b->type = type; - b->all_for_now_scheduled = 0; - b->defer_event = NULL; - - AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b); - - if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) - goto fail; - - avahi_key_unref(k); - - if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains) - b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); - - return b; - -fail: - - if (k) - avahi_key_unref(k); - - avahi_s_domain_browser_free(b); - - return NULL; -} - -void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) { - assert(b); - - assert(b->ref >= 1); - if (--b->ref > 0) - return; - - AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b); - - if (b->record_browser) - avahi_s_record_browser_free(b->record_browser); - - if (b->defer_event) - avahi_time_event_free(b->defer_event); - - avahi_free(b); -} diff --git a/avahi-core/browse-service-type.c b/avahi-core/browse-service-type.c deleted file mode 100644 index 6fff071..0000000 --- a/avahi-core/browse-service-type.c +++ /dev/null @@ -1,157 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" - -struct AvahiSServiceTypeBrowser { - AvahiServer *server; - char *domain_name; - - AvahiSRecordBrowser *record_browser; - - AvahiSServiceTypeBrowserCallback callback; - void* userdata; - - AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser); -}; - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSServiceTypeBrowser *b = userdata; - - assert(rr); - assert(b); - - /* Filter flags */ - flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; - - if (record) { - char type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; - - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) { - avahi_log_warn("Invalid service type '%s'", record->key->name); - return; - } - - b->callback(b, interface, protocol, event, type, domain, flags, b->userdata); - } else - b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->userdata); -} - -AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiLookupFlags flags, - AvahiSServiceTypeBrowserCallback callback, - void* userdata) { - - AvahiSServiceTypeBrowser *b; - AvahiKey *k = NULL; - char n[AVAHI_DOMAIN_NAME_MAX]; - int r; - - assert(server); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - - if (!domain) - domain = server->domain_name; - - if ((r = avahi_service_name_join(n, sizeof(n), NULL, "_services._dns-sd._udp", domain)) < 0) { - avahi_server_set_errno(server, r); - return NULL; - } - - if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - b->server = server; - b->callback = callback; - b->userdata = userdata; - b->record_browser = NULL; - - AVAHI_LLIST_PREPEND(AvahiSServiceTypeBrowser, browser, server->service_type_browsers, b); - - if (!(b->domain_name = avahi_normalize_name_strdup(domain))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) - goto fail; - - avahi_key_unref(k); - - return b; - -fail: - if (k) - avahi_key_unref(k); - - avahi_s_service_type_browser_free(b); - - return NULL; -} - -void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b) { - assert(b); - - AVAHI_LLIST_REMOVE(AvahiSServiceTypeBrowser, browser, b->server->service_type_browsers, b); - - if (b->record_browser) - avahi_s_record_browser_free(b->record_browser); - - avahi_free(b->domain_name); - avahi_free(b); -} - - diff --git a/avahi-core/browse-service.c b/avahi-core/browse-service.c deleted file mode 100644 index dde36bc..0000000 --- a/avahi-core/browse-service.c +++ /dev/null @@ -1,167 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" - -struct AvahiSServiceBrowser { - AvahiServer *server; - char *domain_name; - char *service_type; - - AvahiSRecordBrowser *record_browser; - - AvahiSServiceBrowserCallback callback; - void* userdata; - - AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser); -}; - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSServiceBrowser *b = userdata; - - assert(rr); - assert(b); - - /* Filter flags */ - flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; - - if (record) { - char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; - - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - if (event == AVAHI_BROWSER_NEW && avahi_server_is_service_local(b->server, interface, protocol, record->data.ptr.name)) - flags |= AVAHI_LOOKUP_RESULT_LOCAL; - - if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) { - avahi_log_warn("Failed to split '%s'", record->key->name); - return; - } - - b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata); - - } else - b->callback(b, interface, protocol, event, NULL, b->service_type, b->domain_name, flags, b->userdata); - -} - -AvahiSServiceBrowser *avahi_s_service_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *service_type, - const char *domain, - AvahiLookupFlags flags, - AvahiSServiceBrowserCallback callback, - void* userdata) { - - AvahiSServiceBrowser *b; - AvahiKey *k = NULL; - char n[AVAHI_DOMAIN_NAME_MAX]; - int r; - - assert(server); - assert(callback); - assert(service_type); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE); - - if (!domain) - domain = server->domain_name; - - if ((r = avahi_service_name_join(n, sizeof(n), NULL, service_type, domain)) < 0) { - avahi_server_set_errno(server, r); - return NULL; - } - - if (!(b = avahi_new(AvahiSServiceBrowser, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - b->server = server; - b->domain_name = b->service_type = NULL; - b->callback = callback; - b->userdata = userdata; - b->record_browser = NULL; - - AVAHI_LLIST_PREPEND(AvahiSServiceBrowser, browser, server->service_browsers, b); - - if (!(b->domain_name = avahi_normalize_name_strdup(domain)) || - !(b->service_type = avahi_normalize_name_strdup(service_type))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) - goto fail; - - avahi_key_unref(k); - - return b; - -fail: - - if (k) - avahi_key_unref(k); - - avahi_s_service_browser_free(b); - return NULL; -} - -void avahi_s_service_browser_free(AvahiSServiceBrowser *b) { - assert(b); - - AVAHI_LLIST_REMOVE(AvahiSServiceBrowser, browser, b->server->service_browsers, b); - - if (b->record_browser) - avahi_s_record_browser_free(b->record_browser); - - avahi_free(b->domain_name); - avahi_free(b->service_type); - avahi_free(b); -} diff --git a/avahi-core/browse.c b/avahi-core/browse.c deleted file mode 100644 index eabd7ea..0000000 --- a/avahi-core/browse.c +++ /dev/null @@ -1,613 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" -#include "querier.h" -#include "domain-util.h" -#include "rr-util.h" - -#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15 - -struct AvahiSRBLookup { - AvahiSRecordBrowser *record_browser; - - unsigned ref; - - AvahiIfIndex interface; - AvahiProtocol protocol; - AvahiLookupFlags flags; - - AvahiKey *key; - - AvahiWideAreaLookup *wide_area; - AvahiMulticastLookup *multicast; - - AvahiRList *cname_lookups; - - AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups); -}; - -static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); -static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); - -static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) { - assert(flags); - assert(domain); - - assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA))); - - if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA)) - return; - - if (!s->wide_area_lookup_engine || - !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || - avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || - avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || - avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) - *flags |= AVAHI_LOOKUP_USE_MULTICAST; - else - *flags |= AVAHI_LOOKUP_USE_WIDE_AREA; -} - -static AvahiSRBLookup* lookup_new( - AvahiSRecordBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiLookupFlags flags, - AvahiKey *key) { - - AvahiSRBLookup *l; - - assert(b); - assert(AVAHI_IF_VALID(interface)); - assert(AVAHI_PROTO_VALID(protocol)); - - if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX) - /* We don't like cyclic CNAMEs */ - return NULL; - - if (!(l = avahi_new(AvahiSRBLookup, 1))) - return NULL; - - l->ref = 1; - l->record_browser = b; - l->interface = interface; - l->protocol = protocol; - l->key = avahi_key_ref(key); - l->wide_area = NULL; - l->multicast = NULL; - l->cname_lookups = NULL; - l->flags = flags; - - transport_flags_from_domain(b->server, &l->flags, key->name); - - AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l); - - b->n_lookups ++; - - return l; -} - -static void lookup_unref(AvahiSRBLookup *l) { - assert(l); - assert(l->ref >= 1); - - if (--l->ref >= 1) - return; - - AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l); - l->record_browser->n_lookups --; - - if (l->wide_area) { - avahi_wide_area_lookup_free(l->wide_area); - l->wide_area = NULL; - } - - if (l->multicast) { - avahi_multicast_lookup_free(l->multicast); - l->multicast = NULL; - } - - while (l->cname_lookups) { - lookup_unref(l->cname_lookups->data); - l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups); - } - - avahi_key_unref(l->key); - avahi_free(l); -} - -static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) { - assert(l); - assert(l->ref >= 1); - - l->ref++; - return l; -} - -static AvahiSRBLookup *lookup_find( - AvahiSRecordBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiLookupFlags flags, - AvahiKey *key) { - - AvahiSRBLookup *l; - - assert(b); - - for (l = b->lookups; l; l = l->lookups_next) { - - if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) && - (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) && - l->flags == flags && - avahi_key_equal(l->key, key)) - - return l; - } - - return NULL; -} - -static void browser_cancel(AvahiSRecordBrowser *b) { - assert(b); - - if (b->root_lookup) { - lookup_unref(b->root_lookup); - b->root_lookup = NULL; - } - - if (b->defer_time_event) { - avahi_time_event_free(b->defer_time_event); - b->defer_time_event = NULL; - } -} - -static void lookup_wide_area_callback( - AvahiWideAreaLookupEngine *e, - AvahiBrowserEvent event, - AvahiLookupResultFlags flags, - AvahiRecord *r, - void *userdata) { - - AvahiSRBLookup *l = userdata; - AvahiSRecordBrowser *b; - - assert(e); - assert(l); - assert(l->ref >= 1); - - b = l->record_browser; - - if (b->dead) - return; - - lookup_ref(l); - - switch (event) { - case AVAHI_BROWSER_NEW: - assert(r); - - if (r->key->clazz == AVAHI_DNS_CLASS_IN && - r->key->type == AVAHI_DNS_TYPE_CNAME) - /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */ - lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r); - else { - /* It's a normal record, so let's call the user callback */ - assert(avahi_key_equal(r->key, l->key)); - - b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata); - } - break; - - case AVAHI_BROWSER_REMOVE: - case AVAHI_BROWSER_CACHE_EXHAUSTED: - /* Not defined for wide area DNS */ - abort(); - - case AVAHI_BROWSER_ALL_FOR_NOW: - case AVAHI_BROWSER_FAILURE: - - b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); - break; - } - - lookup_unref(l); - -} - -static void lookup_multicast_callback( - AvahiMulticastLookupEngine *e, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiLookupResultFlags flags, - AvahiRecord *r, - void *userdata) { - - AvahiSRBLookup *l = userdata; - AvahiSRecordBrowser *b; - - assert(e); - assert(l); - - b = l->record_browser; - - if (b->dead) - return; - - lookup_ref(l); - - switch (event) { - case AVAHI_BROWSER_NEW: - assert(r); - - if (r->key->clazz == AVAHI_DNS_CLASS_IN && - r->key->type == AVAHI_DNS_TYPE_CNAME) - /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */ - lookup_handle_cname(l, interface, protocol, b->flags, r); - else { - /* It's a normal record, so let's call the user callback */ - - if (avahi_server_is_record_local(b->server, interface, protocol, r)) - flags |= AVAHI_LOOKUP_RESULT_LOCAL; - - b->callback(b, interface, protocol, event, r, flags, b->userdata); - } - break; - - case AVAHI_BROWSER_REMOVE: - assert(r); - - if (r->key->clazz == AVAHI_DNS_CLASS_IN && - r->key->type == AVAHI_DNS_TYPE_CNAME) - /* It's a CNAME record, so let's drop that query! */ - lookup_drop_cname(l, interface, protocol, 0, r); - else { - /* It's a normal record, so let's call the user callback */ - assert(avahi_key_equal(b->key, l->key)); - - b->callback(b, interface, protocol, event, r, flags, b->userdata); - } - break; - - case AVAHI_BROWSER_ALL_FOR_NOW: - - b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); - break; - - case AVAHI_BROWSER_CACHE_EXHAUSTED: - case AVAHI_BROWSER_FAILURE: - /* Not defined for multicast DNS */ - abort(); - - } - - lookup_unref(l); -} - -static int lookup_start(AvahiSRBLookup *l) { - assert(l); - - assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); - assert(!l->wide_area && !l->multicast); - - if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { - - if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l))) - return -1; - - } else { - assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); - - if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l))) - return -1; - } - - return 0; -} - -static int lookup_scan_cache(AvahiSRBLookup *l) { - int n = 0; - - assert(l); - - assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); - - - if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { - n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l); - - } else { - assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); - n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l); - } - - return n; -} - -static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) { - AvahiSRBLookup *l; - - assert(b); - assert(!b->dead); - - if ((l = lookup_find(b, interface, protocol, flags, key))) - return lookup_ref(l); - - if (!(l = lookup_new(b, interface, protocol, flags, key))) - return NULL; - - return l; -} - -static int lookup_go(AvahiSRBLookup *l) { - int n = 0; - assert(l); - - if (l->record_browser->dead) - return 0; - - lookup_ref(l); - - /* Browse the cache for the root request */ - n = lookup_scan_cache(l); - - /* Start the lookup */ - if (!l->record_browser->dead && l->ref > 1) { - - if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || n == 0) - /* We do no start a query if the cache contained entries and we're on wide area */ - - if (lookup_start(l) < 0) - n = -1; - } - - lookup_unref(l); - - return n; -} - -static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { - AvahiKey *k; - AvahiSRBLookup *n; - - assert(l); - assert(r); - - assert(r->key->clazz == AVAHI_DNS_CLASS_IN); - assert(r->key->type == AVAHI_DNS_TYPE_CNAME); - - k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); - n = lookup_add(l->record_browser, interface, protocol, flags, k); - avahi_key_unref(k); - - if (!n) { - avahi_log_debug(__FILE__": Failed to create SRBLookup."); - return; - } - - l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n)); - - lookup_go(n); - lookup_unref(n); -} - -static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { - AvahiKey *k; - AvahiSRBLookup *n = NULL; - AvahiRList *rl; - - assert(r->key->clazz == AVAHI_DNS_CLASS_IN); - assert(r->key->type == AVAHI_DNS_TYPE_CNAME); - - k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); - - for (rl = l->cname_lookups; rl; rl = rl->rlist_next) { - n = rl->data; - - assert(n); - - if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) && - (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) && - n->flags == flags && - avahi_key_equal(n->key, k)) - break; - } - - avahi_key_unref(k); - - if (rl) { - l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl); - lookup_unref(n); - } -} - -static void defer_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { - AvahiSRecordBrowser *b = userdata; - int n; - - assert(b); - assert(!b->dead); - - /* Remove the defer timeout */ - if (b->defer_time_event) { - avahi_time_event_free(b->defer_time_event); - b->defer_time_event = NULL; - } - - /* Create initial query */ - assert(!b->root_lookup); - b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key); - assert(b->root_lookup); - - n = lookup_go(b->root_lookup); - - if (b->dead) - return; - - if (n < 0) { - /* sending of the initial query failed */ - - avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE); - - b->callback( - b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL, - b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST, - b->userdata); - - browser_cancel(b); - return; - } - - /* Tell the client that we're done with the cache */ - b->callback( - b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL, - b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST, - b->userdata); - - if (!b->dead && b->root_lookup && b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA && n > 0) { - - /* If we do wide area lookups and the the cache contained - * entries, we assume that it is complete, and tell the user - * so by firing ALL_FOR_NOW. */ - - b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_RESULT_WIDE_AREA, b->userdata); - } -} - -void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) { - assert(b); - assert(!b->dead); - - browser_cancel(b); - - /* Request a new iteration of the cache scanning */ - if (!b->defer_time_event) { - b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b); - assert(b->defer_time_event); - } -} - -AvahiSRecordBrowser *avahi_s_record_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiKey *key, - AvahiLookupFlags flags, - AvahiSRecordBrowserCallback callback, - void* userdata) { - - AvahiSRecordBrowser *b; - - assert(server); - assert(key); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - - if (!(b = avahi_new(AvahiSRecordBrowser, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - b->dead = 0; - b->server = server; - b->interface = interface; - b->protocol = protocol; - b->key = avahi_key_ref(key); - b->flags = flags; - b->callback = callback; - b->userdata = userdata; - b->n_lookups = 0; - AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups); - b->root_lookup = NULL; - - AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b); - - /* The currently cached entries are scanned a bit later, and than we will start querying, too */ - b->defer_time_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); - assert(b->defer_time_event); - - return b; -} - -void avahi_s_record_browser_free(AvahiSRecordBrowser *b) { - assert(b); - assert(!b->dead); - - b->dead = 1; - b->server->need_browser_cleanup = 1; - - browser_cancel(b); -} - -void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) { - assert(b); - - browser_cancel(b); - - AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b); - - avahi_key_unref(b->key); - - avahi_free(b); -} - -void avahi_browser_cleanup(AvahiServer *server) { - AvahiSRecordBrowser *b; - AvahiSRecordBrowser *n; - - assert(server); - - while (server->need_browser_cleanup) { - server->need_browser_cleanup = 0; - - for (b = server->record_browsers; b; b = n) { - n = b->browser_next; - - if (b->dead) - avahi_s_record_browser_destroy(b); - } - } - - if (server->wide_area_lookup_engine) - avahi_wide_area_cleanup(server->wide_area_lookup_engine); - avahi_multicast_lookup_engine_cleanup(server->multicast_lookup_engine); -} - diff --git a/avahi-core/browse.h b/avahi-core/browse.h deleted file mode 100644 index a0dc207..0000000 --- a/avahi-core/browse.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef foobrowsehfoo -#define foobrowsehfoo - -/*** - 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 "core.h" -#include "timeeventq.h" -#include "internal.h" -#include "dns.h" -#include "lookup.h" - -typedef struct AvahiSRBLookup AvahiSRBLookup; - -struct AvahiSRecordBrowser { - AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser); - int dead; - AvahiServer *server; - - AvahiKey *key; - AvahiIfIndex interface; - AvahiProtocol protocol; - AvahiLookupFlags flags; - - AvahiTimeEvent *defer_time_event; - - AvahiSRecordBrowserCallback callback; - void* userdata; - - /* Lookup data */ - AVAHI_LLIST_HEAD(AvahiSRBLookup, lookups); - unsigned n_lookups; - - AvahiSRBLookup *root_lookup; -}; - -void avahi_browser_cleanup(AvahiServer *server); - -void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b); -void avahi_s_record_browser_restart(AvahiSRecordBrowser *b); - -#endif diff --git a/avahi-core/cache.c b/avahi-core/cache.c deleted file mode 100644 index 454aac5..0000000 --- a/avahi-core/cache.c +++ /dev/null @@ -1,513 +0,0 @@ -/*** - 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 "cache.h" -#include "log.h" -#include "rr-util.h" - -static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { - AvahiCacheEntry *t; - - assert(c); - assert(e); - -/* avahi_log_debug("removing from cache: %p %p", c, e); */ - - /* Remove from hash table */ - t = avahi_hashmap_lookup(c->hashmap, e->record->key); - AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e); - if (t) - avahi_hashmap_replace(c->hashmap, t->record->key, t); - else - avahi_hashmap_remove(c->hashmap, e->record->key); - - /* Remove from linked list */ - AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e); - - if (e->time_event) - avahi_time_event_free(e->time_event); - - avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_REMOVE); - - avahi_record_unref(e->record); - - avahi_free(e); - - assert(c->n_entries >= 1); - --c->n_entries; -} - -AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) { - AvahiCache *c; - assert(server); - - if (!(c = avahi_new(AvahiCache, 1))) { - avahi_log_error(__FILE__": Out of memory."); - return NULL; /* OOM */ - } - - c->server = server; - c->interface = iface; - - if (!(c->hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL))) { - avahi_log_error(__FILE__": Out of memory."); - avahi_free(c); - return NULL; /* OOM */ - } - - AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries); - c->n_entries = 0; - - c->last_rand_timestamp = 0; - - return c; -} - -void avahi_cache_free(AvahiCache *c) { - assert(c); - - while (c->entries) - remove_entry(c, c->entries); - assert(c->n_entries == 0); - - avahi_hashmap_free(c->hashmap); - - avahi_free(c); -} - -static AvahiCacheEntry *lookup_key(AvahiCache *c, AvahiKey *k) { - assert(c); - assert(k); - - assert(!avahi_key_is_pattern(k)); - - return avahi_hashmap_lookup(c->hashmap, k); -} - -void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata) { - void* ret; - - assert(c); - assert(pattern); - assert(cb); - - if (avahi_key_is_pattern(pattern)) { - AvahiCacheEntry *e, *n; - - for (e = c->entries; e; e = n) { - n = e->entry_next; - - if (avahi_key_pattern_match(pattern, e->record->key)) - if ((ret = cb(c, pattern, e, userdata))) - return ret; - } - - } else { - AvahiCacheEntry *e, *n; - - for (e = lookup_key(c, pattern); e; e = n) { - n = e->by_key_next; - - if ((ret = cb(c, pattern, e, userdata))) - return ret; - } - } - - return NULL; -} - -static void* lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { - assert(c); - assert(pattern); - assert(e); - - if (avahi_record_equal_no_ttl(e->record, userdata)) - return e; - - return NULL; -} - -static AvahiCacheEntry *lookup_record(AvahiCache *c, AvahiRecord *r) { - assert(c); - assert(r); - - return avahi_cache_walk(c, r->key, lookup_record_callback, r); -} - -static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent); - -static void elapse_func(AvahiTimeEvent *t, void *userdata) { - AvahiCacheEntry *e = userdata; -/* char *txt; */ - unsigned percent = 0; - - assert(t); - assert(e); - -/* txt = avahi_record_to_string(e->record); */ - - switch (e->state) { - - case AVAHI_CACHE_EXPIRY_FINAL: - case AVAHI_CACHE_POOF_FINAL: - case AVAHI_CACHE_GOODBYE_FINAL: - case AVAHI_CACHE_REPLACE_FINAL: - - remove_entry(e->cache, e); - - e = NULL; -/* avahi_log_debug("Removing entry from cache due to expiration (%s)", txt); */ - break; - - case AVAHI_CACHE_VALID: - case AVAHI_CACHE_POOF: - e->state = AVAHI_CACHE_EXPIRY1; - percent = 85; - break; - - case AVAHI_CACHE_EXPIRY1: - e->state = AVAHI_CACHE_EXPIRY2; - percent = 90; - break; - case AVAHI_CACHE_EXPIRY2: - e->state = AVAHI_CACHE_EXPIRY3; - percent = 95; - break; - - case AVAHI_CACHE_EXPIRY3: - e->state = AVAHI_CACHE_EXPIRY_FINAL; - percent = 100; - break; - } - - if (e) { - - assert(percent > 0); - - /* Request a cache update if we are subscribed to this entry */ - if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key)) - avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL); - - /* Check again later */ - next_expiry(e->cache, e, percent); - - } - -/* avahi_free(txt); */ -} - -static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) { - assert(c); - assert(e); - - if (e->time_event) - avahi_time_event_update(e->time_event, &e->expiry); - else - e->time_event = avahi_time_event_new(c->server->time_event_queue, &e->expiry, elapse_func, e); -} - -static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent) { - AvahiUsec usec, left, right; - time_t now; - - assert(c); - assert(e); - assert(percent > 0 && percent <= 100); - - usec = (AvahiUsec) e->record->ttl * 10000; - - left = usec * percent; - right = usec * (percent+2); /* 2% jitter */ - - now = time(NULL); - - if (now >= c->last_rand_timestamp + 10) { - c->last_rand = rand(); - c->last_rand_timestamp = now; - } - - usec = left + (AvahiUsec) ((double) (right-left) * c->last_rand / (RAND_MAX+1.0)); - - e->expiry = e->timestamp; - avahi_timeval_add(&e->expiry, usec); - -/* g_message("wake up in +%lu seconds", e->expiry.tv_sec - e->timestamp.tv_sec); */ - - update_time_event(c, e); -} - -static void expire_in_one_second(AvahiCache *c, AvahiCacheEntry *e, AvahiCacheEntryState state) { - assert(c); - assert(e); - - e->state = state; - gettimeofday(&e->expiry, NULL); - avahi_timeval_add(&e->expiry, 1000000); /* 1s */ - update_time_event(c, e); -} - -void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) { -/* char *txt; */ - - assert(c); - assert(r && r->ref >= 1); - -/* txt = avahi_record_to_string(r); */ - - if (r->ttl == 0) { - /* This is a goodbye request */ - - AvahiCacheEntry *e; - - if ((e = lookup_record(c, r))) - expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL); - - } else { - AvahiCacheEntry *e = NULL, *first; - struct timeval now; - - gettimeofday(&now, NULL); - - /* This is an update request */ - - if ((first = lookup_key(c, r->key))) { - - if (cache_flush) { - - /* For unique entries drop all entries older than one second */ - for (e = first; e; e = e->by_key_next) { - AvahiUsec t; - - t = avahi_timeval_diff(&now, &e->timestamp); - - if (t > 1000000) - expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL); - } - } - - /* Look for exactly the same entry */ - for (e = first; e; e = e->by_key_next) - if (avahi_record_equal_no_ttl(e->record, r)) - break; - } - - if (e) { - -/* avahi_log_debug("found matching cache entry"); */ - - /* We need to update the hash table key if we replace the - * record */ - if (e->by_key_prev == NULL) - avahi_hashmap_replace(c->hashmap, r->key, e); - - /* Update the record */ - avahi_record_unref(e->record); - e->record = avahi_record_ref(r); - -/* avahi_log_debug("cache: updating %s", txt); */ - - } else { - /* No entry found, therefore we create a new one */ - -/* avahi_log_debug("cache: couldn't find matching cache entry for %s", txt); */ - - if (c->n_entries >= c->server->config.n_cache_entries_max) - return; - - if (!(e = avahi_new(AvahiCacheEntry, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return; - } - - e->cache = c; - e->time_event = NULL; - e->record = avahi_record_ref(r); - - /* Append to hash table */ - AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e); - avahi_hashmap_replace(c->hashmap, e->record->key, first); - - /* Append to linked list */ - AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); - - c->n_entries++; - - /* Notify subscribers */ - avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW); - } - - e->origin = *a; - e->timestamp = now; - next_expiry(c, e, 80); - e->state = AVAHI_CACHE_VALID; - e->cache_flush = cache_flush; - } - -/* avahi_free(txt); */ -} - -struct dump_data { - AvahiDumpCallback callback; - void* userdata; -}; - -static void dump_callback(void* key, void* data, void* userdata) { - AvahiCacheEntry *e = data; - AvahiKey *k = key; - struct dump_data *dump_data = userdata; - - assert(k); - assert(e); - assert(data); - - for (; e; e = e->by_key_next) { - char *t; - - if (!(t = avahi_record_to_string(e->record))) - continue; /* OOM */ - - dump_data->callback(t, dump_data->userdata); - avahi_free(t); - } -} - -int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata) { - struct dump_data data; - - assert(c); - assert(callback); - - callback(";;; CACHE DUMP FOLLOWS ;;;", userdata); - - data.callback = callback; - data.userdata = userdata; - - avahi_hashmap_foreach(c->hashmap, dump_callback, &data); - - return 0; -} - -int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { - struct timeval now; - unsigned age; - - assert(c); - assert(e); - - gettimeofday(&now, NULL); - - age = (unsigned) (avahi_timeval_diff(&now, &e->timestamp)/1000000); - -/* avahi_log_debug("age: %lli, ttl/2: %u", age, e->record->ttl); */ - - return age >= e->record->ttl/2; -} - -void avahi_cache_flush(AvahiCache *c) { - assert(c); - - while (c->entries) - remove_entry(c, c->entries); -} - -/*** Passive observation of failure ***/ - -static void* start_poof_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { - AvahiAddress *a = userdata; - struct timeval now; - - assert(c); - assert(pattern); - assert(e); - assert(a); - - gettimeofday(&now, NULL); - - switch (e->state) { - case AVAHI_CACHE_VALID: - - /* The entry was perfectly valid till, now, so let's enter - * POOF mode */ - - e->state = AVAHI_CACHE_POOF; - e->poof_address = *a; - e->poof_timestamp = now; - e->poof_num = 0; - - break; - - case AVAHI_CACHE_POOF: - if (avahi_timeval_diff(&now, &e->poof_timestamp) < 1000000) - break; - - e->poof_timestamp = now; - e->poof_address = *a; - e->poof_num ++; - - /* This is the 4th time we got no response, so let's - * fucking remove this entry. */ - if (e->poof_num > 3) - expire_in_one_second(c, e, AVAHI_CACHE_POOF_FINAL); - break; - - default: - ; - } - - return NULL; -} - -void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a) { - assert(c); - assert(key); - - avahi_cache_walk(c, key, start_poof_callback, (void*) a); -} - -void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a) { - AvahiCacheEntry *e; - - assert(c); - assert(record); - assert(a); - - if (!(e = lookup_record(c, record))) - return; - - /* This function is called for each response suppression - record. If the matching cache entry is in POOF state and the - query address is the same, we put it back into valid mode */ - - if (e->state == AVAHI_CACHE_POOF || e->state == AVAHI_CACHE_POOF_FINAL) - if (avahi_address_cmp(a, &e->poof_address) == 0) { - e->state = AVAHI_CACHE_VALID; - next_expiry(c, e, 80); - } -} diff --git a/avahi-core/cache.h b/avahi-core/cache.h deleted file mode 100644 index 49ba9b9..0000000 --- a/avahi-core/cache.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef foocachehfoo -#define foocachehfoo - -/*** - 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 struct AvahiCache AvahiCache; - -#include -#include "prioq.h" -#include "internal.h" -#include "timeeventq.h" -#include "hashmap.h" - -typedef enum { - AVAHI_CACHE_VALID, - AVAHI_CACHE_EXPIRY1, - AVAHI_CACHE_EXPIRY2, - AVAHI_CACHE_EXPIRY3, - AVAHI_CACHE_EXPIRY_FINAL, - AVAHI_CACHE_POOF, /* Passive observation of failure */ - AVAHI_CACHE_POOF_FINAL, - AVAHI_CACHE_GOODBYE_FINAL, - AVAHI_CACHE_REPLACE_FINAL -} AvahiCacheEntryState; - -typedef struct AvahiCacheEntry AvahiCacheEntry; - -struct AvahiCacheEntry { - AvahiCache *cache; - AvahiRecord *record; - struct timeval timestamp; - struct timeval poof_timestamp; - struct timeval expiry; - int cache_flush; - int poof_num; - - AvahiAddress origin; - - AvahiCacheEntryState state; - AvahiTimeEvent *time_event; - - AvahiAddress poof_address; - - AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key); - AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry); -}; - -struct AvahiCache { - AvahiServer *server; - - AvahiInterface *interface; - - AvahiHashmap *hashmap; - - AVAHI_LLIST_HEAD(AvahiCacheEntry, entries); - - unsigned n_entries; - - int last_rand; - time_t last_rand_timestamp; -}; - -AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface); -void avahi_cache_free(AvahiCache *c); - -void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a); - -int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata); - -typedef void* AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata); -void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata); - -int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e); - -/** Start the "Passive observation of Failure" algorithm for all - * records of the specified key. The specified address is */ -void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a); - -/* Stop a previously started POOF algorithm for a record. (Used for response suppresions records */ -void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a); - -void avahi_cache_flush(AvahiCache *c); - -#endif diff --git a/avahi-core/conformance-test.c b/avahi-core/conformance-test.c deleted file mode 100644 index 632f66b..0000000 --- a/avahi-core/conformance-test.c +++ /dev/null @@ -1,158 +0,0 @@ -/*** - 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 - -static char *name = NULL; -static AvahiSEntryGroup *group = NULL; -static int try = 0; -static AvahiServer *avahi = NULL; -static const AvahiPoll *poll_api; - -static void dump_line(const char *text, AVAHI_GCC_UNUSED void* userdata) { - printf("%s\n", text); -} - -static void dump_timeout_callback(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void* userdata) { - struct timeval tv; - - avahi_server_dump(avahi, dump_line, NULL); - - avahi_elapse_time(&tv, 5000, 0); - poll_api->timeout_update(timeout, &tv); -} - -static void entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); - -static void create_service(const char *t) { - char *n; - - assert(t || name); - - n = t ? avahi_strdup(t) : avahi_alternative_service_name(name); - avahi_free(name); - name = n; - - if (group) - avahi_s_entry_group_reset(group); - else - group = avahi_s_entry_group_new(avahi, entry_group_callback, NULL); - - avahi_server_add_service(avahi, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, "_http._tcp", NULL, NULL, 80, "foo", NULL); - avahi_s_entry_group_commit(group); - - try++; -} - -static void rename_timeout_callback(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void *userdata) { - struct timeval tv; - - if (access("flag", F_OK) == 0) { - create_service("New - Bonjour Service Name"); - return; - } - - avahi_elapse_time(&tv, 5000, 0); - poll_api->timeout_update(timeout, &tv); -} - -static void entry_group_callback(AVAHI_GCC_UNUSED AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) { - if (state == AVAHI_ENTRY_GROUP_COLLISION) - create_service(NULL); - else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) { - avahi_log_debug("ESTABLISHED !!!!"); - try = 0; - } -} - -static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) { - avahi_log_debug("server state: %i", state); - - if (state == AVAHI_SERVER_RUNNING) { - avahi_server_dump(avahi, dump_line, NULL); - } else if (state == AVAHI_SERVER_COLLISION) { - char *n; - - n = avahi_alternative_host_name(avahi_server_get_host_name(s)); - avahi_log_warn("Host name conflict, retrying with <%s>", n); - avahi_server_set_host_name(s, n); - avahi_free(n); - - } -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - int error; - AvahiSimplePoll *simple_poll; - struct timeval tv; - struct AvahiServerConfig config; - - simple_poll = avahi_simple_poll_new(); - poll_api = avahi_simple_poll_get(simple_poll); - - avahi_server_config_init(&config); - config.publish_workstation = 0; - config.use_ipv6 = 0; - config.publish_domain = 0; - config.publish_hinfo = 0; - avahi = avahi_server_new(poll_api, &config, server_callback, NULL, &error); - avahi_server_config_free(&config); - - avahi_elapse_time(&tv, 5000, 0); - poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, avahi); - - avahi_elapse_time(&tv, 5000, 0); - poll_api->timeout_new(poll_api, &tv, rename_timeout_callback, avahi); - - /* Evil, but the conformace test requires that*/ - create_service("gurke"); - - - avahi_simple_poll_loop(simple_poll); - - if (group) - avahi_s_entry_group_free(group); - avahi_server_free(avahi); - - avahi_simple_poll_free(simple_poll); - - return 0; -} diff --git a/avahi-core/core.h b/avahi-core/core.h deleted file mode 100644 index f14dc84..0000000 --- a/avahi-core/core.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef foocorehfoo -#define foocorehfoo - -/*** - 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. -***/ - -/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */ - -/** An mDNS responder object */ -typedef struct AvahiServer AvahiServer; - -#include -#include -#include -#include -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Maximum number of defined DNS servers for wide area DNS */ -#define AVAHI_WIDE_AREA_SERVERS_MAX 4 - -/** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */ -typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState state, void* userdata); - -/** Stores configuration options for a server instance */ -typedef struct AvahiServerConfig { - char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */ - char *domain_name; /**< Default domain name. If left empty defaults to .local */ - int use_ipv4; /**< Enable IPv4 support */ - int use_ipv6; /**< Enable IPv6 support */ - AvahiStringList *allow_interfaces;/**< Allow specific interface to be used for Avahi */ - AvahiStringList *deny_interfaces; /**< Deny specific interfaces to be used for Avahi */ - int publish_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */ - int publish_addresses; /**< Register A, AAAA and PTR records for all local IP addresses */ - int publish_no_reverse; /**< Do not register PTR records */ - int publish_workstation; /**< Register a _workstation._tcp service */ - int publish_domain; /**< Announce the local domain for browsing */ - int check_response_ttl; /**< If enabled the server ignores all incoming responses with IP TTL != 255. Newer versions of the RFC do no longer contain this check, so it is disabled by default. */ - int use_iff_running; /**< Require IFF_RUNNING on local network interfaces. This is the official way to check for link beat. Unfortunately this doesn't work with all drivers. So bettere leave this off. */ - int enable_reflector; /**< Reflect incoming mDNS traffic to all local networks. This allows mDNS based network browsing beyond ethernet borders */ - int reflect_ipv; /**< if enable_reflector is 1, enable/disable reflecting between IPv4 and IPv6 */ - int add_service_cookie; /**< Add magic service cookie to all locally generated records implicitly */ - int enable_wide_area; /**< Enable wide area support */ - AvahiAddress wide_area_servers[AVAHI_WIDE_AREA_SERVERS_MAX]; /** Unicast DNS server to use for wide area lookup */ - unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */ - int disallow_other_stacks; /**< Make sure that only one mDNS responder is run at the same time on the local machine. If this is enable Avahi will not set SO_REUSADDR on its sockets, effectively preventing other stacks from running on the local machine */ - 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 */ - unsigned n_cache_entries_max; /**< Maximum number of cache entries per interface */ - AvahiUsec ratelimit_interval; /**< If non-zero, rate-limiting interval parameter. */ - unsigned ratelimit_burst; /**< If ratelimit_interval is non-zero, rate-limiting burst parameter. */ -} AvahiServerConfig; - -/** Allocate a new mDNS responder object. */ -AvahiServer *avahi_server_new( - const AvahiPoll *api, /**< The main loop adapter */ - const AvahiServerConfig *sc, /**< If non-NULL a pointer to a configuration structure for the server. The server makes an internal deep copy of this structure, so you may free it using avahi_server_config_done() immediately after calling this function. */ - AvahiServerCallback callback, /**< A callback which is called whenever the state of the server changes */ - void* userdata, /**< An opaque pointer which is passed to the callback function */ - int *error); - -/** Free an mDNS responder object */ -void avahi_server_free(AvahiServer* s); - -/** Fill in default values for a server configuration structure. If you - * make use of an AvahiServerConfig structure be sure to initialize - * it with this function for the sake of upwards library - * compatibility. This call may allocate strings on the heap. To - * release this memory make sure to call - * avahi_server_config_done(). If you want to replace any strings in - * the structure be sure to free the strings filled in by this - * function with avahi_free() first and allocate the replacements with - * g_malloc() (or g_strdup()).*/ -AvahiServerConfig* avahi_server_config_init( - AvahiServerConfig *c /**< A structure which shall be filled in */ ); - -/** Make a deep copy of the configuration structure *c to *ret. */ -AvahiServerConfig* avahi_server_config_copy( - AvahiServerConfig *ret /**< destination */, - const AvahiServerConfig *c /**< source */); - -/** Free the data in a server configuration structure. */ -void avahi_server_config_free(AvahiServerConfig *c); - -/** Return the currently chosen domain name of the server object. The - * return value points to an internally allocated string. Be sure to - * make a copy of the string before calling any other library - * functions. */ -const char* avahi_server_get_domain_name(AvahiServer *s); - -/** Return the currently chosen host name. The return value points to a internally allocated string. */ -const char* avahi_server_get_host_name(AvahiServer *s); - -/** Return the currently chosen host name as a FQDN ("fully qualified - * domain name", i.e. the concatenation of the host and domain - * name). The return value points to a internally allocated string. */ -const char* avahi_server_get_host_name_fqdn(AvahiServer *s); - -/** Change the host name of a running mDNS responder. This will drop -all automicatilly generated RRs and readd them with the new -name. Since the responder has to probe for the new RRs this function -takes some time to take effect altough it returns immediately. This -function is intended to be called when a host name conflict is -reported using AvahiServerCallback. The caller should readd all user -defined RRs too since they otherwise continue to point to the outdated -host name..*/ -int avahi_server_set_host_name(AvahiServer *s, const char *host_name); - -/** Change the domain name of a running mDNS responder. The same rules - * as with avahi_server_set_host_name() apply. */ -int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name); - -/** Return the opaque user data pointer attached to a server object */ -void* avahi_server_get_data(AvahiServer *s); - -/** Change the opaque user data pointer attached to a server object */ -void avahi_server_set_data(AvahiServer *s, void* userdata); - -/** Return the current state of the server object */ -AvahiServerState avahi_server_get_state(AvahiServer *s); - -/** Callback prototype for avahi_server_dump() */ -typedef void (*AvahiDumpCallback)(const char *text, void* userdata); - -/** Dump the current server status by calling "callback" for each line. */ -int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata); - -/** Return the last error code */ -int avahi_server_errno(AvahiServer *s); - -/** Return the local service cookie */ -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); - -/** Set the browsing domains */ -int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains); - -/** 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/dns-spin-test.c b/avahi-core/dns-spin-test.c deleted file mode 100644 index f50e691..0000000 --- a/avahi-core/dns-spin-test.c +++ /dev/null @@ -1,122 +0,0 @@ -/*** - 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. -***/ - -/* Regression test for Avahi bug #84. - * This program tests whether the avahi_dns_packet_consume_name function - * returns (rather than spinning forever). For a function as simple as - * avahi_dns_packet_consume_name, we assume that 1 second of CPU time ≈ forever - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include - -#include "dns.h" - -#define MAX_CPU_SECONDS 1 - -#define TEST_NAME "dns-spin-test" - -static void fail(const char *fmt, ...) __attribute__((format(printf, 1, 2), noreturn)); -static void unresolved(const char *fmt, ...) __attribute__((format(printf, 1, 2), noreturn)); -static void stdlib_fail(const char *msg) __attribute__((noreturn)); -static void handle(int sig) __attribute__((noreturn)); - -void stdlib_fail(const char *msg) { - perror(msg); - - printf("UNRESOLVED: " TEST_NAME " (stdlib failure)\n"); - - exit(77); -} - -void unresolved(const char *fmt, ...) { - va_list ap; - - printf("UNRESOLVED: " TEST_NAME ": "); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - printf("\n"); - - exit(77); -} - -void fail(const char *fmt, ...) { - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - printf("\n"); - - exit(EXIT_FAILURE); -} - -void handle(AVAHI_GCC_UNUSED int sig) { - fail("Interrupted after %d second of CPU time", MAX_CPU_SECONDS); -} - -#define TRY_EXCEPT(cmd, badresult) \ - do { \ - if ((cmd) == (badresult)) \ - unresolved("%s returned %s", #cmd, #badresult); \ - } while (0) - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - struct itimerval itval; - AvahiDnsPacket *packet; - char name[512]; - int ret; - uint8_t badrr[] = { - 0xC0, AVAHI_DNS_PACKET_HEADER_SIZE, /* self-referential QNAME pointer */ - 0, 1, /* QTYPE A (host addr) */ - 0, 1, /* QCLASS IN (internet/ipv4) */ - }; - - if (signal(SIGVTALRM, handle) == SIG_ERR) - stdlib_fail("signal(SIGVTALRM)"); - - memset(&itval, 0, sizeof(itval)); - itval.it_value.tv_sec = MAX_CPU_SECONDS; - - if (setitimer(ITIMER_VIRTUAL, &itval, NULL) == -1) - stdlib_fail("setitimer()"); - - TRY_EXCEPT(packet = avahi_dns_packet_new_query(512), NULL); - TRY_EXCEPT(avahi_dns_packet_append_bytes(packet, badrr, sizeof(badrr)), NULL); - - /* This is expected to fail (if it returns) */ - ret = avahi_dns_packet_consume_name(packet, name, sizeof(name)); - - if (ret != -1) - fail("avahi_dns_packet_consume_name() returned %d; -1 was expected", ret); - - return EXIT_SUCCESS; -} - -/* vim:ts=4:sw=4:et - */ diff --git a/avahi-core/dns-srv-rr.h b/avahi-core/dns-srv-rr.h deleted file mode 100644 index fdd9010..0000000 --- a/avahi-core/dns-srv-rr.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef foodnssrvhfoo -#define foodnssrvhfoo - -/*** - 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. -***/ - -/** \file avahi-core/dns-srv-rr.h Functions for announcing and browsing for unicast DNS servers via mDNS */ - -/** A domain service browser object. Use this to browse for - * conventional unicast DNS servers which may be used to resolve - * conventional domain names */ -typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser; - -#include -#include -#include -#include - -AVAHI_C_DECL_BEGIN - -/** The type of DNS server */ -typedef enum { - AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/ - AVAHI_DNS_SERVER_UPDATE, /**< Unicast DNS servers for updates (_dns-update._udp)*/ - AVAHI_DNS_SERVER_MAX -} AvahiDNSServerType; - -/** Publish the specified unicast DNS server address via mDNS. You may - * browse for records create this way wit - * avahi_s_dns_server_browser_new(). */ -int avahi_server_add_dns_server_address( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *domain, - AvahiDNSServerType type, - const AvahiAddress *address, - uint16_t port /** should be 53 */); - -/** Callback prototype for AvahiSDNSServerBrowser events */ -typedef void (*AvahiSDNSServerBrowserCallback)( - AvahiSDNSServerBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *host_name, /**< Host name of the DNS server, probably useless */ - const AvahiAddress *a, /**< Address of the DNS server */ - uint16_t port, /**< Port number of the DNS servers, probably 53 */ - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create a new AvahiSDNSServerBrowser object */ -AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDNSServerType type, - AvahiProtocol aprotocol, /**< Address protocol for the DNS server */ - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSDNSServerBrowserCallback callback, - void* userdata); - -/** Free an AvahiSDNSServerBrowser object */ -void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/dns-test.c b/avahi-core/dns-test.c deleted file mode 100644 index ebe2305..0000000 --- a/avahi-core/dns-test.c +++ /dev/null @@ -1,113 +0,0 @@ -/*** - 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 "dns.h" -#include "log.h" -#include "util.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - char t[AVAHI_DOMAIN_NAME_MAX], *m; - const char *a, *b, *c, *d; - AvahiDnsPacket *p; - AvahiRecord *r, *r2; - uint8_t rdata[AVAHI_DNS_RDATA_MAX]; - size_t l; - - p = avahi_dns_packet_new(0); - - assert(avahi_dns_packet_append_name(p, a = "Ahello.hello.hello.de.")); - assert(avahi_dns_packet_append_name(p, b = "Bthis is a test.hello.de.")); - assert(avahi_dns_packet_append_name(p, c = "Cthis\\.is\\.a\\.test\\.with\\.dots.hello.de.")); - assert(avahi_dns_packet_append_name(p, d = "Dthis\\\\is another test.hello.de.")); - - avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size); - - assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); - avahi_log_debug(">%s<", t); - assert(avahi_domain_equal(a, t)); - - assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); - avahi_log_debug(">%s<", t); - assert(avahi_domain_equal(b, t)); - - assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); - avahi_log_debug(">%s<", t); - assert(avahi_domain_equal(c, t)); - - assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); - avahi_log_debug(">%s<", t); - assert(avahi_domain_equal(d, t)); - - avahi_dns_packet_free(p); - - /* RDATA PARSING AND SERIALIZATION */ - - /* Create an AvahiRecord with some usful data */ - r = avahi_record_new_full("foobar.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL); - assert(r); - r->data.hinfo.cpu = avahi_strdup("FOO"); - r->data.hinfo.os = avahi_strdup("BAR"); - - /* Serialize it into a blob */ - assert((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) != (size_t) -1); - - /* Print it */ - avahi_hexdump(rdata, l); - - /* Create a new record and fill in the data from the blob */ - r2 = avahi_record_new(r->key, AVAHI_DEFAULT_TTL); - assert(r2); - assert(avahi_rdata_parse(r2, rdata, l) >= 0); - - /* Compare both versions */ - assert(avahi_record_equal_no_ttl(r, r2)); - - /* Free the records */ - avahi_record_unref(r); - avahi_record_unref(r2); - - r = avahi_record_new_full("foobar", 77, 77, AVAHI_DEFAULT_TTL); - assert(r); - - assert(r->data.generic.data = avahi_memdup("HALLO", r->data.generic.size = 5)); - - m = avahi_record_to_string(r); - assert(m); - - avahi_log_debug(">%s<", m); - - avahi_free(m); - avahi_record_unref(r); - - return 0; -} diff --git a/avahi-core/dns.c b/avahi-core/dns.c deleted file mode 100644 index 2fcd91f..0000000 --- a/avahi-core/dns.c +++ /dev/null @@ -1,877 +0,0 @@ -/*** - 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 "dns.h" -#include "log.h" - -AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) { - AvahiDnsPacket *p; - size_t max_size; - - if (mtu <= 0) - max_size = AVAHI_DNS_PACKET_SIZE_MAX; - else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE) - max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE; - else - max_size = 0; - - if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) - max_size = AVAHI_DNS_PACKET_HEADER_SIZE; - - if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size))) - return p; - - p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; - p->max_size = max_size; - p->name_table = NULL; - p->data = NULL; - - memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); - return p; -} - -AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) { - AvahiDnsPacket *p; - - if (!(p = avahi_dns_packet_new(mtu))) - return NULL; - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); - return p; -} - -AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) { - AvahiDnsPacket *p; - - if (!(p = avahi_dns_packet_new(mtu))) - return NULL; - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0)); - return p; -} - -AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) { - AvahiDnsPacket *r; - assert(p); - - if (!(r = avahi_dns_packet_new_response(mtu, aa))) - return NULL; - - if (copy_queries) { - unsigned saved_rindex; - uint32_t n; - - saved_rindex = p->rindex; - p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; - - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) { - AvahiKey *k; - int unicast_response; - - if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) { - avahi_dns_packet_append_key(r, k, unicast_response); - avahi_key_unref(k); - } - } - - p->rindex = saved_rindex; - - avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT)); - } - - avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); - - avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS, - (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) | - (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE)); - - return r; -} - - -void avahi_dns_packet_free(AvahiDnsPacket *p) { - assert(p); - - if (p->name_table) - avahi_hashmap_free(p->name_table); - - avahi_free(p); -} - -void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) { - assert(p); - assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); - - ((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v); -} - -uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) { - assert(p); - assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); - - return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]); -} - -void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) { - assert(p); - assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); - - avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1); -} - - -static void name_table_cleanup(void *key, void *value, void *user_data) { - AvahiDnsPacket *p = user_data; - - if ((uint8_t*) value >= AVAHI_DNS_PACKET_DATA(p) + p->size) - avahi_hashmap_remove(p->name_table, key); -} - -void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p) { - if (p->name_table) - avahi_hashmap_foreach(p->name_table, name_table_cleanup, p); -} - -uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) { - uint8_t *d, *saved_ptr = NULL; - size_t saved_size; - - assert(p); - assert(name); - - saved_size = p->size; - saved_ptr = avahi_dns_packet_extend(p, 0); - - while (*name) { - uint8_t* prev; - const char *pname; - char label[64], *u; - - /* Check whether we can compress this name. */ - - if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) { - unsigned idx; - - assert(prev >= AVAHI_DNS_PACKET_DATA(p)); - idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p)); - - assert(idx < p->size); - - if (idx < 0x4000) { - uint8_t *t; - if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t)))) - return NULL; - - t[0] = (uint8_t) ((0xC000 | idx) >> 8); - t[1] = (uint8_t) idx; - return saved_ptr; - } - } - - pname = name; - - if (!(avahi_unescape_label(&name, label, sizeof(label)))) - goto fail; - - if (!(d = avahi_dns_packet_append_string(p, label))) - goto fail; - - if (!p->name_table) - /* This works only for normalized domain names */ - p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL); - - if (!(u = avahi_strdup(pname))) - avahi_log_error("avahi_strdup() failed."); - else - avahi_hashmap_insert(p->name_table, u, d); - } - - if (!(d = avahi_dns_packet_extend(p, 1))) - goto fail; - - *d = 0; - - return saved_ptr; - -fail: - p->size = saved_size; - avahi_dns_packet_cleanup_name_table(p); - - return NULL; -} - -uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) { - uint8_t *d; - assert(p); - - if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t)))) - return NULL; - - d[0] = (uint8_t) (v >> 8); - d[1] = (uint8_t) v; - return d; -} - -uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) { - uint8_t *d; - assert(p); - - if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t)))) - return NULL; - - d[0] = (uint8_t) (v >> 24); - d[1] = (uint8_t) (v >> 16); - d[2] = (uint8_t) (v >> 8); - d[3] = (uint8_t) v; - - return d; -} - -uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *b, size_t l) { - uint8_t* d; - - assert(p); - assert(b); - assert(l); - - if (!(d = avahi_dns_packet_extend(p, l))) - return NULL; - - memcpy(d, b, l); - return d; -} - -uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) { - uint8_t* d; - size_t k; - - assert(p); - assert(s); - - if ((k = strlen(s)) >= 255) - k = 255; - - if (!(d = avahi_dns_packet_extend(p, k+1))) - return NULL; - - *d = (uint8_t) k; - memcpy(d+1, s, k); - - return d; -} - -uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) { - uint8_t *d; - - assert(p); - - if (p->size+l > p->max_size) - return NULL; - - d = AVAHI_DNS_PACKET_DATA(p) + p->size; - p->size += l; - - return d; -} - -int avahi_dns_packet_check_valid(AvahiDnsPacket *p) { - uint16_t flags; - assert(p); - - if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE) - return -1; - - flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); - - if (flags & AVAHI_DNS_FLAG_OPCODE) - return -1; - - return 0; -} - -int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) { - uint16_t flags; - assert(p); - - if (avahi_dns_packet_check_valid(p) < 0) - return -1; - - flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); - - if (flags & AVAHI_DNS_FLAG_RCODE) - return -1; - - return 0; -} - -int avahi_dns_packet_is_query(AvahiDnsPacket *p) { - assert(p); - - return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); -} - -static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) { - int ret = 0; - int compressed = 0; - int first_label = 1; - unsigned label_ptr; - int i; - assert(p && ret_name && l); - - for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) { - uint8_t n; - - if (idx+1 > p->size) - return -1; - - n = AVAHI_DNS_PACKET_DATA(p)[idx]; - - if (!n) { - idx++; - if (!compressed) - ret++; - - if (l < 1) - return -1; - *ret_name = 0; - - return ret; - - } else if (n <= 63) { - /* Uncompressed label */ - idx++; - if (!compressed) - ret++; - - if (idx + n > p->size) - return -1; - - if ((size_t) n + 1 > l) - return -1; - - if (!first_label) { - *(ret_name++) = '.'; - l--; - } else - first_label = 0; - - if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l))) - return -1; - - idx += n; - - if (!compressed) - ret += n; - } else if ((n & 0xC0) == 0xC0) { - /* Compressed label */ - - if (idx+2 > p->size) - return -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; - - compressed = 1; - } else - return -1; - } - - return -1; -} - -int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) { - int r; - - if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) - return -1; - - p->rindex += r; - return 0; -} - -int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) { - uint8_t *d; - - assert(p); - assert(ret_v); - - if (p->rindex + sizeof(uint16_t) > p->size) - return -1; - - d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); - *ret_v = (d[0] << 8) | d[1]; - p->rindex += sizeof(uint16_t); - - return 0; -} - -int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) { - uint8_t* d; - - assert(p); - assert(ret_v); - - if (p->rindex + sizeof(uint32_t) > p->size) - return -1; - - d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); - *ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; - p->rindex += sizeof(uint32_t); - - return 0; -} - -int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) { - assert(p); - assert(ret_data); - assert(l > 0); - - if (p->rindex + l > p->size) - return -1; - - memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); - p->rindex += l; - - return 0; -} - -int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) { - size_t k; - - assert(p); - assert(ret_string); - assert(l > 0); - - if (p->rindex >= p->size) - return -1; - - k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; - - if (p->rindex+1+k > p->size) - return -1; - - if (l > k+1) - l = k+1; - - memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); - ret_string[l-1] = 0; - - p->rindex += 1+k; - - return 0; -} - -const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { - assert(p); - - if (p->rindex > p->size) - return NULL; - - return AVAHI_DNS_PACKET_DATA(p) + p->rindex; -} - -int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) { - assert(p); - - if (p->rindex + length > p->size) - return -1; - - p->rindex += length; - return 0; -} - -static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) { - char buf[AVAHI_DOMAIN_NAME_MAX]; - const void* start; - - assert(p); - assert(r); - - start = avahi_dns_packet_get_rptr(p); - - switch (r->key->type) { - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - - if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) - return -1; - - r->data.ptr.name = avahi_strdup(buf); - break; - - - case AVAHI_DNS_TYPE_SRV: - - if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || - avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || - avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || - avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) - return -1; - - r->data.srv.name = avahi_strdup(buf); - break; - - case AVAHI_DNS_TYPE_HINFO: - - if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) - return -1; - - r->data.hinfo.cpu = avahi_strdup(buf); - - if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) - return -1; - - r->data.hinfo.os = avahi_strdup(buf); - break; - - case AVAHI_DNS_TYPE_TXT: - - if (rdlength > 0) { - if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0) - return -1; - - if (avahi_dns_packet_skip(p, rdlength) < 0) - return -1; - } else - r->data.txt.string_list = NULL; - - break; - - case AVAHI_DNS_TYPE_A: - -/* avahi_log_debug("A"); */ - - if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) - return -1; - - break; - - case AVAHI_DNS_TYPE_AAAA: - -/* avahi_log_debug("aaaa"); */ - - if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) - return -1; - - break; - - default: - -/* avahi_log_debug("generic"); */ - - if (rdlength > 0) { - - r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength); - r->data.generic.size = rdlength; - - if (avahi_dns_packet_skip(p, rdlength) < 0) - return -1; - } - - break; - } - - /* Check if we read enough data */ - if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength) - return -1; - - return 0; -} - -AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) { - char name[AVAHI_DOMAIN_NAME_MAX]; - uint16_t type, class; - uint32_t ttl; - uint16_t rdlength; - AvahiRecord *r = NULL; - - assert(p); - - if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || - avahi_dns_packet_consume_uint16(p, &type) < 0 || - avahi_dns_packet_consume_uint16(p, &class) < 0 || - avahi_dns_packet_consume_uint32(p, &ttl) < 0 || - avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || - p->rindex + rdlength > p->size) - goto fail; - - if (ret_cache_flush) - *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); - class &= ~AVAHI_DNS_CACHE_FLUSH; - - if (!(r = avahi_record_new_full(name, class, type, ttl))) - goto fail; - - if (parse_rdata(p, r, rdlength) < 0) - goto fail; - - if (!avahi_record_is_valid(r)) - goto fail; - - return r; - -fail: - if (r) - avahi_record_unref(r); - - return NULL; -} - -AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) { - char name[256]; - uint16_t type, class; - AvahiKey *k; - - assert(p); - - if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || - avahi_dns_packet_consume_uint16(p, &type) < 0 || - avahi_dns_packet_consume_uint16(p, &class) < 0) - return NULL; - - if (ret_unicast_response) - *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); - - class &= ~AVAHI_DNS_UNICAST_RESPONSE; - - if (!(k = avahi_key_new(name, class, type))) - return NULL; - - if (!avahi_key_is_valid(k)) { - avahi_key_unref(k); - return NULL; - } - - return k; -} - -uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) { - uint8_t *t; - size_t size; - - assert(p); - assert(k); - - size = p->size; - - if (!(t = avahi_dns_packet_append_name(p, k->name)) || - !avahi_dns_packet_append_uint16(p, k->type) || - !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { - p->size = size; - avahi_dns_packet_cleanup_name_table(p); - - return NULL; - } - - return t; -} - -static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) { - assert(p); - assert(r); - - switch (r->key->type) { - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - - if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) - return -1; - - break; - - case AVAHI_DNS_TYPE_SRV: - - if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || - !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || - !avahi_dns_packet_append_uint16(p, r->data.srv.port) || - !avahi_dns_packet_append_name(p, r->data.srv.name)) - return -1; - - break; - - case AVAHI_DNS_TYPE_HINFO: - if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || - !avahi_dns_packet_append_string(p, r->data.hinfo.os)) - return -1; - - break; - - case AVAHI_DNS_TYPE_TXT: { - - uint8_t *data; - size_t n; - - n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); - - if (!(data = avahi_dns_packet_extend(p, n))) - return -1; - - avahi_string_list_serialize(r->data.txt.string_list, data, n); - break; - } - - - case AVAHI_DNS_TYPE_A: - - if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) - return -1; - - break; - - case AVAHI_DNS_TYPE_AAAA: - - if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) - return -1; - - break; - - default: - - if (r->data.generic.size) - if (!avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) - return -1; - - break; - } - - return 0; -} - - -uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) { - uint8_t *t, *l, *start; - size_t size; - - assert(p); - assert(r); - - size = p->size; - - if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || - !avahi_dns_packet_append_uint16(p, r->key->type) || - !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) || - !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) || - !(l = avahi_dns_packet_append_uint16(p, 0))) - goto fail; - - start = avahi_dns_packet_extend(p, 0); - - if (append_rdata(p, r) < 0) - goto fail; - - size = avahi_dns_packet_extend(p, 0) - start; - assert(size <= AVAHI_DNS_RDATA_MAX); - -/* avahi_log_debug("appended %u", size); */ - - l[0] = (uint8_t) ((uint16_t) size >> 8); - l[1] = (uint8_t) ((uint16_t) size); - - return t; - - -fail: - p->size = size; - avahi_dns_packet_cleanup_name_table(p); - - return NULL; -} - -int avahi_dns_packet_is_empty(AvahiDnsPacket *p) { - assert(p); - - return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; -} - -size_t avahi_dns_packet_space(AvahiDnsPacket *p) { - assert(p); - - assert(p->size <= p->max_size); - - return p->max_size - p->size; -} - -int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) { - int ret; - AvahiDnsPacket p; - - assert(record); - assert(rdata); - - p.data = (void*) rdata; - p.max_size = p.size = size; - p.rindex = 0; - p.name_table = NULL; - - ret = parse_rdata(&p, record, size); - - assert(!p.name_table); - - return ret; -} - -size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) { - int ret; - AvahiDnsPacket p; - - assert(record); - assert(rdata); - assert(max_size > 0); - - p.data = (void*) rdata; - p.max_size = max_size; - p.size = p.rindex = 0; - p.name_table = NULL; - - ret = append_rdata(&p, record); - - if (p.name_table) - avahi_hashmap_free(p.name_table); - - if (ret < 0) - return (size_t) -1; - - return p.size; -} diff --git a/avahi-core/dns.h b/avahi-core/dns.h deleted file mode 100644 index 52e8d88..0000000 --- a/avahi-core/dns.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef foodnshfoo -#define foodnshfoo - -/*** - 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 "rr.h" -#include "hashmap.h" - -#define AVAHI_DNS_PACKET_HEADER_SIZE 12 -#define AVAHI_DNS_PACKET_EXTRA_SIZE 48 -#define AVAHI_DNS_LABELS_MAX 127 -#define AVAHI_DNS_RDATA_MAX 0xFFFF -#define AVAHI_DNS_PACKET_SIZE_MAX (AVAHI_DNS_PACKET_HEADER_SIZE + 256 + 2 + 2 + 4 + 2 + AVAHI_DNS_RDATA_MAX) - -typedef struct AvahiDnsPacket { - size_t size, rindex, max_size; - AvahiHashmap *name_table; /* for name compression */ - uint8_t *data; -} AvahiDnsPacket; - -#define AVAHI_DNS_PACKET_DATA(p) ((p)->data ? (p)->data : ((uint8_t*) p) + sizeof(AvahiDnsPacket)) - -AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu); -AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu); -AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa); - -AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa); - -void avahi_dns_packet_free(AvahiDnsPacket *p); -void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v); -uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx); -void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx); - -uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l); - -void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p); - -uint8_t *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v); -uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v); -uint8_t *avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name); -uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *d, size_t l); -uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response); -uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl); -uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s); - -int avahi_dns_packet_is_query(AvahiDnsPacket *p); -int avahi_dns_packet_check_valid(AvahiDnsPacket *p); -int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p); - -int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v); -int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v); -int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l); -int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void* ret_data, size_t l); -AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response); -AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush); -int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l); - -const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p); - -int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length); - -int avahi_dns_packet_is_empty(AvahiDnsPacket *p); -size_t avahi_dns_packet_space(AvahiDnsPacket *p); - -#define AVAHI_DNS_FIELD_ID 0 -#define AVAHI_DNS_FIELD_FLAGS 1 -#define AVAHI_DNS_FIELD_QDCOUNT 2 -#define AVAHI_DNS_FIELD_ANCOUNT 3 -#define AVAHI_DNS_FIELD_NSCOUNT 4 -#define AVAHI_DNS_FIELD_ARCOUNT 5 - -#define AVAHI_DNS_FLAG_QR (1 << 15) -#define AVAHI_DNS_FLAG_OPCODE (15 << 11) -#define AVAHI_DNS_FLAG_RCODE (15) -#define AVAHI_DNS_FLAG_TC (1 << 9) -#define AVAHI_DNS_FLAG_AA (1 << 10) - -#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ - (((uint16_t) !!qr << 15) | \ - ((uint16_t) (opcode & 15) << 11) | \ - ((uint16_t) !!aa << 10) | \ - ((uint16_t) !!tc << 9) | \ - ((uint16_t) !!rd << 8) | \ - ((uint16_t) !!ra << 7) | \ - ((uint16_t) !!ad << 5) | \ - ((uint16_t) !!cd << 4) | \ - ((uint16_t) (rcode & 15))) - -#define AVAHI_MDNS_SUFFIX_LOCAL "local" -#define AVAHI_MDNS_SUFFIX_ADDR_IPV4 "254.169.in-addr.arpa" -#define AVAHI_MDNS_SUFFIX_ADDR_IPV6 "0.8.e.f.ip6.arpa" - -#endif - diff --git a/avahi-core/domain-util.c b/avahi-core/domain-util.c deleted file mode 100644 index 6eed27f..0000000 --- a/avahi-core/domain-util.c +++ /dev/null @@ -1,188 +0,0 @@ -/*** - 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 "log.h" -#include "domain-util.h" -#include "util.h" - -static void strip_bad_chars(char *s) { - char *p, *d; - - s[strcspn(s, ".")] = 0; - - for (p = s, d = s; *p; p++) - if ((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '-') - *(d++) = *p; - - *d = 0; -} - -#ifdef __linux__ -static int load_lsb_distrib_id(char *ret_s, size_t size) { - FILE *f; - - assert(ret_s); - assert(size > 0); - - if (!(f = fopen("/etc/lsb-release", "r"))) - return -1; - - while (!feof(f)) { - char ln[256], *p; - - if (!fgets(ln, sizeof(ln), f)) - break; - - if (strncmp(ln, "DISTRIB_ID=", 11)) - continue; - - p = ln + 11; - p += strspn(p, "\""); - p[strcspn(p, "\"")] = 0; - - snprintf(ret_s, size, "%s", p); - - fclose(f); - return 0; - } - - fclose(f); - return -1; -} -#endif - -char *avahi_get_host_name(char *ret_s, size_t size) { - assert(ret_s); - assert(size > 0); - - if (gethostname(ret_s, size) >= 0) { - ret_s[size-1] = 0; - strip_bad_chars(ret_s); - } else - *ret_s = 0; - - if (strcmp(ret_s, "localhost") == 0 || strncmp(ret_s, "localhost.", 10) == 0) { - *ret_s = 0; - avahi_log_warn("System host name is set to 'localhost'. This is not a suitable mDNS host name, looking for alternatives."); - } - - if (*ret_s == 0) { - /* No hostname was set, so let's take the OS name */ - -#ifdef __linux__ - - /* Try LSB distribution name first */ - if (load_lsb_distrib_id(ret_s, size) >= 0) { - strip_bad_chars(ret_s); - avahi_strdown(ret_s); - } - - if (*ret_s == 0) -#endif - - { - /* Try uname() second */ - struct utsname utsname; - - if (uname(&utsname) >= 0) { - snprintf(ret_s, size, "%s", utsname.sysname); - strip_bad_chars(ret_s); - avahi_strdown(ret_s); - } - - /* Give up */ - if (*ret_s == 0) - snprintf(ret_s, size, "unnamed"); - } - } - - if (size >= AVAHI_LABEL_MAX) - ret_s[AVAHI_LABEL_MAX-1] = 0; - - return ret_s; -} - -char *avahi_get_host_name_strdup(void) { - char t[AVAHI_DOMAIN_NAME_MAX]; - - if (!(avahi_get_host_name(t, sizeof(t)))) - return NULL; - - return avahi_strdup(t); -} - -int avahi_binary_domain_cmp(const char *a, const char *b) { - assert(a); - assert(b); - - if (a == b) - return 0; - - for (;;) { - char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *p; - int r; - - p = avahi_unescape_label(&a, ca, sizeof(ca)); - assert(p); - p = avahi_unescape_label(&b, cb, sizeof(cb)); - assert(p); - - if ((r = strcmp(ca, cb))) - return r; - - if (!*a && !*b) - return 0; - } -} - -int avahi_domain_ends_with(const char *domain, const char *suffix) { - assert(domain); - assert(suffix); - - for (;;) { - char dummy[AVAHI_LABEL_MAX], *r; - - if (*domain == 0) - return 0; - - if (avahi_domain_equal(domain, suffix)) - return 1; - - r = avahi_unescape_label(&domain, dummy, sizeof(dummy)); - assert(r); - } -} - diff --git a/avahi-core/domain-util.h b/avahi-core/domain-util.h deleted file mode 100644 index 082fde6..0000000 --- a/avahi-core/domain-util.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef foodomainutilhfoo -#define foodomainutilhfoo - -/*** - 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 - -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Return the local host name. */ -char *avahi_get_host_name(char *ret_s, size_t size); - -/** Return the local host name. avahi_free() the result! */ -char *avahi_get_host_name_strdup(void); - -/** Do a binary comparison of to specified domain names, return -1, 0, or 1, depending on the order. */ -int avahi_binary_domain_cmp(const char *a, const char *b); - -/** Returns 1 if the the end labels of domain are eqal to suffix */ -int avahi_domain_ends_with(const char *domain, const char *suffix); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/entry.c b/avahi-core/entry.c deleted file mode 100644 index 0d86213..0000000 --- a/avahi-core/entry.c +++ /dev/null @@ -1,1233 +0,0 @@ -/*** - 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 "internal.h" -#include "iface.h" -#include "socket.h" -#include "browse.h" -#include "log.h" -#include "util.h" -#include "dns-srv-rr.h" -#include "rr-util.h" -#include "domain-util.h" - -static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain) { - assert(flags); - assert(domain); - - assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA))); - - if (*flags & (AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA)) - return; - - if (!s->wide_area_lookup_engine || - !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || - avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || - avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || - avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) - *flags |= AVAHI_PUBLISH_USE_MULTICAST; - else - *flags |= AVAHI_PUBLISH_USE_WIDE_AREA; -} - -void avahi_entry_free(AvahiServer*s, AvahiEntry *e) { - AvahiEntry *t; - - assert(s); - assert(e); - - avahi_goodbye_entry(s, e, 1, 1); - - /* Remove from linked list */ - AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e); - - /* Remove from hash table indexed by name */ - t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); - AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); - if (t) - avahi_hashmap_replace(s->entries_by_key, t->record->key, t); - else - avahi_hashmap_remove(s->entries_by_key, e->record->key); - - /* Remove from associated group */ - if (e->group) - AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); - - avahi_record_unref(e->record); - avahi_free(e); -} - -void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) { - assert(s); - assert(g); - - while (g->entries) - avahi_entry_free(s, g->entries); - - if (g->register_time_event) - avahi_time_event_free(g->register_time_event); - - AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g); - avahi_free(g); -} - -void avahi_cleanup_dead_entries(AvahiServer *s) { - assert(s); - - if (s->need_group_cleanup) { - AvahiSEntryGroup *g, *next; - - for (g = s->groups; g; g = next) { - next = g->groups_next; - - if (g->dead) - avahi_entry_group_free(s, g); - } - - s->need_group_cleanup = 0; - } - - if (s->need_entry_cleanup) { - AvahiEntry *e, *next; - - for (e = s->entries; e; e = next) { - next = e->entries_next; - - if (e->dead) - avahi_entry_free(s, e); - } - - s->need_entry_cleanup = 0; - } - - if (s->need_browser_cleanup) - avahi_browser_cleanup(s); - - if (s->cleanup_time_event) { - avahi_time_event_free(s->cleanup_time_event); - s->cleanup_time_event = NULL; - } -} - -static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) { - AvahiEntry *e; - - assert(s); - assert(r); - - for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { - if (e->dead) - continue; - - if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE)) - continue; - - if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) ) - continue; - - if (avahi_record_equal_no_ttl(r, e->record)) { - /* The records are the same, not a conflict in any case */ - continue; - } - - if ((interface <= 0 || - e->interface <= 0 || - e->interface == interface) && - (protocol == AVAHI_PROTO_UNSPEC || - e->protocol == AVAHI_PROTO_UNSPEC || - e->protocol == protocol)) - - return -1; - } - - return 0; -} - -static AvahiEntry * server_add_internal( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - AvahiRecord *r) { - - AvahiEntry *e; - - assert(s); - assert(r); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( - flags, - AVAHI_PUBLISH_NO_ANNOUNCE| - AVAHI_PUBLISH_NO_PROBE| - AVAHI_PUBLISH_UNIQUE| - AVAHI_PUBLISH_ALLOW_MULTIPLE| - AVAHI_PUBLISH_UPDATE| - AVAHI_PUBLISH_USE_WIDE_AREA| - AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, - (r->key->type != 0) && - (r->key->type != AVAHI_DNS_TYPE_ANY) && - (r->key->type != AVAHI_DNS_TYPE_OPT) && - (r->key->type != AVAHI_DNS_TYPE_TKEY) && - (r->key->type != AVAHI_DNS_TYPE_TSIG) && - (r->key->type != AVAHI_DNS_TYPE_IXFR) && - (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE); - - transport_flags_from_domain(s, &flags, r->key->name); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, - !g || - (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) || - (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); - - if (flags & AVAHI_PUBLISH_UPDATE) { - AvahiRecord *old_record; - int is_first = 1; - - /* Update and existing record */ - - /* Find the first matching entry */ - for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { - if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) - break; - - is_first = 0; - } - - /* Hmm, nothing found? */ - if (!e) { - avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); - return NULL; - } - - /* Update the entry */ - old_record = e->record; - e->record = avahi_record_ref(r); - e->flags = flags; - - /* Announce our changes when needed */ - if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) { - - /* Remove the old entry from all caches, if needed */ - if (!(e->flags & AVAHI_PUBLISH_UNIQUE)) - avahi_goodbye_entry(s, e, 1, 0); - - /* Reannounce our updated entry */ - avahi_reannounce_entry(s, e); - } - - /* If we were the first entry in the list, we need to update the key */ - if (is_first) - avahi_hashmap_replace(s->entries_by_key, e->record->key, e); - - avahi_record_unref(old_record); - - } else { - AvahiEntry *t; - - /* Add a new record */ - - if (check_record_conflict(s, interface, protocol, r, flags) < 0) { - avahi_server_set_errno(s, AVAHI_ERR_COLLISION); - return NULL; - } - - if (!(e = avahi_new(AvahiEntry, 1))) { - avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - e->server = s; - e->record = avahi_record_ref(r); - e->group = g; - e->interface = interface; - e->protocol = protocol; - e->flags = flags; - e->dead = 0; - - AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers); - - AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); - - /* Insert into hash table indexed by name */ - t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); - AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); - avahi_hashmap_replace(s->entries_by_key, e->record->key, t); - - /* Insert into group list */ - if (g) - AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); - - avahi_announce_entry(s, e); - } - - return e; -} - -int avahi_server_add( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - AvahiRecord *r) { - - if (!server_add_internal(s, g, interface, protocol, flags, r)) - return avahi_server_errno(s); - - return AVAHI_OK; -} - -const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) { - AvahiEntry **e = (AvahiEntry**) state; - assert(s); - assert(e); - - if (!*e) - *e = g ? g->entries : s->entries; - - while (*e && (*e)->dead) - *e = g ? (*e)->by_group_next : (*e)->entries_next; - - if (!*e) - return NULL; - - return avahi_record_ref((*e)->record); -} - -int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) { - AvahiEntry *e; - - assert(s); - assert(callback); - - callback(";;; ZONE DUMP FOLLOWS ;;;", userdata); - - for (e = s->entries; e; e = e->entries_next) { - char *t; - char ln[256]; - - if (e->dead) - continue; - - if (!(t = avahi_record_to_string(e->record))) - return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - - snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol); - avahi_free(t); - - callback(ln, userdata); - } - - avahi_dump_caches(s->monitor, callback, userdata); - - if (s->wide_area_lookup_engine) - avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata); - return AVAHI_OK; -} - -static AvahiEntry *server_add_ptr_internal( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - uint32_t ttl, - const char *name, - const char *dest) { - - AvahiRecord *r; - AvahiEntry *e; - - assert(s); - assert(dest); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME); - - if (!name) - name = s->host_name_fqdn; - - if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) { - avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - r->data.ptr.name = avahi_normalize_name_strdup(dest); - e = server_add_internal(s, g, interface, protocol, flags, r); - avahi_record_unref(r); - return e; -} - -int avahi_server_add_ptr( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - uint32_t ttl, - const char *name, - const char *dest) { - - AvahiEntry *e; - - assert(s); - - if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest))) - return avahi_server_errno(s); - - return AVAHI_OK; -} - -int avahi_server_add_address( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - AvahiAddress *a) { - - char n[AVAHI_DOMAIN_NAME_MAX]; - int ret = AVAHI_OK; - AvahiEntry *entry = NULL, *reverse = NULL; - AvahiRecord *r; - - assert(s); - assert(a); - - AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, - AVAHI_PUBLISH_NO_REVERSE| - AVAHI_PUBLISH_NO_ANNOUNCE| - AVAHI_PUBLISH_NO_PROBE| - AVAHI_PUBLISH_UPDATE| - AVAHI_PUBLISH_USE_WIDE_AREA| - AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); - - /* Prepare the host naem */ - - if (!name) - name = s->host_name_fqdn; - else { - AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n))); - name = n; - } - - transport_flags_from_domain(s, &flags, name); - AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - - /* Create the A/AAAA record */ - - if (a->proto == AVAHI_PROTO_INET) { - - if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) { - ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - goto finish; - } - - r->data.a.address = a->data.ipv4; - - } else { - assert(a->proto == AVAHI_PROTO_INET6); - - if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) { - ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - goto finish; - } - - r->data.aaaa.address = a->data.ipv6; - } - - entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); - avahi_record_unref(r); - - if (!entry) { - ret = avahi_server_errno(s); - goto finish; - } - - /* Create the reverse lookup entry */ - - if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) { - char reverse_n[AVAHI_DOMAIN_NAME_MAX]; - avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n)); - - if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) { - ret = avahi_server_errno(s); - goto finish; - } - } - -finish: - - if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { - if (entry) - avahi_entry_free(s, entry); - if (reverse) - avahi_entry_free(s, reverse); - } - - return ret; -} - -static AvahiEntry *server_add_txt_strlst_nocopy( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - uint32_t ttl, - const char *name, - AvahiStringList *strlst) { - - AvahiRecord *r; - AvahiEntry *e; - - assert(s); - - if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) { - avahi_string_list_free(strlst); - avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - r->data.txt.string_list = strlst; - e = server_add_internal(s, g, interface, protocol, flags, r); - avahi_record_unref(r); - - return e; -} - -static AvahiStringList *add_magic_cookie( - AvahiServer *s, - AvahiStringList *strlst) { - - assert(s); - - if (!s->config.add_service_cookie) - return strlst; - - if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE)) - /* This string list already contains a magic cookie */ - return strlst; - - return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie); -} - -static int server_add_service_strlst_nocopy( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - const char *host, - uint16_t port, - AvahiStringList *strlst) { - - char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL; - AvahiRecord *r = NULL; - int ret = AVAHI_OK; - AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL; - - assert(s); - assert(type); - assert(name); - - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, - AVAHI_PUBLISH_NO_COOKIE| - AVAHI_PUBLISH_UPDATE| - AVAHI_PUBLISH_USE_WIDE_AREA| - AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME); - - if (!domain) - domain = s->domain_name; - - if (!host) - host = s->host_name_fqdn; - - transport_flags_from_domain(s, &flags, domain); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - - if (!(h = avahi_normalize_name_strdup(host))) { - ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || - (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 || - (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) { - avahi_server_set_errno(s, ret); - goto fail; - } - - /* Add service enumeration PTR record */ - - if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) { - ret = avahi_server_errno(s); - goto fail; - } - - /* Add SRV record */ - - if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { - ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - goto fail; - } - - r->data.srv.priority = 0; - r->data.srv.weight = 0; - r->data.srv.port = port; - r->data.srv.name = h; - h = NULL; - srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r); - avahi_record_unref(r); - - if (!srv_entry) { - ret = avahi_server_errno(s); - goto fail; - } - - /* Add TXT record */ - - if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) - strlst = add_magic_cookie(s, strlst); - - txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst); - strlst = NULL; - - if (!txt_entry) { - ret = avahi_server_errno(s); - goto fail; - } - - /* Add service type enumeration record */ - - if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) { - ret = avahi_server_errno(s); - goto fail; - } - -fail: - if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { - if (srv_entry) - avahi_entry_free(s, srv_entry); - if (txt_entry) - avahi_entry_free(s, txt_entry); - if (ptr_entry) - avahi_entry_free(s, ptr_entry); - if (enum_entry) - avahi_entry_free(s, enum_entry); - } - - avahi_string_list_free(strlst); - avahi_free(h); - - return ret; -} - -int avahi_server_add_service_strlst( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - const char *host, - uint16_t port, - AvahiStringList *strlst) { - - assert(s); - assert(type); - assert(name); - - return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst)); -} - -int avahi_server_add_service( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - const char *host, - uint16_t port, - ... ){ - - va_list va; - int ret; - - va_start(va, port); - ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va)); - va_end(va); - - return ret; -} - -static int server_update_service_txt_strlst_nocopy( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - AvahiStringList *strlst) { - - char svc_name[AVAHI_DOMAIN_NAME_MAX]; - int ret = AVAHI_OK; - AvahiEntry *e; - - assert(s); - assert(type); - assert(name); - - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, - AVAHI_PUBLISH_NO_COOKIE| - AVAHI_PUBLISH_USE_WIDE_AREA| - AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - - if (!domain) - domain = s->domain_name; - - transport_flags_from_domain(s, &flags, domain); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - - if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) { - avahi_server_set_errno(s, ret); - goto fail; - } - - /* Add TXT record */ - if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) - strlst = add_magic_cookie(s, strlst); - - e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst); - strlst = NULL; - - if (!e) - ret = avahi_server_errno(s); - -fail: - - avahi_string_list_free(strlst); - - return ret; -} - -int avahi_server_update_service_txt_strlst( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - AvahiStringList *strlst) { - - return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst)); -} - -/** Update the TXT record for a service with the NULL termonate list of strings */ -int avahi_server_update_service_txt( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - ...) { - - va_list va; - int ret; - - va_start(va, domain); - ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va)); - va_end(va); - - return ret; -} - -int avahi_server_add_service_subtype( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - const char *subtype) { - - int ret = AVAHI_OK; - char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX]; - - assert(name); - assert(type); - assert(subtype); - - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE); - - if (!domain) - domain = s->domain_name; - - transport_flags_from_domain(s, &flags, domain); - AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - - if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || - (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) { - avahi_server_set_errno(s, ret); - goto fail; - } - - if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0) - goto fail; - -fail: - - return ret; -} - -static void hexstring(char *s, size_t sl, const void *p, size_t pl) { - static const char hex[] = "0123456789abcdef"; - int b = 0; - const uint8_t *k = p; - - while (sl > 1 && pl > 0) { - *(s++) = hex[(b ? *k : *k >> 4) & 0xF]; - - if (b) { - k++; - pl--; - } - - b = !b; - - sl--; - } - - if (sl > 0) - *s = 0; -} - -static AvahiEntry *server_add_dns_server_name( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *domain, - AvahiDNSServerType type, - const char *name, - uint16_t port /** should be 53 */) { - - AvahiEntry *e; - char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n; - - AvahiRecord *r; - - assert(s); - assert(name); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - - if (!domain) - domain = s->domain_name; - - transport_flags_from_domain(s, &flags, domain); - AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - - if (!(n = avahi_normalize_name_strdup(name))) { - avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d))); - - snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d); - - if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { - avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - avahi_free(n); - return NULL; - } - - r->data.srv.priority = 0; - r->data.srv.weight = 0; - r->data.srv.port = port; - r->data.srv.name = n; - e = server_add_internal(s, g, interface, protocol, 0, r); - avahi_record_unref(r); - - return e; -} - -int avahi_server_add_dns_server_address( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *domain, - AvahiDNSServerType type, - const AvahiAddress *address, - uint16_t port /** should be 53 */) { - - AvahiRecord *r; - char n[64], h[64]; - AvahiEntry *a_entry, *s_entry; - - assert(s); - assert(address); - - AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); - AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT); - AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - - if (!domain) - domain = s->domain_name; - - transport_flags_from_domain(s, &flags, domain); - AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); - - if (address->proto == AVAHI_PROTO_INET) { - hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address)); - snprintf(n, sizeof(n), "ip-%s.%s", h, domain); - r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME); - r->data.a.address = address->data.ipv4; - } else { - hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address)); - snprintf(n, sizeof(n), "ip6-%s.%s", h, domain); - r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME); - r->data.aaaa.address = address->data.ipv6; - } - - if (!r) - return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - - a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); - avahi_record_unref(r); - - if (!a_entry) - return avahi_server_errno(s); - - if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) { - if (!(flags & AVAHI_PUBLISH_UPDATE)) - avahi_entry_free(s, a_entry); - return avahi_server_errno(s); - } - - return AVAHI_OK; -} - -void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) { - assert(g); - - if (g->state == state) - return; - - assert(state <= AVAHI_ENTRY_GROUP_COLLISION); - - if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) { - - /* If the entry group was established for a time longer then - * 5s, reset the establishment trial counter */ - - if (avahi_age(&g->established_at) > 5000000) - g->n_register_try = 0; - } else if (g->state == AVAHI_ENTRY_GROUP_REGISTERING) { - if (g->register_time_event) { - avahi_time_event_free(g->register_time_event); - g->register_time_event = NULL; - } - } - - if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) - - /* If the entry group is now established, remember the time - * this happened */ - - gettimeofday(&g->established_at, NULL); - - g->state = state; - - if (g->callback) - g->callback(g->server, g, state, g->userdata); -} - -AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) { - AvahiSEntryGroup *g; - - assert(s); - - if (!(g = avahi_new(AvahiSEntryGroup, 1))) { - avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - g->server = s; - g->callback = callback; - g->userdata = userdata; - g->dead = 0; - g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; - g->n_probing = 0; - g->n_register_try = 0; - g->register_time_event = NULL; - g->register_time.tv_sec = 0; - g->register_time.tv_usec = 0; - AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); - - AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g); - return g; -} - -static void cleanup_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { - AvahiServer *s = userdata; - - assert(s); - - avahi_cleanup_dead_entries(s); -} - -static void schedule_cleanup(AvahiServer *s) { - struct timeval tv; - - assert(s); - - if (!s->cleanup_time_event) - s->cleanup_time_event = avahi_time_event_new(s->time_event_queue, avahi_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s); -} - -void avahi_s_entry_group_free(AvahiSEntryGroup *g) { - AvahiEntry *e; - - assert(g); - assert(g->server); - - for (e = g->entries; e; e = e->by_group_next) { - if (!e->dead) { - avahi_goodbye_entry(g->server, e, 1, 1); - e->dead = 1; - } - } - - if (g->register_time_event) { - avahi_time_event_free(g->register_time_event); - g->register_time_event = NULL; - } - - g->dead = 1; - - g->server->need_group_cleanup = 1; - g->server->need_entry_cleanup = 1; - - schedule_cleanup(g->server); -} - -static void entry_group_commit_real(AvahiSEntryGroup *g) { - assert(g); - - gettimeofday(&g->register_time, NULL); - - avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); - - if (g->dead) - return; - - avahi_announce_group(g->server, g); - avahi_s_entry_group_check_probed(g, 0); -} - -static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { - AvahiSEntryGroup *g = userdata; - assert(g); - - avahi_time_event_free(g->register_time_event); - g->register_time_event = NULL; - - /* Holdoff time passed, so let's start probing */ - entry_group_commit_real(g); -} - -int avahi_s_entry_group_commit(AvahiSEntryGroup *g) { - struct timeval now; - - assert(g); - assert(!g->dead); - - if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION) - return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE); - - if (avahi_s_entry_group_is_empty(g)) - return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY); - - g->n_register_try++; - - avahi_timeval_add(&g->register_time, - 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ? - AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT : - AVAHI_RR_HOLDOFF_MSEC)); - - gettimeofday(&now, NULL); - - if (avahi_timeval_compare(&g->register_time, &now) <= 0) { - - /* Holdoff time passed, so let's start probing */ - entry_group_commit_real(g); - } else { - - /* Holdoff time has not yet passed, so let's wait */ - assert(!g->register_time_event); - g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g); - - avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); - } - - return AVAHI_OK; -} - -void avahi_s_entry_group_reset(AvahiSEntryGroup *g) { - AvahiEntry *e; - assert(g); - - for (e = g->entries; e; e = e->by_group_next) { - if (!e->dead) { - avahi_goodbye_entry(g->server, e, 1, 1); - e->dead = 1; - } - } - g->server->need_entry_cleanup = 1; - - g->n_probing = 0; - - avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED); - - schedule_cleanup(g->server); -} - -int avahi_entry_is_commited(AvahiEntry *e) { - assert(e); - assert(!e->dead); - - return !e->group || - e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || - e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; -} - -AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) { - assert(g); - assert(!g->dead); - - return g->state; -} - -void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) { - assert(g); - - g->userdata = userdata; -} - -void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) { - assert(g); - - return g->userdata; -} - -int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) { - AvahiEntry *e; - assert(g); - - /* Look for an entry that is not dead */ - for (e = g->entries; e; e = e->by_group_next) - if (!e->dead) - return 0; - - return 1; -} diff --git a/avahi-core/fdutil.c b/avahi-core/fdutil.c deleted file mode 100644 index c294754..0000000 --- a/avahi-core/fdutil.c +++ /dev/null @@ -1,72 +0,0 @@ -/*** - 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 "fdutil.h" - -int avahi_set_cloexec(int fd) { - int n; - - assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFD)) < 0) - return -1; - - if (n & FD_CLOEXEC) - return 0; - - return fcntl(fd, F_SETFD, n|FD_CLOEXEC); -} - -int avahi_set_nonblock(int fd) { - int n; - - assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFL)) < 0) - return -1; - - if (n & O_NONBLOCK) - return 0; - - return fcntl(fd, F_SETFL, n|O_NONBLOCK); -} - -int avahi_wait_for_write(int fd) { - fd_set fds; - int r; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) - return -1; - - assert(r > 0); - - return 0; -} diff --git a/avahi-core/fdutil.h b/avahi-core/fdutil.h deleted file mode 100644 index 68607f8..0000000 --- a/avahi-core/fdutil.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef foofdutilhfoo -#define foofdutilhfoo - -/*** - 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 - -AVAHI_C_DECL_BEGIN - -int avahi_set_cloexec(int fd); -int avahi_set_nonblock(int fd); -int avahi_wait_for_write(int fd); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/findstatic.pl b/avahi-core/findstatic.pl deleted file mode 100755 index 43a4916..0000000 --- a/avahi-core/findstatic.pl +++ /dev/null @@ -1,70 +0,0 @@ -#!/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"; - } -} - diff --git a/avahi-core/hashmap-test.c b/avahi-core/hashmap-test.c deleted file mode 100644 index 209d0e5..0000000 --- a/avahi-core/hashmap-test.c +++ /dev/null @@ -1,62 +0,0 @@ -/*** - 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 "hashmap.h" -#include "util.h" - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - unsigned n; - AvahiHashmap *m; - const char *t; - - m = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, avahi_free); - - avahi_hashmap_insert(m, avahi_strdup("bla"), avahi_strdup("#1")); - avahi_hashmap_insert(m, avahi_strdup("bla2"), avahi_strdup("asdf")); - avahi_hashmap_insert(m, avahi_strdup("gurke"), avahi_strdup("ffsdf")); - avahi_hashmap_insert(m, avahi_strdup("blubb"), avahi_strdup("sadfsd")); - avahi_hashmap_insert(m, avahi_strdup("bla"), avahi_strdup("#2")); - - for (n = 0; n < 1000; n ++) - avahi_hashmap_insert(m, avahi_strdup_printf("key %u", n), avahi_strdup_printf("value %u", n)); - - printf("%s\n", (const char*) avahi_hashmap_lookup(m, "bla")); - - avahi_hashmap_replace(m, avahi_strdup("bla"), avahi_strdup("#3")); - - printf("%s\n", (const char*) avahi_hashmap_lookup(m, "bla")); - - avahi_hashmap_remove(m, "bla"); - - t = (const char*) avahi_hashmap_lookup(m, "bla"); - printf("%s\n", t ? t : "(null)"); - - avahi_hashmap_free(m); - - return 0; -} diff --git a/avahi-core/hashmap.c b/avahi-core/hashmap.c deleted file mode 100644 index 9b55bd3..0000000 --- a/avahi-core/hashmap.c +++ /dev/null @@ -1,248 +0,0 @@ -/*** - 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 "hashmap.h" -#include "util.h" - -#define HASH_MAP_SIZE 123 - -typedef struct Entry Entry; -struct Entry { - AvahiHashmap *hashmap; - void *key; - void *value; - - AVAHI_LLIST_FIELDS(Entry, bucket); - AVAHI_LLIST_FIELDS(Entry, entries); -}; - -struct AvahiHashmap { - AvahiHashFunc hash_func; - AvahiEqualFunc equal_func; - AvahiFreeFunc key_free_func, value_free_func; - - Entry *entries[HASH_MAP_SIZE]; - AVAHI_LLIST_HEAD(Entry, entries_list); -}; - -static Entry* entry_get(AvahiHashmap *m, const void *key) { - unsigned idx; - Entry *e; - - idx = m->hash_func(key) % HASH_MAP_SIZE; - - for (e = m->entries[idx]; e; e = e->bucket_next) - if (m->equal_func(key, e->key)) - return e; - - return NULL; -} - -static void entry_free(AvahiHashmap *m, Entry *e, int stolen) { - unsigned idx; - assert(m); - assert(e); - - idx = m->hash_func(e->key) % HASH_MAP_SIZE; - - AVAHI_LLIST_REMOVE(Entry, bucket, m->entries[idx], e); - AVAHI_LLIST_REMOVE(Entry, entries, m->entries_list, e); - - if (m->key_free_func) - m->key_free_func(e->key); - if (m->value_free_func && !stolen) - m->value_free_func(e->value); - - avahi_free(e); -} - -AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func) { - AvahiHashmap *m; - - assert(hash_func); - assert(equal_func); - - if (!(m = avahi_new0(AvahiHashmap, 1))) - return NULL; - - m->hash_func = hash_func; - m->equal_func = equal_func; - m->key_free_func = key_free_func; - m->value_free_func = value_free_func; - - AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list); - - return m; -} - -void avahi_hashmap_free(AvahiHashmap *m) { - assert(m); - - while (m->entries_list) - entry_free(m, m->entries_list, 0); - - avahi_free(m); -} - -void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key) { - Entry *e; - - assert(m); - - if (!(e = entry_get(m, key))) - return NULL; - - return e->value; -} - -int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value) { - unsigned idx; - Entry *e; - - assert(m); - - if ((e = entry_get(m, key))) { - if (m->key_free_func) - m->key_free_func(key); - if (m->value_free_func) - m->value_free_func(value); - - return 1; - } - - if (!(e = avahi_new(Entry, 1))) - return -1; - - e->hashmap = m; - e->key = key; - e->value = value; - - AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e); - - idx = m->hash_func(key) % HASH_MAP_SIZE; - AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e); - - return 0; -} - - -int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value) { - unsigned idx; - Entry *e; - - assert(m); - - if ((e = entry_get(m, key))) { - if (m->key_free_func) - m->key_free_func(e->key); - if (m->value_free_func) - m->value_free_func(e->value); - - e->key = key; - e->value = value; - - return 1; - } - - if (!(e = avahi_new(Entry, 1))) - return -1; - - e->hashmap = m; - e->key = key; - e->value = value; - - AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e); - - idx = m->hash_func(key) % HASH_MAP_SIZE; - AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e); - - return 0; -} - -void avahi_hashmap_remove(AvahiHashmap *m, const void *key) { - Entry *e; - - assert(m); - - if (!(e = entry_get(m, key))) - return; - - entry_free(m, e, 0); -} - -void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata) { - Entry *e, *next; - assert(m); - assert(callback); - - for (e = m->entries_list; e; e = next) { - next = e->entries_next; - - callback(e->key, e->value, userdata); - } -} - -unsigned avahi_string_hash(const void *data) { - const char *p = data; - unsigned hash = 0; - - assert(p); - - for (; *p; p++) - hash = 31 * hash + *p; - - return hash; -} - -int avahi_string_equal(const void *a, const void *b) { - const char *p = a, *q = b; - - assert(p); - assert(q); - - return strcmp(p, q) == 0; -} - -unsigned avahi_int_hash(const void *data) { - const int *i = data; - - assert(i); - - return (unsigned) *i; -} - -int avahi_int_equal(const void *a, const void *b) { - const int *_a = a, *_b = b; - - assert(_a); - assert(_b); - - return *_a == *_b; -} diff --git a/avahi-core/hashmap.h b/avahi-core/hashmap.h deleted file mode 100644 index 9d7e81f..0000000 --- a/avahi-core/hashmap.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef foohashmaphfoo -#define foohashmaphfoo - -/*** - 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 - -AVAHI_C_DECL_BEGIN - -typedef struct AvahiHashmap AvahiHashmap; - -typedef unsigned (*AvahiHashFunc)(const void *data); -typedef int (*AvahiEqualFunc)(const void *a, const void *b); -typedef void (*AvahiFreeFunc)(void *p); - -AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func); - -void avahi_hashmap_free(AvahiHashmap *m); -void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key); -int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value); -int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value); -void avahi_hashmap_remove(AvahiHashmap *m, const void *key); - -typedef void (*AvahiHashmapForeachCallback)(void *key, void *value, void *userdata); - -void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata); - -unsigned avahi_string_hash(const void *data); -int avahi_string_equal(const void *a, const void *b); - -unsigned avahi_int_hash(const void *data); -int avahi_int_equal(const void *a, const void *b); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/iface-linux.c b/avahi-core/iface-linux.c deleted file mode 100644 index da497bc..0000000 --- a/avahi-core/iface-linux.c +++ /dev/null @@ -1,391 +0,0 @@ -/*** - 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 "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)))) -#endif - -#ifndef IFA_RTA -#include -#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) -#endif - -static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) { - struct nlmsghdr *n; - struct rtgenmsg *gen; - uint8_t req[1024]; - - /* Issue a wild dump NETLINK request */ - - memset(&req, 0, sizeof(req)); - n = (struct nlmsghdr*) req; - n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - n->nlmsg_type = type; - n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; - n->nlmsg_pid = 0; - - gen = NLMSG_DATA(n); - memset(gen, 0, sizeof(struct rtgenmsg)); - gen->rtgen_family = AF_UNSPEC; - - return avahi_netlink_send(nl, n, ret_seq); -} - -static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) { - AvahiInterfaceMonitor *m = userdata; - - /* This routine is called for every RTNETLINK response packet */ - - assert(m); - assert(n); - assert(m->osdep.netlink == nl); - - if (n->nlmsg_type == RTM_NEWLINK) { - - /* A new interface appeared or an existing one has been modified */ - - struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); - AvahiHwInterface *hw; - struct rtattr *a = NULL; - size_t l; - - /* A (superfluous?) sanity check */ - if (ifinfomsg->ifi_family != AF_UNSPEC) - return; - - /* Check whether there already is an AvahiHwInterface object - * for this link, so that we can update its data. Note that - * Netlink sends us an RTM_NEWLINK not only when a new - * interface appears, but when it changes, too */ - - if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index))) - - /* No object found, so let's create a new - * one. avahi_hw_interface_new() will call - * avahi_interface_new() internally twice for IPv4 and - * IPv6, so there is no need for us to do that - * ourselves */ - if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index))) - return; /* OOM */ - - /* Check whether the flags of this interface are OK for us */ - hw->flags_ok = - (ifinfomsg->ifi_flags & IFF_UP) && - (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) && - !(ifinfomsg->ifi_flags & IFF_LOOPBACK) && - (ifinfomsg->ifi_flags & IFF_MULTICAST) && - (m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT)); - - /* Handle interface attributes */ - l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)); - a = IFLA_RTA(ifinfomsg); - - while (RTA_OK(a, l)) { - switch(a->rta_type) { - case IFLA_IFNAME: - - /* Fill in interface name */ - avahi_free(hw->name); - hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a)); - break; - - case IFLA_MTU: - - /* Fill in MTU */ - assert(RTA_PAYLOAD(a) == sizeof(unsigned int)); - hw->mtu = *((unsigned int*) RTA_DATA(a)); - break; - - case IFLA_ADDRESS: - - /* Fill in hardware (MAC) address */ - hw->mac_address_size = RTA_PAYLOAD(a); - if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX) - hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX; - - memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size); - break; - - default: - ; - } - - a = RTA_NEXT(a, l); - } - - /* Check whether this interface is now "relevant" for us. If - * it is Avahi will start to announce its records on this - * interface and send out queries for subscribed records on - * it */ - avahi_hw_interface_check_relevant(hw); - - /* Update any associated RRs of this interface. (i.e. the - * _workstation._tcp record containing the MAC address) */ - avahi_hw_interface_update_rrs(hw, 0); - - } else if (n->nlmsg_type == RTM_DELLINK) { - - /* An interface has been removed */ - - struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); - AvahiHwInterface *hw; - - /* A (superfluous?) sanity check */ - if (ifinfomsg->ifi_family != AF_UNSPEC) - return; - - /* Get a reference to our AvahiHwInterface object of this interface */ - if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index))) - return; - - /* Free our object */ - avahi_hw_interface_free(hw, 0); - - } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { - - /* An address has been added, modified or removed */ - - struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); - AvahiInterface *i; - struct rtattr *a = NULL; - size_t l; - AvahiAddress raddr, rlocal, *r; - int raddr_valid = 0, rlocal_valid = 0; - - /* We are only interested in IPv4 and IPv6 */ - if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) - return; - - /* Try to get a reference to our AvahiInterface object for the - * interface this address is assigned to. If ther is no object - * for this interface, we ignore this address. */ - if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family)))) - return; - - /* Fill in address family for our new address */ - rlocal.proto = raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family); - - l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)); - a = IFA_RTA(ifaddrmsg); - - while (RTA_OK(a, l)) { - - switch(a->rta_type) { - - case IFA_ADDRESS: - - if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || - (rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) - return; - - memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); - rlocal_valid = 1; - - break; - - case IFA_LOCAL: - - /* Fill in local address data. Usually this is - * preferable over IFA_ADDRESS if both are set, - * since this refers to the local address of a PPP - * link while IFA_ADDRESS refers to the other - * end. */ - - if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || - (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) - return; - - memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); - raddr_valid = 1; - - break; - - default: - ; - } - - a = RTA_NEXT(a, l); - } - - /* If there was no adress attached to this message, let's quit. */ - if (rlocal_valid) - r = &rlocal; - else if (raddr_valid) - r = &raddr; - else - return; - - if (n->nlmsg_type == RTM_NEWADDR) { - AvahiInterfaceAddress *addr; - - /* This address is new or has been modified, so let's get an object for it */ - if (!(addr = avahi_interface_monitor_get_address(m, i, r))) - - /* Mmm, no object existing yet, so let's create a new one */ - if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen))) - return; /* OOM */ - - /* Update the scope field for the address */ - addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; - addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED); - } else { - AvahiInterfaceAddress *addr; - assert(n->nlmsg_type == RTM_DELADDR); - - /* Try to get a reference to our AvahiInterfaceAddress object for this address */ - if (!(addr = avahi_interface_monitor_get_address(m, i, r))) - return; - - /* And free it */ - avahi_interface_address_free(addr); - } - - /* Avahi only considers interfaces with at least one address - * attached relevant. Since we migh have added or removed an - * address, let's have it check again whether the interface is - * now relevant */ - avahi_interface_check_relevant(i); - - /* Update any associated RRs, like A or AAAA for our new/removed address */ - avahi_interface_update_rrs(i, 0); - - } else if (n->nlmsg_type == NLMSG_DONE) { - - /* This wild dump request ended, so let's see what we do next */ - - if (m->osdep.list == LIST_IFACE) { - - /* Mmmm, interfaces have been wild dumped already, so - * let's go on with wild dumping the addresses */ - - if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) { - avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno)); - m->osdep.list = LIST_DONE; - } else - - /* Update state information */ - m->osdep.list = LIST_ADDR; - - } else - /* We're done. Tell avahi_interface_monitor_sync() to finish. */ - m->osdep.list = LIST_DONE; - - if (m->osdep.list == LIST_DONE) { - - /* Only after this boolean variable has been set, Avahi - * will start to announce or browse on all interfaces. It - * is originaly set to 0, which means that relevancy - * checks and RR updates are disabled during the wild - * dumps. */ - m->list_complete = 1; - - /* So let's check if any interfaces are relevant now */ - avahi_interface_monitor_check_relevant(m); - - /* And update all RRs attached to any interface */ - avahi_interface_monitor_update_rrs(m, 0); - - /* Tell the user that the wild dump is complete */ - avahi_log_info("Network interface enumeration completed."); - } - - } else if (n->nlmsg_type == NLMSG_ERROR && - (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) { - struct nlmsgerr *e = NLMSG_DATA (n); - - /* Some kind of error happened. Let's just tell the user and - * ignore it otherwise */ - - if (e->error) - avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error)); - } -} - -int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { - assert(m); - - /* Initialize our own data */ - - m->osdep.netlink = NULL; - m->osdep.query_addr_seq = m->osdep.query_link_seq = 0; - - /* Create a netlink object for us. It abstracts some things and - * makes netlink easier to use. It will attach to the main loop - * for us and call netlink_callback() whenever an event - * happens. */ - if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m))) - goto fail; - - /* Set the initial state. */ - m->osdep.list = LIST_IFACE; - - /* Start the wild dump for the interfaces */ - if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0) - goto fail; - - return 0; - -fail: - - if (m->osdep.netlink) { - avahi_netlink_free(m->osdep.netlink); - m->osdep.netlink = NULL; - } - - return -1; -} - -void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { - assert(m); - - if (m->osdep.netlink) { - avahi_netlink_free(m->osdep.netlink); - m->osdep.netlink = NULL; - } -} - -void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { - assert(m); - - /* Let's handle netlink events until we are done with wild - * dumping */ - - while (!m->list_complete) - if (!avahi_netlink_work(m->osdep.netlink, 1) == 0) - break; - - /* At this point Avahi knows about all local interfaces and - * addresses in existance. */ -} diff --git a/avahi-core/iface-linux.h b/avahi-core/iface-linux.h deleted file mode 100644 index 677f86d..0000000 --- a/avahi-core/iface-linux.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef fooifacelinuxhfoo -#define fooifacelinuxhfoo - -/*** - 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 struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; - -#include "netlink.h" - -struct AvahiInterfaceMonitorOSDep { - AvahiNetlink *netlink; - - unsigned query_addr_seq, query_link_seq; - - enum { - LIST_IFACE, - LIST_ADDR, - LIST_DONE - } list; -}; - - -#endif diff --git a/avahi-core/iface-none.c b/avahi-core/iface-none.c deleted file mode 100644 index 715e497..0000000 --- a/avahi-core/iface-none.c +++ /dev/null @@ -1,30 +0,0 @@ -/*** - 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 "iface.h" - -int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { - return 0; -} - -void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { -} - -void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { -} diff --git a/avahi-core/iface-pfroute.c b/avahi-core/iface-pfroute.c deleted file mode 100644 index 9a2e953..0000000 --- a/avahi-core/iface-pfroute.c +++ /dev/null @@ -1,543 +0,0 @@ -/*** - 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 -#ifdef HAVE_SYS_SYSCTL_H -#include -#else -#include -#endif - -#include -#include -#include -#include - -#include "log.h" -#include "iface.h" -#include "iface-pfroute.h" -#include "util.h" - -static int bitcount (unsigned int n) -{ - int count=0 ; - while (n) - { - count++ ; - n &= (n - 1) ; - } - return count ; -} - -static void rtm_info(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) -{ - AvahiHwInterface *hw; - struct if_msghdr *ifm = (struct if_msghdr *)rtm; - struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1); - - if (sdl->sdl_family != AF_LINK) - return; - - if (ifm->ifm_addrs == 0 && ifm->ifm_index > 0) { - if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifm->ifm_index))) - return; - avahi_hw_interface_free(hw, 0); - return; - } - - if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifm->ifm_index))) - if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifm->ifm_index))) - return; /* OOM */ - - hw->flags_ok = - (ifm->ifm_flags & IFF_UP) && - (!m->server->config.use_iff_running || (ifm->ifm_flags & IFF_RUNNING)) && - !(ifm->ifm_flags & IFF_LOOPBACK) && - (ifm->ifm_flags & IFF_MULTICAST) && - (m->server->config.allow_point_to_point || !(ifm->ifm_flags & IFF_POINTOPOINT)); - - avahi_free(hw->name); - hw->name = avahi_strndup(sdl->sdl_data, sdl->sdl_nlen); - - hw->mtu = ifm->ifm_data.ifi_mtu; - - hw->mac_address_size = sdl->sdl_alen; - if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX) - hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX; - - memcpy(hw->mac_address, sdl->sdl_data + sdl->sdl_nlen, hw->mac_address_size); - -/* { */ -/* char mac[256]; */ -/* avahi_log_debug("======\n name: %s\n index:%d\n mtu:%d\n mac:%s\n flags_ok:%d\n======", */ -/* hw->name, hw->index, */ -/* hw->mtu, */ -/* avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size), */ -/* hw->flags_ok); */ -/* } */ - - avahi_hw_interface_check_relevant(hw); - avahi_hw_interface_update_rrs(hw, 0); -} - -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) -#ifdef HAVE_SYS_SYSCTL_H -#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) -#else -#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr))) -#endif - -static void rtm_addr(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) -{ - AvahiInterface *iface; - AvahiAddress raddr; - int raddr_valid = 0; - struct ifa_msghdr *ifam = (struct ifa_msghdr *) rtm; - char *cp = (char *)(ifam + 1); - char *cp0; - int i; - int prefixlen = 0; - struct sockaddr *sa =NULL; - -#if defined(__NetBSD__) || defined(__OpenBSD__) - if(((struct sockaddr *)cp)->sa_family == AF_UNSPEC) - ((struct sockaddr *)cp)->sa_family = AF_INET; -#endif - - for (cp0 = cp, i = 0; i < RTAX_MAX; i++) { - if (!(ifam->ifam_addrs & (1<sa_family != AF_INET && sa->sa_family != AF_INET6) - return; - - if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifam->ifam_index, avahi_af_to_proto(sa->sa_family)))) - return; - - raddr.proto = avahi_af_to_proto(sa->sa_family); - - for(cp = cp0, i = 0; i < RTAX_MAX; i++) - { - if (!(ifam->ifam_addrs & (1<sa_len == 0) - continue; -#endif - switch(sa->sa_family) { - case AF_INET: - switch (1<sin_addr.s_addr); - break; - case RTA_IFA: - memcpy(raddr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr)); - raddr_valid = 1; - default: - break; - } - break; - case AF_INET6: - switch (1<sin6_addr.s6_addr); - break; - case RTA_IFA: - memcpy(raddr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr)); -#ifdef __KAME__ - if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)raddr.data.data)) - { - ((struct in6_addr *)raddr.data.data)->s6_addr[2] = 0; - ((struct in6_addr *)raddr.data.data)->s6_addr[3] = 0; - } -#endif - raddr_valid = 1; - default: - break; - } - break; - default: - break; - } -#ifdef SA_SIZE - cp += SA_SIZE(sa); -#else - ADVANCE(cp, sa); -#endif - } - - if (!raddr_valid) - return; - - if(rtm->rtm_type == RTM_NEWADDR) - { - AvahiInterfaceAddress *addriface; - if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) - if (!(addriface = avahi_interface_address_new(m, iface, &raddr, prefixlen))) - return; /* OOM */ - if (raddr.proto == AVAHI_PROTO_INET6) - { - addriface->global_scope = !(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)raddr.data.data) || IN6_IS_ADDR_MULTICAST((struct in6_addr *)raddr.data.data)); - } - else - addriface->global_scope = 1; - } - else - { - AvahiInterfaceAddress *addriface; - assert(rtm->rtm_type == RTM_DELADDR); - if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) - return; - avahi_interface_address_free(addriface); - } - - avahi_interface_check_relevant(iface); - avahi_interface_update_rrs(iface, 0); -} - -static void parse_rtmsg(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) -{ - assert(m); - assert(rtm); - - if (rtm->rtm_version != RTM_VERSION) { - avahi_log_warn("routing message version %d not understood", - rtm->rtm_version); - return; - } - - switch (rtm->rtm_type) { - case RTM_IFINFO: - rtm_info(rtm,m); - break; - case RTM_NEWADDR: - case RTM_DELADDR: - rtm_addr(rtm,m); - break; - default: - break; - } -} - -static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event,void *userdata) { - AvahiInterfaceMonitor *m = (AvahiInterfaceMonitor *)userdata; - AvahiPfRoute *nl = m->osdep.pfroute; - ssize_t bytes; - char msg[2048]; - - assert(m); - assert(w); - assert(nl); - assert(fd == nl->fd); - - do { - if((bytes = recv(nl->fd, msg, 2048, MSG_DONTWAIT)) < 0) { - if (errno == EAGAIN || errno == EINTR) - return; - avahi_log_error(__FILE__": recv() failed: %s", strerror(errno)); - return; - } - parse_rtmsg((struct rt_msghdr *)msg, m); - } - while (bytes > 0); -} - -int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { - int fd = -1; - - assert(m); - - m->osdep.pfroute = NULL; - - if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { - avahi_log_error(__FILE__": socket(PF_ROUTE): %s", strerror(errno)); - goto fail; - } - - if (!(m->osdep.pfroute = avahi_new(AvahiPfRoute , 1))) { - avahi_log_error(__FILE__": avahi_new() failed."); - goto fail; - } - m->osdep.pfroute->fd = fd; - - if (!(m->osdep.pfroute->watch = m->server->poll_api->watch_new(m->server->poll_api, - m->osdep.pfroute->fd, - AVAHI_WATCH_IN, - socket_event, - m))) { - avahi_log_error(__FILE__": Failed to create watch."); - goto fail; - } - - return 0; - -fail: - - if (m->osdep.pfroute) { - if (m->osdep.pfroute->watch) - m->server->poll_api->watch_free(m->osdep.pfroute->watch); - - if (fd >= 0) - close(fd); - - m->osdep.pfroute = NULL; - } - - return -1; -} - -void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { - assert(m); - - if (m->osdep.pfroute) { - if (m->osdep.pfroute->watch) - m->server->poll_api->watch_free(m->osdep.pfroute->watch); - - if (m->osdep.pfroute->fd >= 0) - close(m->osdep.pfroute->fd); - - avahi_free(m->osdep.pfroute); - m->osdep.pfroute = NULL; - } -} - -#if defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */ -/* - * I got this function from GNU zsbra - */ -static int ip6_masklen (struct in6_addr netmask) { - int len = 0; - unsigned char val; - unsigned char *pnt; - - pnt = (unsigned char *) & netmask; - - while ((*pnt == 0xff) && len < 128) { - len += 8; - pnt++; - } - - if (len < 128) { - val = *pnt; - while (val) { - len++; - val <<= 1; - } - } - return len; -} - -static void if_add_interface(struct lifreq *lifreq, AvahiInterfaceMonitor *m, int fd, int count) -{ - AvahiHwInterface *hw; - AvahiAddress addr; - struct lifreq lifrcopy; - unsigned int index; - int flags; - int mtu; - int prefixlen; - AvahiInterfaceAddress *addriface; - AvahiInterface *iface; - struct sockaddr_in mask; - struct sockaddr_in6 mask6; - char caddr[AVAHI_ADDRESS_STR_MAX]; - - lifrcopy = *lifreq; - - if (ioctl(fd, SIOCGLIFFLAGS, &lifrcopy) < 0) { - avahi_log_error(__FILE__": ioctl(SIOCGLIFFLAGS) %s", strerror(errno)); - return; - } - flags = lifrcopy.lifr_flags; - - if (ioctl(fd, SIOCGLIFMTU, &lifrcopy) < 0) { - avahi_log_error(__FILE__": ioctl(SIOCGLIFMTU) %s", strerror(errno)); - return; - } - mtu = lifrcopy.lifr_metric; - - if (ioctl(fd, SIOCGLIFADDR, &lifrcopy) < 0) { - avahi_log_error(__FILE__": ioctl(SIOCGLIFADDR) %s", strerror(errno)); - return; - } - addr.proto = avahi_af_to_proto(lifreq->lifr_addr.ss_family); - if (ioctl(fd, SIOCGLIFNETMASK, &lifrcopy) < 0) { - avahi_log_error(__FILE__": ioctl(SIOCGLIFNETMASK) %s", strerror(errno)); - return; - } - switch (lifreq->lifr_addr.ss_family) { - case AF_INET: - memcpy(addr.data.data, &((struct sockaddr_in *)&lifreq->lifr_addr)->sin_addr, sizeof(struct in_addr)); - memcpy(&mask, &((struct sockaddr_in *)&lifrcopy.lifr_addr)->sin_addr, sizeof(struct in_addr)); - prefixlen = bitcount((unsigned int) mask.sin_addr.s_addr); - break; - case AF_INET6: - memcpy(addr.data.data, &((struct sockaddr_in6 *)&lifreq->lifr_addr)->sin6_addr, sizeof(struct in6_addr)); - memcpy(&mask6, &((struct sockaddr_in6 *)&lifrcopy.lifr_addr)->sin6_addr, sizeof(struct in6_addr)); - prefixlen = lifrcopy.lifr_addrlen; - break; - default: - break; - } - index = if_nametoindex(lifreq->lifr_name); - - if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) index))) { - if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) index))) - return; /* OOM */ - - hw->flags_ok = - (flags & IFF_UP) && - (!m->server->config.use_iff_running || (flags & IFF_RUNNING)) && - !(flags & IFF_LOOPBACK) && - (flags & IFF_MULTICAST) && - (m->server->config.allow_point_to_point || !(flags & IFF_POINTOPOINT)); - hw->name = avahi_strdup(lifreq->lifr_name); - hw->mtu = mtu; - /* TODO get mac address */ - } - - if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex)index, addr.proto))) - return; - - if (!(addriface = avahi_interface_monitor_get_address(m, iface, &addr))) - if (!(addriface = avahi_interface_address_new(m, iface, &addr, prefixlen))) - return; /* OOM */ - - addriface->global_scope = 1; - - avahi_hw_interface_check_relevant(hw); - avahi_hw_interface_update_rrs(hw, 0); -} -#endif - -void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { -#ifndef HAVE_STRUCT_LIFCONF - size_t needed; - int mib[6]; - char *buf, *lim, *next, count = 0; - struct rt_msghdr *rtm; - - assert(m); - - retry2: - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; /* protocol */ - mib[3] = 0; /* wildcard address family */ - mib[4] = NET_RT_IFLIST; - mib[5] = 0; /* no flags */ - if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) - { - avahi_log_error("sysctl failed: %s", strerror(errno)); - avahi_log_error("route-sysctl-estimate"); - return; - } - if ((buf = avahi_malloc(needed)) == NULL) - { - avahi_log_error("malloc failed in avahi_interface_monitor_sync"); - return; - } - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { - avahi_log_warn("sysctl failed: %s", strerror(errno)); - if (errno == ENOMEM && count++ < 10) { - avahi_log_warn("Routing table grew, retrying"); - sleep(1); - avahi_free(buf); - goto retry2; - } - } - lim = buf + needed; - for (next = buf; next < lim; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)next; - parse_rtmsg(rtm, m); - } - - m->list_complete = 1; - avahi_interface_monitor_check_relevant(m); - avahi_interface_monitor_update_rrs(m, 0); - avahi_log_info("Network interface enumeration completed."); -#elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */ - int sockfd; - int ret; - int n; - struct lifnum lifn; - struct lifconf lifc; - struct lifreq *lifreq; - - if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - avahi_log_error(__FILE__": socket(PFROUTE): %s", strerror(errno)); - return; - } - lifc.lifc_buf = NULL; - lifn.lifn_family = AF_UNSPEC; - lifn.lifn_flags = 0; - if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0) { - avahi_log_error(__FILE__": ioctl(SIOCGLIFNUM): %s", strerror(errno)); - goto end; - } - lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); - if ((lifc.lifc_buf = avahi_malloc(lifc.lifc_len)) == NULL) { - avahi_log_error("malloc failed in avahi_interface_monitor_sync"); - goto end; - } - lifc.lifc_family = NULL; - lifc.lifc_flags = 0; - if(ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0) { - avahi_log_error(__FILE__": ioctl(SIOCGLIFCONF): %s", strerror(errno)); - goto end; - } - lifreq = lifc.lifc_req; - - for (n = 0; n < lifc.lifc_len; n += sizeof(struct lifreq)) { - if_add_interface(lifreq, m, sockfd, lifn.lifn_count); - lifreq++; - } - m->list_complete = 1; - avahi_interface_monitor_check_relevant(m); - avahi_interface_monitor_update_rrs(m, 0); -end: - close(sockfd); - avahi_free(lifc.lifc_buf); - - avahi_log_info("Network interface enumeration completed."); -#endif -} diff --git a/avahi-core/iface-pfroute.h b/avahi-core/iface-pfroute.h deleted file mode 100644 index 3766cb0..0000000 --- a/avahi-core/iface-pfroute.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef fooifacepfroutehfoo -#define fooifacepfroutehfoo - -/*** - 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 - -typedef struct AvahiPfRoute AvahiPfRoute; -struct AvahiPfRoute { - int fd; - AvahiWatch *watch; - AvahiInterfaceMonitor *m; -}; - -typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; - -struct AvahiInterfaceMonitorOSDep { - AvahiPfRoute *pfroute; -}; - -#endif diff --git a/avahi-core/iface.c b/avahi-core/iface.c deleted file mode 100644 index 60a4f49..0000000 --- a/avahi-core/iface.c +++ /dev/null @@ -1,865 +0,0 @@ -/*** - 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 "iface.h" -#include "dns.h" -#include "socket.h" -#include "announce.h" -#include "util.h" -#include "log.h" -#include "multicast-lookup.h" -#include "querier.h" - -void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs) { - AvahiInterfaceMonitor *m; - - assert(a); - m = a->monitor; - - 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 || - m->server->state == AVAHI_SERVER_REGISTERING)) { - - /* Fill the entry group */ - if (!a->entry_group) - a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL); - - if (!a->entry_group) /* OOM */ - return; - - if (avahi_s_entry_group_is_empty(a->entry_group)) { - char t[AVAHI_ADDRESS_STR_MAX]; - AvahiProtocol p; - - 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, p, m->server->config.publish_no_reverse ? AVAHI_PUBLISH_NO_REVERSE : 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; - return; - } - - avahi_s_entry_group_commit(a->entry_group); - } - } else { - - /* Clear the entry group */ - - if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) { - char t[AVAHI_ADDRESS_STR_MAX]; - avahi_address_snprint(t, sizeof(t), &a->address); - - avahi_log_info("Withdrawing address record for %s on %s.", t, a->interface->hardware->name); - - if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING && - m->server->state == AVAHI_SERVER_REGISTERING) - avahi_server_decrease_host_rr_pending(m->server); - - avahi_s_entry_group_reset(a->entry_group); - } - } -} - -void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs) { - AvahiInterfaceAddress *a; - - assert(i); - - for (a = i->addresses; a; a = a->address_next) - avahi_interface_address_update_rrs(a, remove_rrs); -} - -void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs) { - AvahiInterface *i; - AvahiInterfaceMonitor *m; - - assert(hw); - m = hw->monitor; - - for (i = hw->interfaces; i; i = i->by_hardware_next) - avahi_interface_update_rrs(i, remove_rrs); - - if (m->list_complete && - !remove_rrs && - m->server->config.publish_workstation && - (m->server->state == AVAHI_SERVER_RUNNING)) { - - if (!hw->entry_group) - hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL); - - if (!hw->entry_group) - return; /* OOM */ - - if (avahi_s_entry_group_is_empty(hw->entry_group)) { - char name[AVAHI_LABEL_MAX], unescaped[AVAHI_LABEL_MAX], mac[256]; - const char *p = m->server->host_name; - - avahi_unescape_label(&p, unescaped, sizeof(unescaped)); - avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size); - snprintf(name, sizeof(name), "%s [%s]", unescaped, mac); - - if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, 0, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) { - avahi_log_warn(__FILE__": avahi_server_add_service() failed: %s", avahi_strerror(m->server->error)); - avahi_s_entry_group_free(hw->entry_group); - hw->entry_group = NULL; - } else - avahi_s_entry_group_commit(hw->entry_group); - } - - } else { - - if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) { - - avahi_log_info("Withdrawing workstation service for %s.", hw->name); - - if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING && - m->server->state == AVAHI_SERVER_REGISTERING) - avahi_server_decrease_host_rr_pending(m->server); - - avahi_s_entry_group_reset(hw->entry_group); - } - } -} - -void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs) { - AvahiHwInterface *hw; - - assert(m); - - for (hw = m->hw_interfaces; hw; hw = hw->hardware_next) - avahi_hw_interface_update_rrs(hw, remove_rrs); -} - -static int interface_mdns_mcast_join(AvahiInterface *i, int join) { - char at[AVAHI_ADDRESS_STR_MAX]; - int r; - assert(i); - - 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; - - /* Look if there's an address with global scope */ - for (a = i->addresses; a; a = a->address_next) - if (a->global_scope) - break; - - /* No address with a global scope has been found, so let's use - * any. */ - if (!a) - a = i->addresses; - - /* Hmm, there is no address available. */ - if (!a) - return -1; - - i->local_mcast_address = a->address; - } - - avahi_log_info("%s mDNS multicast group on interface %s.%s with address %s.", - join ? "Joining" : "Leaving", - i->hardware->name, - avahi_proto_to_string(i->protocol), - avahi_address_snprint(at, sizeof(at), &i->local_mcast_address)); - - if (i->protocol == AVAHI_PROTO_INET6) - r = avahi_mdns_mcast_join_ipv6(i->monitor->server->fd_ipv6, &i->local_mcast_address.data.ipv6, i->hardware->index, join); - else { - assert(i->protocol == AVAHI_PROTO_INET); - - r = avahi_mdns_mcast_join_ipv4(i->monitor->server->fd_ipv4, &i->local_mcast_address.data.ipv4, i->hardware->index, join); - } - - if (r < 0) - i->mcast_joined = 0; - else - i->mcast_joined = join; - - return 0; -} - -static int interface_mdns_mcast_rejoin(AvahiInterface *i) { - AvahiInterfaceAddress *a, *usable = NULL, *found = NULL; - assert(i); - - if (!i->mcast_joined) - return 0; - - /* Check whether old address we joined with is still available. If - * not, rejoin using an other address. */ - - for (a = i->addresses; a; a = a->address_next) { - if (a->global_scope && !usable) - usable = a; - - if (avahi_address_cmp(&a->address, &i->local_mcast_address) == 0) { - - if (a->global_scope) - /* No action necessary: the address still exists and - * has global scope. */ - return 0; - - found = a; - } - } - - if (found && !usable) - /* No action necessary: the address still exists and no better one has been found */ - return 0; - - interface_mdns_mcast_join(i, 0); - return interface_mdns_mcast_join(i, 1); -} - -void avahi_interface_address_free(AvahiInterfaceAddress *a) { - assert(a); - assert(a->interface); - - avahi_interface_address_update_rrs(a, 1); - AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a); - - if (a->entry_group) - avahi_s_entry_group_free(a->entry_group); - - interface_mdns_mcast_rejoin(a->interface); - - avahi_free(a); -} - -void avahi_interface_free(AvahiInterface *i, int send_goodbye) { - assert(i); - - /* Handle goodbyes and remove announcers */ - avahi_goodbye_interface(i->monitor->server, i, send_goodbye, 1); - avahi_response_scheduler_force(i->response_scheduler); - assert(!i->announcers); - - if (i->mcast_joined) - interface_mdns_mcast_join(i, 0); - - /* Remove queriers */ - avahi_querier_free_all(i); - avahi_hashmap_free(i->queriers_by_key); - - /* Remove local RRs */ - avahi_interface_update_rrs(i, 1); - - while (i->addresses) - avahi_interface_address_free(i->addresses); - - avahi_response_scheduler_free(i->response_scheduler); - avahi_query_scheduler_free(i->query_scheduler); - avahi_probe_scheduler_free(i->probe_scheduler); - avahi_cache_free(i->cache); - - AVAHI_LLIST_REMOVE(AvahiInterface, interface, i->monitor->interfaces, i); - AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i); - - avahi_free(i); -} - -void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye) { - assert(hw); - - avahi_hw_interface_update_rrs(hw, 1); - - while (hw->interfaces) - avahi_interface_free(hw->interfaces, send_goodbye); - - if (hw->entry_group) - avahi_s_entry_group_free(hw->entry_group); - - AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, hw->monitor->hw_interfaces, hw); - avahi_hashmap_remove(hw->monitor->hashmap, &hw->index); - - avahi_free(hw->name); - avahi_free(hw); -} - -AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) { - AvahiInterface *i; - - assert(m); - assert(hw); - assert(AVAHI_PROTO_VALID(protocol)); - - if (!(i = avahi_new(AvahiInterface, 1))) - goto fail; /* OOM */ - - i->monitor = m; - i->hardware = hw; - i->protocol = protocol; - i->announcing = 0; - i->mcast_joined = 0; - - AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); - AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, i->announcers); - - AVAHI_LLIST_HEAD_INIT(AvahiQuerier, i->queriers); - i->queriers_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); - - i->cache = avahi_cache_new(m->server, i); - i->response_scheduler = avahi_response_scheduler_new(i); - i->query_scheduler = avahi_query_scheduler_new(i); - i->probe_scheduler = avahi_probe_scheduler_new(i); - - if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler) - goto fail; /* OOM */ - - AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i); - AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i); - - return i; - -fail: - - if (i) { - if (i->cache) - avahi_cache_free(i->cache); - if (i->response_scheduler) - avahi_response_scheduler_free(i->response_scheduler); - if (i->query_scheduler) - avahi_query_scheduler_free(i->query_scheduler); - if (i->probe_scheduler) - avahi_probe_scheduler_free(i->probe_scheduler); - } - - return NULL; -} - -AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx) { - AvahiHwInterface *hw; - - assert(m); - assert(AVAHI_IF_VALID(idx)); - - if (!(hw = avahi_new(AvahiHwInterface, 1))) - return NULL; - - hw->monitor = m; - hw->name = NULL; - hw->flags_ok = 0; - hw->mtu = 1500; - hw->index = idx; - hw->mac_address_size = 0; - hw->entry_group = NULL; - hw->ratelimit_begin.tv_sec = 0; - hw->ratelimit_begin.tv_usec = 0; - hw->ratelimit_counter = 0; - - AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces); - AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw); - - avahi_hashmap_insert(m->hashmap, &hw->index, hw); - - 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 || m->server->config.publish_aaaa_on_ipv4) - avahi_interface_new(m, hw, AVAHI_PROTO_INET6); - - return hw; -} - -AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len) { - AvahiInterfaceAddress *a; - - assert(m); - assert(i); - - if (!(a = avahi_new(AvahiInterfaceAddress, 1))) - return NULL; - - a->interface = i; - a->monitor = m; - a->address = *addr; - a->prefix_len = prefix_len; - a->global_scope = 0; - a->deprecated = 0; - a->entry_group = NULL; - - AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, a); - - return a; -} - -void avahi_interface_check_relevant(AvahiInterface *i) { - int b; - AvahiInterfaceMonitor *m; - - assert(i); - m = i->monitor; - - b = avahi_interface_is_relevant(i); - - if (m->list_complete && b && !i->announcing) { - interface_mdns_mcast_join(i, 1); - - 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)); - - interface_mdns_mcast_join(i, 0); - - avahi_goodbye_interface(m->server, i, 0, 1); - avahi_querier_free_all(i); - - avahi_response_scheduler_clear(i->response_scheduler); - avahi_query_scheduler_clear(i->query_scheduler); - avahi_probe_scheduler_clear(i->probe_scheduler); - avahi_cache_flush(i->cache); - - i->announcing = 0; - - } else - interface_mdns_mcast_rejoin(i); -} - -void avahi_hw_interface_check_relevant(AvahiHwInterface *hw) { - AvahiInterface *i; - - assert(hw); - - for (i = hw->interfaces; i; i = i->by_hardware_next) - avahi_interface_check_relevant(i); -} - -void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m) { - AvahiInterface *i; - - assert(m); - - for (i = m->interfaces; i; i = i->interface_next) - avahi_interface_check_relevant(i); -} - -AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) { - AvahiInterfaceMonitor *m = NULL; - - if (!(m = avahi_new0(AvahiInterfaceMonitor, 1))) - return NULL; /* OOM */ - - m->server = s; - m->list_complete = 0; - m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL); - - AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces); - AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces); - - if (avahi_interface_monitor_init_osdep(m) < 0) - goto fail; - - return m; - -fail: - avahi_interface_monitor_free(m); - return NULL; -} - -void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { - assert(m); - - while (m->hw_interfaces) - avahi_hw_interface_free(m->hw_interfaces, 1); - - assert(!m->interfaces); - - avahi_interface_monitor_free_osdep(m); - - if (m->hashmap) - avahi_hashmap_free(m->hashmap); - - avahi_free(m); -} - - -AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) { - AvahiHwInterface *hw; - AvahiInterface *i; - - assert(m); - assert(idx >= 0); - assert(protocol != AVAHI_PROTO_UNSPEC); - - if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx))) - return NULL; - - for (i = hw->interfaces; i; i = i->by_hardware_next) - if (i->protocol == protocol) - return i; - - return NULL; -} - -AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) { - assert(m); - assert(idx >= 0); - - return avahi_hashmap_lookup(m->hashmap, &idx); -} - -AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) { - AvahiInterfaceAddress *ia; - - assert(m); - assert(i); - assert(raddr); - - for (ia = i->addresses; ia; ia = ia->address_next) - if (avahi_address_cmp(&ia->address, raddr) == 0) - return ia; - - return NULL; -} - -void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) { - assert(i); - assert(p); - - if (!i->announcing) - return; - - assert(!a || a->proto == i->protocol); - - if (i->monitor->server->config.ratelimit_interval > 0) { - struct timeval now, end; - - gettimeofday(&now, NULL); - - end = i->hardware->ratelimit_begin; - avahi_timeval_add(&end, i->monitor->server->config.ratelimit_interval); - - if (i->hardware->ratelimit_begin.tv_sec <= 0 || - avahi_timeval_compare(&end, &now) < 0) { - - i->hardware->ratelimit_begin = now; - i->hardware->ratelimit_counter = 0; - } - - if (i->hardware->ratelimit_counter > i->monitor->server->config.ratelimit_burst) - return; - - i->hardware->ratelimit_counter++; - } - - if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0) - avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port); - else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0) - avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv6 : NULL, a ? &a->data.ipv6 : NULL, port); -} - -void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { - assert(i); - assert(p); - - avahi_interface_send_packet_unicast(i, p, NULL, 0); -} - -int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) { - assert(i); - assert(key); - - 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) { - - return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id); -} - -int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { - assert(i); - assert(record); - - 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 (!i->announcing) - return 0; - - return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately); -} - -int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) { - AvahiInterface *i; - assert(m); - - for (i = m->interfaces; i; i = i->interface_next) { - if (avahi_interface_is_relevant(i)) { - char ln[256]; - snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol)); - callback(ln, userdata); - if (avahi_cache_dump(i->cache, callback, userdata) < 0) - return -1; - } - } - - return 0; -} - -static int avahi_interface_is_relevant_internal(AvahiInterface *i) { - AvahiInterfaceAddress *a; - - assert(i); - - if (!i->hardware->flags_ok) - return 0; - - for (a = i->addresses; a; a = a->address_next) - if (avahi_interface_address_is_relevant(a)) - return 1; - - return 0; -} - -int avahi_interface_is_relevant(AvahiInterface *i) { - AvahiStringList *l; - assert(i); - - for (l = i->monitor->server->config.deny_interfaces; l; l = l->next) - if (strcasecmp((char*) l->text, i->hardware->name) == 0) - return 0; - - if (i->monitor->server->config.allow_interfaces) { - - for (l = i->monitor->server->config.allow_interfaces; l; l = l->next) - if (strcasecmp((char*) l->text, i->hardware->name) == 0) - goto good; - - return 0; - } - -good: - return avahi_interface_is_relevant_internal(i); -} - -int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) { - AvahiInterfaceAddress *b; - assert(a); - - /* Publish public and non-deprecated IP addresses */ - if (a->global_scope && !a->deprecated) - return 1; - - /* Publish link-local and deprecated IP addresses only if they are - * the only ones on the link */ - for (b = a->interface->addresses; b; b = b->address_next) { - if (b == a) - continue; - - if (b->global_scope && !b->deprecated) - return 0; - } - - return 1; -} - -int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) { - assert(i); - - if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index) - return 0; - - if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol) - return 0; - - return 1; -} - -void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) { - assert(m); - assert(callback); - - if (interface != AVAHI_IF_UNSPEC) { - if (protocol != AVAHI_PROTO_UNSPEC) { - AvahiInterface *i; - - if ((i = avahi_interface_monitor_get_interface(m, interface, protocol))) - callback(m, i, userdata); - - } else { - AvahiHwInterface *hw; - AvahiInterface *i; - - if ((hw = avahi_interface_monitor_get_hw_interface(m, interface))) - for (i = hw->interfaces; i; i = i->by_hardware_next) - if (avahi_interface_match(i, interface, protocol)) - callback(m, i, userdata); - } - - } else { - AvahiInterface *i; - - for (i = m->interfaces; i; i = i->interface_next) - if (avahi_interface_match(i, interface, protocol)) - callback(m, i, userdata); - } -} - - -int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) { - AvahiInterface *i; - AvahiInterfaceAddress *ia; - assert(m); - assert(a); - - for (i = m->interfaces; i; i = i->interface_next) - for (ia = i->addresses; ia; ia = ia->address_next) - if (avahi_address_cmp(a, &ia->address) == 0) - return 1; - - return 0; -} - -int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) { - AvahiInterfaceAddress *ia; - - assert(i); - assert(a); - - if (a->proto != i->protocol) - return 0; - - for (ia = i->addresses; ia; ia = ia->address_next) { - - if (a->proto == AVAHI_PROTO_INET) { - uint32_t m; - - m = ~(((uint32_t) -1) >> ia->prefix_len); - - if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m)) - return 1; - } else { - unsigned j; - unsigned char pl; - assert(a->proto == AVAHI_PROTO_INET6); - - pl = ia->prefix_len; - - for (j = 0; j < 16; j++) { - uint8_t m; - - if (pl == 0) - return 1; - - if (pl >= 8) { - m = 0xFF; - pl -= 8; - } else { - m = ~(0xFF >> pl); - pl = 0; - } - - if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m)) - break; - } - } - } - - return 0; -} - -int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) { - AvahiInterface *i; - AvahiInterfaceAddress *j; - - assert(m); - assert(iface != AVAHI_IF_UNSPEC); - assert(a); - - if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto))) - return 0; - - for (j = i->addresses; j; j = j->address_next) - if (avahi_address_cmp(a, &j->address) == 0) - return 1; - - return 0; -} - -AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a) { - AvahiInterface *i; - assert(m); - - /* Some stupid OS don't support passing the interface index when a - * packet is received. We have to work around that limitation by - * looking for an interface that has the incoming address - * attached. This is sometimes ambiguous, but we have to live with - * it. */ - - for (i = m->interfaces; i; i = i->interface_next) { - AvahiInterfaceAddress *ai; - - if (i->protocol != a->proto) - continue; - - for (ai = i->addresses; ai; ai = ai->address_next) - if (avahi_address_cmp(a, &ai->address) == 0) - return i->hardware->index; - } - - return AVAHI_IF_UNSPEC; -} diff --git a/avahi-core/iface.h b/avahi-core/iface.h deleted file mode 100644 index c3f24af..0000000 --- a/avahi-core/iface.h +++ /dev/null @@ -1,195 +0,0 @@ -#ifndef fooifacehfoo -#define fooifacehfoo - -/*** - 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 struct AvahiInterfaceMonitor AvahiInterfaceMonitor; -typedef struct AvahiInterfaceAddress AvahiInterfaceAddress; -typedef struct AvahiInterface AvahiInterface; -typedef struct AvahiHwInterface AvahiHwInterface; - -#include -#include - -#include "internal.h" -#include "cache.h" -#include "response-sched.h" -#include "query-sched.h" -#include "probe-sched.h" -#include "dns.h" -#include "announce.h" -#include "browse.h" -#include "querier.h" - -#ifdef HAVE_NETLINK -#include "iface-linux.h" -#elif defined(HAVE_PF_ROUTE) -#include "iface-pfroute.h" -#else -typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; -struct AvahiInterfaceMonitorOSDep { - - unsigned query_addr_seq, query_link_seq; - - enum { - LIST_IFACE, - LIST_ADDR, - LIST_DONE - } list; -}; -#endif - -#define AVAHI_MAC_ADDRESS_MAX 32 - -struct AvahiInterfaceMonitor { - AvahiServer *server; - AvahiHashmap *hashmap; - - AVAHI_LLIST_HEAD(AvahiInterface, interfaces); - AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces); - - int list_complete; - AvahiInterfaceMonitorOSDep osdep; -}; - -struct AvahiHwInterface { - AvahiInterfaceMonitor *monitor; - - AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware); - - char *name; - AvahiIfIndex index; - int flags_ok; - - unsigned mtu; - - uint8_t mac_address[AVAHI_MAC_ADDRESS_MAX]; - size_t mac_address_size; - - AvahiSEntryGroup *entry_group; - - /* Packet rate limiting */ - struct timeval ratelimit_begin; - unsigned ratelimit_counter; - - AVAHI_LLIST_HEAD(AvahiInterface, interfaces); -}; - -struct AvahiInterface { - AvahiInterfaceMonitor *monitor; - AvahiHwInterface *hardware; - - AVAHI_LLIST_FIELDS(AvahiInterface, interface); - AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware); - - AvahiProtocol protocol; - int announcing; - AvahiAddress local_mcast_address; - int mcast_joined; - - AvahiCache *cache; - - AvahiQueryScheduler *query_scheduler; - AvahiResponseScheduler * response_scheduler; - AvahiProbeScheduler *probe_scheduler; - - AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses); - AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers); - - AvahiHashmap *queriers_by_key; - AVAHI_LLIST_HEAD(AvahiQuerier, queriers); -}; - -struct AvahiInterfaceAddress { - AvahiInterfaceMonitor *monitor; - AvahiInterface *interface; - - AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address); - - AvahiAddress address; - unsigned prefix_len; - - int global_scope; - int deprecated; - - AvahiSEntryGroup *entry_group; -}; - -AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server); -void avahi_interface_monitor_free(AvahiInterfaceMonitor *m); - -int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m); -void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m); -void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m); - -typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata); -void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata); -int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata); - -void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs); -int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a); -void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m); - -/* AvahiHwInterface */ - -AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx); -void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye); - -void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs); -void avahi_hw_interface_check_relevant(AvahiHwInterface *hw); - -AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, int idx); - -/* AvahiInterface */ - -AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol); -void avahi_interface_free(AvahiInterface *i, int send_goodbye); - -void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs); -void avahi_interface_check_relevant(AvahiInterface *i); -int avahi_interface_is_relevant(AvahiInterface *i); - -void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); -void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port); - -int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id); -int avahi_interface_withraw_query(AvahiInterface *i, unsigned id); -int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); -int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately); - -int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol); -int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a); -int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a); - -AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol); - -/* AvahiInterfaceAddress */ - -AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len); -void avahi_interface_address_free(AvahiInterfaceAddress *a); - -void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs); -int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a); - -AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr); - -AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a); - -#endif diff --git a/avahi-core/internal.h b/avahi-core/internal.h deleted file mode 100644 index b8f9302..0000000 --- a/avahi-core/internal.h +++ /dev/null @@ -1,227 +0,0 @@ -#ifndef foointernalhfoo -#define foointernalhfoo - -/*** - 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. -***/ - -/** A locally registered DNS resource record */ -typedef struct AvahiEntry AvahiEntry; - -#include -#include -#include - -#include "core.h" -#include "iface.h" -#include "prioq.h" -#include "timeeventq.h" -#include "announce.h" -#include "browse.h" -#include "dns.h" -#include "rrlist.h" -#include "hashmap.h" -#include "wide-area.h" -#include "multicast-lookup.h" -#include "dns-srv-rr.h" - -#define AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX 100 - -#define AVAHI_FLAGS_VALID(flags, max) (!((flags) & ~(max))) - -#define AVAHI_RR_HOLDOFF_MSEC 1000 -#define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 20000 -#define AVAHI_RR_RATE_LIMIT_COUNT 15 - -typedef struct AvahiLegacyUnicastReflectSlot AvahiLegacyUnicastReflectSlot; - -struct AvahiLegacyUnicastReflectSlot { - AvahiServer *server; - - uint16_t id, original_id; - AvahiAddress address; - uint16_t port; - int interface; - struct timeval elapse_time; - AvahiTimeEvent *time_event; -}; - -struct AvahiEntry { - AvahiServer *server; - AvahiSEntryGroup *group; - - int dead; - - AvahiPublishFlags flags; - AvahiRecord *record; - AvahiIfIndex interface; - AvahiProtocol protocol; - - AVAHI_LLIST_FIELDS(AvahiEntry, entries); - AVAHI_LLIST_FIELDS(AvahiEntry, by_key); - AVAHI_LLIST_FIELDS(AvahiEntry, by_group); - - AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers); -}; - -struct AvahiSEntryGroup { - AvahiServer *server; - int dead; - - AvahiEntryGroupState state; - void* userdata; - AvahiSEntryGroupCallback callback; - - unsigned n_probing; - - unsigned n_register_try; - struct timeval register_time; - AvahiTimeEvent *register_time_event; - - struct timeval established_at; - - AVAHI_LLIST_FIELDS(AvahiSEntryGroup, groups); - AVAHI_LLIST_HEAD(AvahiEntry, entries); -}; - -struct AvahiServer { - const AvahiPoll *poll_api; - - AvahiInterfaceMonitor *monitor; - AvahiServerConfig config; - - AVAHI_LLIST_HEAD(AvahiEntry, entries); - AvahiHashmap *entries_by_key; - - AVAHI_LLIST_HEAD(AvahiSEntryGroup, groups); - - AVAHI_LLIST_HEAD(AvahiSRecordBrowser, record_browsers); - AvahiHashmap *record_browser_hashmap; - AVAHI_LLIST_HEAD(AvahiSHostNameResolver, host_name_resolvers); - AVAHI_LLIST_HEAD(AvahiSAddressResolver, address_resolvers); - AVAHI_LLIST_HEAD(AvahiSDomainBrowser, domain_browsers); - AVAHI_LLIST_HEAD(AvahiSServiceTypeBrowser, service_type_browsers); - AVAHI_LLIST_HEAD(AvahiSServiceBrowser, service_browsers); - AVAHI_LLIST_HEAD(AvahiSServiceResolver, service_resolvers); - AVAHI_LLIST_HEAD(AvahiSDNSServerBrowser, dns_server_browsers); - - int need_entry_cleanup, need_group_cleanup, need_browser_cleanup; - - /* Used for scheduling RR cleanup */ - AvahiTimeEvent *cleanup_time_event; - - AvahiTimeEventQueue *time_event_queue; - - char *host_name, *host_name_fqdn, *domain_name; - - int fd_ipv4, fd_ipv6, - /* The following two sockets two are used for reflection only */ - fd_legacy_unicast_ipv4, fd_legacy_unicast_ipv6; - - AvahiWatch *watch_ipv4, *watch_ipv6, - *watch_legacy_unicast_ipv4, *watch_legacy_unicast_ipv6; - - AvahiServerState state; - AvahiServerCallback callback; - void* userdata; - - AvahiSEntryGroup *hinfo_entry_group; - AvahiSEntryGroup *browse_domain_entry_group; - unsigned n_host_rr_pending; - - /* Used for assembling responses */ - AvahiRecordList *record_list; - - /* Used for reflection of legacy unicast packets */ - AvahiLegacyUnicastReflectSlot **legacy_unicast_reflect_slots; - uint16_t legacy_unicast_reflect_id; - - /* The last error code */ - int error; - - /* The local service cookie */ - uint32_t local_service_cookie; - - AvahiMulticastLookupEngine *multicast_lookup_engine; - AvahiWideAreaLookupEngine *wide_area_lookup_engine; -}; - -void avahi_entry_free(AvahiServer*s, AvahiEntry *e); -void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g); - -void avahi_cleanup_dead_entries(AvahiServer *s); - -void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary); -void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response); -void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int is_probe); - -void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state); - -int avahi_entry_is_commited(AvahiEntry *e); - -void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata); - -void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata); - -void avahi_server_decrease_host_rr_pending(AvahiServer *s); - -int avahi_server_set_errno(AvahiServer *s, int error); - -int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name); -int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record); - -int avahi_server_add_ptr( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - uint32_t ttl, - const char *name, - const char *dest); - -#define AVAHI_CHECK_VALIDITY(server, expression, error) { \ - if (!(expression)) \ - return avahi_server_set_errno((server), (error)); \ -} - -#define AVAHI_CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \ - if (!(expression)) { \ - avahi_server_set_errno((server), (error)); \ - return NULL; \ - } \ -} - -#define AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(server, expression, error) {\ - if (!(expression)) { \ - ret = avahi_server_set_errno((server), (error)); \ - goto fail; \ - } \ -} - -#define AVAHI_ASSERT_TRUE(expression) { \ - int __tmp = !!(expression); \ - assert(__tmp); \ -} - -#define AVAHI_ASSERT_SUCCESS(expression) { \ - int __tmp = (expression); \ - assert(__tmp == 0); \ -} - -#endif diff --git a/avahi-core/log.c b/avahi-core/log.c deleted file mode 100644 index d110765..0000000 --- a/avahi-core/log.c +++ /dev/null @@ -1,86 +0,0 @@ -/*** - 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 "log.h" - -static AvahiLogFunction log_function = NULL; - -void avahi_set_log_function(AvahiLogFunction function) { - log_function = function; -} - -void avahi_log_ap(AvahiLogLevel level, const char*format, va_list ap) { - char txt[256]; - - vsnprintf(txt, sizeof(txt), format, ap); - - if (log_function) - log_function(level, txt); - else - fprintf(stderr, "%s\n", txt); -} - -void avahi_log(AvahiLogLevel level, const char*format, ...) { - va_list ap; - va_start(ap, format); - avahi_log_ap(level, format, ap); - va_end(ap); -} - -void avahi_log_error(const char*format, ...) { - va_list ap; - va_start(ap, format); - avahi_log_ap(AVAHI_LOG_ERROR, format, ap); - va_end(ap); -} - -void avahi_log_warn(const char*format, ...) { - va_list ap; - va_start(ap, format); - avahi_log_ap(AVAHI_LOG_WARN, format, ap); - va_end(ap); -} - -void avahi_log_notice(const char*format, ...) { - va_list ap; - va_start(ap, format); - avahi_log_ap(AVAHI_LOG_NOTICE, format, ap); - va_end(ap); -} - -void avahi_log_info(const char*format, ...) { - va_list ap; - va_start(ap, format); - avahi_log_ap(AVAHI_LOG_INFO, format, ap); - va_end(ap); -} - -void avahi_log_debug(const char*format, ...) { - va_list ap; - va_start(ap, format); - avahi_log_ap(AVAHI_LOG_DEBUG, format, ap); - va_end(ap); -} diff --git a/avahi-core/log.h b/avahi-core/log.h deleted file mode 100644 index 878f07a..0000000 --- a/avahi-core/log.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef foologhfoo -#define foologhfoo - -/*** - 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 -#include - -/** \file log.h Extensible logging subsystem */ - -AVAHI_C_DECL_BEGIN - -/** Log level for avahi_log_xxx() */ -typedef enum { - AVAHI_LOG_ERROR = 0, /**< Error messages */ - AVAHI_LOG_WARN = 1, /**< Warning messages */ - AVAHI_LOG_NOTICE = 2, /**< Notice messages */ - AVAHI_LOG_INFO = 3, /**< Info messages */ - AVAHI_LOG_DEBUG = 4, /**< Debug messages */ - AVAHI_LOG_LEVEL_MAX -} AvahiLogLevel; - -/** Prototype for a user supplied log function */ -typedef void (*AvahiLogFunction)(AvahiLogLevel level, const char *txt); - -/** Set a user supplied log function, replacing the default which - * prints to log messages unconditionally to STDERR. Pass NULL for - * resetting to the default log function */ -void avahi_set_log_function(AvahiLogFunction function); - -/** Issue a log message using a va_list object */ -void avahi_log_ap(AvahiLogLevel level, const char *format, va_list ap); - -/** Issue a log message by passing a log level and a format string */ -void avahi_log(AvahiLogLevel level, const char*format, ...) AVAHI_GCC_PRINTF_ATTR23; - -/** Shortcut for avahi_log(AVAHI_LOG_ERROR, ...) */ -void avahi_log_error(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; - -/** Shortcut for avahi_log(AVAHI_LOG_WARN, ...) */ -void avahi_log_warn(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; - -/** Shortcut for avahi_log(AVAHI_LOG_NOTICE, ...) */ -void avahi_log_notice(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; - -/** Shortcut for avahi_log(AVAHI_LOG_INFO, ...) */ -void avahi_log_info(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; - -/** Shortcut for avahi_log(AVAHI_LOG_DEBUG, ...) */ -void avahi_log_debug(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/lookup.h b/avahi-core/lookup.h deleted file mode 100644 index 06dec6e..0000000 --- a/avahi-core/lookup.h +++ /dev/null @@ -1,235 +0,0 @@ -#ifndef foolookuphfoo -#define foolookuphfoo - -/*** - 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. -***/ - -/** \file avahi-core/lookup.h Functions for browsing/resolving services and other RRs */ - -/** \example core-browse-services.c Example how to browse for DNS-SD - * services using an embedded mDNS stack. */ - -/** A browsing object for arbitrary RRs */ -typedef struct AvahiSRecordBrowser AvahiSRecordBrowser; - -/** A host name to IP adddress resolver object */ -typedef struct AvahiSHostNameResolver AvahiSHostNameResolver; - -/** An IP address to host name resolver object ("reverse lookup") */ -typedef struct AvahiSAddressResolver AvahiSAddressResolver; - -/** A local domain browsing object. May be used to enumerate domains used on the local LAN */ -typedef struct AvahiSDomainBrowser AvahiSDomainBrowser; - -/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */ -typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser; - -/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */ -typedef struct AvahiSServiceBrowser AvahiSServiceBrowser; - -/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */ -typedef struct AvahiSServiceResolver AvahiSServiceResolver; - -#include -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Callback prototype for AvahiSRecordBrowser events */ -typedef void (*AvahiSRecordBrowserCallback)( - AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */ - AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */ - AvahiProtocol protocol, /**< Protocol number the record was found. */ - AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */ - AvahiRecord *record, /**< The record that was found */ - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ ); - -/** Create a new browsing object for arbitrary RRs */ -AvahiSRecordBrowser *avahi_s_record_browser_new( - AvahiServer *server, /**< The server object to which attach this query */ - AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */ - AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */ - AvahiKey *key, /**< The search key */ - AvahiLookupFlags flags, /**< Lookup flags. Must have set either AVAHI_LOOKUP_FORCE_WIDE_AREA or AVAHI_LOOKUP_FORCE_MULTICAST, since domain based detection is not available here. */ - AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */ - void* userdata /**< Arbitrary use suppliable data which is passed to the callback */); - -/** Free an AvahiSRecordBrowser object */ -void avahi_s_record_browser_free(AvahiSRecordBrowser *b); - -/** Callback prototype for AvahiSHostNameResolver events */ -typedef void (*AvahiSHostNameResolverCallback)( - AvahiSHostNameResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, /**< Resolving event */ - const char *host_name, /**< Host name which should be resolved. May differ in case from the query */ - const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */ - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */ -AvahiSHostNameResolver *avahi_s_host_name_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *host_name, /**< The host name to look for */ - AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */ - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSHostNameResolverCallback calback, - void* userdata); - -/** Free a AvahiSHostNameResolver object */ -void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r); - -/** Callback prototype for AvahiSAddressResolver events */ -typedef void (*AvahiSAddressResolverCallback)( - AvahiSAddressResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const AvahiAddress *a, - const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */ - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */ -AvahiSAddressResolver *avahi_s_address_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const AvahiAddress *address, - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSAddressResolverCallback calback, - void* userdata); - -/** Free an AvahiSAddressResolver object */ -void avahi_s_address_resolver_free(AvahiSAddressResolver *r); - -/** Callback prototype for AvahiSDomainBrowser events */ -typedef void (*AvahiSDomainBrowserCallback)( - AvahiSDomainBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *domain, - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create a new AvahiSDomainBrowser object */ -AvahiSDomainBrowser *avahi_s_domain_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDomainBrowserType type, - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSDomainBrowserCallback callback, - void* userdata); - -/** Free an AvahiSDomainBrowser object */ -void avahi_s_domain_browser_free(AvahiSDomainBrowser *b); - -/** Callback prototype for AvahiSServiceTypeBrowser events */ -typedef void (*AvahiSServiceTypeBrowserCallback)( - AvahiSServiceTypeBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *type, - const char *domain, - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create a new AvahiSServiceTypeBrowser object. */ -AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSServiceTypeBrowserCallback callback, - void* userdata); - -/** Free an AvahiSServiceTypeBrowser object */ -void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b); - -/** Callback prototype for AvahiSServiceBrowser events */ -typedef void (*AvahiSServiceBrowserCallback)( - AvahiSServiceBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name /**< Service name, e.g. "Lennart's Files" */, - const char *type /**< DNS-SD type, e.g. "_http._tcp" */, - const char *domain /**< Domain of this service, e.g. "local" */, - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create a new AvahiSServiceBrowser object. */ -AvahiSServiceBrowser *avahi_s_service_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */, - const char *domain, - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSServiceBrowserCallback callback, - void* userdata); - -/** Free an AvahiSServiceBrowser object */ -void avahi_s_service_browser_free(AvahiSServiceBrowser *b); - -/** Callback prototype for AvahiSServiceResolver events */ -typedef void (*AvahiSServiceResolverCallback)( - AvahiSServiceResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */ - const char *name, /**< Service name */ - const char *type, /**< Service Type */ - const char *domain, - const char *host_name, /**< Host name of the service */ - const AvahiAddress *a, /**< The resolved host name */ - uint16_t port, /**< Service name */ - AvahiStringList *txt, /**< TXT record data */ - AvahiLookupResultFlags flags, /**< Lookup flags */ - void* userdata); - -/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */ -AvahiSServiceResolver *avahi_s_service_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *name, - const char *type, - const char *domain, - AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */ - AvahiLookupFlags flags, /**< Lookup flags. */ - AvahiSServiceResolverCallback calback, - void* userdata); - -/** Free an AvahiSServiceResolver object */ -void avahi_s_service_resolver_free(AvahiSServiceResolver *r); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/multicast-lookup.c b/avahi-core/multicast-lookup.c deleted file mode 100644 index 75988bf..0000000 --- a/avahi-core/multicast-lookup.c +++ /dev/null @@ -1,350 +0,0 @@ -/*** - 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 "internal.h" -#include "browse.h" -#include "socket.h" -#include "log.h" -#include "hashmap.h" -#include "multicast-lookup.h" -#include "rr-util.h" - -struct AvahiMulticastLookup { - AvahiMulticastLookupEngine *engine; - int dead; - - AvahiKey *key, *cname_key; - - AvahiMulticastLookupCallback callback; - void *userdata; - - AvahiIfIndex interface; - AvahiProtocol protocol; - - int queriers_added; - - AvahiTimeEvent *all_for_now_event; - - AVAHI_LLIST_FIELDS(AvahiMulticastLookup, lookups); - AVAHI_LLIST_FIELDS(AvahiMulticastLookup, by_key); -}; - -struct AvahiMulticastLookupEngine { - AvahiServer *server; - - /* Lookups */ - AVAHI_LLIST_HEAD(AvahiMulticastLookup, lookups); - AvahiHashmap *lookups_by_key; - - int cleanup_dead; -}; - -static void all_for_now_callback(AvahiTimeEvent *e, void* userdata) { - AvahiMulticastLookup *l = userdata; - - assert(e); - assert(l); - - avahi_time_event_free(l->all_for_now_event); - l->all_for_now_event = NULL; - - l->callback(l->engine, l->interface, l->protocol, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_RESULT_MULTICAST, NULL, l->userdata); -} - -AvahiMulticastLookup *avahi_multicast_lookup_new( - AvahiMulticastLookupEngine *e, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiKey *key, - AvahiMulticastLookupCallback callback, - void *userdata) { - - AvahiMulticastLookup *l, *t; - struct timeval tv; - - assert(e); - assert(AVAHI_IF_VALID(interface)); - assert(AVAHI_PROTO_VALID(protocol)); - assert(key); - assert(callback); - - l = avahi_new(AvahiMulticastLookup, 1); - l->engine = e; - l->dead = 0; - l->key = avahi_key_ref(key); - l->cname_key = avahi_key_new_cname(l->key); - l->callback = callback; - l->userdata = userdata; - l->interface = interface; - l->protocol = protocol; - l->all_for_now_event = NULL; - l->queriers_added = 0; - - t = avahi_hashmap_lookup(e->lookups_by_key, l->key); - AVAHI_LLIST_PREPEND(AvahiMulticastLookup, by_key, t, l); - avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); - - AVAHI_LLIST_PREPEND(AvahiMulticastLookup, lookups, e->lookups, l); - - avahi_querier_add_for_all(e->server, interface, protocol, l->key, &tv); - l->queriers_added = 1; - - /* Add a second */ - avahi_timeval_add(&tv, 1000000); - - /* Issue the ALL_FOR_NOW event one second after the querier was initially created */ - l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, all_for_now_callback, l); - - return l; -} - -static void lookup_stop(AvahiMulticastLookup *l) { - assert(l); - - l->callback = NULL; - - if (l->queriers_added) { - avahi_querier_remove_for_all(l->engine->server, l->interface, l->protocol, l->key); - l->queriers_added = 0; - } - - if (l->all_for_now_event) { - avahi_time_event_free(l->all_for_now_event); - l->all_for_now_event = NULL; - } -} - -static void lookup_destroy(AvahiMulticastLookup *l) { - AvahiMulticastLookup *t; - assert(l); - - lookup_stop(l); - - t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); - AVAHI_LLIST_REMOVE(AvahiMulticastLookup, by_key, t, l); - if (t) - avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); - else - avahi_hashmap_remove(l->engine->lookups_by_key, l->key); - - AVAHI_LLIST_REMOVE(AvahiMulticastLookup, lookups, l->engine->lookups, l); - - if (l->key) - avahi_key_unref(l->key); - - if (l->cname_key) - avahi_key_unref(l->cname_key); - - avahi_free(l); -} - -void avahi_multicast_lookup_free(AvahiMulticastLookup *l) { - assert(l); - - if (l->dead) - return; - - l->dead = 1; - l->engine->cleanup_dead = 1; - lookup_stop(l); -} - -void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e) { - AvahiMulticastLookup *l, *n; - assert(e); - - while (e->cleanup_dead) { - e->cleanup_dead = 0; - - for (l = e->lookups; l; l = n) { - n = l->lookups_next; - - if (l->dead) - lookup_destroy(l); - } - } -} - -struct cbdata { - AvahiMulticastLookupEngine *engine; - AvahiMulticastLookupCallback callback; - void *userdata; - AvahiKey *key, *cname_key; - AvahiInterface *interface; - unsigned n_found; -}; - -static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { - struct cbdata *cbdata = userdata; - - assert(c); - assert(pattern); - assert(e); - assert(cbdata); - - cbdata->callback( - cbdata->engine, - cbdata->interface->hardware->index, - cbdata->interface->protocol, - AVAHI_BROWSER_NEW, - AVAHI_LOOKUP_RESULT_CACHED|AVAHI_LOOKUP_RESULT_MULTICAST, - e->record, - cbdata->userdata); - - cbdata->n_found ++; - - return NULL; -} - -static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - struct cbdata *cbdata = userdata; - - assert(m); - assert(i); - assert(cbdata); - - cbdata->interface = i; - - avahi_cache_walk(i->cache, cbdata->key, scan_cache_callback, cbdata); - - if (cbdata->cname_key) - avahi_cache_walk(i->cache, cbdata->cname_key, scan_cache_callback, cbdata); - - cbdata->interface = NULL; -} - -unsigned avahi_multicast_lookup_engine_scan_cache( - AvahiMulticastLookupEngine *e, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiKey *key, - AvahiMulticastLookupCallback callback, - void *userdata) { - - struct cbdata cbdata; - - assert(e); - assert(key); - assert(callback); - - assert(AVAHI_IF_VALID(interface)); - assert(AVAHI_PROTO_VALID(protocol)); - - cbdata.engine = e; - cbdata.key = key; - cbdata.cname_key = avahi_key_new_cname(key); - cbdata.callback = callback; - cbdata.userdata = userdata; - cbdata.interface = NULL; - cbdata.n_found = 0; - - avahi_interface_monitor_walk(e->server->monitor, interface, protocol, scan_interface_callback, &cbdata); - - if (cbdata.cname_key) - avahi_key_unref(cbdata.cname_key); - - return cbdata.n_found; -} - -void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i) { - AvahiMulticastLookup *l; - - assert(e); - assert(i); - - for (l = e->lookups; l; l = l->lookups_next) { - - if (l->dead || !l->callback) - continue; - - if (l->queriers_added && avahi_interface_match(i, l->interface, l->protocol)) - avahi_querier_add(i, l->key, NULL); - } -} - -void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) { - AvahiMulticastLookup *l; - - assert(e); - assert(record); - assert(i); - - for (l = avahi_hashmap_lookup(e->lookups_by_key, record->key); l; l = l->by_key_next) { - if (l->dead || !l->callback) - continue; - - if (avahi_interface_match(i, l->interface, l->protocol)) - l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata); - } - - - if (record->key->clazz == AVAHI_DNS_CLASS_IN && record->key->type == AVAHI_DNS_TYPE_CNAME) { - /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ - - for (l = e->lookups; l; l = l->lookups_next) { - AvahiKey *key; - - if (l->dead || !l->callback) - continue; - - if ((key = avahi_key_new_cname(l->key))) { - if (avahi_key_equal(record->key, key)) - l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata); - - avahi_key_unref(key); - } - } - } -} - -AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s) { - AvahiMulticastLookupEngine *e; - - assert(s); - - e = avahi_new(AvahiMulticastLookupEngine, 1); - e->server = s; - e->cleanup_dead = 0; - - /* Initialize lookup list */ - e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); - AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); - - return e; -} - -void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e) { - assert(e); - - while (e->lookups) - lookup_destroy(e->lookups); - - avahi_hashmap_free(e->lookups_by_key); - avahi_free(e); -} - diff --git a/avahi-core/multicast-lookup.h b/avahi-core/multicast-lookup.h deleted file mode 100644 index 2707666..0000000 --- a/avahi-core/multicast-lookup.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef foomulticastlookuphfoo -#define foomulticastlookuphfoo - -/*** - 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 "lookup.h" -#include "browse.h" - -typedef struct AvahiMulticastLookupEngine AvahiMulticastLookupEngine; -typedef struct AvahiMulticastLookup AvahiMulticastLookup; - -typedef void (*AvahiMulticastLookupCallback)( - AvahiMulticastLookupEngine *e, - AvahiIfIndex idx, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiLookupResultFlags flags, - AvahiRecord *r, - void *userdata); - -AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s); -void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e); - -unsigned avahi_multicast_lookup_engine_scan_cache(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); -void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i); -void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e); -void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event); - -AvahiMulticastLookup *avahi_multicast_lookup_new(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); -void avahi_multicast_lookup_free(AvahiMulticastLookup *q); - - -#endif - diff --git a/avahi-core/netlink.c b/avahi-core/netlink.c deleted file mode 100644 index acea696..0000000 --- a/avahi-core/netlink.c +++ /dev/null @@ -1,208 +0,0 @@ -/*** - 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 "netlink.h" -#include "log.h" - -struct AvahiNetlink { - int fd; - unsigned seq; - AvahiNetlinkCallback callback; - void* userdata; - uint8_t* buffer; - size_t buffer_length; - - const AvahiPoll *poll_api; - AvahiWatch *watch; -}; - -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); - - iov.iov_base = nl->buffer; - iov.iov_len = nl->buffer_length; - - smsg.msg_name = 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__": recvmsg() failed: %s", strerror(errno)); - return -1; - } - - cmsg = CMSG_FIRSTHDR(&smsg); - - if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { - avahi_log_warn("No sender credentials received, ignoring data."); - return -1; - } - - cred = (struct ucred*) CMSG_DATA(cmsg); - - if (cred->uid != 0) - return -1; - - p = (struct nlmsghdr *) nl->buffer; - - assert(nl->callback); - - for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) { - if (!NLMSG_OK(p, (size_t) bytes)) { - avahi_log_warn(__FILE__": packet truncated"); - return -1; - } - - nl->callback(nl, p, nl->userdata); - } - - return 0; -} - -static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, void *userdata) { - AvahiNetlink *nl = userdata; - - assert(w); - assert(nl); - assert(fd == nl->fd); - - avahi_netlink_work(nl, 0); -} - -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; - - assert(poll_api); - assert(cb); - - if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { - avahi_log_error(__FILE__": socket(PF_NETLINK): %s", strerror(errno)); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_groups = groups; - addr.nl_pid = 0; // use 0 instead of getpid() to allow multiple instances of avahi in one process - - if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - avahi_log_error(__FILE__": bind(): %s", strerror(errno)); - goto fail; - } - - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { - avahi_log_error(__FILE__": SO_PASSCRED: %s", strerror(errno)); - goto fail; - } - - if (!(nl = avahi_new(AvahiNetlink, 1))) { - avahi_log_error(__FILE__": avahi_new() failed."); - goto fail; - } - - nl->poll_api = poll_api; - nl->fd = fd; - nl->seq = 0; - nl->callback = cb; - nl->userdata = userdata; - - if (!(nl->buffer = avahi_new(uint8_t, nl->buffer_length = 64*1024))) { - avahi_log_error(__FILE__": avahi_new() failed."); - goto fail; - } - - if (!(nl->watch = poll_api->watch_new(poll_api, fd, AVAHI_WATCH_IN, socket_event, nl))) { - avahi_log_error(__FILE__": Failed to create watch."); - goto fail; - } - - return nl; - -fail: - - if (fd >= 0) - close(fd); - - if (nl) { - avahi_free(nl->buffer); - avahi_free(nl); - } - - return NULL; -} - -void avahi_netlink_free(AvahiNetlink *nl) { - assert(nl); - - if (nl->watch) - nl->poll_api->watch_free(nl->watch); - - if (nl->fd >= 0) - close(nl->fd); - - avahi_free(nl->buffer); - avahi_free(nl); -} - -int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, unsigned *ret_seq) { - assert(nl); - assert(m); - - m->nlmsg_seq = nl->seq++; - m->nlmsg_flags |= NLM_F_ACK; - - if (send(nl->fd, m, m->nlmsg_len, 0) < 0) { - avahi_log_error(__FILE__": send(): %s", strerror(errno)); - return -1; - } - - if (ret_seq) - *ret_seq = m->nlmsg_seq; - - return 0; -} diff --git a/avahi-core/netlink.h b/avahi-core/netlink.h deleted file mode 100644 index 9c6eb28..0000000 --- a/avahi-core/netlink.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef foonetlinkhfoo -#define foonetlinkhfoo - -/*** - 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 -#include - -#include -#include - -#include - -typedef struct AvahiNetlink AvahiNetlink; - -typedef void (*AvahiNetlinkCallback)(AvahiNetlink *n, struct nlmsghdr *m, void* userdata); - -AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, AvahiNetlinkCallback callback, void* userdata); -void avahi_netlink_free(AvahiNetlink *n); -int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, unsigned *ret_seq); -int avahi_netlink_work(AvahiNetlink *n, int block); - -#endif diff --git a/avahi-core/prioq-test.c b/avahi-core/prioq-test.c deleted file mode 100644 index 6d0fd62..0000000 --- a/avahi-core/prioq-test.c +++ /dev/null @@ -1,120 +0,0 @@ -/*** - 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 "prioq.h" - -#define POINTER_TO_INT(p) ((int) (long) (p)) -#define INT_TO_POINTER(i) ((void*) (long) (i)) - -static int compare_int(const void* a, const void* b) { - int i = POINTER_TO_INT(a), j = POINTER_TO_INT(b); - - return i < j ? -1 : (i > j ? 1 : 0); -} - -static int compare_ptr(const void* a, const void* b) { - return a < b ? -1 : (a > b ? 1 : 0); -} - -static void rec(AvahiPrioQueueNode *n) { - if (!n) - return; - - if (n->left) - assert(n->left->parent == n); - - if (n->right) - assert(n->right->parent == n); - - if (n->parent) { - assert(n->parent->left == n || n->parent->right == n); - - if (n->parent->left == n) - assert(n->next == n->parent->right); - } - - if (!n->next) { - assert(n->queue->last == n); - - if (n->parent && n->parent->left == n) - assert(n->parent->right == NULL); - } - - - if (n->parent) { - int a = POINTER_TO_INT(n->parent->data), b = POINTER_TO_INT(n->data); - if (a > b) { - printf("%i <= %i: NO\n", a, b); - abort(); - } - } - - rec(n->left); - rec(n->right); -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - AvahiPrioQueue *q, *q2; - int i; - - q = avahi_prio_queue_new(compare_int); - q2 = avahi_prio_queue_new(compare_ptr); - - srand(time(NULL)); - - for (i = 0; i < 10000; i++) - avahi_prio_queue_put(q2, avahi_prio_queue_put(q, INT_TO_POINTER(random() & 0xFFFF))); - - while (q2->root) { - rec(q->root); - rec(q2->root); - - assert(q->n_nodes == q2->n_nodes); - - printf("%i\n", POINTER_TO_INT(((AvahiPrioQueueNode*)q2->root->data)->data)); - - avahi_prio_queue_remove(q, q2->root->data); - avahi_prio_queue_remove(q2, q2->root); - } - - -/* prev = 0; */ -/* while (q->root) { */ -/* int v = GPOINTER_TO_INT(q->root->data); */ -/* rec(q->root); */ -/* printf("%i\n", v); */ -/* avahi_prio_queue_remove(q, q->root); */ -/* assert(v >= prev); */ -/* prev = v; */ -/* } */ - - avahi_prio_queue_free(q); - return 0; -} diff --git a/avahi-core/prioq.c b/avahi-core/prioq.c deleted file mode 100644 index 28b5018..0000000 --- a/avahi-core/prioq.c +++ /dev/null @@ -1,388 +0,0 @@ -/*** - 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 "prioq.h" - -AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare) { - AvahiPrioQueue *q; - assert(compare); - - if (!(q = avahi_new(AvahiPrioQueue, 1))) - return NULL; /* OOM */ - - q->root = q->last = NULL; - q->n_nodes = 0; - q->compare = compare; - - return q; -} - -void avahi_prio_queue_free(AvahiPrioQueue *q) { - assert(q); - - while (q->last) - avahi_prio_queue_remove(q, q->last); - - assert(!q->n_nodes); - avahi_free(q); -} - -static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, unsigned x, unsigned y) { - unsigned r; - AvahiPrioQueueNode *n; - assert(q); - - n = q->root; - assert(n); - - for (r = 0; r < y; r++) { - assert(n); - - if ((x >> (y-r-1)) & 1) - n = n->right; - else - n = n->left; - } - - assert(n->x == x); - assert(n->y == y); - - return n; -} - -static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) { - AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn; - unsigned t; - assert(q); - assert(a); - assert(b); - assert(a != b); - - /* Swap positions */ - t = a->x; a->x = b->x; b->x = t; - t = a->y; a->y = b->y; b->y = t; - - if (a->parent == b) { - /* B is parent of A */ - - p = b->parent; - b->parent = a; - - if ((a->parent = p)) { - if (a->parent->left == b) - a->parent->left = a; - else - a->parent->right = a; - } else - q->root = a; - - if (b->left == a) { - if ((b->left = a->left)) - b->left->parent = b; - a->left = b; - - r = a->right; - if ((a->right = b->right)) - a->right->parent = a; - if ((b->right = r)) - b->right->parent = b; - - } else { - if ((b->right = a->right)) - b->right->parent = b; - a->right = b; - - l = a->left; - if ((a->left = b->left)) - a->left->parent = a; - if ((b->left = l)) - b->left->parent = b; - } - } else if (b->parent == a) { - /* A ist parent of B */ - - p = a->parent; - a->parent = b; - - if ((b->parent = p)) { - if (b->parent->left == a) - b->parent->left = b; - else - b->parent->right = b; - } else - q->root = b; - - if (a->left == b) { - if ((a->left = b->left)) - a->left->parent = a; - b->left = a; - - r = a->right; - if ((a->right = b->right)) - a->right->parent = a; - if ((b->right = r)) - b->right->parent = b; - } else { - if ((a->right = b->right)) - a->right->parent = a; - b->right = a; - - l = a->left; - if ((a->left = b->left)) - a->left->parent = a; - if ((b->left = l)) - b->left->parent = b; - } - } else { - AvahiPrioQueueNode *apl = NULL, *bpl = NULL; - - /* Swap parents */ - ap = a->parent; - bp = b->parent; - - if (ap) - apl = ap->left; - if (bp) - bpl = bp->left; - - if ((a->parent = bp)) { - if (bpl == b) - bp->left = a; - else - bp->right = a; - } else - q->root = a; - - if ((b->parent = ap)) { - if (apl == a) - ap->left = b; - else - ap->right = b; - } else - q->root = b; - - /* Swap children */ - l = a->left; - r = a->right; - - if ((a->left = b->left)) - a->left->parent = a; - - if ((b->left = l)) - b->left->parent = b; - - if ((a->right = b->right)) - a->right->parent = a; - - if ((b->right = r)) - b->right->parent = b; - } - - /* Swap siblings */ - ap = a->prev; an = a->next; - bp = b->prev; bn = b->next; - - if (a->next == b) { - /* A is predecessor of B */ - a->prev = b; - b->next = a; - - if ((a->next = bn)) - a->next->prev = a; - else - q->last = a; - - if ((b->prev = ap)) - b->prev->next = b; - - } else if (b->next == a) { - /* B is predecessor of A */ - a->next = b; - b->prev = a; - - if ((a->prev = bp)) - a->prev->next = a; - - if ((b->next = an)) - b->next->prev = b; - else - q->last = b; - - } else { - /* A is no neighbour of B */ - - if ((a->prev = bp)) - a->prev->next = a; - - if ((a->next = bn)) - a->next->prev = a; - else - q->last = a; - - if ((b->prev = ap)) - b->prev->next = b; - - if ((b->next = an)) - b->next->prev = b; - else - q->last = b; - } -} - -/* Move a node to the correct position */ -void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { - assert(q); - assert(n); - assert(n->queue == q); - - /* Move up until the position is OK */ - while (n->parent && q->compare(n->parent->data, n->data) > 0) - exchange_nodes(q, n, n->parent); - - /* Move down until the position is OK */ - for (;;) { - AvahiPrioQueueNode *min; - - if (!(min = n->left)) { - /* No children */ - assert(!n->right); - break; - } - - if (n->right && q->compare(n->right->data, min->data) < 0) - min = n->right; - - /* min now contains the smaller one of our two children */ - - if (q->compare(n->data, min->data) <= 0) - /* Order OK */ - break; - - exchange_nodes(q, n, min); - } -} - -AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data) { - AvahiPrioQueueNode *n; - assert(q); - - if (!(n = avahi_new(AvahiPrioQueueNode, 1))) - return NULL; /* OOM */ - - n->queue = q; - n->data = data; - - if (q->last) { - assert(q->root); - assert(q->n_nodes); - - n->y = q->last->y; - n->x = q->last->x+1; - - if (n->x >= ((unsigned) 1 << n->y)) { - n->x = 0; - n->y++; - } - - q->last->next = n; - n->prev = q->last; - - assert(n->y > 0); - n->parent = get_node_at_xy(q, n->x/2, n->y-1); - - if (n->x & 1) - n->parent->right = n; - else - n->parent->left = n; - } else { - assert(!q->root); - assert(!q->n_nodes); - - n->y = n->x = 0; - q->root = n; - n->prev = n->parent = NULL; - } - - n->next = n->left = n->right = NULL; - q->last = n; - q->n_nodes++; - - avahi_prio_queue_shuffle(q, n); - - return n; -} - -void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { - assert(q); - assert(n); - assert(q == n->queue); - - if (n != q->last) { - AvahiPrioQueueNode *replacement = q->last; - exchange_nodes(q, replacement, n); - avahi_prio_queue_remove(q, n); - avahi_prio_queue_shuffle(q, replacement); - return; - } - - assert(n == q->last); - assert(!n->next); - assert(!n->left); - assert(!n->right); - - q->last = n->prev; - - if (n->prev) { - n->prev->next = NULL; - assert(n->parent); - } else - assert(!n->parent); - - if (n->parent) { - assert(n->prev); - if (n->parent->left == n) { - assert(n->parent->right == NULL); - n->parent->left = NULL; - } else { - assert(n->parent->right == n); - assert(n->parent->left != NULL); - n->parent->right = NULL; - } - } else { - assert(q->root == n); - assert(!n->prev); - assert(q->n_nodes == 1); - q->root = NULL; - } - - avahi_free(n); - - assert(q->n_nodes > 0); - q->n_nodes--; -} - diff --git a/avahi-core/prioq.h b/avahi-core/prioq.h deleted file mode 100644 index b3d31eb..0000000 --- a/avahi-core/prioq.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef fooprioqhfoo -#define fooprioqhfoo - -/*** - 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 struct AvahiPrioQueue AvahiPrioQueue; -typedef struct AvahiPrioQueueNode AvahiPrioQueueNode; - -typedef int (*AvahiPQCompareFunc)(const void* a, const void* b); - -struct AvahiPrioQueue { - AvahiPrioQueueNode *root, *last; - unsigned n_nodes; - AvahiPQCompareFunc compare; -}; - -struct AvahiPrioQueueNode { - AvahiPrioQueue *queue; - void* data; - unsigned x, y; - AvahiPrioQueueNode *left, *right, *parent, *next, *prev; -}; - -AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare); -void avahi_prio_queue_free(AvahiPrioQueue *q); - -AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data); -void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n); - -void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n); - -#endif diff --git a/avahi-core/probe-sched.c b/avahi-core/probe-sched.c deleted file mode 100644 index 1e63411..0000000 --- a/avahi-core/probe-sched.c +++ /dev/null @@ -1,397 +0,0 @@ -/*** - 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 "probe-sched.h" -#include "log.h" -#include "rr-util.h" - -#define AVAHI_PROBE_HISTORY_MSEC 150 -#define AVAHI_PROBE_DEFER_MSEC 50 - -typedef struct AvahiProbeJob AvahiProbeJob; - -struct AvahiProbeJob { - AvahiProbeScheduler *scheduler; - AvahiTimeEvent *time_event; - - int chosen; /* Use for packet assembling */ - int done; - struct timeval delivery; - - AvahiRecord *record; - - AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs); -}; - -struct AvahiProbeScheduler { - AvahiInterface *interface; - AvahiTimeEventQueue *time_event_queue; - - AVAHI_LLIST_HEAD(AvahiProbeJob, jobs); - AVAHI_LLIST_HEAD(AvahiProbeJob, history); -}; - -static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) { - AvahiProbeJob *pj; - - assert(s); - assert(record); - - if (!(pj = avahi_new(AvahiProbeJob, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; /* OOM */ - } - - pj->scheduler = s; - pj->record = avahi_record_ref(record); - pj->time_event = NULL; - pj->chosen = 0; - - if ((pj->done = done)) - AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); - else - AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj); - - return pj; -} - -static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) { - assert(pj); - - if (pj->time_event) - avahi_time_event_free(pj->time_event); - - if (pj->done) - AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj); - else - AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); - - avahi_record_unref(pj->record); - avahi_free(pj); -} - -static void elapse_callback(AvahiTimeEvent *e, void* data); - -static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, unsigned msec, unsigned jitter) { - struct timeval tv; - - assert(s); - assert(pj); - - avahi_elapse_time(&tv, msec, jitter); - - if (pj->time_event) - avahi_time_event_update(pj->time_event, &tv); - else - pj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, pj); -} - -static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) { - assert(s); - assert(pj); - - assert(!pj->done); - - AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); - AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); - - pj->done = 1; - - job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0); - gettimeofday(&pj->delivery, NULL); -} - -AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) { - AvahiProbeScheduler *s; - - assert(i); - - if (!(s = avahi_new(AvahiProbeScheduler, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; - } - - s->interface = i; - s->time_event_queue = i->monitor->server->time_event_queue; - - AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs); - AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history); - - return s; -} - -void avahi_probe_scheduler_free(AvahiProbeScheduler *s) { - assert(s); - - avahi_probe_scheduler_clear(s); - avahi_free(s); -} - -void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) { - assert(s); - - while (s->jobs) - job_free(s, s->jobs); - while (s->history) - job_free(s, s->history); -} - -static int packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) { - size_t size; - AvahiKey *k; - int b; - - assert(s); - assert(p); - assert(pj); - - assert(!pj->chosen); - - /* Estimate the size for this record */ - size = - avahi_key_get_estimate_size(pj->record->key) + - avahi_record_get_estimate_size(pj->record); - - /* Too large */ - if (size > avahi_dns_packet_space(p)) - return 0; - - /* Create the probe query */ - if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) - return 0; /* OOM */ - - b = !!avahi_dns_packet_append_key(p, k, 0); - assert(b); - - /* Mark this job for addition to the packet */ - pj->chosen = 1; - - /* Scan for more jobs whith matching key pattern */ - for (pj = s->jobs; pj; pj = pj->jobs_next) { - if (pj->chosen) - continue; - - /* Does the record match the probe? */ - if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name)) - continue; - - /* This job wouldn't fit in */ - if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p)) - break; - - /* Mark this job for addition to the packet */ - pj->chosen = 1; - } - - avahi_key_unref(k); - - return 1; -} - -static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { - AvahiProbeJob *pj = data, *next; - AvahiProbeScheduler *s; - AvahiDnsPacket *p; - unsigned n; - - assert(pj); - s = pj->scheduler; - - if (pj->done) { - /* Lets remove it from the history */ - job_free(s, pj); - return; - } - - if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) - return; /* OOM */ - n = 1; - - /* Add the import probe */ - if (!packet_add_probe_query(s, p, pj)) { - size_t size; - AvahiKey *k; - int b; - - avahi_dns_packet_free(p); - - /* The probe didn't fit in the package, so let's allocate a larger one */ - - size = - avahi_key_get_estimate_size(pj->record->key) + - avahi_record_get_estimate_size(pj->record) + - AVAHI_DNS_PACKET_HEADER_SIZE; - - if (!(p = avahi_dns_packet_new_query(size + AVAHI_DNS_PACKET_EXTRA_SIZE))) - return; /* OOM */ - - if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) { - avahi_dns_packet_free(p); - return; /* OOM */ - } - - b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0); - avahi_key_unref(k); - - if (b) { - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1); - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1); - avahi_interface_send_packet(s->interface, p); - } else - avahi_log_warn("Probe record too large, cannot send"); - - avahi_dns_packet_free(p); - job_mark_done(s, pj); - - return; - } - - /* Try to fill up packet with more probes, if available */ - for (pj = s->jobs; pj; pj = pj->jobs_next) { - - if (pj->chosen) - continue; - - if (!packet_add_probe_query(s, p, pj)) - break; - - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); - - n = 0; - - /* Now add the chosen records to the authorative section */ - for (pj = s->jobs; pj; pj = next) { - - next = pj->jobs_next; - - if (!pj->chosen) - continue; - - if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) { -/* avahi_log_warn("Bad probe size estimate!"); */ - - /* Unmark all following jobs */ - for (; pj; pj = pj->jobs_next) - pj->chosen = 0; - - break; - } - - job_mark_done(s, pj); - - n ++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); - - /* Send it now */ - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); -} - -static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) { - AvahiProbeJob *pj; - - assert(s); - assert(record); - - for (pj = s->jobs; pj; pj = pj->jobs_next) { - assert(!pj->done); - - if (avahi_record_equal_no_ttl(pj->record, record)) - return pj; - } - - return NULL; -} - -static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) { - AvahiProbeJob *pj; - - assert(s); - assert(record); - - for (pj = s->history; pj; pj = pj->jobs_next) { - assert(pj->done); - - if (avahi_record_equal_no_ttl(pj->record, record)) { - /* Check whether this entry is outdated */ - - if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) { - /* it is outdated, so let's remove it */ - job_free(s, pj); - return NULL; - } - - return pj; - } - } - - return NULL; -} - -int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately) { - AvahiProbeJob *pj; - struct timeval tv; - - assert(s); - assert(record); - assert(!avahi_key_is_pattern(record->key)); - - if ((pj = find_history_job(s, record))) - return 0; - - avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0); - - if ((pj = find_scheduled_job(s, record))) { - - if (avahi_timeval_compare(&tv, &pj->delivery) < 0) { - /* If the new entry should be scheduled earlier, update the old entry */ - pj->delivery = tv; - avahi_time_event_update(pj->time_event, &pj->delivery); - } - - return 1; - } else { - /* Create a new job and schedule it */ - if (!(pj = job_new(s, record, 0))) - return 0; /* OOM */ - - pj->delivery = tv; - pj->time_event = avahi_time_event_new(s->time_event_queue, &pj->delivery, elapse_callback, pj); - - -/* avahi_log_debug("Accepted new probe job."); */ - - return 1; - } -} diff --git a/avahi-core/probe-sched.h b/avahi-core/probe-sched.h deleted file mode 100644 index e47de41..0000000 --- a/avahi-core/probe-sched.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef fooprobeschedhfoo -#define fooprobeschedhfoo - -/*** - 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 struct AvahiProbeScheduler AvahiProbeScheduler; - -#include -#include "iface.h" - -AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i); -void avahi_probe_scheduler_free(AvahiProbeScheduler *s); -void avahi_probe_scheduler_clear(AvahiProbeScheduler *s); - -int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately); - -#endif diff --git a/avahi-core/publish.h b/avahi-core/publish.h deleted file mode 100644 index 90797de..0000000 --- a/avahi-core/publish.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef foopublishhfoo -#define foopublishhfoo - -/*** - 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. -***/ - -/** \file core/publish.h Functions for publising local services and RRs */ - -/** \example core-publish-service.c Example how to register a DNS-SD - * service using an embedded mDNS stack. It behaves like a network - * printer registering both an IPP and a BSD LPR service. */ - -/** A group of locally registered DNS RRs */ -typedef struct AvahiSEntryGroup AvahiSEntryGroup; - -#include -#include - -AVAHI_C_DECL_BEGIN - -/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */ -typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); - -/** Iterate through all local entries of the server. (when g is NULL) - * or of a specified entry group. At the first call state should point - * to a NULL initialized void pointer, That pointer is used to track - * the current iteration. It is not safe to call any other - * avahi_server_xxx() function during the iteration. If the last entry - * has been read, NULL is returned. */ -const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state); - -/** Create a new entry group. The specified callback function is - * called whenever the state of the group changes. Use entry group - * objects to keep track of you RRs. Add new RRs to a group using - * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit() - * to start the registration process for your RRs */ -AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata); - -/** Free an entry group. All RRs assigned to the group are removed from the server */ -void avahi_s_entry_group_free(AvahiSEntryGroup *g); - -/** Commit an entry group. This starts the probing and registration process for all RRs in the group */ -int avahi_s_entry_group_commit(AvahiSEntryGroup *g); - -/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */ -void avahi_s_entry_group_reset(AvahiSEntryGroup *g); - -/** Return 1 if the entry group is empty, i.e. has no records attached. */ -int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g); - -/** Return the current state of the specified entry group */ -AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g); - -/** Change the opaque user data pointer attached to an entry group object */ -void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata); - -/** Return the opaque user data pointer currently set for the entry group object */ -void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g); - -/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */ -int avahi_server_add( - AvahiServer *s, /**< The server object to add this record to */ - AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */ - AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */ - AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */ - AvahiPublishFlags flags, /**< Special flags for this record */ - AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */); - -/** Add an IP address mapping to the server. This will add both the - * host-name-to-address and the reverse mapping to the server. See - * avahi_server_add() for more information. If adding one of the RRs - * fails, the function returns with an error, but it is not defined if - * the other RR is deleted from the server or not. Therefore, you have - * to free the AvahiSEntryGroup and create a new one before - * proceeding. */ -int avahi_server_add_address( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - AvahiAddress *a); - -/** Add an DNS-SD service to the Server. This will add all required - * RRs to the server. See avahi_server_add() for more information. If - * adding one of the RRs fails, the function returns with an error, - * but it is not defined if the other RR is deleted from the server or - * not. Therefore, you have to free the AvahiSEntryGroup and create a - * new one before proceeding. */ -int avahi_server_add_service( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, /**< Service name, e.g. "Lennart's Files" */ - const char *type, /**< DNS-SD type, e.g. "_http._tcp" */ - const char *domain, - const char *host, /**< Host name where this servcie resides, or NULL if on the local host */ - uint16_t port, /**< Port number of the service */ - ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL; - -/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */ -int avahi_server_add_service_strlst( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - const char *host, - uint16_t port, - AvahiStringList *strlst); - -/** Add a subtype for an already existing service */ -int avahi_server_add_service_subtype( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, /**< Specify the name of main service you already added here */ - const char *type, /**< Specify the main type of the service you already added here */ - const char *domain, /**< Specify the main type of the service you already added here */ - const char *subtype /**< The new subtype for the specified service */ ); - -/** Update the TXT record for a service with the data from the specified string list */ -int avahi_server_update_service_txt_strlst( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - AvahiStringList *strlst); - -/** Update the TXT record for a service with the NULL termonate list of strings */ -int avahi_server_update_service_txt( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiPublishFlags flags, - const char *name, - const char *type, - const char *domain, - ...) AVAHI_GCC_SENTINEL; - -/** Check if there is a service locally defined and return the entry group it is attached to. Returns NULL if the service isn't local*/ -int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/querier-test.c b/avahi-core/querier-test.c deleted file mode 100644 index e1ce913..0000000 --- a/avahi-core/querier-test.c +++ /dev/null @@ -1,122 +0,0 @@ -/*** - 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 - -#define DOMAIN NULL -#define SERVICE_TYPE "_http._tcp" - -static AvahiSServiceBrowser *service_browser1 = NULL, *service_browser2 = NULL; -static const AvahiPoll * poll_api = NULL; -static AvahiServer *server = NULL; -static AvahiSimplePoll *simple_poll; - -static const char *browser_event_to_string(AvahiBrowserEvent event) { - switch (event) { - case AVAHI_BROWSER_NEW : return "NEW"; - case AVAHI_BROWSER_REMOVE : return "REMOVE"; - case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED"; - case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW"; - case AVAHI_BROWSER_FAILURE : return "FAILURE"; - } - - abort(); -} - -static void sb_callback( - AvahiSServiceBrowser *b, - AvahiIfIndex iface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, - const char *service_type, - const char *domain, - AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { - avahi_log_debug("SB%i: (%i.%s) <%s> as <%s> in <%s> [%s] cached=%i", b == service_browser1 ? 1 : 2, iface, avahi_proto_to_string(protocol), name, service_type, domain, browser_event_to_string(event), !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); -} - -static void create_second_service_browser(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void* userdata) { - - service_browser2 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL); - assert(service_browser2); - - poll_api->timeout_free(timeout); -} - -static void quit(AVAHI_GCC_UNUSED AvahiTimeout *timeout, AVAHI_GCC_UNUSED void *userdata) { - avahi_simple_poll_quit(simple_poll); -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - struct timeval tv; - AvahiServerConfig config; - - simple_poll = avahi_simple_poll_new(); - assert(simple_poll); - - poll_api = avahi_simple_poll_get(simple_poll); - assert(poll_api); - - avahi_server_config_init(&config); - config.publish_hinfo = 0; - config.publish_addresses = 0; - config.publish_workstation = 0; - config.publish_domain = 0; - - avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); - config.n_wide_area_servers = 1; - config.enable_wide_area = 1; - - server = avahi_server_new(poll_api, &config, NULL, NULL, NULL); - assert(server); - avahi_server_config_free(&config); - - service_browser1 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL); - assert(service_browser1); - - poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 10000, 0), create_second_service_browser, NULL); - - poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 60000, 0), quit, NULL); - - - for (;;) - if (avahi_simple_poll_iterate(simple_poll, -1) != 0) - break; - - avahi_server_free(server); - avahi_simple_poll_free(simple_poll); - - return 0; -} diff --git a/avahi-core/querier.c b/avahi-core/querier.c deleted file mode 100644 index d9dc1fb..0000000 --- a/avahi-core/querier.c +++ /dev/null @@ -1,268 +0,0 @@ -/*** - 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 "querier.h" -#include "log.h" - -struct AvahiQuerier { - AvahiInterface *interface; - - AvahiKey *key; - int n_used; - - unsigned sec_delay; - - AvahiTimeEvent *time_event; - - struct timeval creation_time; - - unsigned post_id; - int post_id_valid; - - AVAHI_LLIST_FIELDS(AvahiQuerier, queriers); -}; - -void avahi_querier_free(AvahiQuerier *q) { - assert(q); - - AVAHI_LLIST_REMOVE(AvahiQuerier, queriers, q->interface->queriers, q); - avahi_hashmap_remove(q->interface->queriers_by_key, q->key); - - avahi_key_unref(q->key); - avahi_time_event_free(q->time_event); - - avahi_free(q); -} - -static void querier_elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { - AvahiQuerier *q = userdata; - struct timeval tv; - - assert(q); - - if (q->n_used <= 0) { - - /* We are not referenced by anyone anymore, so let's free - * ourselves. We should not send out any further queries from - * this querier object anymore. */ - - avahi_querier_free(q); - return; - } - - if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) { - - /* The queue accepted our query. We store the query id here, - * that allows us to drop the query at a later point if the - * query is very short-lived. */ - - q->post_id_valid = 1; - } - - q->sec_delay *= 2; - - if (q->sec_delay >= 60*60) /* 1h */ - q->sec_delay = 60*60; - - avahi_elapse_time(&tv, q->sec_delay*1000, 0); - avahi_time_event_update(q->time_event, &tv); -} - -void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime) { - AvahiQuerier *q; - struct timeval tv; - - assert(i); - assert(key); - - if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) { - - /* Someone is already browsing for records of this RR key */ - q->n_used++; - - /* Return the creation time. This is used for generating the - * ALL_FOR_NOW event one second after the querier was - * initially created. */ - if (ret_ctime) - *ret_ctime = q->creation_time; - return; - } - - /* No one is browsing for this RR key, so we add a new querier */ - if (!(q = avahi_new(AvahiQuerier, 1))) - return; /* OOM */ - - q->key = avahi_key_ref(key); - q->interface = i; - q->n_used = 1; - q->sec_delay = 1; - q->post_id_valid = 0; - gettimeofday(&q->creation_time, NULL); - - /* Do the initial query */ - if (avahi_interface_post_query(i, key, 0, &q->post_id)) - q->post_id_valid = 1; - - /* Schedule next queries */ - q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q); - - AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q); - avahi_hashmap_insert(i->queriers_by_key, q->key, q); - - /* Return the creation time. This is used for generating the - * ALL_FOR_NOW event one second after the querier was initially - * created. */ - if (ret_ctime) - *ret_ctime = q->creation_time; -} - -void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) { - AvahiQuerier *q; - - /* There was no querier for this RR key, or it wasn't referenced - * by anyone. */ - if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0) - return; - - if ((--q->n_used) <= 0) { - - /* Nobody references us anymore. */ - - if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) { - - /* We succeeded in withdrawing our query from the queue, - * so let's drop dead. */ - - avahi_querier_free(q); - } - - /* If we failed to withdraw our query from the queue, we stay - * alive, in case someone else might recycle our querier at a - * later point. We are freed at our next expiry, in case - * nobody recycled us. */ - } -} - -static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - assert(m); - assert(i); - assert(userdata); - - if (i->announcing) - avahi_querier_remove(i, (AvahiKey*) userdata); -} - -void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key) { - assert(s); - assert(key); - - avahi_interface_monitor_walk(s->monitor, idx, protocol, remove_querier_callback, key); -} - -struct cbdata { - AvahiKey *key; - struct timeval *ret_ctime; -}; - -static void add_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - struct cbdata *cbdata = userdata; - - assert(m); - assert(i); - assert(cbdata); - - if (i->announcing) { - struct timeval tv; - avahi_querier_add(i, cbdata->key, &tv); - - if (cbdata->ret_ctime && avahi_timeval_compare(&tv, cbdata->ret_ctime) > 0) - *cbdata->ret_ctime = tv; - } -} - -void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime) { - struct cbdata cbdata; - - assert(s); - assert(key); - - cbdata.key = key; - cbdata.ret_ctime = ret_ctime; - - if (ret_ctime) - ret_ctime->tv_sec = ret_ctime->tv_usec = 0; - - avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata); -} - -int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) { - AvahiQuerier *q; - - assert(i); - assert(key); - - /* Called by the cache maintainer */ - - if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) - /* This key is currently not subscribed at all, so no cache - * refresh is needed */ - return 0; - - if (q->n_used <= 0) { - - /* If this is an entry nobody references right now, don't - * consider it "existing". */ - - /* Remove this querier since it is referenced by nobody - * and the cached data will soon be out of date */ - avahi_querier_free(q); - - /* Tell the cache that no refresh is needed */ - return 0; - - } else { - struct timeval tv; - - /* We can defer our query a little, since the cache will now - * issue a refresh query anyway. */ - avahi_elapse_time(&tv, q->sec_delay*1000, 0); - avahi_time_event_update(q->time_event, &tv); - - /* Tell the cache that a refresh should be issued */ - return 1; - } -} - -void avahi_querier_free_all(AvahiInterface *i) { - assert(i); - - while (i->queriers) - avahi_querier_free(i->queriers); -} diff --git a/avahi-core/querier.h b/avahi-core/querier.h deleted file mode 100644 index 6a32a3b..0000000 --- a/avahi-core/querier.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef fooquerierhfoo -#define fooquerierhfoo - -/*** - 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 struct AvahiQuerier AvahiQuerier; - -#include "iface.h" - -/** Add querier for the specified key to the specified interface */ -void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime); - -/** Remove a querier for the specified key from the specified interface */ -void avahi_querier_remove(AvahiInterface *i, AvahiKey *key); - -/** Add a querier for the specified key on all interfaces that mach */ -void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime); - -/** Remove a querier for the specified key on all interfaces that mach */ -void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key); - -/** Free all queriers */ -void avahi_querier_free(AvahiQuerier *q); - -/** Free all queriers on the specified interface */ -void avahi_querier_free_all(AvahiInterface *i); - -/** Return 1 if there is a querier for the specified key on the specified interface */ -int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key); - -#endif diff --git a/avahi-core/query-sched.c b/avahi-core/query-sched.c deleted file mode 100644 index ff833f9..0000000 --- a/avahi-core/query-sched.c +++ /dev/null @@ -1,450 +0,0 @@ -/*** - 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 "query-sched.h" -#include "log.h" - -#define AVAHI_QUERY_HISTORY_MSEC 100 -#define AVAHI_QUERY_DEFER_MSEC 100 - -typedef struct AvahiQueryJob AvahiQueryJob; -typedef struct AvahiKnownAnswer AvahiKnownAnswer; - -struct AvahiQueryJob { - unsigned id; - int n_posted; - - AvahiQueryScheduler *scheduler; - AvahiTimeEvent *time_event; - - int done; - struct timeval delivery; - - AvahiKey *key; - - /* Jobs are stored in a simple linked list. It might turn out in - * the future that this list grows too long and we must switch to - * some other kind of data structure. This needs further - * investigation. I expect the list to be very short (< 20 - * entries) most of the time, but this might be a wrong - * assumption, especially on setups where traffic reflection is - * involved. */ - - AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs); -}; - -struct AvahiKnownAnswer { - AvahiQueryScheduler *scheduler; - AvahiRecord *record; - - AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer); -}; - -struct AvahiQueryScheduler { - AvahiInterface *interface; - AvahiTimeEventQueue *time_event_queue; - - unsigned next_id; - - AVAHI_LLIST_HEAD(AvahiQueryJob, jobs); - AVAHI_LLIST_HEAD(AvahiQueryJob, history); - AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers); -}; - -static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) { - AvahiQueryJob *qj; - - assert(s); - assert(key); - - if (!(qj = avahi_new(AvahiQueryJob, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; - } - - qj->scheduler = s; - qj->key = avahi_key_ref(key); - qj->time_event = NULL; - qj->n_posted = 1; - qj->id = s->next_id++; - - if ((qj->done = done)) - AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); - else - AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->jobs, qj); - - return qj; -} - -static void job_free(AvahiQueryScheduler *s, AvahiQueryJob *qj) { - assert(s); - assert(qj); - - if (qj->time_event) - avahi_time_event_free(qj->time_event); - - if (qj->done) - AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->history, qj); - else - AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj); - - avahi_key_unref(qj->key); - avahi_free(qj); -} - -static void elapse_callback(AvahiTimeEvent *e, void* data); - -static void job_set_elapse_time(AvahiQueryScheduler *s, AvahiQueryJob *qj, unsigned msec, unsigned jitter) { - struct timeval tv; - - assert(s); - assert(qj); - - avahi_elapse_time(&tv, msec, jitter); - - if (qj->time_event) - avahi_time_event_update(qj->time_event, &tv); - else - qj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, qj); -} - -static void job_mark_done(AvahiQueryScheduler *s, AvahiQueryJob *qj) { - assert(s); - assert(qj); - - assert(!qj->done); - - AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj); - AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); - - qj->done = 1; - - job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); - gettimeofday(&qj->delivery, NULL); -} - -AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) { - AvahiQueryScheduler *s; - assert(i); - - if (!(s = avahi_new(AvahiQueryScheduler, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; /* OOM */ - } - - s->interface = i; - s->time_event_queue = i->monitor->server->time_event_queue; - s->next_id = 0; - - AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs); - AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history); - AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers); - - return s; -} - -void avahi_query_scheduler_free(AvahiQueryScheduler *s) { - assert(s); - - assert(!s->known_answers); - avahi_query_scheduler_clear(s); - avahi_free(s); -} - -void avahi_query_scheduler_clear(AvahiQueryScheduler *s) { - assert(s); - - while (s->jobs) - job_free(s, s->jobs); - while (s->history) - job_free(s, s->history); -} - -static void* known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { - AvahiQueryScheduler *s = userdata; - AvahiKnownAnswer *ka; - - assert(c); - assert(pattern); - assert(e); - assert(s); - - if (avahi_cache_entry_half_ttl(c, e)) - return NULL; - - if (!(ka = avahi_new0(AvahiKnownAnswer, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; - } - - ka->scheduler = s; - ka->record = avahi_record_ref(e->record); - - AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka); - return NULL; -} - -static int packet_add_query_job(AvahiQueryScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) { - assert(s); - assert(p); - assert(qj); - - if (!avahi_dns_packet_append_key(p, qj->key, 0)) - return 0; - - /* Add all matching known answers to the list */ - avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s); - - job_mark_done(s, qj); - - return 1; -} - -static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) { - AvahiKnownAnswer *ka; - unsigned n; - assert(s); - assert(p); - - n = 0; - - while ((ka = s->known_answers)) { - int too_large = 0; - - while (!avahi_dns_packet_append_record(p, ka->record, 0, 0)) { - - if (avahi_dns_packet_is_empty(p)) { - /* The record is too large to fit into one packet, so - there's no point in sending it. Better is letting - the owner of the record send it as a response. This - has the advantage of a cache refresh. */ - - too_large = 1; - break; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC); - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); - - p = avahi_dns_packet_new_query(s->interface->hardware->mtu); - n = 0; - } - - AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka); - avahi_record_unref(ka->record); - avahi_free(ka); - - if (!too_large) - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); -} - -static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { - AvahiQueryJob *qj = data; - AvahiQueryScheduler *s; - AvahiDnsPacket *p; - unsigned n; - int b; - - assert(qj); - s = qj->scheduler; - - if (qj->done) { - /* Lets remove it from the history */ - job_free(s, qj); - return; - } - - assert(!s->known_answers); - - if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) - return; /* OOM */ - - b = packet_add_query_job(s, p, qj); - assert(b); /* An query must always fit in */ - n = 1; - - /* Try to fill up packet with more queries, if available */ - while (s->jobs) { - - if (!packet_add_query_job(s, p, s->jobs)) - break; - - n++; - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); - - /* Now add known answers */ - append_known_answers_and_send(s, p); -} - -static AvahiQueryJob* find_scheduled_job(AvahiQueryScheduler *s, AvahiKey *key) { - AvahiQueryJob *qj; - - assert(s); - assert(key); - - for (qj = s->jobs; qj; qj = qj->jobs_next) { - assert(!qj->done); - - if (avahi_key_equal(qj->key, key)) - return qj; - } - - return NULL; -} - -static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) { - AvahiQueryJob *qj; - - assert(s); - assert(key); - - for (qj = s->history; qj; qj = qj->jobs_next) { - assert(qj->done); - - if (avahi_key_equal(qj->key, key)) { - /* Check whether this entry is outdated */ - - if (avahi_age(&qj->delivery) > AVAHI_QUERY_HISTORY_MSEC*1000) { - /* it is outdated, so let's remove it */ - job_free(s, qj); - return NULL; - } - - return qj; - } - } - - return NULL; -} - -int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) { - struct timeval tv; - AvahiQueryJob *qj; - - assert(s); - assert(key); - - if ((qj = find_history_job(s, key))) - return 0; - - avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0); - - if ((qj = find_scheduled_job(s, key))) { - /* Duplicate questions suppression */ - - if (avahi_timeval_compare(&tv, &qj->delivery) < 0) { - /* If the new entry should be scheduled earlier, - * update the old entry */ - qj->delivery = tv; - avahi_time_event_update(qj->time_event, &qj->delivery); - } - - qj->n_posted++; - - } else { - - if (!(qj = job_new(s, key, 0))) - return 0; /* OOM */ - - qj->delivery = tv; - qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj); - } - - if (ret_id) - *ret_id = qj->id; - - return 1; -} - -void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) { - AvahiQueryJob *qj; - - assert(s); - assert(key); - - /* This function is called whenever an incoming query was - * received. We drop scheduled queries that match. The keyword is - * "DUPLICATE QUESTION SUPPRESION". */ - - if ((qj = find_scheduled_job(s, key))) { - job_mark_done(s, qj); - return; - } - - /* Look if there's a history job for this key. If there is, just - * update the elapse time */ - if (!(qj = find_history_job(s, key))) - if (!(qj = job_new(s, key, 1))) - return; /* OOM */ - - gettimeofday(&qj->delivery, NULL); - job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); -} - -int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) { - AvahiQueryJob *qj; - - assert(s); - - /* Very short lived queries can withdraw an already scheduled item - * from the queue using this function, simply by passing the id - * returned by avahi_query_scheduler_post(). */ - - for (qj = s->jobs; qj; qj = qj->jobs_next) { - assert(!qj->done); - - if (qj->id == id) { - /* Entry found */ - - assert(qj->n_posted >= 1); - - if (--qj->n_posted <= 0) { - - /* We withdraw this job only if the calling object was - * the only remaining poster. (Usually this is the - * case since there should exist only one querier per - * key, but there are exceptions, notably reflected - * traffic.) */ - - job_free(s, qj); - return 1; - } - } - } - - return 0; -} diff --git a/avahi-core/query-sched.h b/avahi-core/query-sched.h deleted file mode 100644 index b45520c..0000000 --- a/avahi-core/query-sched.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef fooqueryschedhfoo -#define fooqueryschedhfoo - -/*** - 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 struct AvahiQueryScheduler AvahiQueryScheduler; - -#include -#include "iface.h" - -AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i); -void avahi_query_scheduler_free(AvahiQueryScheduler *s); -void avahi_query_scheduler_clear(AvahiQueryScheduler *s); - -int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id); -int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id); -void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key); - -#endif diff --git a/avahi-core/resolve-address.c b/avahi-core/resolve-address.c deleted file mode 100644 index dd4adbc..0000000 --- a/avahi-core/resolve-address.c +++ /dev/null @@ -1,268 +0,0 @@ -/*** - 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 "browse.h" - -#define TIMEOUT_MSEC 5000 - -struct AvahiSAddressResolver { - AvahiServer *server; - AvahiAddress address; - - AvahiSRecordBrowser *record_browser; - - AvahiSAddressResolverCallback callback; - void* userdata; - - AvahiRecord *ptr_record; - AvahiIfIndex interface; - AvahiProtocol protocol; - AvahiLookupResultFlags flags; - - int retry_with_multicast; - AvahiKey *key; - - AvahiTimeEvent *time_event; - - AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver); -}; - -static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) { - assert(r); - - if (r->time_event) { - avahi_time_event_free(r->time_event); - r->time_event = NULL; - } - - switch (event) { - case AVAHI_RESOLVER_FAILURE: - r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata); - break; - - case AVAHI_RESOLVER_FOUND: - assert(r->ptr_record); - r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata); - break; - } -} - -static void time_event_callback(AvahiTimeEvent *e, void *userdata) { - AvahiSAddressResolver *r = userdata; - - assert(e); - assert(r); - - avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); - finish(r, AVAHI_RESOLVER_FAILURE); -} - -static void start_timeout(AvahiSAddressResolver *r) { - struct timeval tv; - assert(r); - - if (r->time_event) - return; - - avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); - r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); -} - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSAddressResolver *r = userdata; - - assert(rr); - assert(r); - - switch (event) { - case AVAHI_BROWSER_NEW: - assert(record); - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - if (r->interface > 0 && interface != r->interface) - return; - - if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) - return; - - if (r->interface <= 0) - r->interface = interface; - - if (r->protocol == AVAHI_PROTO_UNSPEC) - r->protocol = protocol; - - if (!r->ptr_record) { - r->ptr_record = avahi_record_ref(record); - r->flags = flags; - - finish(r, AVAHI_RESOLVER_FOUND); - } - break; - - case AVAHI_BROWSER_REMOVE: - assert(record); - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { - avahi_record_unref(r->ptr_record); - r->ptr_record = NULL; - r->flags = flags; - - /** Look for a replacement */ - avahi_s_record_browser_restart(r->record_browser); - start_timeout(r); - } - - break; - - case AVAHI_BROWSER_CACHE_EXHAUSTED: - case AVAHI_BROWSER_ALL_FOR_NOW: - break; - - case AVAHI_BROWSER_FAILURE: - - if (r->retry_with_multicast) { - r->retry_with_multicast = 0; - - avahi_s_record_browser_free(r->record_browser); - r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r); - - if (r->record_browser) { - start_timeout(r); - break; - } - } - - r->flags = flags; - finish(r, AVAHI_RESOLVER_FAILURE); - break; - } -} - -AvahiSAddressResolver *avahi_s_address_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const AvahiAddress *address, - AvahiLookupFlags flags, - AvahiSAddressResolverCallback callback, - void* userdata) { - - AvahiSAddressResolver *r; - AvahiKey *k; - char n[AVAHI_DOMAIN_NAME_MAX]; - - assert(server); - assert(address); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - - avahi_reverse_lookup_name(address, n, sizeof(n)); - - if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - if (!(r = avahi_new(AvahiSAddressResolver, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - avahi_key_unref(k); - return NULL; - } - - r->server = server; - r->address = *address; - r->callback = callback; - r->userdata = userdata; - r->ptr_record = NULL; - r->interface = interface; - r->protocol = protocol; - r->flags = 0; - r->retry_with_multicast = 0; - r->key = k; - - r->record_browser = NULL; - AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r); - - r->time_event = NULL; - - if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) { - - if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine)) - flags |= AVAHI_LOOKUP_USE_MULTICAST; - else { - flags |= AVAHI_LOOKUP_USE_WIDE_AREA; - r->retry_with_multicast = 1; - } - } - - r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); - - if (!r->record_browser) { - avahi_s_address_resolver_free(r); - return NULL; - } - - start_timeout(r); - - return r; -} - -void avahi_s_address_resolver_free(AvahiSAddressResolver *r) { - assert(r); - - AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r); - - if (r->record_browser) - avahi_s_record_browser_free(r->record_browser); - - if (r->time_event) - avahi_time_event_free(r->time_event); - - if (r->ptr_record) - avahi_record_unref(r->ptr_record); - - if (r->key) - avahi_key_unref(r->key); - - avahi_free(r); -} diff --git a/avahi-core/resolve-host-name.c b/avahi-core/resolve-host-name.c deleted file mode 100644 index 08f209b..0000000 --- a/avahi-core/resolve-host-name.c +++ /dev/null @@ -1,297 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" - -#define TIMEOUT_MSEC 5000 - -struct AvahiSHostNameResolver { - AvahiServer *server; - char *host_name; - - AvahiSRecordBrowser *record_browser_a; - AvahiSRecordBrowser *record_browser_aaaa; - - AvahiSHostNameResolverCallback callback; - void* userdata; - - AvahiRecord *address_record; - AvahiIfIndex interface; - AvahiProtocol protocol; - AvahiLookupResultFlags flags; - - AvahiTimeEvent *time_event; - - AVAHI_LLIST_FIELDS(AvahiSHostNameResolver, resolver); -}; - -static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) { - assert(r); - - if (r->time_event) { - avahi_time_event_free(r->time_event); - r->time_event = NULL; - } - - switch (event) { - case AVAHI_RESOLVER_FOUND: { - AvahiAddress a; - - assert(r->address_record); - - switch (r->address_record->key->type) { - case AVAHI_DNS_TYPE_A: - a.proto = AVAHI_PROTO_INET; - a.data.ipv4 = r->address_record->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - a.proto = AVAHI_PROTO_INET6; - a.data.ipv6 = r->address_record->data.aaaa.address; - break; - - default: - abort(); - } - - r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->flags, r->userdata); - break; - - } - - case AVAHI_RESOLVER_FAILURE: - - r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata); - break; - } -} - -static void time_event_callback(AvahiTimeEvent *e, void *userdata) { - AvahiSHostNameResolver *r = userdata; - - assert(e); - assert(r); - - avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); - finish(r, AVAHI_RESOLVER_FAILURE); -} - -static void start_timeout(AvahiSHostNameResolver *r) { - struct timeval tv; - assert(r); - - if (r->time_event) - return; - - avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); - - r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); -} - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSHostNameResolver *r = userdata; - - assert(rr); - assert(r); - - - switch (event) { - case AVAHI_BROWSER_NEW: - assert(record); - assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); - - if (r->interface > 0 && interface != r->interface) - return; - - if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) - return; - - if (r->interface <= 0) - r->interface = interface; - - if (r->protocol == AVAHI_PROTO_UNSPEC) - r->protocol = protocol; - - if (!r->address_record) { - r->address_record = avahi_record_ref(record); - r->flags = flags; - - finish(r, AVAHI_RESOLVER_FOUND); - } - - break; - - case AVAHI_BROWSER_REMOVE: - assert(record); - assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); - - if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { - avahi_record_unref(r->address_record); - r->address_record = NULL; - - r->flags = flags; - - - /** Look for a replacement */ - if (r->record_browser_aaaa) - avahi_s_record_browser_restart(r->record_browser_aaaa); - if (r->record_browser_a) - avahi_s_record_browser_restart(r->record_browser_a); - - start_timeout(r); - } - - break; - - case AVAHI_BROWSER_CACHE_EXHAUSTED: - case AVAHI_BROWSER_ALL_FOR_NOW: - /* Ignore */ - break; - - case AVAHI_BROWSER_FAILURE: - - /* Stop browsers */ - - if (r->record_browser_aaaa) - avahi_s_record_browser_free(r->record_browser_aaaa); - if (r->record_browser_a) - avahi_s_record_browser_free(r->record_browser_a); - - r->record_browser_a = r->record_browser_aaaa = NULL; - r->flags = flags; - - finish(r, AVAHI_RESOLVER_FAILURE); - break; - } -} - -AvahiSHostNameResolver *avahi_s_host_name_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *host_name, - AvahiProtocol aprotocol, - AvahiLookupFlags flags, - AvahiSHostNameResolverCallback callback, - void* userdata) { - - AvahiSHostNameResolver *r; - AvahiKey *k; - - assert(server); - assert(host_name); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_fqdn(host_name), AVAHI_ERR_INVALID_HOST_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); - - if (!(r = avahi_new(AvahiSHostNameResolver, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - r->server = server; - r->host_name = avahi_normalize_name_strdup(host_name); - r->callback = callback; - r->userdata = userdata; - r->address_record = NULL; - r->interface = interface; - r->protocol = protocol; - r->flags = 0; - - r->record_browser_a = r->record_browser_aaaa = NULL; - - r->time_event = NULL; - - AVAHI_LLIST_PREPEND(AvahiSHostNameResolver, resolver, server->host_name_resolvers, r); - - r->record_browser_aaaa = r->record_browser_a = NULL; - - if (aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_UNSPEC) { - k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); - r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); - avahi_key_unref(k); - - if (!r->record_browser_a) - goto fail; - } - - if (aprotocol == AVAHI_PROTO_INET6 || aprotocol == AVAHI_PROTO_UNSPEC) { - k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); - r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); - avahi_key_unref(k); - - if (!r->record_browser_aaaa) - goto fail; - } - - assert(r->record_browser_aaaa || r->record_browser_a); - - start_timeout(r); - - return r; - -fail: - avahi_s_host_name_resolver_free(r); - return NULL; -} - -void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r) { - assert(r); - - AVAHI_LLIST_REMOVE(AvahiSHostNameResolver, resolver, r->server->host_name_resolvers, r); - - if (r->record_browser_a) - avahi_s_record_browser_free(r->record_browser_a); - - if (r->record_browser_aaaa) - avahi_s_record_browser_free(r->record_browser_aaaa); - - if (r->time_event) - avahi_time_event_free(r->time_event); - - if (r->address_record) - avahi_record_unref(r->address_record); - - avahi_free(r->host_name); - avahi_free(r); -} diff --git a/avahi-core/resolve-service.c b/avahi-core/resolve-service.c deleted file mode 100644 index 3377a50..0000000 --- a/avahi-core/resolve-service.c +++ /dev/null @@ -1,489 +0,0 @@ -/*** - 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 "browse.h" -#include "log.h" - -#define TIMEOUT_MSEC 5000 - -struct AvahiSServiceResolver { - AvahiServer *server; - char *service_name; - char *service_type; - char *domain_name; - AvahiProtocol address_protocol; - - AvahiIfIndex interface; - AvahiProtocol protocol; - - AvahiSRecordBrowser *record_browser_srv; - AvahiSRecordBrowser *record_browser_txt; - AvahiSRecordBrowser *record_browser_a; - AvahiSRecordBrowser *record_browser_aaaa; - - AvahiRecord *srv_record, *txt_record, *address_record; - AvahiLookupResultFlags srv_flags, txt_flags, address_flags; - - AvahiSServiceResolverCallback callback; - void* userdata; - AvahiLookupFlags user_flags; - - AvahiTimeEvent *time_event; - - AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver); -}; - -static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) { - AvahiLookupResultFlags flags; - - assert(r); - - if (r->time_event) { - avahi_time_event_free(r->time_event); - r->time_event = NULL; - } - - flags = - r->txt_flags | - r->srv_flags | - r->address_flags; - - switch (event) { - case AVAHI_RESOLVER_FAILURE: - - r->callback( - r, - r->interface, - r->protocol, - event, - r->service_name, - r->service_type, - r->domain_name, - NULL, - NULL, - 0, - NULL, - flags, - r->userdata); - - break; - - case AVAHI_RESOLVER_FOUND: { - AvahiAddress a; - - assert(event == AVAHI_RESOLVER_FOUND); - - assert(r->srv_record); - - if (r->address_record) { - switch (r->address_record->key->type) { - case AVAHI_DNS_TYPE_A: - a.proto = AVAHI_PROTO_INET; - a.data.ipv4 = r->address_record->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - a.proto = AVAHI_PROTO_INET6; - a.data.ipv6 = r->address_record->data.aaaa.address; - break; - - default: - assert(0); - } - } - - r->callback( - r, - r->interface, - r->protocol, - event, - r->service_name, - r->service_type, - r->domain_name, - r->srv_record->data.srv.name, - r->address_record ? &a : NULL, - r->srv_record->data.srv.port, - r->txt_record ? r->txt_record->data.txt.string_list : NULL, - flags, - r->userdata); - - break; - } - } -} - -static void time_event_callback(AvahiTimeEvent *e, void *userdata) { - AvahiSServiceResolver *r = userdata; - - assert(e); - assert(r); - - avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); - finish(r, AVAHI_RESOLVER_FAILURE); -} - -static void start_timeout(AvahiSServiceResolver *r) { - struct timeval tv; - assert(r); - - if (r->time_event) - return; - - avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); - - r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); -} - -static void record_browser_callback( - AvahiSRecordBrowser*rr, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - AvahiRecord *record, - AvahiLookupResultFlags flags, - void* userdata) { - - AvahiSServiceResolver *r = userdata; - - assert(rr); - assert(r); - - if (rr == r->record_browser_aaaa || rr == r->record_browser_a) - r->address_flags = flags; - else if (rr == r->record_browser_srv) - r->srv_flags = flags; - else if (rr == r->record_browser_txt) - r->txt_flags = flags; - - switch (event) { - - case AVAHI_BROWSER_NEW: { - int changed = 0; - assert(record); - - if (r->interface > 0 && interface > 0 && interface != r->interface) - return; - - if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) - return; - - if (r->interface <= 0) - r->interface = interface; - - if (r->protocol == AVAHI_PROTO_UNSPEC) - r->protocol = protocol; - - switch (record->key->type) { - case AVAHI_DNS_TYPE_SRV: - if (!r->srv_record) { - r->srv_record = avahi_record_ref(record); - changed = 1; - - if (r->record_browser_a) { - avahi_s_record_browser_free(r->record_browser_a); - r->record_browser_a = NULL; - } - - if (r->record_browser_aaaa) { - avahi_s_record_browser_free(r->record_browser_aaaa); - r->record_browser_aaaa = NULL; - } - - if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) { - - if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) { - AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); - r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); - avahi_key_unref(k); - } - - if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) { - AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); - r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); - avahi_key_unref(k); - } - } - } - break; - - case AVAHI_DNS_TYPE_TXT: - - assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); - - if (!r->txt_record) { - r->txt_record = avahi_record_ref(record); - changed = 1; - } - break; - - case AVAHI_DNS_TYPE_A: - case AVAHI_DNS_TYPE_AAAA: - - assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); - - if (!r->address_record) { - r->address_record = avahi_record_ref(record); - changed = 1; - } - break; - - default: - abort(); - } - - - if (changed && - r->srv_record && - (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) && - (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS))) - finish(r, AVAHI_RESOLVER_FOUND); - - break; - - } - - case AVAHI_BROWSER_REMOVE: - - assert(record); - - switch (record->key->type) { - case AVAHI_DNS_TYPE_SRV: - - if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) { - avahi_record_unref(r->srv_record); - r->srv_record = NULL; - - if (r->record_browser_a) { - avahi_s_record_browser_free(r->record_browser_a); - r->record_browser_a = NULL; - } - - if (r->record_browser_aaaa) { - avahi_s_record_browser_free(r->record_browser_aaaa); - r->record_browser_aaaa = NULL; - } - - /** Look for a replacement */ - avahi_s_record_browser_restart(r->record_browser_srv); - start_timeout(r); - } - - break; - - case AVAHI_DNS_TYPE_TXT: - - assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); - - if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) { - avahi_record_unref(r->txt_record); - r->txt_record = NULL; - - /** Look for a replacement */ - avahi_s_record_browser_restart(r->record_browser_txt); - start_timeout(r); - } - break; - - case AVAHI_DNS_TYPE_A: - case AVAHI_DNS_TYPE_AAAA: - - assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); - - if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { - avahi_record_unref(r->address_record); - r->address_record = NULL; - - /** Look for a replacement */ - if (r->record_browser_aaaa) - avahi_s_record_browser_restart(r->record_browser_aaaa); - if (r->record_browser_a) - avahi_s_record_browser_restart(r->record_browser_a); - start_timeout(r); - } - break; - - default: - abort(); - } - - break; - - case AVAHI_BROWSER_CACHE_EXHAUSTED: - case AVAHI_BROWSER_ALL_FOR_NOW: - break; - - case AVAHI_BROWSER_FAILURE: - - if (rr == r->record_browser_a && r->record_browser_aaaa) { - /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ - avahi_s_record_browser_free(r->record_browser_a); - r->record_browser_a = NULL; - break; - } - - if (rr == r->record_browser_aaaa && r->record_browser_a) { - /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ - avahi_s_record_browser_free(r->record_browser_aaaa); - r->record_browser_aaaa = NULL; - break; - } - - /* Hmm, everything's lost, tell the user */ - - if (r->record_browser_srv) - avahi_s_record_browser_free(r->record_browser_srv); - if (r->record_browser_txt) - avahi_s_record_browser_free(r->record_browser_txt); - if (r->record_browser_a) - avahi_s_record_browser_free(r->record_browser_a); - if (r->record_browser_aaaa) - avahi_s_record_browser_free(r->record_browser_aaaa); - - r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL; - - finish(r, AVAHI_RESOLVER_FAILURE); - break; - } -} - -AvahiSServiceResolver *avahi_s_service_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *name, - const char *type, - const char *domain, - AvahiProtocol aprotocol, - AvahiLookupFlags flags, - AvahiSServiceResolverCallback callback, - void* userdata) { - - AvahiSServiceResolver *r; - AvahiKey *k; - char n[AVAHI_DOMAIN_NAME_MAX]; - int ret; - - assert(server); - assert(type); - assert(callback); - - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !name || avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); - AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), AVAHI_ERR_INVALID_FLAGS); - - if (!domain) - domain = server->domain_name; - - if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain)) < 0) { - avahi_server_set_errno(server, ret); - return NULL; - } - - if (!(r = avahi_new(AvahiSServiceResolver, 1))) { - avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); - return NULL; - } - - r->server = server; - r->service_name = avahi_strdup(name); - r->service_type = avahi_normalize_name_strdup(type); - r->domain_name = avahi_normalize_name_strdup(domain); - r->callback = callback; - r->userdata = userdata; - r->address_protocol = aprotocol; - r->srv_record = r->txt_record = r->address_record = NULL; - r->srv_flags = r->txt_flags = r->address_flags = 0; - r->interface = interface; - r->protocol = protocol; - r->user_flags = flags; - r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL; - r->time_event = NULL; - AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r); - - k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); - r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); - avahi_key_unref(k); - - if (!r->record_browser_srv) { - avahi_s_service_resolver_free(r); - return NULL; - } - - if (!(flags & AVAHI_LOOKUP_NO_TXT)) { - k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); - r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); - avahi_key_unref(k); - - if (!r->record_browser_txt) { - avahi_s_service_resolver_free(r); - return NULL; - } - } - - start_timeout(r); - - return r; -} - -void avahi_s_service_resolver_free(AvahiSServiceResolver *r) { - assert(r); - - AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r); - - if (r->time_event) - avahi_time_event_free(r->time_event); - - if (r->record_browser_srv) - avahi_s_record_browser_free(r->record_browser_srv); - if (r->record_browser_txt) - avahi_s_record_browser_free(r->record_browser_txt); - if (r->record_browser_a) - avahi_s_record_browser_free(r->record_browser_a); - if (r->record_browser_aaaa) - avahi_s_record_browser_free(r->record_browser_aaaa); - - if (r->srv_record) - avahi_record_unref(r->srv_record); - if (r->txt_record) - avahi_record_unref(r->txt_record); - if (r->address_record) - avahi_record_unref(r->address_record); - - avahi_free(r->service_name); - avahi_free(r->service_type); - avahi_free(r->domain_name); - avahi_free(r); -} diff --git a/avahi-core/response-sched.c b/avahi-core/response-sched.c deleted file mode 100644 index abac0a1..0000000 --- a/avahi-core/response-sched.c +++ /dev/null @@ -1,511 +0,0 @@ -/*** - 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 "response-sched.h" -#include "log.h" -#include "rr-util.h" - -/* Local packets are supressed this long after sending them */ -#define AVAHI_RESPONSE_HISTORY_MSEC 500 - -/* Local packets are deferred this long before sending them */ -#define AVAHI_RESPONSE_DEFER_MSEC 20 - -/* Additional jitter for deferred packets */ -#define AVAHI_RESPONSE_JITTER_MSEC 100 - -/* Remote packets can suppress local traffic as long as this value */ -#define AVAHI_RESPONSE_SUPPRESS_MSEC 700 - -typedef struct AvahiResponseJob AvahiResponseJob; - -typedef enum { - AVAHI_SCHEDULED, - AVAHI_DONE, - AVAHI_SUPPRESSED -} AvahiResponseJobState; - -struct AvahiResponseJob { - AvahiResponseScheduler *scheduler; - AvahiTimeEvent *time_event; - - AvahiResponseJobState state; - struct timeval delivery; - - AvahiRecord *record; - int flush_cache; - AvahiAddress querier; - int querier_valid; - - AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs); -}; - -struct AvahiResponseScheduler { - AvahiInterface *interface; - AvahiTimeEventQueue *time_event_queue; - - AVAHI_LLIST_HEAD(AvahiResponseJob, jobs); - AVAHI_LLIST_HEAD(AvahiResponseJob, history); - AVAHI_LLIST_HEAD(AvahiResponseJob, suppressed); -}; - -static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) { - AvahiResponseJob *rj; - - assert(s); - assert(record); - - if (!(rj = avahi_new(AvahiResponseJob, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; - } - - rj->scheduler = s; - rj->record = avahi_record_ref(record); - rj->time_event = NULL; - rj->flush_cache = 0; - rj->querier_valid = 0; - - if ((rj->state = state) == AVAHI_SCHEDULED) - AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->jobs, rj); - else if (rj->state == AVAHI_DONE) - AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj); - else /* rj->state == AVAHI_SUPPRESSED */ - AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->suppressed, rj); - - return rj; -} - -static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) { - assert(s); - assert(rj); - - if (rj->time_event) - avahi_time_event_free(rj->time_event); - - if (rj->state == AVAHI_SCHEDULED) - AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj); - else if (rj->state == AVAHI_DONE) - AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj); - else /* rj->state == AVAHI_SUPPRESSED */ - AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj); - - avahi_record_unref(rj->record); - avahi_free(rj); -} - -static void elapse_callback(AvahiTimeEvent *e, void* data); - -static void job_set_elapse_time(AvahiResponseScheduler *s, AvahiResponseJob *rj, unsigned msec, unsigned jitter) { - struct timeval tv; - - assert(s); - assert(rj); - - avahi_elapse_time(&tv, msec, jitter); - - if (rj->time_event) - avahi_time_event_update(rj->time_event, &tv); - else - rj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, rj); -} - -static void job_mark_done(AvahiResponseScheduler *s, AvahiResponseJob *rj) { - assert(s); - assert(rj); - - assert(rj->state == AVAHI_SCHEDULED); - - AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj); - AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj); - - rj->state = AVAHI_DONE; - - job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); - - gettimeofday(&rj->delivery, NULL); -} - -AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i) { - AvahiResponseScheduler *s; - assert(i); - - if (!(s = avahi_new(AvahiResponseScheduler, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; - } - - s->interface = i; - s->time_event_queue = i->monitor->server->time_event_queue; - - AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->jobs); - AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->history); - AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->suppressed); - - return s; -} - -void avahi_response_scheduler_free(AvahiResponseScheduler *s) { - assert(s); - - avahi_response_scheduler_clear(s); - avahi_free(s); -} - -void avahi_response_scheduler_clear(AvahiResponseScheduler *s) { - assert(s); - - while (s->jobs) - job_free(s, s->jobs); - while (s->history) - job_free(s, s->history); - while (s->suppressed) - job_free(s, s->suppressed); -} - -static void enumerate_aux_records_callback(AVAHI_GCC_UNUSED AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { - AvahiResponseJob *rj = userdata; - - assert(r); - assert(rj); - - avahi_response_scheduler_post(rj->scheduler, r, flush_cache, rj->querier_valid ? &rj->querier : NULL, 0); -} - -static int packet_add_response_job(AvahiResponseScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) { - assert(s); - assert(p); - assert(rj); - - /* Try to add this record to the packet */ - if (!avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0)) - return 0; - - /* Ok, this record will definitely be sent, so schedule the - * auxilliary packets, too */ - avahi_server_enumerate_aux_records(s->interface->monitor->server, s->interface, rj->record, enumerate_aux_records_callback, rj); - job_mark_done(s, rj); - - return 1; -} - -static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) { - AvahiDnsPacket *p; - unsigned n; - - assert(s); - assert(rj); - - if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1))) - return; /* OOM */ - n = 1; - - /* Put it in the packet. */ - if (packet_add_response_job(s, p, rj)) { - - /* Try to fill up packet with more responses, if available */ - while (s->jobs) { - - if (!packet_add_response_job(s, p, s->jobs)) - break; - - n++; - } - - } else { - size_t size; - - avahi_dns_packet_free(p); - - /* OK, the packet was too small, so create one that fits */ - size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE; - - if (!(p = avahi_dns_packet_new_response(size + AVAHI_DNS_PACKET_EXTRA_SIZE, 1))) - return; /* OOM */ - - if (!packet_add_response_job(s, p, rj)) { - avahi_dns_packet_free(p); - - avahi_log_warn("Record too large, cannot send"); - job_mark_done(s, rj); - return; - } - } - - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); - avahi_interface_send_packet(s->interface, p); - avahi_dns_packet_free(p); -} - -static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { - AvahiResponseJob *rj = data; - - assert(rj); - - if (rj->state == AVAHI_DONE || rj->state == AVAHI_SUPPRESSED) - job_free(rj->scheduler, rj); /* Lets drop this entry */ - else - send_response_packet(rj->scheduler, rj); -} - -static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) { - AvahiResponseJob *rj; - - assert(s); - assert(record); - - for (rj = s->jobs; rj; rj = rj->jobs_next) { - assert(rj->state == AVAHI_SCHEDULED); - - if (avahi_record_equal_no_ttl(rj->record, record)) - return rj; - } - - return NULL; -} - -static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) { - AvahiResponseJob *rj; - - assert(s); - assert(record); - - for (rj = s->history; rj; rj = rj->jobs_next) { - assert(rj->state == AVAHI_DONE); - - if (avahi_record_equal_no_ttl(rj->record, record)) { - /* Check whether this entry is outdated */ - -/* avahi_log_debug("history age: %u", (unsigned) (avahi_age(&rj->delivery)/1000)); */ - - if (avahi_age(&rj->delivery)/1000 > AVAHI_RESPONSE_HISTORY_MSEC) { - /* it is outdated, so let's remove it */ - job_free(s, rj); - return NULL; - } - - return rj; - } - } - - return NULL; -} - -static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { - AvahiResponseJob *rj; - - assert(s); - assert(record); - assert(querier); - - for (rj = s->suppressed; rj; rj = rj->jobs_next) { - assert(rj->state == AVAHI_SUPPRESSED); - assert(rj->querier_valid); - - if (avahi_record_equal_no_ttl(rj->record, record) && - avahi_address_cmp(&rj->querier, querier) == 0) { - /* Check whether this entry is outdated */ - - if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_SUPPRESS_MSEC*1000) { - /* it is outdated, so let's remove it */ - job_free(s, rj); - return NULL; - } - - return rj; - } - } - - return NULL; -} - -int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { - AvahiResponseJob *rj; - struct timeval tv; -/* char *t; */ - - assert(s); - assert(record); - - assert(!avahi_key_is_pattern(record->key)); - -/* t = avahi_record_to_string(record); */ -/* avahi_log_debug("post %i %s", immediately, t); */ -/* avahi_free(t); */ - - /* Check whether this response is suppressed */ - if (querier && - (rj = find_suppressed_job(s, record, querier)) && - avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && - rj->record->ttl >= record->ttl/2) { - -/* avahi_log_debug("Response suppressed by known answer suppression."); */ - return 0; - } - - /* Check if we already sent this response recently */ - if ((rj = find_history_job(s, record))) { - - if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && - rj->record->ttl >= record->ttl/2 && - (rj->flush_cache || !flush_cache)) { -/* avahi_log_debug("Response suppressed by local duplicate suppression (history)"); */ - return 0; - } - - /* Outdated ... */ - job_free(s, rj); - } - - avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC); - - if ((rj = find_scheduled_job(s, record))) { -/* avahi_log_debug("Response suppressed by local duplicate suppression (scheduled)"); */ - - /* Update a little ... */ - - /* Update the time if the new is prior to the old */ - if (avahi_timeval_compare(&tv, &rj->delivery) < 0) { - rj->delivery = tv; - avahi_time_event_update(rj->time_event, &rj->delivery); - } - - /* Update the flush cache bit */ - if (flush_cache) - rj->flush_cache = 1; - - /* Update the querier field */ - if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0)) - rj->querier_valid = 0; - - /* Update record data (just for the TTL) */ - avahi_record_unref(rj->record); - rj->record = avahi_record_ref(record); - - return 1; - } else { -/* avahi_log_debug("Accepted new response job."); */ - - /* Create a new job and schedule it */ - if (!(rj = job_new(s, record, AVAHI_SCHEDULED))) - return 0; /* OOM */ - - rj->delivery = tv; - rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj); - rj->flush_cache = flush_cache; - - if ((rj->querier_valid = !!querier)) - rj->querier = *querier; - - return 1; - } -} - -void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) { - AvahiResponseJob *rj; - assert(s); - - /* This function is called whenever an incoming response was - * receieved. We drop scheduled responses which match here. The - * keyword is "DUPLICATE ANSWER SUPPRESION". */ - - if ((rj = find_scheduled_job(s, record))) { - - if ((!rj->flush_cache || flush_cache) && /* flush cache bit was set correctly */ - avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */ - record->ttl >= rj->record->ttl/2) { /* sensible TTL */ - - /* A matching entry was found, so let's mark it done */ -/* avahi_log_debug("Response suppressed by distributed duplicate suppression"); */ - job_mark_done(s, rj); - } - - return; - } - - if ((rj = find_history_job(s, record))) { - /* Found a history job, let's update it */ - avahi_record_unref(rj->record); - rj->record = avahi_record_ref(record); - } else - /* Found no existing history job, so let's create a new one */ - if (!(rj = job_new(s, record, AVAHI_DONE))) - return; /* OOM */ - - rj->flush_cache = flush_cache; - rj->querier_valid = 0; - - gettimeofday(&rj->delivery, NULL); - job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); -} - -void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { - AvahiResponseJob *rj; - - assert(s); - assert(record); - assert(querier); - - if ((rj = find_scheduled_job(s, record))) { - - if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */ - avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */ - record->ttl >= rj->record->ttl/2) { /* sensible TTL */ - - /* A matching entry was found, so let's drop it */ -/* avahi_log_debug("Known answer suppression active!"); */ - job_free(s, rj); - } - } - - if ((rj = find_suppressed_job(s, record, querier))) { - - /* Let's update the old entry */ - avahi_record_unref(rj->record); - rj->record = avahi_record_ref(record); - - } else { - - /* Create a new entry */ - if (!(rj = job_new(s, record, AVAHI_SUPPRESSED))) - return; /* OOM */ - rj->querier_valid = 1; - rj->querier = *querier; - } - - gettimeofday(&rj->delivery, NULL); - job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0); -} - -void avahi_response_scheduler_force(AvahiResponseScheduler *s) { - assert(s); - - /* Send all scheduled responses immediately */ - while (s->jobs) - send_response_packet(s, s->jobs); -} diff --git a/avahi-core/response-sched.h b/avahi-core/response-sched.h deleted file mode 100644 index 548839e..0000000 --- a/avahi-core/response-sched.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef fooresponseschedhfoo -#define fooresponseschedhfoo - -/*** - 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 struct AvahiResponseScheduler AvahiResponseScheduler; - -#include -#include "iface.h" - -AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i); -void avahi_response_scheduler_free(AvahiResponseScheduler *s); -void avahi_response_scheduler_clear(AvahiResponseScheduler *s); -void avahi_response_scheduler_force(AvahiResponseScheduler *s); - -int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); -void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache); -void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier); - -#endif diff --git a/avahi-core/rr-util.h b/avahi-core/rr-util.h deleted file mode 100644 index 0eebc00..0000000 --- a/avahi-core/rr-util.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef foorrutilhfoo -#define foorrutilhfoo - -/*** - 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 "rr.h" - -AVAHI_C_DECL_BEGIN - -/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */ -AvahiKey *avahi_key_new_cname(AvahiKey *key); - -/** Match a key to a key pattern. The pattern has a type of -AVAHI_DNS_CLASS_ANY, the classes are taken to be equal. Same for the -type. If the pattern has neither class nor type with ANY constants, -this function is identical to avahi_key_equal(). In contrast to -avahi_equal() this function is not commutative. */ -int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); - -/** Check whether a key is a pattern key, i.e. the class/type has a - * value of AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY */ -int avahi_key_is_pattern(const AvahiKey *k); - -/** Returns a maximum estimate for the space that is needed to store - * this key in a DNS packet. */ -size_t avahi_key_get_estimate_size(AvahiKey *k); - -/** Returns a maximum estimate for the space that is needed to store - * the record in a DNS packet. */ -size_t avahi_record_get_estimate_size(AvahiRecord *r); - -/** Do a mDNS spec conforming lexicographical comparison of the two - * records. Return a negative value if a < b, a positive if a > b, - * zero if equal. */ -int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); - -/** Return 1 if the specified record is an mDNS goodbye record. i.e. TTL is zero. */ -int avahi_record_is_goodbye(AvahiRecord *r); - -/** Make a deep copy of an AvahiRecord object */ -AvahiRecord *avahi_record_copy(AvahiRecord *r); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/rr.c b/avahi-core/rr.c deleted file mode 100644 index 7fa0bee..0000000 --- a/avahi-core/rr.c +++ /dev/null @@ -1,733 +0,0 @@ -/*** - 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 "rr.h" -#include "log.h" -#include "util.h" -#include "hashmap.h" -#include "domain-util.h" -#include "rr-util.h" -#include "addr-util.h" - -AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) { - AvahiKey *k; - assert(name); - - if (!(k = avahi_new(AvahiKey, 1))) { - avahi_log_error("avahi_new() failed."); - return NULL; - } - - if (!(k->name = avahi_normalize_name_strdup(name))) { - avahi_log_error("avahi_normalize_name() failed."); - avahi_free(k); - return NULL; - } - - k->ref = 1; - k->clazz = class; - k->type = type; - - return k; -} - -AvahiKey *avahi_key_new_cname(AvahiKey *key) { - assert(key); - - if (key->clazz != AVAHI_DNS_CLASS_IN) - return NULL; - - if (key->type == AVAHI_DNS_TYPE_CNAME) - return NULL; - - return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME); -} - -AvahiKey *avahi_key_ref(AvahiKey *k) { - assert(k); - assert(k->ref >= 1); - - k->ref++; - - return k; -} - -void avahi_key_unref(AvahiKey *k) { - assert(k); - assert(k->ref >= 1); - - if ((--k->ref) <= 0) { - avahi_free(k->name); - avahi_free(k); - } -} - -AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) { - AvahiRecord *r; - - assert(k); - - if (!(r = avahi_new(AvahiRecord, 1))) { - avahi_log_error("avahi_new() failed."); - return NULL; - } - - r->ref = 1; - r->key = avahi_key_ref(k); - - memset(&r->data, 0, sizeof(r->data)); - - r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL; - - return r; -} - -AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) { - AvahiRecord *r; - AvahiKey *k; - - assert(name); - - if (!(k = avahi_key_new(name, class, type))) { - avahi_log_error("avahi_key_new() failed."); - return NULL; - } - - r = avahi_record_new(k, ttl); - avahi_key_unref(k); - - if (!r) { - avahi_log_error("avahi_record_new() failed."); - return NULL; - } - - return r; -} - -AvahiRecord *avahi_record_ref(AvahiRecord *r) { - assert(r); - assert(r->ref >= 1); - - r->ref++; - return r; -} - -void avahi_record_unref(AvahiRecord *r) { - assert(r); - assert(r->ref >= 1); - - if ((--r->ref) <= 0) { - switch (r->key->type) { - - case AVAHI_DNS_TYPE_SRV: - avahi_free(r->data.srv.name); - break; - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - avahi_free(r->data.ptr.name); - break; - - case AVAHI_DNS_TYPE_HINFO: - avahi_free(r->data.hinfo.cpu); - avahi_free(r->data.hinfo.os); - break; - - case AVAHI_DNS_TYPE_TXT: - avahi_string_list_free(r->data.txt.string_list); - break; - - case AVAHI_DNS_TYPE_A: - case AVAHI_DNS_TYPE_AAAA: - break; - - default: - avahi_free(r->data.generic.data); - } - - avahi_key_unref(r->key); - avahi_free(r); - } -} - -const char *avahi_dns_class_to_string(uint16_t class) { - if (class & AVAHI_DNS_CACHE_FLUSH) - return "FLUSH"; - - switch (class) { - case AVAHI_DNS_CLASS_IN: - return "IN"; - case AVAHI_DNS_CLASS_ANY: - return "ANY"; - default: - return NULL; - } -} - -const char *avahi_dns_type_to_string(uint16_t type) { - switch (type) { - case AVAHI_DNS_TYPE_CNAME: - return "CNAME"; - case AVAHI_DNS_TYPE_A: - return "A"; - case AVAHI_DNS_TYPE_AAAA: - return "AAAA"; - case AVAHI_DNS_TYPE_PTR: - return "PTR"; - case AVAHI_DNS_TYPE_HINFO: - return "HINFO"; - case AVAHI_DNS_TYPE_TXT: - return "TXT"; - case AVAHI_DNS_TYPE_SRV: - return "SRV"; - case AVAHI_DNS_TYPE_ANY: - return "ANY"; - case AVAHI_DNS_TYPE_SOA: - return "SOA"; - case AVAHI_DNS_TYPE_NS: - return "NS"; - default: - return NULL; - } -} - -char *avahi_key_to_string(const AvahiKey *k) { - char class[16], type[16]; - const char *c, *t; - - assert(k); - assert(k->ref >= 1); - - /* According to RFC3597 */ - - if (!(c = avahi_dns_class_to_string(k->clazz))) { - snprintf(class, sizeof(class), "CLASS%u", k->clazz); - c = class; - } - - if (!(t = avahi_dns_type_to_string(k->type))) { - snprintf(type, sizeof(type), "TYPE%u", k->type); - t = type; - } - - return avahi_strdup_printf("%s\t%s\t%s", k->name, c, t); -} - -char *avahi_record_to_string(const AvahiRecord *r) { - char *p, *s; - char buf[1024], *t = NULL, *d = NULL; - - assert(r); - assert(r->ref >= 1); - - switch (r->key->type) { - case AVAHI_DNS_TYPE_A: - inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); - break; - - case AVAHI_DNS_TYPE_AAAA: - inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); - break; - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - - t = r->data.ptr.name; - break; - - case AVAHI_DNS_TYPE_TXT: - t = d = avahi_string_list_to_string(r->data.txt.string_list); - break; - - case AVAHI_DNS_TYPE_HINFO: - - snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); - break; - - case AVAHI_DNS_TYPE_SRV: - - snprintf(t = buf, sizeof(buf), "%u %u %u %s", - r->data.srv.priority, - r->data.srv.weight, - r->data.srv.port, - r->data.srv.name); - - break; - - default: { - - uint8_t *c; - uint16_t n; - int i; - char *e; - - /* According to RFC3597 */ - - snprintf(t = buf, sizeof(buf), "\\# %u", r->data.generic.size); - - e = strchr(t, 0); - - for (c = r->data.generic.data, n = r->data.generic.size, i = 0; - n > 0 && i < 20; - c ++, n --, i++) { - - sprintf(e, " %02X", *c); - e = strchr(e, 0); - } - - break; - } - } - - p = avahi_key_to_string(r->key); - s = avahi_strdup_printf("%s %s ; ttl=%u", p, t, r->ttl); - avahi_free(p); - avahi_free(d); - - return s; -} - -int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { - assert(a); - assert(b); - - if (a == b) - return 1; - - return avahi_domain_equal(a->name, b->name) && - a->type == b->type && - a->clazz == b->clazz; -} - -int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { - assert(pattern); - assert(k); - - assert(!avahi_key_is_pattern(k)); - - if (pattern == k) - return 1; - - return avahi_domain_equal(pattern->name, k->name) && - (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && - (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY); -} - -int avahi_key_is_pattern(const AvahiKey *k) { - assert(k); - - return - k->type == AVAHI_DNS_TYPE_ANY || - k->clazz == AVAHI_DNS_CLASS_ANY; -} - -unsigned avahi_key_hash(const AvahiKey *k) { - assert(k); - - return - avahi_domain_hash(k->name) + - k->type + - k->clazz; -} - -static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { - assert(a); - assert(b); - assert(a->key->type == b->key->type); - - switch (a->key->type) { - case AVAHI_DNS_TYPE_SRV: - return - a->data.srv.priority == b->data.srv.priority && - a->data.srv.weight == b->data.srv.weight && - a->data.srv.port == b->data.srv.port && - avahi_domain_equal(a->data.srv.name, b->data.srv.name); - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); - - case AVAHI_DNS_TYPE_HINFO: - return - !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) && - !strcmp(a->data.hinfo.os, b->data.hinfo.os); - - case AVAHI_DNS_TYPE_TXT: - return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); - - case AVAHI_DNS_TYPE_A: - return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; - - case AVAHI_DNS_TYPE_AAAA: - return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; - - default: - return a->data.generic.size == b->data.generic.size && - (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); - } - -} - -int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { - assert(a); - assert(b); - - if (a == b) - return 1; - - return - avahi_key_equal(a->key, b->key) && - rdata_equal(a, b); -} - - -AvahiRecord *avahi_record_copy(AvahiRecord *r) { - AvahiRecord *copy; - - if (!(copy = avahi_new(AvahiRecord, 1))) { - avahi_log_error("avahi_new() failed."); - return NULL; - } - - copy->ref = 1; - copy->key = avahi_key_ref(r->key); - copy->ttl = r->ttl; - - switch (r->key->type) { - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name))) - goto fail; - break; - - case AVAHI_DNS_TYPE_SRV: - copy->data.srv.priority = r->data.srv.priority; - copy->data.srv.weight = r->data.srv.weight; - copy->data.srv.port = r->data.srv.port; - if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name))) - goto fail; - break; - - case AVAHI_DNS_TYPE_HINFO: - if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os))) - goto fail; - - if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) { - avahi_free(r->data.hinfo.os); - goto fail; - } - break; - - case AVAHI_DNS_TYPE_TXT: - copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); - break; - - case AVAHI_DNS_TYPE_A: - copy->data.a.address = r->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - copy->data.aaaa.address = r->data.aaaa.address; - break; - - default: - if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size))) - goto fail; - copy->data.generic.size = r->data.generic.size; - break; - - } - - return copy; - -fail: - avahi_log_error("Failed to allocate memory"); - - avahi_key_unref(copy->key); - avahi_free(copy); - - return NULL; -} - - -size_t avahi_key_get_estimate_size(AvahiKey *k) { - assert(k); - - return strlen(k->name)+1+4; -} - -size_t avahi_record_get_estimate_size(AvahiRecord *r) { - size_t n; - assert(r); - - n = avahi_key_get_estimate_size(r->key) + 4 + 2; - - switch (r->key->type) { - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - n += strlen(r->data.ptr.name) + 1; - break; - - case AVAHI_DNS_TYPE_SRV: - n += 6 + strlen(r->data.srv.name) + 1; - break; - - case AVAHI_DNS_TYPE_HINFO: - n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; - break; - - case AVAHI_DNS_TYPE_TXT: - n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); - break; - - case AVAHI_DNS_TYPE_A: - n += sizeof(AvahiIPv4Address); - break; - - case AVAHI_DNS_TYPE_AAAA: - n += sizeof(AvahiIPv6Address); - break; - - default: - n += r->data.generic.size; - } - - return n; -} - -static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) { - size_t c; - int ret; - - assert(a); - assert(b); - - c = al < bl ? al : bl; - if ((ret = memcmp(a, b, c))) - return ret; - - if (al == bl) - return 0; - else - return al == c ? 1 : -1; -} - -static int uint16_cmp(uint16_t a, uint16_t b) { - return a == b ? 0 : (a < b ? -1 : 1); -} - -int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { - int r; -/* char *t1, *t2; */ - - assert(a); - assert(b); - -/* t1 = avahi_record_to_string(a); */ -/* t2 = avahi_record_to_string(b); */ -/* g_message("lexicocmp: %s %s", t1, t2); */ -/* avahi_free(t1); */ -/* avahi_free(t2); */ - - if (a == b) - return 0; - - if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) || - (r = uint16_cmp(a->key->type, b->key->type))) - return r; - - switch (a->key->type) { - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name); - - case AVAHI_DNS_TYPE_SRV: { - if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && - (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && - (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) - r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name); - - return r; - } - - case AVAHI_DNS_TYPE_HINFO: { - - if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) || - (r = strcmp(a->data.hinfo.os, b->data.hinfo.os))) - return r; - - return 0; - - } - - case AVAHI_DNS_TYPE_TXT: { - - uint8_t *ma = NULL, *mb = NULL; - size_t asize, bsize; - - asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0); - bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0); - - if (asize > 0 && !(ma = avahi_new(uint8_t, asize))) - goto fail; - - if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) { - avahi_free(ma); - goto fail; - } - - avahi_string_list_serialize(a->data.txt.string_list, ma, asize); - avahi_string_list_serialize(b->data.txt.string_list, mb, bsize); - - if (asize && bsize) - r = lexicographical_memcmp(ma, asize, mb, bsize); - else if (asize && !bsize) - r = 1; - else if (!asize && bsize) - r = -1; - else - r = 0; - - avahi_free(ma); - avahi_free(mb); - - return r; - } - - case AVAHI_DNS_TYPE_A: - return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); - - case AVAHI_DNS_TYPE_AAAA: - return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); - - default: - return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, - b->data.generic.data, b->data.generic.size); - } - - -fail: - avahi_log_error(__FILE__": Out of memory"); - return -1; /* or whatever ... */ -} - -int avahi_record_is_goodbye(AvahiRecord *r) { - assert(r); - - return r->ttl == 0; -} - -int avahi_key_is_valid(AvahiKey *k) { - assert(k); - - if (!avahi_is_valid_domain_name(k->name)) - return 0; - - return 1; -} - -int avahi_record_is_valid(AvahiRecord *r) { - assert(r); - - if (!avahi_key_is_valid(r->key)) - return 0; - - switch (r->key->type) { - - case AVAHI_DNS_TYPE_PTR: - case AVAHI_DNS_TYPE_CNAME: - case AVAHI_DNS_TYPE_NS: - return avahi_is_valid_domain_name(r->data.ptr.name); - - case AVAHI_DNS_TYPE_SRV: - return avahi_is_valid_domain_name(r->data.srv.name); - - case AVAHI_DNS_TYPE_HINFO: - return - strlen(r->data.hinfo.os) <= 255 && - strlen(r->data.hinfo.cpu) <= 255; - - case AVAHI_DNS_TYPE_TXT: { - - AvahiStringList *strlst; - - for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next) - if (strlst->size > 255 || strlst->size <= 0) - return 0; - - return 1; - } - } - - return 1; -} - -static AvahiAddress *get_address(const AvahiRecord *r, AvahiAddress *a) { - assert(r); - - switch (r->key->type) { - case AVAHI_DNS_TYPE_A: - a->proto = AVAHI_PROTO_INET; - a->data.ipv4 = r->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - a->proto = AVAHI_PROTO_INET6; - a->data.ipv6 = r->data.aaaa.address; - break; - - default: - return NULL; - } - - return a; -} - -int avahi_record_is_link_local_address(const AvahiRecord *r) { - AvahiAddress a; - - assert(r); - - if (!get_address(r, &a)) - return 0; - - return avahi_address_is_link_local(&a); -} diff --git a/avahi-core/rr.h b/avahi-core/rr.h deleted file mode 100644 index 794b83f..0000000 --- a/avahi-core/rr.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef foorrhfoo -#define foorrhfoo - -/*** - 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. -***/ - -/** \file rr.h Functions and definitions for manipulating DNS resource record (RR) data. */ - -#include -#include - -#include -#include -#include - -AVAHI_C_DECL_BEGIN - -/** DNS record types, see RFC 1035, in addition to those defined in defs.h */ -enum { - AVAHI_DNS_TYPE_ANY = 0xFF, /**< Special query type for requesting all records */ - AVAHI_DNS_TYPE_OPT = 41, /**< EDNS0 option */ - AVAHI_DNS_TYPE_TKEY = 249, - AVAHI_DNS_TYPE_TSIG = 250, - AVAHI_DNS_TYPE_IXFR = 251, - AVAHI_DNS_TYPE_AXFR = 252 -}; - -/** DNS record classes, see RFC 1035, in addition to those defined in defs.h */ -enum { - AVAHI_DNS_CLASS_ANY = 0xFF, /**< Special query type for requesting all records */ - AVAHI_DNS_CACHE_FLUSH = 0x8000, /**< Not really a class but a bit which may be set in response packets, see mDNS spec for more information */ - AVAHI_DNS_UNICAST_RESPONSE = 0x8000 /**< Not really a class but a bit which may be set in query packets, see mDNS spec for more information */ -}; - -/** Encapsulates a DNS query key consisting of class, type and - name. Use avahi_key_ref()/avahi_key_unref() for manipulating the - reference counter. The structure is intended to be treated as "immutable", no - changes should be imposed after creation */ -typedef struct AvahiKey { - int ref; /**< Reference counter */ - char *name; /**< Record name */ - uint16_t clazz; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */ - uint16_t type; /**< Record type, one of the AVAHI_DNS_TYPE_xxx constants */ -} AvahiKey; - -/** Encapsulates a DNS resource record. The structure is intended to - * be treated as "immutable", no changes should be imposed after - * creation. */ -typedef struct AvahiRecord { - int ref; /**< Reference counter */ - AvahiKey *key; /**< Reference to the query key of this record */ - - uint32_t ttl; /**< DNS TTL of this record */ - - union { - - struct { - void* data; - uint16_t size; - } generic; /**< Generic record data for unknown types */ - - struct { - uint16_t priority; - uint16_t weight; - uint16_t port; - char *name; - } srv; /**< Data for SRV records */ - - struct { - char *name; - } ptr, ns, cname; /**< Data for PTR, NS and CNAME records */ - - struct { - char *cpu; - char *os; - } hinfo; /**< Data for HINFO records */ - - struct { - AvahiStringList *string_list; - } txt; /**< Data for TXT records */ - - struct { - AvahiIPv4Address address; - } a; /**< Data for A records */ - - struct { - AvahiIPv6Address address; - } aaaa; /**< Data for AAAA records */ - - } data; /**< Record data */ - -} AvahiRecord; - -/** Create a new AvahiKey object. The reference counter will be set to 1. */ -AvahiKey *avahi_key_new(const char *name, uint16_t clazz, uint16_t type); - -/** Increase the reference counter of an AvahiKey object by one */ -AvahiKey *avahi_key_ref(AvahiKey *k); - -/** Decrease the reference counter of an AvahiKey object by one */ -void avahi_key_unref(AvahiKey *k); - -/** Check whether two AvahiKey object contain the same - * data. AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY are treated like any - * other class/type. */ -int avahi_key_equal(const AvahiKey *a, const AvahiKey *b); - -/** Return a numeric hash value for a key for usage in hash tables. */ -unsigned avahi_key_hash(const AvahiKey *k); - -/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ -AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl); - -/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ -AvahiRecord *avahi_record_new_full(const char *name, uint16_t clazz, uint16_t type, uint32_t ttl); - -/** Increase the reference counter of an AvahiRecord by one. */ -AvahiRecord *avahi_record_ref(AvahiRecord *r); - -/** Decrease the reference counter of an AvahiRecord by one. */ -void avahi_record_unref(AvahiRecord *r); - -/** Return a textual representation of the specified DNS class. The - * returned pointer points to a read only internal string. */ -const char *avahi_dns_class_to_string(uint16_t clazz); - -/** Return a textual representation of the specified DNS class. The - * returned pointer points to a read only internal string. */ -const char *avahi_dns_type_to_string(uint16_t type); - -/** Create a textual representation of the specified key. avahi_free() the - * result! */ -char *avahi_key_to_string(const AvahiKey *k); - -/** Create a textual representation of the specified record, similar - * in style to BIND zone file data. avahi_free() the result! */ -char *avahi_record_to_string(const AvahiRecord *r); - -/** Check whether two records are equal (regardless of the TTL */ -int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b); - -/** Check whether the specified key is valid */ -int avahi_key_is_valid(AvahiKey *k); - -/** Check whether the specified record is valid */ -int avahi_record_is_valid(AvahiRecord *r); - -/** Parse a binary rdata object and fill it into *record. This function is actually implemented in dns.c */ -int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size); - -/** Serialize an AvahiRecord object into binary rdata. This function is actually implemented in dns.c */ -size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size); - -/** Return TRUE if the AvahiRecord object is a link-local A or AAAA address */ -int avahi_record_is_link_local_address(const AvahiRecord *r); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/rrlist.c b/avahi-core/rrlist.c deleted file mode 100644 index 0a794ab..0000000 --- a/avahi-core/rrlist.c +++ /dev/null @@ -1,188 +0,0 @@ -/*** - 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 "rrlist.h" -#include "log.h" - -typedef struct AvahiRecordListItem AvahiRecordListItem; - -struct AvahiRecordListItem { - int read; - AvahiRecord *record; - int unicast_response; - int flush_cache; - int auxiliary; - AVAHI_LLIST_FIELDS(AvahiRecordListItem, items); -}; - -struct AvahiRecordList { - AVAHI_LLIST_HEAD(AvahiRecordListItem, read); - AVAHI_LLIST_HEAD(AvahiRecordListItem, unread); - - int all_flush_cache; -}; - -AvahiRecordList *avahi_record_list_new(void) { - AvahiRecordList *l; - - if (!(l = avahi_new(AvahiRecordList, 1))) { - avahi_log_error("avahi_new() failed."); - return NULL; - } - - AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read); - AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread); - - l->all_flush_cache = 1; - return l; -} - -void avahi_record_list_free(AvahiRecordList *l) { - assert(l); - - avahi_record_list_flush(l); - avahi_free(l); -} - -static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) { - assert(l); - assert(i); - - if (i->read) - AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->read, i); - else - AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); - - avahi_record_unref(i->record); - avahi_free(i); -} - -void avahi_record_list_flush(AvahiRecordList *l) { - assert(l); - - while (l->read) - item_free(l, l->read); - while (l->unread) - item_free(l, l->unread); - - l->all_flush_cache = 1; -} - -AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) { - AvahiRecord *r; - AvahiRecordListItem *i; - - if (!(i = l->unread)) - return NULL; - - assert(!i->read); - - r = avahi_record_ref(i->record); - if (ret_unicast_response) - *ret_unicast_response = i->unicast_response; - if (ret_flush_cache) - *ret_flush_cache = i->flush_cache; - if (ret_auxiliary) - *ret_auxiliary = i->auxiliary; - - AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); - AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i); - - i->read = 1; - - return r; -} - -static AvahiRecordListItem *get(AvahiRecordList *l, AvahiRecord *r) { - AvahiRecordListItem *i; - - assert(l); - assert(r); - - for (i = l->read; i; i = i->items_next) - if (avahi_record_equal_no_ttl(i->record, r)) - return i; - - for (i = l->unread; i; i = i->items_next) - if (avahi_record_equal_no_ttl(i->record, r)) - return i; - - return NULL; -} - -void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary) { - AvahiRecordListItem *i; - - assert(l); - assert(r); - - if (get(l, r)) - return; - - if (!(i = avahi_new(AvahiRecordListItem, 1))) { - avahi_log_error("avahi_new() failed."); - return; - } - - i->unicast_response = unicast_response; - i->flush_cache = flush_cache; - i->auxiliary = auxiliary; - i->record = avahi_record_ref(r); - i->read = 0; - - l->all_flush_cache = l->all_flush_cache && flush_cache; - - AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i); -} - -void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) { - AvahiRecordListItem *i; - - assert(l); - assert(r); - - if (!(i = get(l, r))) - return; - - item_free(l, i); -} - -int avahi_record_list_is_empty(AvahiRecordList *l) { - assert(l); - - return !l->unread && !l->read; -} - -int avahi_record_list_all_flush_cache(AvahiRecordList *l) { - assert(l); - - /* Return TRUE if all entries in this list have flush_cache set */ - - return l->all_flush_cache; -} diff --git a/avahi-core/rrlist.h b/avahi-core/rrlist.h deleted file mode 100644 index c7b5aba..0000000 --- a/avahi-core/rrlist.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef foorrlisthfoo -#define foorrlisthfoo - -/*** - 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 "rr.h" - -typedef struct AvahiRecordList AvahiRecordList; - -AvahiRecordList *avahi_record_list_new(void); -void avahi_record_list_free(AvahiRecordList *l); -void avahi_record_list_flush(AvahiRecordList *l); - -AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary); -void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary); -void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r); - -int avahi_record_list_all_flush_cache(AvahiRecordList *l); - -int avahi_record_list_is_empty(AvahiRecordList *l); - -#endif diff --git a/avahi-core/server.c b/avahi-core/server.c deleted file mode 100644 index b348269..0000000 --- a/avahi-core/server.c +++ /dev/null @@ -1,1806 +0,0 @@ -/*** - 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 "internal.h" -#include "iface.h" -#include "socket.h" -#include "browse.h" -#include "log.h" -#include "util.h" -#include "dns-srv-rr.h" -#include "addr-util.h" -#include "domain-util.h" -#include "rr-util.h" - -#define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096 - -static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { - assert(s); - assert(i); - assert(name); - assert(callback); - - if (type == AVAHI_DNS_TYPE_ANY) { - AvahiEntry *e; - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead && - avahi_entry_is_registered(s, e, i) && - e->record->key->clazz == AVAHI_DNS_CLASS_IN && - avahi_domain_equal(name, e->record->key->name)) - callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); - - } else { - AvahiEntry *e; - AvahiKey *k; - - if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type))) - return; /** OOM */ - - for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next) - if (!e->dead && avahi_entry_is_registered(s, e, i)) - callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); - - avahi_key_unref(k); - } -} - -void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { - assert(s); - assert(i); - assert(r); - assert(callback); - - /* Call the specified callback far all records referenced by the one specified in *r */ - - if (r->key->clazz == AVAHI_DNS_CLASS_IN) { - if (r->key->type == AVAHI_DNS_TYPE_PTR) { - enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata); - enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata); - } else if (r->key->type == AVAHI_DNS_TYPE_SRV) { - enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata); - enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata); - } else if (r->key->type == AVAHI_DNS_TYPE_CNAME) - enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata); - } -} - -void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) { - assert(s); - assert(i); - assert(e); - - avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary); -} - -void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) { - assert(s); - assert(i); - assert(k); - - /* Push all records that match the specified key to the record list */ - - if (avahi_key_is_pattern(k)) { - AvahiEntry *e; - - /* Handle ANY query */ - - for (e = s->entries; e; e = e->entries_next) - if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i)) - avahi_server_prepare_response(s, i, e, unicast_response, 0); - - } else { - AvahiEntry *e; - - /* Handle all other queries */ - - for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next) - if (!e->dead && avahi_entry_is_registered(s, e, i)) - avahi_server_prepare_response(s, i, e, unicast_response, 0); - } - - /* Look for CNAME records */ - - if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY) - && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) { - - AvahiKey *cname_key; - - if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME))) - return; - - avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response); - avahi_key_unref(cname_key); - } -} - -static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { - assert(s); - assert(e); - - /* Withdraw the specified entry, and if is part of an entry group, - * put that into COLLISION state */ - - if (e->dead) - return; - - if (e->group) { - AvahiEntry *k; - - for (k = e->group->entries; k; k = k->by_group_next) - if (!k->dead) { - avahi_goodbye_entry(s, k, 0, 1); - k->dead = 1; - } - - e->group->n_probing = 0; - - avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION); - } else { - avahi_goodbye_entry(s, e, 0, 1); - e->dead = 1; - } - - s->need_entry_cleanup = 1; -} - -static void withdraw_rrset(AvahiServer *s, AvahiKey *key) { - AvahiEntry *e; - - assert(s); - assert(key); - - /* Withdraw an entry RRSset */ - - for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) - withdraw_entry(s, e); -} - -static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { - AvahiEntry *e, *n; - int ours = 0, won = 0, lost = 0; - - assert(s); - assert(record); - assert(i); - - /* Handle incoming probes and check if they conflict our own probes */ - - for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) { - int cmp; - n = e->by_key_next; - - if (e->dead) - continue; - - if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) { - ours = 1; - break; - } else { - - if (avahi_entry_is_probing(s, e, i)) { - if (cmp > 0) - won = 1; - else /* cmp < 0 */ - lost = 1; - } - } - } - - if (!ours) { - char *t = avahi_record_to_string(record); - - if (won) - avahi_log_debug("Received conflicting probe [%s]. Local host won.", t); - else if (lost) { - avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t); - withdraw_rrset(s, record->key); - } - - avahi_free(t); - } -} - -static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) { - int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0; - AvahiEntry *e, *n, *conflicting_entry = NULL; - - assert(s); - assert(i); - assert(record); - - /* Check whether an incoming record conflicts with one of our own */ - - for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) { - n = e->by_key_next; - - if (e->dead) - continue; - - /* Check if the incoming is a goodbye record */ - if (avahi_record_is_goodbye(record)) { - - if (avahi_record_equal_no_ttl(e->record, record)) { - char *t; - - /* Refresh */ - t = avahi_record_to_string(record); - avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t); - avahi_server_prepare_matching_responses(s, i, e->record->key, 0); - - valid = 0; - avahi_free(t); - break; - } - - /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */ - continue; - } - - if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique) - continue; - - /* Either our entry or the other is intended to be unique, so let's check */ - - if (avahi_record_equal_no_ttl(e->record, record)) { - ours = 1; /* We have an identical record, so this is no conflict */ - - /* Check wheter there is a TTL conflict */ - if (record->ttl <= e->record->ttl/2 && - avahi_entry_is_registered(s, e, i)) { - char *t; - /* Refresh */ - t = avahi_record_to_string(record); - - avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t); - avahi_server_prepare_matching_responses(s, i, e->record->key, 0); - valid = 0; - - avahi_free(t); - } - - /* There's no need to check the other entries of this RRset */ - break; - - } else { - - if (avahi_entry_is_registered(s, e, i)) { - - /* A conflict => we have to return to probe mode */ - conflict = 1; - conflicting_entry = e; - - } else if (avahi_entry_is_probing(s, e, i)) { - - /* We are currently registering a matching record, but - * someone else already claimed it, so let's - * withdraw */ - conflict = 1; - withdraw_immediately = 1; - } - } - } - - if (!ours && conflict) { - char *t; - - valid = 0; - - t = avahi_record_to_string(record); - - if (withdraw_immediately) { - avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t); - withdraw_rrset(s, record->key); - } else { - assert(conflicting_entry); - avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t); - avahi_entry_return_to_initial_state(s, conflicting_entry, i); - - /* Local unique records are returned to probing - * state. Local shared records are reannounced. */ - } - - avahi_free(t); - } - - return valid; -} - -static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { - int *unicast_response = userdata; - - assert(s); - assert(r); - assert(unicast_response); - - avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1); -} - -static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) { - assert(s); - assert(r); - - avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response); -} - -void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) { - - assert(s); - assert(i); - assert(!legacy_unicast || (a && port > 0 && p)); - - if (legacy_unicast) { - AvahiDnsPacket *reply; - AvahiRecord *r; - - if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1))) - return; /* OOM */ - - while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) { - - append_aux_records_to_list(s, i, r, 0); - - if (avahi_dns_packet_append_record(reply, r, 0, 10)) - avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); - else { - char *t = avahi_record_to_string(r); - avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t); - avahi_free(t); - } - - avahi_record_unref(r); - } - - if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) - avahi_interface_send_packet_unicast(i, reply, a, port); - - avahi_dns_packet_free(reply); - - } else { - int unicast_response, flush_cache, auxiliary; - AvahiDnsPacket *reply = NULL; - AvahiRecord *r; - - /* In case the query packet was truncated never respond - immediately, because known answer suppression records might be - contained in later packets */ - int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC); - - while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) { - - int im = immediately; - - /* Only send the response immediately if it contains a - * unique entry AND it is not in reply to a truncated - * packet AND it is not an auxiliary record AND all other - * responses for this record are unique too. */ - - if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list)) - im = 1; - - if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) { - - /* Due to some reasons the record has not been scheduled. - * The client requested an unicast response in that - * case. Therefore we prepare such a response */ - - append_aux_records_to_list(s, i, r, unicast_response); - - for (;;) { - - if (!reply) { - assert(p); - - if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0))) - break; /* OOM */ - } - - if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { - - /* Appending this record succeeded, so incremeant - * the specific header field, and return to the caller */ - - avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); - break; - } - - if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) { - size_t size; - - /* The record is too large for one packet, so create a larger packet */ - - avahi_dns_packet_free(reply); - size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE; - - if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1))) - break; /* OOM */ - - if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { - - /* Appending this record succeeded, so incremeant - * the specific header field, and return to the caller */ - - avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); - break; - - } else { - - /* We completely fucked up, there's - * nothing we can do. The RR just doesn't - * fit in. Let's ignore it. */ - - char *t; - avahi_dns_packet_free(reply); - reply = NULL; - t = avahi_record_to_string(r); - avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t); - avahi_free(t); - break; - } - } - - /* Appending the record didn't succeeed, so let's send this packet, and create a new one */ - avahi_interface_send_packet_unicast(i, reply, a, port); - avahi_dns_packet_free(reply); - reply = NULL; - } - } - - avahi_record_unref(r); - } - - if (reply) { - if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) - avahi_interface_send_packet_unicast(i, reply, a, port); - avahi_dns_packet_free(reply); - } - } - - avahi_record_list_flush(s->record_list); -} - -static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) { - AvahiInterface *j; - - assert(s); - assert(i); - assert(r); - - if (!s->config.enable_reflector) - return; - - for (j = s->monitor->interfaces; j; j = j->interface_next) - if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) - avahi_interface_post_response(j, r, flush_cache, NULL, 1); -} - -static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { - AvahiServer *s = userdata; - AvahiRecord* r; - - assert(c); - assert(pattern); - assert(e); - assert(s); - - /* Don't reflect cache entry with ipv6 link-local addresses. */ - r = e->record; - if ((r->key->type == AVAHI_DNS_TYPE_AAAA) && - (r->data.aaaa.address.address[0] == 0xFE) && - (r->data.aaaa.address.address[1] == 0x80)) - return NULL; - - avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0); - return NULL; -} - -static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) { - AvahiInterface *j; - - assert(s); - assert(i); - assert(k); - - if (!s->config.enable_reflector) - return; - - for (j = s->monitor->interfaces; j; j = j->interface_next) - if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) { - /* Post the query to other networks */ - avahi_interface_post_query(j, k, 1, NULL); - - /* Reply from caches of other network. This is needed to - * "work around" known answer suppression. */ - - avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s); - } -} - -static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) { - AvahiInterface *j; - - assert(s); - assert(i); - assert(r); - - if (!s->config.enable_reflector) - return; - - for (j = s->monitor->interfaces; j; j = j->interface_next) - if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) - avahi_interface_post_probe(j, r, 1); -} - -static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) { - size_t n; - int is_probe; - - assert(s); - assert(p); - assert(i); - assert(a); - - assert(avahi_record_list_is_empty(s->record_list)); - - is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0; - - /* Handle the questions */ - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) { - AvahiKey *key; - int unicast_response = 0; - - if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { - avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); - goto fail; - } - - if (!legacy_unicast && !from_local_iface) { - reflect_query(s, i, key); - if (!unicast_response) - avahi_cache_start_poof(i->cache, key, a); - } - - if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 && - !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC)) - /* Allow our own queries to be suppressed by incoming - * queries only when they do not include known answers */ - avahi_query_scheduler_incoming(i->query_scheduler, key); - - avahi_server_prepare_matching_responses(s, i, key, unicast_response); - avahi_key_unref(key); - } - - if (!legacy_unicast) { - - /* Known Answer Suppression */ - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { - AvahiRecord *record; - int unique = 0; - - if (!(record = avahi_dns_packet_consume_record(p, &unique))) { - avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)"); - goto fail; - } - - avahi_response_scheduler_suppress(i->response_scheduler, record, a); - avahi_record_list_drop(s->record_list, record); - avahi_cache_stop_poof(i->cache, record, a); - - avahi_record_unref(record); - } - - /* Probe record */ - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) { - AvahiRecord *record; - int unique = 0; - - if (!(record = avahi_dns_packet_consume_record(p, &unique))) { - avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)"); - goto fail; - } - - if (!avahi_key_is_pattern(record->key)) { - if (!from_local_iface) - reflect_probe(s, i, record); - incoming_probe(s, record, i); - } - - avahi_record_unref(record); - } - } - - if (!avahi_record_list_is_empty(s->record_list)) - avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe); - - return; - -fail: - avahi_record_list_flush(s->record_list); -} - -static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) { - unsigned n; - - assert(s); - assert(p); - assert(i); - assert(a); - - for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) { - AvahiRecord *record; - int cache_flush = 0; - - if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) { - avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); - break; - } - - if (!avahi_key_is_pattern(record->key)) { - - if (handle_conflict(s, i, record, cache_flush)) { - if (!from_local_iface && !avahi_record_is_link_local_address(record)) - reflect_response(s, i, record, cache_flush); - avahi_cache_update(i->cache, record, cache_flush, a); - avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush); - } - } - - avahi_record_unref(record); - } - - /* If the incoming response contained a conflicting record, some - records have been scheduled for sending. We need to flush them - here. */ - if (!avahi_record_list_is_empty(s->record_list)) - avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1); -} - -static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) { - unsigned n, idx = (unsigned) -1; - AvahiLegacyUnicastReflectSlot *slot; - - assert(s); - - if (!s->legacy_unicast_reflect_slots) - s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX); - - for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) { - idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; - - if (!s->legacy_unicast_reflect_slots[idx]) - break; - } - - if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx]) - return NULL; - - if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1))) - return NULL; /* OOM */ - - s->legacy_unicast_reflect_slots[idx] = slot; - slot->id = s->legacy_unicast_reflect_id++; - slot->server = s; - - return slot; -} - -static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) { - unsigned idx; - - assert(s); - assert(slot); - - idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; - - assert(s->legacy_unicast_reflect_slots[idx] == slot); - - avahi_time_event_free(slot->time_event); - - avahi_free(slot); - s->legacy_unicast_reflect_slots[idx] = NULL; -} - -static void free_slots(AvahiServer *s) { - unsigned idx; - assert(s); - - if (!s->legacy_unicast_reflect_slots) - return; - - for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++) - if (s->legacy_unicast_reflect_slots[idx]) - deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]); - - avahi_free(s->legacy_unicast_reflect_slots); - s->legacy_unicast_reflect_slots = NULL; -} - -static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) { - unsigned idx; - - assert(s); - - if (!s->legacy_unicast_reflect_slots) - return NULL; - - idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; - - if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id) - return NULL; - - return s->legacy_unicast_reflect_slots[idx]; -} - -static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) { - AvahiLegacyUnicastReflectSlot *slot = userdata; - - assert(e); - assert(slot); - assert(slot->time_event == e); - - deallocate_slot(slot->server, slot); -} - -static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) { - AvahiLegacyUnicastReflectSlot *slot; - AvahiInterface *j; - - assert(s); - assert(p); - assert(i); - assert(a); - assert(port > 0); - assert(i->protocol == a->proto); - - if (!s->config.enable_reflector) - return; - - /* Reflecting legacy unicast queries is a little more complicated - than reflecting normal queries, since we must route the - responses back to the right client. Therefore we must store - some information for finding the right client contact data for - response packets. In contrast to normal queries legacy - unicast query and response packets are reflected untouched and - are not reassembled into larger packets */ - - if (!(slot = allocate_slot(s))) { - /* No slot available, we drop this legacy unicast query */ - avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet."); - return; - } - - slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID); - slot->address = *a; - slot->port = port; - slot->interface = i->hardware->index; - - avahi_elapse_time(&slot->elapse_time, 2000, 0); - slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot); - - /* Patch the packet with our new locally generatet id */ - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); - - for (j = s->monitor->interfaces; j; j = j->interface_next) - if (j->announcing && - j != i && - (s->config.reflect_ipv || j->protocol == i->protocol)) { - - if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) { - avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0); - } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) - avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0); - } - - /* Reset the id */ - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); -} - -static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) { - assert(s); - assert(address); - assert(port > 0); - - if (!s->config.enable_reflector) - return 0; - - if (!avahi_address_is_local(s->monitor, address)) - return 0; - - if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) { - struct sockaddr_in lsa; - socklen_t l = sizeof(lsa); - - if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0) - avahi_log_warn("getsockname(): %s", strerror(errno)); - else - return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; - - } - - if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) { - struct sockaddr_in6 lsa; - socklen_t l = sizeof(lsa); - - if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0) - avahi_log_warn("getsockname(): %s", strerror(errno)); - else - return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; - } - - return 0; -} - -static int is_mdns_mcast_address(const AvahiAddress *a) { - AvahiAddress b; - assert(a); - - avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b); - return avahi_address_cmp(a, &b) == 0; -} - -static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) { - assert(s); - assert(iface != AVAHI_IF_UNSPEC); - assert(a); - - /* If it isn't the MDNS port it can't be generated by us */ - if (port != AVAHI_MDNS_PORT) - return 0; - - return avahi_interface_has_address(s->monitor, iface, a); -} - -static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) { - AvahiInterface *i; - int from_local_iface = 0; - - assert(s); - assert(p); - assert(src_address); - assert(dst_address); - assert(iface > 0); - assert(src_address->proto == dst_address->proto); - - if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) || - !i->announcing) { - avahi_log_warn("Received packet from invalid interface."); - return; - } - - if (port <= 0) { - /* This fixes RHBZ #475394 */ - avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port); - return; - } - - if (avahi_address_is_ipv4_in_ipv6(src_address)) - /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ - return; - - if (originates_from_local_legacy_unicast_socket(s, src_address, port)) - /* This originates from our local reflector, so let's ignore it */ - return; - - /* We don't want to reflect local traffic, so we check if this packet is generated locally. */ - if (s->config.enable_reflector) - from_local_iface = originates_from_local_iface(s, iface, src_address, port); - - if (avahi_dns_packet_check_valid_multicast(p) < 0) { - avahi_log_warn("Received invalid packet."); - return; - } - - if (avahi_dns_packet_is_query(p)) { - int legacy_unicast = 0; - - /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the - * AR section completely here, so far. Until the day we add - * EDNS0 support. */ - - if (port != AVAHI_MDNS_PORT) { - /* Legacy Unicast */ - - if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) { - avahi_log_warn("Invalid legacy unicast query packet."); - return; - } - - legacy_unicast = 1; - } - - if (legacy_unicast) - reflect_legacy_unicast_query_packet(s, p, i, src_address, port); - - handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface); - - } else { - char t[AVAHI_ADDRESS_STR_MAX]; - - if (port != AVAHI_MDNS_PORT) { - avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol); - return; - } - - if (ttl != 255 && s->config.check_response_ttl) { - avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol); - return; - } - - if (!is_mdns_mcast_address(dst_address) && - !avahi_interface_address_on_link(i, src_address)) { - - avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol); - return; - } - - if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) { - - avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address)); - return; - } - - handle_response_packet(s, p, i, src_address, from_local_iface); - } -} - -static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) { - AvahiInterface *j; - AvahiLegacyUnicastReflectSlot *slot; - - assert(s); - assert(p); - - if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { - avahi_log_warn("Received invalid packet."); - return; - } - - if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) { - avahi_log_warn("Received legacy unicast response with unknown id"); - return; - } - - if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) || - !j->announcing) - return; - - /* Patch the original ID into this response */ - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); - - /* Forward the response to the correct client */ - avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port); - - /* Undo changes to packet */ - avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); -} - -static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { - AvahiServer *s = userdata; - AvahiAddress dest, src; - AvahiDnsPacket *p = NULL; - AvahiIfIndex iface; - uint16_t port; - uint8_t ttl; - - assert(w); - assert(fd >= 0); - assert(events & AVAHI_WATCH_IN); - - if (fd == s->fd_ipv4) { - dest.proto = src.proto = AVAHI_PROTO_INET; - p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl); - } else { - assert(fd == s->fd_ipv6); - dest.proto = src.proto = AVAHI_PROTO_INET6; - p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl); - } - - if (p) { - if (iface == AVAHI_IF_UNSPEC) - iface = avahi_find_interface_for_address(s->monitor, &dest); - - if (iface != AVAHI_IF_UNSPEC) - dispatch_packet(s, p, &src, port, &dest, iface, ttl); - else - avahi_log_error("Incoming packet received on address that isn't local."); - - avahi_dns_packet_free(p); - - avahi_cleanup_dead_entries(s); - } -} - -static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { - AvahiServer *s = userdata; - AvahiDnsPacket *p = NULL; - - assert(w); - assert(fd >= 0); - assert(events & AVAHI_WATCH_IN); - - if (fd == s->fd_legacy_unicast_ipv4) - p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL); - else { - assert(fd == s->fd_legacy_unicast_ipv6); - p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL); - } - - if (p) { - dispatch_legacy_unicast_packet(s, p); - avahi_dns_packet_free(p); - - avahi_cleanup_dead_entries(s); - } -} - -static void server_set_state(AvahiServer *s, AvahiServerState state) { - assert(s); - - if (s->state == state) - return; - - s->state = state; - - avahi_interface_monitor_update_rrs(s->monitor, 0); - - if (s->callback) - s->callback(s, state, s->userdata); -} - -static void withdraw_host_rrs(AvahiServer *s) { - assert(s); - - if (s->hinfo_entry_group) - avahi_s_entry_group_reset(s->hinfo_entry_group); - - if (s->browse_domain_entry_group) - avahi_s_entry_group_reset(s->browse_domain_entry_group); - - avahi_interface_monitor_update_rrs(s->monitor, 1); - s->n_host_rr_pending = 0; -} - -void avahi_server_decrease_host_rr_pending(AvahiServer *s) { - assert(s); - - assert(s->n_host_rr_pending > 0); - - if (--s->n_host_rr_pending == 0) - server_set_state(s, AVAHI_SERVER_RUNNING); -} - -void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { - assert(s); - assert(g); - - if (state == AVAHI_ENTRY_GROUP_REGISTERING && - s->state == AVAHI_SERVER_REGISTERING) - s->n_host_rr_pending ++; - - else if (state == AVAHI_ENTRY_GROUP_COLLISION && - (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) { - withdraw_host_rrs(s); - server_set_state(s, AVAHI_SERVER_COLLISION); - - } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED && - s->state == AVAHI_SERVER_REGISTERING) - avahi_server_decrease_host_rr_pending(s); -} - -static void register_hinfo(AvahiServer *s) { - struct utsname utsname; - AvahiRecord *r; - - assert(s); - - if (!s->config.publish_hinfo) - return; - - if (s->hinfo_entry_group) - assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group)); - else - s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL); - - if (!s->hinfo_entry_group) { - avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error)); - return; - } - - /* Fill in HINFO rr */ - if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) { - - if (uname(&utsname) < 0) - avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno)); - else { - - r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine)); - r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname)); - - avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os); - - if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) { - avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error)); - return; - } - } - - avahi_record_unref(r); - } - - if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0) - avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error)); - -} - -static void register_localhost(AvahiServer *s) { - AvahiAddress a; - assert(s); - - /* Add localhost entries */ - avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a); - avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a); - - avahi_address_parse("::1", AVAHI_PROTO_INET6, &a); - avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a); -} - -static void register_browse_domain(AvahiServer *s) { - assert(s); - - if (!s->config.publish_domain) - return; - - if (avahi_domain_equal(s->domain_name, "local")) - return; - - if (s->browse_domain_entry_group) - assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group)); - else - s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL); - - if (!s->browse_domain_entry_group) { - avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error)); - return; - } - - if (avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) { - avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error)); - return; - } - - if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0) - avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error)); -} - -static void register_stuff(AvahiServer *s) { - assert(s); - - server_set_state(s, AVAHI_SERVER_REGISTERING); - s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */ - - register_hinfo(s); - register_browse_domain(s); - avahi_interface_monitor_update_rrs(s->monitor, 0); - - assert(s->n_host_rr_pending > 0); - s->n_host_rr_pending --; - - if (s->n_host_rr_pending == 0) - server_set_state(s, AVAHI_SERVER_RUNNING); -} - -static void update_fqdn(AvahiServer *s) { - char *n; - - assert(s); - assert(s->host_name); - assert(s->domain_name); - - if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name))) - return; /* OOM */ - - avahi_free(s->host_name_fqdn); - s->host_name_fqdn = n; -} - -int avahi_server_set_host_name(AvahiServer *s, const char *host_name) { - char *hn = NULL; - assert(s); - - AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME); - - if (!host_name) - hn = avahi_get_host_name_strdup(); - else - hn = avahi_normalize_name_strdup(host_name); - - hn[strcspn(hn, ".")] = 0; - - if (avahi_domain_equal(s->host_name, hn) && 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 = hn; - - update_fqdn(s); - - register_stuff(s); - return AVAHI_OK; -} - -int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) { - char *dn = NULL; - assert(s); - - 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"); - else - dn = avahi_normalize_name_strdup(domain_name); - - 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 = dn; - update_fqdn(s); - - register_stuff(s); - - avahi_free(dn); - return AVAHI_OK; -} - -static int valid_server_config(const AvahiServerConfig *sc) { - AvahiStringList *l; - - assert(sc); - - if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX) - return AVAHI_ERR_INVALID_CONFIG; - - if (sc->host_name && !avahi_is_valid_host_name(sc->host_name)) - return AVAHI_ERR_INVALID_HOST_NAME; - - if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name)) - return AVAHI_ERR_INVALID_DOMAIN_NAME; - - for (l = sc->browse_domains; l; l = l->next) - if (!avahi_is_valid_domain_name((char*) l->text)) - return AVAHI_ERR_INVALID_DOMAIN_NAME; - - return AVAHI_OK; -} - -static int setup_sockets(AvahiServer *s) { - assert(s); - - s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1; - s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1; - - if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) - return AVAHI_ERR_NO_NETWORK; - - if (s->fd_ipv4 < 0 && s->config.use_ipv4) - avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode"); - else if (s->fd_ipv6 < 0 && s->config.use_ipv6) - avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode"); - - s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1; - s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1; - - s->watch_ipv4 = - s->watch_ipv6 = - s->watch_legacy_unicast_ipv4 = - s->watch_legacy_unicast_ipv6 = NULL; - - if (s->fd_ipv4 >= 0) - s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s); - if (s->fd_ipv6 >= 0) - s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s); - - if (s->fd_legacy_unicast_ipv4 >= 0) - s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); - if (s->fd_legacy_unicast_ipv6 >= 0) - s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); - - return 0; -} - -AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) { - AvahiServer *s; - int e; - - if (sc && (e = valid_server_config(sc)) < 0) { - if (error) - *error = e; - return NULL; - } - - if (!(s = avahi_new(AvahiServer, 1))) { - if (error) - *error = AVAHI_ERR_NO_MEMORY; - - return NULL; - } - - s->poll_api = poll_api; - - if (sc) - avahi_server_config_copy(&s->config, sc); - else - avahi_server_config_init(&s->config); - - if ((e = setup_sockets(s)) < 0) { - if (error) - *error = e; - - avahi_server_config_free(&s->config); - avahi_free(s); - - return NULL; - } - - s->n_host_rr_pending = 0; - s->need_entry_cleanup = 0; - s->need_group_cleanup = 0; - s->need_browser_cleanup = 0; - s->cleanup_time_event = NULL; - s->hinfo_entry_group = NULL; - s->browse_domain_entry_group = NULL; - s->error = AVAHI_OK; - s->state = AVAHI_SERVER_INVALID; - - s->callback = callback; - s->userdata = userdata; - - s->time_event_queue = avahi_time_event_queue_new(poll_api); - - s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); - AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries); - AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups); - - s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); - AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers); - AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers); - AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers); - AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers); - AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers); - AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers); - AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers); - AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers); - - s->legacy_unicast_reflect_slots = NULL; - s->legacy_unicast_reflect_id = 0; - - s->record_list = avahi_record_list_new(); - - /* Get host name */ - s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup(); - s->host_name[strcspn(s->host_name, ".")] = 0; - s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local"); - s->host_name_fqdn = NULL; - update_fqdn(s); - - do { - s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand(); - } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID); - - if (s->config.enable_wide_area) { - s->wide_area_lookup_engine = avahi_wide_area_engine_new(s); - avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers); - } else - s->wide_area_lookup_engine = NULL; - - s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s); - - s->monitor = avahi_interface_monitor_new(s); - avahi_interface_monitor_sync(s->monitor); - - register_localhost(s); - register_stuff(s); - - return s; -} - -void avahi_server_free(AvahiServer* s) { - assert(s); - - /* Remove all browsers */ - - while (s->dns_server_browsers) - avahi_s_dns_server_browser_free(s->dns_server_browsers); - while (s->host_name_resolvers) - avahi_s_host_name_resolver_free(s->host_name_resolvers); - while (s->address_resolvers) - avahi_s_address_resolver_free(s->address_resolvers); - while (s->domain_browsers) - avahi_s_domain_browser_free(s->domain_browsers); - while (s->service_type_browsers) - avahi_s_service_type_browser_free(s->service_type_browsers); - while (s->service_browsers) - avahi_s_service_browser_free(s->service_browsers); - while (s->service_resolvers) - avahi_s_service_resolver_free(s->service_resolvers); - while (s->record_browsers) - avahi_s_record_browser_destroy(s->record_browsers); - - /* Remove all locally rgeistered stuff */ - - while(s->entries) - avahi_entry_free(s, s->entries); - - avahi_interface_monitor_free(s->monitor); - - while (s->groups) - avahi_entry_group_free(s, s->groups); - - free_slots(s); - - avahi_hashmap_free(s->entries_by_key); - avahi_record_list_free(s->record_list); - avahi_hashmap_free(s->record_browser_hashmap); - - if (s->wide_area_lookup_engine) - avahi_wide_area_engine_free(s->wide_area_lookup_engine); - avahi_multicast_lookup_engine_free(s->multicast_lookup_engine); - - if (s->cleanup_time_event) - avahi_time_event_free(s->cleanup_time_event); - - avahi_time_event_queue_free(s->time_event_queue); - - /* Free watches */ - - if (s->watch_ipv4) - s->poll_api->watch_free(s->watch_ipv4); - if (s->watch_ipv6) - s->poll_api->watch_free(s->watch_ipv6); - - if (s->watch_legacy_unicast_ipv4) - s->poll_api->watch_free(s->watch_legacy_unicast_ipv4); - if (s->watch_legacy_unicast_ipv6) - s->poll_api->watch_free(s->watch_legacy_unicast_ipv6); - - /* Free sockets */ - - if (s->fd_ipv4 >= 0) - close(s->fd_ipv4); - if (s->fd_ipv6 >= 0) - close(s->fd_ipv6); - - if (s->fd_legacy_unicast_ipv4 >= 0) - close(s->fd_legacy_unicast_ipv4); - if (s->fd_legacy_unicast_ipv6 >= 0) - close(s->fd_legacy_unicast_ipv6); - - /* Free other stuff */ - - avahi_free(s->host_name); - avahi_free(s->domain_name); - avahi_free(s->host_name_fqdn); - - avahi_server_config_free(&s->config); - - avahi_free(s); -} - -const char* avahi_server_get_domain_name(AvahiServer *s) { - assert(s); - - return s->domain_name; -} - -const char* avahi_server_get_host_name(AvahiServer *s) { - assert(s); - - return s->host_name; -} - -const char* avahi_server_get_host_name_fqdn(AvahiServer *s) { - assert(s); - - return s->host_name_fqdn; -} - -void* avahi_server_get_data(AvahiServer *s) { - assert(s); - - return s->userdata; -} - -void avahi_server_set_data(AvahiServer *s, void* userdata) { - assert(s); - - s->userdata = userdata; -} - -AvahiServerState avahi_server_get_state(AvahiServer *s) { - assert(s); - - return s->state; -} - -AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) { - assert(c); - - memset(c, 0, sizeof(AvahiServerConfig)); - c->use_ipv6 = 1; - c->use_ipv4 = 1; - c->allow_interfaces = NULL; - c->deny_interfaces = NULL; - c->host_name = NULL; - c->domain_name = NULL; - c->check_response_ttl = 0; - c->publish_hinfo = 0; - c->publish_addresses = 1; - c->publish_no_reverse = 0; - c->publish_workstation = 0; - c->publish_domain = 1; - c->use_iff_running = 0; - c->enable_reflector = 0; - c->reflect_ipv = 0; - c->add_service_cookie = 0; - c->enable_wide_area = 0; - c->n_wide_area_servers = 0; - c->disallow_other_stacks = 0; - 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; - c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX; - c->ratelimit_interval = 0; - c->ratelimit_burst = 0; - - return c; -} - -void avahi_server_config_free(AvahiServerConfig *c) { - assert(c); - - avahi_free(c->host_name); - avahi_free(c->domain_name); - avahi_string_list_free(c->browse_domains); - avahi_string_list_free(c->allow_interfaces); - avahi_string_list_free(c->deny_interfaces); -} - -AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) { - char *d = NULL, *h = NULL; - AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL; - assert(ret); - assert(c); - - if (c->host_name) - if (!(h = avahi_strdup(c->host_name))) - return NULL; - - if (c->domain_name) - if (!(d = avahi_strdup(c->domain_name))) { - avahi_free(h); - return NULL; - } - - if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) { - avahi_free(h); - avahi_free(d); - return NULL; - } - - if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) { - avahi_string_list_free(browse); - avahi_free(h); - avahi_free(d); - return NULL; - } - - if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) { - avahi_string_list_free(allow); - avahi_string_list_free(browse); - avahi_free(h); - avahi_free(d); - return NULL; - } - - *ret = *c; - ret->host_name = h; - ret->domain_name = d; - ret->browse_domains = browse; - ret->allow_interfaces = allow; - ret->deny_interfaces = deny; - - return ret; -} - -int avahi_server_errno(AvahiServer *s) { - assert(s); - - return s->error; -} - -/* Just for internal use */ -int avahi_server_set_errno(AvahiServer *s, int error) { - assert(s); - - return s->error = error; -} - -uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) { - assert(s); - - return s->local_service_cookie; -} - -static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) { - AvahiEntry *e; - - assert(s); - assert(key); - - for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) - - if ((e->interface == interface || e->interface <= 0 || interface <= 0) && - (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && - (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING)) - - return e; - - return NULL; -} - -int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) { - AvahiKey *key = NULL; - AvahiEntry *e; - int ret; - char n[AVAHI_DOMAIN_NAME_MAX]; - - assert(s); - assert(name); - assert(type); - assert(ret_group); - - AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); - AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); - AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); - AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); - AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); - - if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0)) - return avahi_server_set_errno(s, ret); - - if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) - return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); - - e = find_entry(s, interface, protocol, key); - avahi_key_unref(key); - - if (e) { - *ret_group = e->group; - return AVAHI_OK; - } - - return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); -} - -int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) { - AvahiKey *key = NULL; - AvahiEntry *e; - - assert(s); - assert(name); - - if (!s->host_name_fqdn) - return 0; - - if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) - return 0; - - e = find_entry(s, interface, protocol, key); - avahi_key_unref(key); - - if (!e) - return 0; - - return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name); -} - -int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) { - AvahiEntry *e; - - assert(s); - assert(record); - - for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next) - - if ((e->interface == interface || e->interface <= 0 || interface <= 0) && - (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && - (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) && - avahi_record_equal_no_ttl(record, e->record)) - return 1; - - return 0; -} - -/** Set the wide area DNS servers */ -int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) { - assert(s); - - if (!s->wide_area_lookup_engine) - return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG); - - 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; -} - -/** Set the browsing domains */ -int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) { - AvahiStringList *l; - - assert(s); - - for (l = s->config.browse_domains; l; l = l->next) - if (!avahi_is_valid_domain_name((char*) l->text)) - return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME); - - avahi_string_list_free(s->config.browse_domains); - s->config.browse_domains = avahi_string_list_copy(domains); - - return AVAHI_OK; -} diff --git a/avahi-core/socket.c b/avahi-core/socket.c deleted file mode 100644 index 17ab6e5..0000000 --- a/avahi-core/socket.c +++ /dev/null @@ -1,993 +0,0 @@ -/*** - 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 -#ifdef HAVE_SYS_FILIO_H -#include -#endif -#include - -#include -#include -#include -#include -#include -#include - -#ifdef IP_RECVIF -#include -#endif - -#include "dns.h" -#include "fdutil.h" -#include "socket.h" -#include "log.h" -#include "addr-util.h" - -/* this is a portability hack */ -#ifndef IPV6_ADD_MEMBERSHIP -#ifdef IPV6_JOIN_GROUP -#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -#endif -#endif - -#ifndef IPV6_DROP_MEMBERSHIP -#ifdef IPV6_LEAVE_GROUP -#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP -#endif -#endif - -static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { - assert(ret_sa); - - memset(ret_sa, 0, sizeof(struct sockaddr_in)); - ret_sa->sin_family = AF_INET; - ret_sa->sin_port = htons(AVAHI_MDNS_PORT); - inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr); -} - -static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { - assert(ret_sa); - - memset(ret_sa, 0, sizeof(struct sockaddr_in6)); - ret_sa->sin6_family = AF_INET6; - ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); - inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr); -} - -static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) { - assert(ret_sa); - assert(a); - assert(port > 0); - - memset(ret_sa, 0, sizeof(struct sockaddr_in)); - ret_sa->sin_family = AF_INET; - ret_sa->sin_port = htons(port); - memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address)); -} - -static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) { - assert(ret_sa); - assert(a); - assert(port > 0); - - memset(ret_sa, 0, sizeof(struct sockaddr_in6)); - ret_sa->sin6_family = AF_INET6; - ret_sa->sin6_port = htons(port); - memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address)); -} - -int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { -#ifdef HAVE_STRUCT_IP_MREQN - struct ip_mreqn mreq; -#else - struct ip_mreq mreq; -#endif - struct sockaddr_in sa; - - assert(fd >= 0); - assert(idx >= 0); - assert(a); - - memset(&mreq, 0, sizeof(mreq)); -#ifdef HAVE_STRUCT_IP_MREQN - mreq.imr_ifindex = idx; - mreq.imr_address.s_addr = a->address; -#else - mreq.imr_interface.s_addr = a->address; -#endif - mdns_mcast_group_ipv4(&sa); - mreq.imr_multiaddr = sa.sin_addr; - - /* Some network drivers have issues with dropping membership of - * mcast groups when the iface is down, but don't allow rejoining - * when it comes back up. This is an ugly workaround */ - if (join) - setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); - - if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno)); - return -1; - } - - return 0; -} - -int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) { - struct ipv6_mreq mreq6; - struct sockaddr_in6 sa6; - - assert(fd >= 0); - assert(idx >= 0); - assert(a); - - memset(&mreq6, 0, sizeof(mreq6)); - mdns_mcast_group_ipv6 (&sa6); - mreq6.ipv6mr_multiaddr = sa6.sin6_addr; - mreq6.ipv6mr_interface = idx; - - if (join) - setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); - - if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { - avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno)); - return -1; - } - - return 0; -} - -static int reuseaddr(int fd) { - int yes; - - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { - avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno)); - return -1; - } - -#ifdef SO_REUSEPORT - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { - avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno)); - return -1; - } -#endif - - return 0; -} - -static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { - - assert(fd >= 0); - assert(sa); - assert(l > 0); - - if (bind(fd, sa, l) < 0) { - - if (errno != EADDRINUSE) { - avahi_log_warn("bind() failed: %s", strerror(errno)); - return -1; - } - - avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***", - sa->sa_family == AF_INET ? "IPv4" : "IPv6"); - - /* Try again, this time with SO_REUSEADDR set */ - if (reuseaddr(fd) < 0) - return -1; - - if (bind(fd, sa, l) < 0) { - avahi_log_warn("bind() failed: %s", strerror(errno)); - return -1; - } - } else { - - /* We enable SO_REUSEADDR afterwards, to make sure that the - * user may run other mDNS implementations if he really - * wants. */ - - if (reuseaddr(fd) < 0) - return -1; - } - - return 0; -} - -static int ipv4_pktinfo(int fd) { - int yes; - -#ifdef IP_PKTINFO - yes = 1; - if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno)); - return -1; - } -#else - -#ifdef IP_RECVINTERFACE - yes = 1; - if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno)); - return -1; - } -#elif defined(IP_RECVIF) - yes = 1; - if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IP_RECVIF failed: %s", strerror(errno)); - return -1; - } -#endif - -#ifdef IP_RECVDSTADDR - yes = 1; - if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno)); - return -1; - } -#endif - -#endif /* IP_PKTINFO */ - -#ifdef IP_RECVTTL - yes = 1; - if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno)); - return -1; - } -#endif - - return 0; -} - -static int ipv6_pktinfo(int fd) { - int yes; - -#ifdef IPV6_RECVPKTINFO - yes = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno)); - return -1; - } -#elif defined(IPV6_PKTINFO) - yes = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno)); - return -1; - } -#endif - -#ifdef IPV6_RECVHOPS - yes = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno)); - return -1; - } -#elif defined(IPV6_RECVHOPLIMIT) - yes = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno)); - return -1; - } -#elif defined(IPV6_HOPLIMIT) - yes = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno)); - return -1; - } -#endif - - return 0; -} - -int avahi_open_socket_ipv4(int no_reuse) { - struct sockaddr_in local; - int fd = -1, r, ittl; - uint8_t ttl, cyes; - - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - avahi_log_warn("socket() failed: %s", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { - avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno)); - goto fail; - } - - ittl = 255; - if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) { - avahi_log_warn("IP_TTL failed: %s", strerror(errno)); - goto fail; - } - - cyes = 1; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) { - avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno)); - goto fail; - } - - memset(&local, 0, sizeof(local)); - local.sin_family = AF_INET; - local.sin_port = htons(AVAHI_MDNS_PORT); - - if (no_reuse) - r = bind(fd, (struct sockaddr*) &local, sizeof(local)); - else - r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); - - if (r < 0) - goto fail; - - if (ipv4_pktinfo (fd) < 0) - goto fail; - - if (avahi_set_cloexec(fd) < 0) { - avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); - goto fail; - } - - if (avahi_set_nonblock(fd) < 0) { - avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} - -int avahi_open_socket_ipv6(int no_reuse) { - struct sockaddr_in6 sa, local; - int fd = -1, yes, r; - int ttl; - - mdns_mcast_group_ipv6(&sa); - - if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - avahi_log_warn("socket() failed: %s", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { - avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { - avahi_log_warn("IPV6_UNICAST_HOPS 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; - } - - yes = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { - avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno)); - goto fail; - } - - memset(&local, 0, sizeof(local)); - local.sin6_family = AF_INET6; - local.sin6_port = htons(AVAHI_MDNS_PORT); - - if (no_reuse) - r = bind(fd, (struct sockaddr*) &local, sizeof(local)); - else - r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); - - if (r < 0) - goto fail; - - if (ipv6_pktinfo(fd) < 0) - goto fail; - - if (avahi_set_cloexec(fd) < 0) { - avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); - goto fail; - } - - if (avahi_set_nonblock(fd) < 0) { - avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} - -static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { - assert(fd >= 0); - assert(msg); - - for (;;) { - - if (sendmsg(fd, msg, flags) >= 0) - break; - - if (errno == EINTR) - continue; - - if (errno != EAGAIN) { - char where[64]; - struct sockaddr_in *sin = msg->msg_name; - - inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where)); - avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno)); - return -1; - } - - if (avahi_wait_for_write(fd) < 0) - return -1; - } - - return 0; -} - -int avahi_send_dns_packet_ipv4( - int fd, - AvahiIfIndex interface, - AvahiDnsPacket *p, - const AvahiIPv4Address *src_address, - const AvahiIPv4Address *dst_address, - uint16_t dst_port) { - - struct sockaddr_in sa; - struct msghdr msg; - struct iovec io; -#ifdef IP_PKTINFO - struct cmsghdr *cmsg; - size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; -#elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR) - struct cmsghdr *cmsg; - size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1]; -#endif - - assert(fd >= 0); - assert(p); - assert(avahi_dns_packet_check_valid(p) >= 0); - assert(!dst_address || dst_port > 0); - - if (!dst_address) - mdns_mcast_group_ipv4(&sa); - else - ipv4_address_to_sockaddr(&sa, dst_address, dst_port); - - memset(&io, 0, sizeof(io)); - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->size; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - -#ifdef IP_PKTINFO - if (interface > 0 || src_address) { - struct in_pktinfo *pkti; - - memset(cmsg_data, 0, sizeof(cmsg_data)); - msg.msg_control = cmsg_data; - msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = msg.msg_controllen; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - - pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); - - if (interface > 0) - pkti->ipi_ifindex = interface; - - if (src_address) - pkti->ipi_spec_dst.s_addr = src_address->address; - } -#elif defined(IP_MULTICAST_IF) - if (src_address) { - struct in_addr any = { INADDR_ANY }; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) { - avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno)); - return -1; - } - } -#elif defined(IP_SENDSRCADDR) - if (src_address) { - struct in_addr *addr; - - memset(cmsg_data, 0, sizeof(cmsg_data)); - msg.msg_control = cmsg_data; - msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr)); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = msg.msg_controllen; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_SENDSRCADDR; - - addr = (struct in_addr *)CMSG_DATA(cmsg); - addr->s_addr = src_address->address; - } -#elif defined(__GNUC__) -#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available" -#endif - - 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) { - - struct sockaddr_in6 sa; - struct msghdr msg; - struct iovec io; - struct cmsghdr *cmsg; - size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1]; - - assert(fd >= 0); - assert(p); - assert(avahi_dns_packet_check_valid(p) >= 0); - assert(!dst_address || dst_port > 0); - - if (!dst_address) - mdns_mcast_group_ipv6(&sa); - else - ipv6_address_to_sockaddr(&sa, dst_address, dst_port); - - memset(&io, 0, sizeof(io)); - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->size; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - - if (interface > 0 || src_address) { - struct in6_pktinfo *pkti; - - memset(cmsg_data, 0, sizeof(cmsg_data)); - msg.msg_control = cmsg_data; - msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = msg.msg_controllen; - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - - pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg); - - if (interface > 0) - pkti->ipi6_ifindex = interface; - - if (src_address) - memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address)); - } else { - msg.msg_control = NULL; - msg.msg_controllen = 0; - } - - 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 *p= NULL; - struct msghdr msg; - struct iovec io; - size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */ - ssize_t l; - struct cmsghdr *cmsg; - int found_addr = 0; - int ms; - struct sockaddr_in sa; - - assert(fd >= 0); - - if (ioctl(fd, FIONREAD, &ms) < 0) { - avahi_log_warn("ioctl(): %s", strerror(errno)); - goto fail; - } - - if (ms < 0) { - avahi_log_warn("FIONREAD returned negative value."); - goto fail; - } - - p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); - - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->max_size; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = aux; - msg.msg_controllen = sizeof(aux); - msg.msg_flags = 0; - - if ((l = recvmsg(fd, &msg, 0)) < 0) { - /* Linux returns EAGAIN when an invalid IP packet has been - received. 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; - } - - /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So - * fail after having read them. */ - if (!ms) - goto fail; - - if (sa.sin_addr.s_addr == INADDR_ANY) - /* Linux 2.4 behaves very strangely sometimes! */ - goto fail; - - assert(!(msg.msg_flags & MSG_CTRUNC)); - assert(!(msg.msg_flags & MSG_TRUNC)); - - p->size = (size_t) l; - - if (ret_src_port) - *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); - - if (ret_src_address) { - AvahiAddress a; - avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); - *ret_src_address = a.data.ipv4; - } - - if (ret_ttl) - *ret_ttl = 255; - - if (ret_iface) - *ret_iface = AVAHI_IF_UNSPEC; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - - if (cmsg->cmsg_level == IPPROTO_IP) { - - switch (cmsg->cmsg_type) { -#ifdef IP_RECVTTL - case IP_RECVTTL: -#endif - case IP_TTL: - if (ret_ttl) - *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); - - break; - -#ifdef IP_PKTINFO - case IP_PKTINFO: { - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - - if (ret_iface && i->ipi_ifindex > 0) - *ret_iface = (int) i->ipi_ifindex; - - if (ret_dst_address) - ret_dst_address->address = i->ipi_addr.s_addr; - - found_addr = 1; - - break; - } -#endif - -#ifdef IP_RECVIF - case IP_RECVIF: { - struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg); - - if (ret_iface) { -#ifdef __sun - if (*(uint_t*) sdl > 0) - *ret_iface = *(uint_t*) sdl; -#else - - if (sdl->sdl_index > 0) - *ret_iface = (int) sdl->sdl_index; -#endif - } - - break; - } -#endif - -#ifdef IP_RECVDSTADDR - case IP_RECVDSTADDR: - if (ret_dst_address) - memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4); - - found_addr = 1; - break; -#endif - - default: - avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type); - break; - } - } - } - - assert(found_addr); - - return p; - -fail: - if (p) - avahi_dns_packet_free(p); - - 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 *p = NULL; - struct msghdr msg; - struct iovec io; - size_t aux[1024 / sizeof(size_t)]; - ssize_t l; - int ms; - struct cmsghdr *cmsg; - int found_ttl = 0, found_iface = 0; - struct sockaddr_in6 sa; - - assert(fd >= 0); - - if (ioctl(fd, FIONREAD, &ms) < 0) { - avahi_log_warn("ioctl(): %s", strerror(errno)); - goto fail; - } - - if (ms < 0) { - avahi_log_warn("FIONREAD returned negative value."); - goto fail; - } - - p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); - - io.iov_base = AVAHI_DNS_PACKET_DATA(p); - io.iov_len = p->max_size; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = (struct sockaddr*) &sa; - msg.msg_namelen = sizeof(sa); - - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = aux; - msg.msg_controllen = sizeof(aux); - msg.msg_flags = 0; - - if ((l = recvmsg(fd, &msg, 0)) < 0) { - /* Linux returns EAGAIN when an invalid IP packet has been - received. 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; - } - - /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So - * fail after having read them. */ - if (!ms) - goto fail; - - assert(!(msg.msg_flags & MSG_CTRUNC)); - assert(!(msg.msg_flags & MSG_TRUNC)); - - p->size = (size_t) l; - - if (ret_src_port) - *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); - - if (ret_src_address) { - AvahiAddress a; - avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); - *ret_src_address = a.data.ipv6; - } - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - - if (cmsg->cmsg_level == IPPROTO_IPV6) { - - switch (cmsg->cmsg_type) { - - case IPV6_HOPLIMIT: - - if (ret_ttl) - *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); - - found_ttl = 1; - - break; - - case IPV6_PKTINFO: { - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - - if (ret_iface && i->ipi6_ifindex > 0) - *ret_iface = i->ipi6_ifindex; - - if (ret_dst_address) - memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16); - - found_iface = 1; - break; - } - - default: - avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type); - break; - } - } - } - - assert(found_iface); - assert(found_ttl); - - return p; - -fail: - if (p) - avahi_dns_packet_free(p); - - return NULL; -} - -int avahi_open_unicast_socket_ipv4(void) { - struct sockaddr_in local; - int fd = -1; - - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - avahi_log_warn("socket() failed: %s", strerror(errno)); - goto fail; - } - - memset(&local, 0, sizeof(local)); - local.sin_family = AF_INET; - - if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { - avahi_log_warn("bind() failed: %s", strerror(errno)); - goto fail; - } - - if (ipv4_pktinfo(fd) < 0) { - goto fail; - } - - if (avahi_set_cloexec(fd) < 0) { - avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); - goto fail; - } - - if (avahi_set_nonblock(fd) < 0) { - avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} - -int avahi_open_unicast_socket_ipv6(void) { - struct sockaddr_in6 local; - 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; - - if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { - avahi_log_warn("bind() failed: %s", strerror(errno)); - goto fail; - } - - if (ipv6_pktinfo(fd) < 0) - goto fail; - - if (avahi_set_cloexec(fd) < 0) { - avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); - goto fail; - } - - if (avahi_set_nonblock(fd) < 0) { - avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} diff --git a/avahi-core/socket.h b/avahi-core/socket.h deleted file mode 100644 index 92f12d7..0000000 --- a/avahi-core/socket.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef foosockethfoo -#define foosockethfoo - -/*** - 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 "dns.h" - -#define AVAHI_MDNS_PORT 5353 -#define AVAHI_DNS_PORT 53 -#define AVAHI_IPV4_MCAST_GROUP "224.0.0.251" -#define AVAHI_IPV6_MCAST_GROUP "ff02::fb" - -int avahi_open_socket_ipv4(int no_reuse); -int avahi_open_socket_ipv6(int no_reuse); - -int avahi_open_unicast_socket_ipv4(void); -int avahi_open_unicast_socket_ipv6(void); - -int avahi_send_dns_packet_ipv4(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv4Address *src_address, const AvahiIPv4Address *dst_address, uint16_t dst_port); -int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port); - -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_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl); - -int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *local_address, int iface, int join); -int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *local_address, int iface, int join); - -#endif diff --git a/avahi-core/timeeventq-test.c b/avahi-core/timeeventq-test.c deleted file mode 100644 index d7b2e39..0000000 --- a/avahi-core/timeeventq-test.c +++ /dev/null @@ -1,67 +0,0 @@ -/*** - 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 "timeeventq.h" -#include "log.h" - -#define POINTER_TO_INT(p) ((int) (long) (p)) -#define INT_TO_POINTER(i) ((void*) (long) (i)) - -static AvahiTimeEventQueue *q = NULL; - -static void callback(AvahiTimeEvent*e, void* userdata) { - struct timeval tv = {0, 0}; - assert(e); - avahi_log_info("callback(%i)", POINTER_TO_INT(userdata)); - avahi_elapse_time(&tv, 1000, 100); - avahi_time_event_update(e, &tv); -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - struct timeval tv; - AvahiSimplePoll *s; - - s = avahi_simple_poll_new(); - - q = avahi_time_event_queue_new(avahi_simple_poll_get(s)); - - avahi_time_event_new(q, avahi_elapse_time(&tv, 5000, 100), callback, INT_TO_POINTER(1)); - avahi_time_event_new(q, avahi_elapse_time(&tv, 5000, 100), callback, INT_TO_POINTER(2)); - - avahi_log_info("starting"); - - for (;;) - if (avahi_simple_poll_iterate(s, -1) != 0) - break; - - avahi_time_event_queue_free(q); - avahi_simple_poll_free(s); - - return 0; -} diff --git a/avahi-core/timeeventq.c b/avahi-core/timeeventq.c deleted file mode 100644 index 2799bf2..0000000 --- a/avahi-core/timeeventq.c +++ /dev/null @@ -1,225 +0,0 @@ -/*** - 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 "timeeventq.h" -#include "log.h" - -struct AvahiTimeEvent { - AvahiTimeEventQueue *queue; - AvahiPrioQueueNode *node; - struct timeval expiry; - struct timeval last_run; - AvahiTimeEventCallback callback; - void* userdata; -}; - -struct AvahiTimeEventQueue { - const AvahiPoll *poll_api; - AvahiPrioQueue *prioq; - AvahiTimeout *timeout; -}; - -static int compare(const void* _a, const void* _b) { - const AvahiTimeEvent *a = _a, *b = _b; - int ret; - - if ((ret = avahi_timeval_compare(&a->expiry, &b->expiry)) != 0) - return ret; - - /* If both exevents are scheduled for the same time, put the entry - * that has been run earlier the last time first. */ - return avahi_timeval_compare(&a->last_run, &b->last_run); -} - -static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) { - assert(q); - - return q->prioq->root ? q->prioq->root->data : NULL; -} - -static void update_timeout(AvahiTimeEventQueue *q) { - AvahiTimeEvent *e; - assert(q); - - if ((e = time_event_queue_root(q))) - q->poll_api->timeout_update(q->timeout, &e->expiry); - else - q->poll_api->timeout_update(q->timeout, NULL); -} - -static void expiration_event(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) { - AvahiTimeEventQueue *q = userdata; - AvahiTimeEvent *e; - - if ((e = time_event_queue_root(q))) { - struct timeval now; - - gettimeofday(&now, NULL); - - /* Check if expired */ - if (avahi_timeval_compare(&now, &e->expiry) >= 0) { - - /* Make sure to move the entry away from the front */ - e->last_run = now; - avahi_prio_queue_shuffle(q->prioq, e->node); - - /* Run it */ - assert(e->callback); - e->callback(e, e->userdata); - - update_timeout(q); - return; - } - } - - avahi_log_debug(__FILE__": Strange, expiration_event() called, but nothing really happened."); - update_timeout(q); -} - -static void fix_expiry_time(AvahiTimeEvent *e) { - struct timeval now; - assert(e); - - return; /*** DO WE REALLY NEED THIS? ***/ - - gettimeofday(&now, NULL); - - if (avahi_timeval_compare(&now, &e->expiry) > 0) - e->expiry = now; -} - -AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api) { - AvahiTimeEventQueue *q; - - if (!(q = avahi_new(AvahiTimeEventQueue, 1))) { - avahi_log_error(__FILE__": Out of memory"); - goto oom; - } - - q->poll_api = poll_api; - - if (!(q->prioq = avahi_prio_queue_new(compare))) - goto oom; - - if (!(q->timeout = poll_api->timeout_new(poll_api, NULL, expiration_event, q))) - goto oom; - - return q; - -oom: - - if (q) { - avahi_free(q); - - if (q->prioq) - avahi_prio_queue_free(q->prioq); - } - - return NULL; -} - -void avahi_time_event_queue_free(AvahiTimeEventQueue *q) { - AvahiTimeEvent *e; - - assert(q); - - while ((e = time_event_queue_root(q))) - avahi_time_event_free(e); - avahi_prio_queue_free(q->prioq); - - q->poll_api->timeout_free(q->timeout); - - avahi_free(q); -} - -AvahiTimeEvent* avahi_time_event_new( - AvahiTimeEventQueue *q, - const struct timeval *timeval, - AvahiTimeEventCallback callback, - void* userdata) { - - AvahiTimeEvent *e; - - assert(q); - assert(callback); - assert(userdata); - - if (!(e = avahi_new(AvahiTimeEvent, 1))) { - avahi_log_error(__FILE__": Out of memory"); - return NULL; /* OOM */ - } - - e->queue = q; - e->callback = callback; - e->userdata = userdata; - - if (timeval) - e->expiry = *timeval; - else { - e->expiry.tv_sec = 0; - e->expiry.tv_usec = 0; - } - - fix_expiry_time(e); - - e->last_run.tv_sec = 0; - e->last_run.tv_usec = 0; - - if (!(e->node = avahi_prio_queue_put(q->prioq, e))) { - avahi_free(e); - return NULL; - } - - update_timeout(q); - return e; -} - -void avahi_time_event_free(AvahiTimeEvent *e) { - AvahiTimeEventQueue *q; - assert(e); - - q = e->queue; - - avahi_prio_queue_remove(q->prioq, e->node); - avahi_free(e); - - update_timeout(q); -} - -void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval) { - assert(e); - assert(timeval); - - e->expiry = *timeval; - fix_expiry_time(e); - avahi_prio_queue_shuffle(e->queue->prioq, e->node); - - update_timeout(e->queue); -} - diff --git a/avahi-core/timeeventq.h b/avahi-core/timeeventq.h deleted file mode 100644 index a695b6a..0000000 --- a/avahi-core/timeeventq.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef footimeeventqhfoo -#define footimeeventqhfoo - -/*** - 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 - -typedef struct AvahiTimeEventQueue AvahiTimeEventQueue; -typedef struct AvahiTimeEvent AvahiTimeEvent; - -#include - -#include "prioq.h" - -typedef void (*AvahiTimeEventCallback)(AvahiTimeEvent *e, void* userdata); - -AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api); -void avahi_time_event_queue_free(AvahiTimeEventQueue *q); - -AvahiTimeEvent* avahi_time_event_new( - AvahiTimeEventQueue *q, - const struct timeval *timeval, - AvahiTimeEventCallback callback, - void* userdata); - -void avahi_time_event_free(AvahiTimeEvent *e); -void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval); - -#endif diff --git a/avahi-core/update-test.c b/avahi-core/update-test.c deleted file mode 100644 index feb884a..0000000 --- a/avahi-core/update-test.c +++ /dev/null @@ -1,91 +0,0 @@ -/*** - 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 - -static AvahiSEntryGroup *group = NULL; - -static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) { - - avahi_log_debug("server state: %i", state); - - if (state == AVAHI_SERVER_RUNNING) { - int ret; - - group = avahi_s_entry_group_new(s, NULL, NULL); - assert(group); - - ret = avahi_server_add_service(s, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "foo", "_http._tcp", NULL, NULL, 80, "test1", NULL); - assert(ret == AVAHI_OK); - - avahi_s_entry_group_commit(group); - } -} - -static void modify_txt_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, void *userdata) { - int ret; - AvahiServer *s = userdata; - - avahi_log_debug("modifying"); - - ret = avahi_server_update_service_txt(s, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "foo", "_http._tcp", NULL, "test2", NULL); - assert(ret == AVAHI_OK); -} - -int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { - AvahiSimplePoll *simple_poll; - const AvahiPoll *poll_api; - AvahiServer *server; - struct timeval tv; - AvahiServerConfig config; - - simple_poll = avahi_simple_poll_new(); - assert(simple_poll); - - poll_api = avahi_simple_poll_get(simple_poll); - assert(poll_api); - - avahi_server_config_init(&config); - config.publish_domain = config.publish_workstation = config.use_ipv6 = config.publish_hinfo = 0; - server = avahi_server_new(poll_api, &config, server_callback, NULL, NULL); - assert(server); - avahi_server_config_free(&config); - - poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 1000*10, 0), modify_txt_callback, server); - - avahi_simple_poll_loop(simple_poll); - return 0; -} diff --git a/avahi-core/util.c b/avahi-core/util.c deleted file mode 100644 index 21ef94c..0000000 --- a/avahi-core/util.c +++ /dev/null @@ -1,120 +0,0 @@ -/*** - 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 "util.h" - -void avahi_hexdump(const void* p, size_t size) { - const uint8_t *c = p; - assert(p); - - printf("Dumping %lu bytes from %p:\n", (unsigned long) size, p); - - while (size > 0) { - unsigned i; - - for (i = 0; i < 16; i++) { - if (i < size) - printf("%02x ", c[i]); - else - printf(" "); - } - - for (i = 0; i < 16; i++) { - if (i < size) - printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.'); - else - printf(" "); - } - - printf("\n"); - - c += 16; - - if (size <= 16) - break; - - size -= 16; - } -} - -char *avahi_format_mac_address(char *r, size_t l, const uint8_t* mac, size_t size) { - char *t = r; - unsigned i; - static const char hex[] = "0123456789abcdef"; - - assert(r); - assert(l > 0); - assert(mac); - - if (size <= 0) { - *r = 0; - return r; - } - - for (i = 0; i < size; i++) { - if (l < 3) - break; - - *(t++) = hex[*mac >> 4]; - *(t++) = hex[*mac & 0xF]; - *(t++) = ':'; - - l -= 3; - - mac++; - } - - if (t > r) - *(t-1) = 0; - else - *r = 0; - - return r; -} - -char *avahi_strup(char *s) { - char *c; - assert(s); - - for (c = s; *c; c++) - *c = (char) toupper(*c); - - return s; -} - -char *avahi_strdown(char *s) { - char *c; - assert(s); - - for (c = s; *c; c++) - *c = (char) tolower(*c); - - return s; -} diff --git a/avahi-core/util.h b/avahi-core/util.h deleted file mode 100644 index e13c334..0000000 --- a/avahi-core/util.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef fooutilhfoo -#define fooutilhfoo - -/*** - 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 - -AVAHI_C_DECL_BEGIN - -void avahi_hexdump(const void *p, size_t size); - -char *avahi_format_mac_address(char *t, size_t l, const uint8_t* mac, size_t size); - -/** Change every character in the string to upper case (ASCII), return a pointer to the string */ -char *avahi_strup(char *s); - -/** Change every character in the string to lower case (ASCII), return a pointer to the string */ -char *avahi_strdown(char *s); - -AVAHI_C_DECL_END - -#endif diff --git a/avahi-core/wide-area.c b/avahi-core/wide-area.c deleted file mode 100644 index d5e64e5..0000000 --- a/avahi-core/wide-area.c +++ /dev/null @@ -1,723 +0,0 @@ -/*** - 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 "internal.h" -#include "browse.h" -#include "socket.h" -#include "log.h" -#include "hashmap.h" -#include "wide-area.h" -#include "addr-util.h" -#include "rr-util.h" - -#define CACHE_ENTRIES_MAX 500 - -typedef struct AvahiWideAreaCacheEntry AvahiWideAreaCacheEntry; - -struct AvahiWideAreaCacheEntry { - AvahiWideAreaLookupEngine *engine; - - AvahiRecord *record; - struct timeval timestamp; - struct timeval expiry; - - AvahiTimeEvent *time_event; - - AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, by_key); - AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, cache); -}; - -struct AvahiWideAreaLookup { - AvahiWideAreaLookupEngine *engine; - int dead; - - uint32_t id; /* effectively just an uint16_t, but we need it as an index for a hash table */ - AvahiTimeEvent *time_event; - - AvahiKey *key, *cname_key; - - int n_send; - AvahiDnsPacket *packet; - - AvahiWideAreaLookupCallback callback; - void *userdata; - - AvahiAddress dns_server_used; - - AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, lookups); - AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, by_key); -}; - -struct AvahiWideAreaLookupEngine { - AvahiServer *server; - - int fd_ipv4, fd_ipv6; - AvahiWatch *watch_ipv4, *watch_ipv6; - - uint16_t next_id; - - /* Cache */ - AVAHI_LLIST_HEAD(AvahiWideAreaCacheEntry, cache); - AvahiHashmap *cache_by_key; - unsigned cache_n_entries; - - /* Lookups */ - AVAHI_LLIST_HEAD(AvahiWideAreaLookup, lookups); - AvahiHashmap *lookups_by_id; - AvahiHashmap *lookups_by_key; - - int cleanup_dead; - - AvahiAddress dns_servers[AVAHI_WIDE_AREA_SERVERS_MAX]; - unsigned n_dns_servers; - unsigned current_dns_server; -}; - -static AvahiWideAreaLookup* find_lookup(AvahiWideAreaLookupEngine *e, uint16_t id) { - AvahiWideAreaLookup *l; - int i = (int) id; - - assert(e); - - if (!(l = avahi_hashmap_lookup(e->lookups_by_id, &i))) - return NULL; - - assert(l->id == id); - - if (l->dead) - return NULL; - - return l; -} - -static int send_to_dns_server(AvahiWideAreaLookup *l, AvahiDnsPacket *p) { - AvahiAddress *a; - - assert(l); - assert(p); - - if (l->engine->n_dns_servers <= 0) - return -1; - - assert(l->engine->current_dns_server < l->engine->n_dns_servers); - - a = &l->engine->dns_servers[l->engine->current_dns_server]; - l->dns_server_used = *a; - - if (a->proto == AVAHI_PROTO_INET) { - - if (l->engine->fd_ipv4 < 0) - return -1; - - return avahi_send_dns_packet_ipv4(l->engine->fd_ipv4, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT); - - } else { - assert(a->proto == AVAHI_PROTO_INET6); - - if (l->engine->fd_ipv6 < 0) - return -1; - - return avahi_send_dns_packet_ipv6(l->engine->fd_ipv6, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv6, AVAHI_DNS_PORT); - } -} - -static void next_dns_server(AvahiWideAreaLookupEngine *e) { - assert(e); - - e->current_dns_server++; - - if (e->current_dns_server >= e->n_dns_servers) - e->current_dns_server = 0; -} - -static void lookup_stop(AvahiWideAreaLookup *l) { - assert(l); - - l->callback = NULL; - - if (l->time_event) { - avahi_time_event_free(l->time_event); - l->time_event = NULL; - } -} - -static void sender_timeout_callback(AvahiTimeEvent *e, void *userdata) { - AvahiWideAreaLookup *l = userdata; - struct timeval tv; - - assert(l); - - /* Try another DNS server after three retries */ - if (l->n_send >= 3 && avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) { - next_dns_server(l->engine); - - if (avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) - /* There is no other DNS server, fail */ - l->n_send = 1000; - } - - if (l->n_send >= 6) { - avahi_log_warn(__FILE__": Query timed out."); - avahi_server_set_errno(l->engine->server, AVAHI_ERR_TIMEOUT); - l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); - lookup_stop(l); - return; - } - - assert(l->packet); - send_to_dns_server(l, l->packet); - l->n_send++; - - avahi_time_event_update(e, avahi_elapse_time(&tv, 1000, 0)); -} - -AvahiWideAreaLookup *avahi_wide_area_lookup_new( - AvahiWideAreaLookupEngine *e, - AvahiKey *key, - AvahiWideAreaLookupCallback callback, - void *userdata) { - - struct timeval tv; - AvahiWideAreaLookup *l, *t; - uint8_t *p; - - assert(e); - assert(key); - assert(callback); - assert(userdata); - - l = avahi_new(AvahiWideAreaLookup, 1); - l->engine = e; - l->dead = 0; - l->key = avahi_key_ref(key); - l->cname_key = avahi_key_new_cname(l->key); - l->callback = callback; - l->userdata = userdata; - - /* If more than 65K wide area quries are issued simultaneously, - * this will break. This should be limited by some higher level */ - - for (;; e->next_id++) - if (!find_lookup(e, e->next_id)) - break; /* This ID is not yet used. */ - - l->id = e->next_id++; - - /* We keep the packet around in case we need to repeat our query */ - l->packet = avahi_dns_packet_new(0); - - avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_ID, (uint16_t) l->id); - avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0, 0)); - - p = avahi_dns_packet_append_key(l->packet, key, 0); - assert(p); - - avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_QDCOUNT, 1); - - if (send_to_dns_server(l, l->packet) < 0) { - avahi_log_error(__FILE__": Failed to send packet."); - avahi_dns_packet_free(l->packet); - avahi_key_unref(l->key); - if (l->cname_key) - avahi_key_unref(l->cname_key); - avahi_free(l); - return NULL; - } - - l->n_send = 1; - - l->time_event = avahi_time_event_new(e->server->time_event_queue, avahi_elapse_time(&tv, 500, 0), sender_timeout_callback, l); - - avahi_hashmap_insert(e->lookups_by_id, &l->id, l); - - t = avahi_hashmap_lookup(e->lookups_by_key, l->key); - AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, by_key, t, l); - avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); - - AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, lookups, e->lookups, l); - - return l; -} - -static void lookup_destroy(AvahiWideAreaLookup *l) { - AvahiWideAreaLookup *t; - assert(l); - - lookup_stop(l); - - t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); - AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, by_key, t, l); - if (t) - avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); - else - avahi_hashmap_remove(l->engine->lookups_by_key, l->key); - - AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, lookups, l->engine->lookups, l); - - avahi_hashmap_remove(l->engine->lookups_by_id, &l->id); - avahi_dns_packet_free(l->packet); - - if (l->key) - avahi_key_unref(l->key); - - if (l->cname_key) - avahi_key_unref(l->cname_key); - - avahi_free(l); -} - -void avahi_wide_area_lookup_free(AvahiWideAreaLookup *l) { - assert(l); - - if (l->dead) - return; - - l->dead = 1; - l->engine->cleanup_dead = 1; - lookup_stop(l); -} - -void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e) { - AvahiWideAreaLookup *l, *n; - assert(e); - - while (e->cleanup_dead) { - e->cleanup_dead = 0; - - for (l = e->lookups; l; l = n) { - n = l->lookups_next; - - if (l->dead) - lookup_destroy(l); - } - } -} - -static void cache_entry_free(AvahiWideAreaCacheEntry *c) { - AvahiWideAreaCacheEntry *t; - assert(c); - - if (c->time_event) - avahi_time_event_free(c->time_event); - - AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, cache, c->engine->cache, c); - - t = avahi_hashmap_lookup(c->engine->cache_by_key, c->record->key); - AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, by_key, t, c); - if (t) - avahi_hashmap_replace(c->engine->cache_by_key, avahi_key_ref(c->record->key), t); - else - avahi_hashmap_remove(c->engine->cache_by_key, c->record->key); - - c->engine->cache_n_entries --; - - avahi_record_unref(c->record); - avahi_free(c); -} - -static void expiry_event(AvahiTimeEvent *te, void *userdata) { - AvahiWideAreaCacheEntry *e = userdata; - - assert(te); - assert(e); - - cache_entry_free(e); -} - -static AvahiWideAreaCacheEntry* find_record_in_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { - AvahiWideAreaCacheEntry *c; - - assert(e); - assert(r); - - for (c = avahi_hashmap_lookup(e->cache_by_key, r->key); c; c = c->by_key_next) - if (avahi_record_equal_no_ttl(r, c->record)) - return c; - - return NULL; -} - -static void run_callbacks(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { - AvahiWideAreaLookup *l; - - assert(e); - assert(r); - - for (l = avahi_hashmap_lookup(e->lookups_by_key, r->key); l; l = l->by_key_next) { - if (l->dead || !l->callback) - continue; - - l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA, r, l->userdata); - } - - if (r->key->clazz == AVAHI_DNS_CLASS_IN && r->key->type == AVAHI_DNS_TYPE_CNAME) { - /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ - - for (l = e->lookups; l; l = l->lookups_next) { - AvahiKey *key; - - if (l->dead || !l->callback) - continue; - - if ((key = avahi_key_new_cname(l->key))) { - if (avahi_key_equal(r->key, key)) - l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA, r, l->userdata); - - avahi_key_unref(key); - } - } - } -} - -static void add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { - AvahiWideAreaCacheEntry *c; - int is_new; - - assert(e); - assert(r); - - if ((c = find_record_in_cache(e, r))) { - is_new = 0; - - /* Update the existing entry */ - avahi_record_unref(c->record); - } else { - AvahiWideAreaCacheEntry *t; - - is_new = 1; - - /* Enforce cache size */ - if (e->cache_n_entries >= CACHE_ENTRIES_MAX) - /* Eventually we should improve the caching algorithm here */ - goto finish; - - c = avahi_new(AvahiWideAreaCacheEntry, 1); - c->engine = e; - c->time_event = NULL; - - AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c); - - /* Add the new entry to the cache entry hash table */ - t = avahi_hashmap_lookup(e->cache_by_key, r->key); - AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c); - avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t); - - e->cache_n_entries ++; - } - - c->record = avahi_record_ref(r); - - gettimeofday(&c->timestamp, NULL); - c->expiry = c->timestamp; - avahi_timeval_add(&c->expiry, r->ttl * 1000000); - - if (c->time_event) - avahi_time_event_update(c->time_event, &c->expiry); - else - c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c); - -finish: - - if (is_new) - run_callbacks(e, r); -} - -static int map_dns_error(uint16_t error) { - static const int table[16] = { - AVAHI_OK, - AVAHI_ERR_DNS_FORMERR, - AVAHI_ERR_DNS_SERVFAIL, - AVAHI_ERR_DNS_NXDOMAIN, - AVAHI_ERR_DNS_NOTIMP, - AVAHI_ERR_DNS_REFUSED, - AVAHI_ERR_DNS_YXDOMAIN, - AVAHI_ERR_DNS_YXRRSET, - AVAHI_ERR_DNS_NXRRSET, - AVAHI_ERR_DNS_NOTAUTH, - AVAHI_ERR_DNS_NOTZONE, - AVAHI_ERR_INVALID_DNS_ERROR, - AVAHI_ERR_INVALID_DNS_ERROR, - AVAHI_ERR_INVALID_DNS_ERROR, - AVAHI_ERR_INVALID_DNS_ERROR, - AVAHI_ERR_INVALID_DNS_ERROR - }; - - assert(error <= 15); - - return table[error]; -} - -static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) { - AvahiWideAreaLookup *l = NULL; - int i, r; - - AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW; - - assert(e); - assert(p); - - /* Some superficial validity tests */ - if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { - avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram."); - goto finish; - } - - /* Look for the lookup that issued this query */ - if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead) - goto finish; - - /* Check whether this a packet indicating a failure */ - if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 || - avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) { - - avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r)); - /* Tell the user about the failure */ - final_event = AVAHI_BROWSER_FAILURE; - - /* We go on here, since some of the records contained in the - reply might be interesting in some way */ - } - - /* Skip over the question */ - for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) { - AvahiKey *k; - - if (!(k = avahi_dns_packet_consume_key(p, NULL))) { - avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); - avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); - final_event = AVAHI_BROWSER_FAILURE; - goto finish; - } - - avahi_key_unref(k); - } - - /* Process responses */ - for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + - (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) + - (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) { - - AvahiRecord *rr; - - if (!(rr = avahi_dns_packet_consume_record(p, NULL))) { - avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); - avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); - final_event = AVAHI_BROWSER_FAILURE; - goto finish; - } - - add_to_cache(e, rr); - avahi_record_unref(rr); - } - -finish: - - if (l && !l->dead) { - if (l->callback) - l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); - - lookup_stop(l); - } -} - -static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) { - AvahiWideAreaLookupEngine *e = userdata; - AvahiDnsPacket *p = NULL; - - if (fd == e->fd_ipv4) - p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL); - else { - assert(fd == e->fd_ipv6); - p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL); - } - - if (p) { - handle_packet(e, p); - avahi_dns_packet_free(p); - } -} - -AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) { - AvahiWideAreaLookupEngine *e; - - assert(s); - - e = avahi_new(AvahiWideAreaLookupEngine, 1); - e->server = s; - e->cleanup_dead = 0; - - /* Create sockets */ - 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)); - - if (e->fd_ipv6 >= 0) - close(e->fd_ipv6); - - if (e->fd_ipv4 >= 0) - close(e->fd_ipv4); - - avahi_free(e); - return NULL; - } - - /* Create watches */ - - e->watch_ipv4 = e->watch_ipv6 = NULL; - - if (e->fd_ipv4 >= 0) - e->watch_ipv4 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv4, AVAHI_WATCH_IN, socket_event, e); - if (e->fd_ipv6 >= 0) - e->watch_ipv6 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv6, AVAHI_WATCH_IN, socket_event, e); - - e->n_dns_servers = e->current_dns_server = 0; - e->next_id = (uint16_t) rand(); - - /* Initialize cache */ - AVAHI_LLIST_HEAD_INIT(AvahiWideAreaCacheEntry, e->cache); - e->cache_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); - e->cache_n_entries = 0; - - /* Initialize lookup list */ - e->lookups_by_id = avahi_hashmap_new((AvahiHashFunc) avahi_int_hash, (AvahiEqualFunc) avahi_int_equal, NULL, NULL); - e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); - AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); - - return e; -} - -void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e) { - assert(e); - - avahi_wide_area_clear_cache(e); - - while (e->lookups) - lookup_destroy(e->lookups); - - avahi_hashmap_free(e->cache_by_key); - avahi_hashmap_free(e->lookups_by_id); - avahi_hashmap_free(e->lookups_by_key); - - if (e->watch_ipv4) - e->server->poll_api->watch_free(e->watch_ipv4); - - if (e->watch_ipv6) - e->server->poll_api->watch_free(e->watch_ipv6); - - if (e->fd_ipv6 >= 0) - close(e->fd_ipv6); - - if (e->fd_ipv4 >= 0) - close(e->fd_ipv4); - - avahi_free(e); -} - -void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e) { - assert(e); - - while (e->cache) - cache_entry_free(e->cache); - - assert(e->cache_n_entries == 0); -} - -void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n) { - assert(e); - - if (a) { - for (e->n_dns_servers = 0; n > 0 && e->n_dns_servers < AVAHI_WIDE_AREA_SERVERS_MAX; a++, n--) - if ((a->proto == AVAHI_PROTO_INET && e->fd_ipv4 >= 0) || (a->proto == AVAHI_PROTO_INET6 && e->fd_ipv6 >= 0)) - e->dns_servers[e->n_dns_servers++] = *a; - } else { - assert(n == 0); - e->n_dns_servers = 0; - } - - e->current_dns_server = 0; - - avahi_wide_area_clear_cache(e); -} - -void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata) { - AvahiWideAreaCacheEntry *c; - - assert(e); - assert(callback); - - callback(";; WIDE AREA CACHE ;;; ", userdata); - - for (c = e->cache; c; c = c->cache_next) { - char *t = avahi_record_to_string(c->record); - callback(t, userdata); - avahi_free(t); - } -} - -unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata) { - AvahiWideAreaCacheEntry *c; - AvahiKey *cname_key; - unsigned n = 0; - - assert(e); - assert(key); - assert(callback); - - for (c = avahi_hashmap_lookup(e->cache_by_key, key); c; c = c->by_key_next) { - callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_CACHED, c->record, userdata); - n++; - } - - if ((cname_key = avahi_key_new_cname(key))) { - - for (c = avahi_hashmap_lookup(e->cache_by_key, cname_key); c; c = c->by_key_next) { - callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_CACHED, c->record, userdata); - n++; - } - - avahi_key_unref(cname_key); - } - - return n; -} - -int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e) { - assert(e); - - return e->n_dns_servers > 0; -} - - - diff --git a/avahi-core/wide-area.h b/avahi-core/wide-area.h deleted file mode 100644 index b1dc570..0000000 --- a/avahi-core/wide-area.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef foowideareahfoo -#define foowideareahfoo - -/*** - 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 "lookup.h" -#include "browse.h" - -typedef struct AvahiWideAreaLookupEngine AvahiWideAreaLookupEngine; -typedef struct AvahiWideAreaLookup AvahiWideAreaLookup; - -typedef void (*AvahiWideAreaLookupCallback)( - AvahiWideAreaLookupEngine *e, - AvahiBrowserEvent event, - AvahiLookupResultFlags flags, - AvahiRecord *r, - void *userdata); - -AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s); -void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e); - -unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); -void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata); -void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n); -void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e); -void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e); -int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e); - -AvahiWideAreaLookup *avahi_wide_area_lookup_new(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); -void avahi_wide_area_lookup_free(AvahiWideAreaLookup *q); - - - -#endif - diff --git a/avahi.pc.in b/avahi.pc.in new file mode 100644 index 0000000..8be903c --- /dev/null +++ b/avahi.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=@libdir@ +includedir=${prefix}/include + +Name: avahi +Description: Avahi Multicast DNS Responder (Embeddable Stack) +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lavahi +Cflags: -D_REENTRANT -I${includedir} diff --git a/avahi/.gitignore b/avahi/.gitignore new file mode 100644 index 0000000..49b7739 --- /dev/null +++ b/avahi/.gitignore @@ -0,0 +1,27 @@ +*.o +*.lo +*.la +Makefile +Makefile.in +.deps +.libs +alternative-test +domain-test +strlst-test +timeval-test +utf8-test +watch-test +watch-test-thread +avahi-reflector +avahi-test +conformance-test +dns-spin-test +dns-test +hashmap-test +prioq-test +querier-test +timeeventq-test +update-test +*-test.log +*-test.trs +test-suite.log diff --git a/avahi/Makefile.am b/avahi/Makefile.am new file mode 100644 index 0000000..bcb101c --- /dev/null +++ b/avahi/Makefile.am @@ -0,0 +1,212 @@ +# 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. + +AM_CFLAGS=-I$(top_srcdir) + +# This cool debug trap works on i386/gcc only +AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' + +avahiincludedir=$(includedir)/src + +avahiinclude_HEADERS = \ + strlst.h \ + address.h \ + alternative.h \ + domain.h \ + cdecl.h \ + defs.h \ + malloc.h \ + watch.h \ + timeval.h \ + simple-watch.h \ + thread-watch.h \ + gccmacro.h \ + error.h \ + llist.h \ + rlist.h \ + core.h \ + log.h \ + rr.h \ + publish.h \ + lookup.h + +lib_LTLIBRARIES = libavahi.la + +if ENABLE_TESTS +noinst_PROGRAMS = \ + strlst-test \ + domain-test \ + alternative-test \ + timeval-test \ + watch-test \ + watch-test-thread \ + utf8-test \ + prioq-test \ + avahi-test \ + conformance-test \ + avahi-reflector \ + dns-test \ + dns-spin-test \ + timeeventq-test \ + hashmap-test \ + querier-test \ + update-test + +TESTS = \ + dns-spin-test \ + dns-test \ + hashmap-test +endif + +libavahi_la_SOURCES = \ + malloc.c malloc.h \ + address.c address.h \ + alternative.c alternative.h \ + error.c error.h \ + strlst.c strlst.h \ + domain.c domain.h \ + timeval.c timeval.h \ + simple-watch.c simple-watch.h \ + thread-watch.c thread-watch.h \ + watch.h gccmacro.h \ + rlist.h rlist.c \ + utf8.c utf8.h \ + i18n.c i18n.h \ + timeeventq.c timeeventq.h\ + iface.c iface.h \ + server.c internal.h entry.c \ + prioq.c prioq.h \ + cache.c cache.h \ + socket.c socket.h \ + response-sched.c response-sched.h \ + query-sched.c query-sched.h \ + probe-sched.c probe-sched.h \ + announce.c announce.h \ + browse.c browse.h \ + rrlist.c rrlist.h \ + resolve-host-name.c \ + resolve-address.c \ + browse-domain.c \ + browse-service-type.c \ + browse-service.c \ + resolve-service.c \ + dns.c dns.h \ + rr.c rr.h rr-util.h \ + core.h lookup.h publish.h \ + log.c log.h \ + browse-dns-server.c \ + fdutil.h fdutil.c \ + util.c util.h \ + hashmap.c hashmap.h \ + wide-area.c wide-area.h \ + multicast-lookup.c multicast-lookup.h \ + querier.c querier.h \ + addr-util.h addr-util.c \ + domain-util.h domain-util.c \ + dns-srv-rr.h + +if HAVE_NETLINK +libavahi_la_SOURCES += \ + iface-linux.c iface-linux.h \ + netlink.c netlink.h +else +if HAVE_PF_ROUTE +libavahi_la_SOURCES += \ + iface-pfroute.c iface-pfroute.h +else +libavahi_la_SOURCES += \ + iface-none.c +endif +endif + +libavahi_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) -DAVAHI_LOCALEDIR=\"$(avahilocaledir)\" +libavahi_la_LIBADD = $(AM_LDADD) $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) $(INTLLIBS) +libavahi_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBAVAHI_VERSION_INFO) + +strlst_test_SOURCES = strlst-test.c +strlst_test_CFLAGS = $(AM_CFLAGS) +strlst_test_LDADD = $(AM_LDADD) libavahi.la + +alternative_test_SOURCES = alternative-test.c +alternative_test_CFLAGS = $(AM_CFLAGS) +alternative_test_LDADD = $(AM_LDADD) libavahi.la + +domain_test_SOURCES = domain-test.c +domain_test_CFLAGS = $(AM_CFLAGS) +domain_test_LDADD = $(AM_LDADD) libavahi.la + +watch_test_SOURCES = watch-test.c +watch_test_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) +watch_test_LDADD = $(AM_LDADD) $(PTHREAD_LIBS) $(PTHREAD_CFLAGS) libavahi.la + +watch_test_thread_SOURCES = $(watch_test_SOURCES) +watch_test_thread_CFLAGS = $(watch_test_CFLAGS) -DUSE_THREAD +watch_test_thread_LDADD = $(watch_test_LDADD) libavahi.la + +timeval_test_SOURCES = timeval-test.c +timeval_test_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) +timeval_test_LDADD = $(AM_LDADD) $(PTHREAD_LIBS) $(PTHREAD_CFLAGS) libavahi.la + +utf8_test_SOURCES = utf8-test.c +utf8_test_CFLAGS = $(AM_CFLAGS) +utf8_test_LDADD = $(AM_LDADD) libavahi.la + +prioq_test_SOURCES = prioq-test.c +prioq_test_CFLAGS = $(AM_CFLAGS) +prioq_test_LDADD = $(AM_LDADD) libavahi.la + +avahi_test_SOURCES = avahi-test.c +avahi_test_CFLAGS = $(AM_CFLAGS) +avahi_test_LDADD = $(AM_LDADD) libavahi.la + +update_test_SOURCES = update-test.c +update_test_CFLAGS = $(AM_CFLAGS) +update_test_LDADD = $(AM_LDADD) libavahi.la + +querier_test_SOURCES = querier-test.c +querier_test_CFLAGS = $(AM_CFLAGS) +querier_test_LDADD = $(AM_LDADD) libavahi.la + +conformance_test_SOURCES = conformance-test.c +conformance_test_CFLAGS = $(AM_CFLAGS) +conformance_test_LDADD = $(AM_LDADD) libavahi.la + +avahi_reflector_SOURCES = avahi-reflector.c +avahi_reflector_CFLAGS = $(AM_CFLAGS) +avahi_reflector_LDADD = $(AM_LDADD) libavahi.la + +dns_test_SOURCES = dns-test.c +dns_test_CFLAGS = $(AM_CFLAGS) +dns_test_LDADD = $(AM_LDADD) libavahi.la + +dns_spin_test_SOURCES = dns-spin-test.c +dns_spin_test_CFLAGS = $(AM_CFLAGS) +dns_spin_test_LDADD = $(AM_LDADD) libavahi.la + +timeeventq_test_SOURCES = timeeventq-test.c +timeeventq_test_CFLAGS = $(AM_CFLAGS) +timeeventq_test_LDADD = $(AM_LDADD) libavahi.la + +hashmap_test_SOURCES = hashmap-test.c +hashmap_test_CFLAGS = $(AM_CFLAGS) +hashmap_test_LDADD = $(AM_LDADD) libavahi.la + +valgrind: avahi-test + libtool --mode=execute valgrind ./avahi-test + +gdb: avahi-test + libtool --mode=execute gdb ./avahi-test diff --git a/avahi/addr-util.c b/avahi/addr-util.c new file mode 100644 index 0000000..979b1b7 --- /dev/null +++ b/avahi/addr-util.c @@ -0,0 +1,94 @@ +/*** + 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 "addr-util.h" + +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) { + assert(sa); + assert(ret_addr); + + assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + ret_addr->proto = avahi_af_to_proto(sa->sa_family); + + if (sa->sa_family == AF_INET) + memcpy(&ret_addr->data.ipv4, &((const struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4)); + else + memcpy(&ret_addr->data.ipv6, &((const struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6)); + + return ret_addr; +} + +uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa) { + assert(sa); + + assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + if (sa->sa_family == AF_INET) + return ntohs(((const struct sockaddr_in*) sa)->sin_port); + else + return ntohs(((const struct sockaddr_in6*) sa)->sin6_port); +} + +int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a) { + + static const uint8_t ipv4_in_ipv6[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + assert(a); + + if (a->proto != AVAHI_PROTO_INET6) + return 0; + + return memcmp(a->data.ipv6.address, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0; +} + +#define IPV4LL_NETWORK 0xA9FE0000L +#define IPV4LL_NETMASK 0xFFFF0000L +#define IPV6LL_NETWORK 0xFE80 +#define IPV6LL_NETMASK 0xFFC0 + +int avahi_address_is_link_local(const AvahiAddress *a) { + assert(a); + + if (a->proto == AVAHI_PROTO_INET) { + uint32_t n = ntohl(a->data.ipv4.address); + return (n & IPV4LL_NETMASK) == IPV4LL_NETWORK; + } + else if (a->proto == AVAHI_PROTO_INET6) { + unsigned n = (a->data.ipv6.address[0] << 8) | (a->data.ipv6.address[1] << 0); + return (n & IPV6LL_NETMASK) == IPV6LL_NETWORK; + } + + return 0; +} diff --git a/avahi/addr-util.h b/avahi/addr-util.h new file mode 100644 index 0000000..313772b --- /dev/null +++ b/avahi/addr-util.h @@ -0,0 +1,47 @@ +#ifndef fooaddrutilhfoo +#define fooaddrutilhfoo + +/*** + 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 + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Make an address structture of a sockaddr structure */ +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr); + +/** Return the port number of a sockaddr structure (either IPv4 or IPv6) */ +uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa); + +/** Check whether the specified IPv6 address is in fact an + * encapsulated IPv4 address, returns 1 if yes, 0 otherwise */ +int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a); + +/** Check whether the specified address is a link-local IPv4 or IPv6 address; + * returns 1 if yes, 0 otherwise */ +int avahi_address_is_link_local(const AvahiAddress *a); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/address.c b/avahi/address.c new file mode 100644 index 0000000..e8f6148 --- /dev/null +++ b/avahi/address.c @@ -0,0 +1,155 @@ +/*** + 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 "address.h" +#include "malloc.h" + +static size_t address_get_size(const AvahiAddress *a) { + assert(a); + + if (a->proto == AVAHI_PROTO_INET) + return 4; + else if (a->proto == AVAHI_PROTO_INET6) + return 16; + + return 0; +} + +int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) { + assert(a); + assert(b); + + if (a->proto != b->proto) + return -1; + + return memcmp(a->data.data, b->data.data, address_get_size(a)); +} + +char *avahi_address_snprint(char *s, size_t length, const AvahiAddress *a) { + assert(s); + assert(length); + assert(a); + + if (!(inet_ntop(avahi_proto_to_af(a->proto), a->data.data, s, length))) + return NULL; + + return s; +} + +char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length) { + assert(ret_s); + assert(length > 0); + assert(a); + + if (a->proto == AVAHI_PROTO_INET) { + uint32_t n = ntohl(a->data.ipv4.address); + snprintf( + ret_s, length, + "%u.%u.%u.%u.in-addr.arpa", + n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24); + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + snprintf( + ret_s, length, + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", + a->data.ipv6.address[15] & 0xF, a->data.ipv6.address[15] >> 4, + a->data.ipv6.address[14] & 0xF, a->data.ipv6.address[14] >> 4, + a->data.ipv6.address[13] & 0xF, a->data.ipv6.address[13] >> 4, + a->data.ipv6.address[12] & 0xF, a->data.ipv6.address[12] >> 4, + a->data.ipv6.address[11] & 0xF, a->data.ipv6.address[11] >> 4, + a->data.ipv6.address[10] & 0xF, a->data.ipv6.address[10] >> 4, + a->data.ipv6.address[ 9] & 0xF, a->data.ipv6.address[ 9] >> 4, + a->data.ipv6.address[ 8] & 0xF, a->data.ipv6.address[ 8] >> 4, + a->data.ipv6.address[ 7] & 0xF, a->data.ipv6.address[ 7] >> 4, + a->data.ipv6.address[ 6] & 0xF, a->data.ipv6.address[ 6] >> 4, + a->data.ipv6.address[ 5] & 0xF, a->data.ipv6.address[ 5] >> 4, + a->data.ipv6.address[ 4] & 0xF, a->data.ipv6.address[ 4] >> 4, + a->data.ipv6.address[ 3] & 0xF, a->data.ipv6.address[ 3] >> 4, + a->data.ipv6.address[ 2] & 0xF, a->data.ipv6.address[ 2] >> 4, + a->data.ipv6.address[ 1] & 0xF, a->data.ipv6.address[ 1] >> 4, + a->data.ipv6.address[ 0] & 0xF, a->data.ipv6.address[ 0] >> 4); + } + + return ret_s; +} + +AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol proto, AvahiAddress *ret_addr) { + assert(ret_addr); + assert(s); + + if (proto == AVAHI_PROTO_UNSPEC) { + if (inet_pton(AF_INET, s, ret_addr->data.data) <= 0) { + if (inet_pton(AF_INET6, s, ret_addr->data.data) <= 0) + return NULL; + else + ret_addr->proto = AVAHI_PROTO_INET6; + } else + ret_addr->proto = AVAHI_PROTO_INET; + } else { + if (inet_pton(avahi_proto_to_af(proto), s, ret_addr->data.data) <= 0) + return NULL; + + ret_addr->proto = proto; + } + + return ret_addr; +} + +int avahi_proto_to_af(AvahiProtocol proto) { + if (proto == AVAHI_PROTO_INET) + return AF_INET; + if (proto == AVAHI_PROTO_INET6) + return AF_INET6; + + assert(proto == AVAHI_PROTO_UNSPEC); + return AF_UNSPEC; +} + +AvahiProtocol avahi_af_to_proto(int af) { + if (af == AF_INET) + return AVAHI_PROTO_INET; + if (af == AF_INET6) + return AVAHI_PROTO_INET6; + + assert(af == AF_UNSPEC); + return AVAHI_PROTO_UNSPEC; +} + +const char* avahi_proto_to_string(AvahiProtocol proto) { + if (proto == AVAHI_PROTO_INET) + return "IPv4"; + if (proto == AVAHI_PROTO_INET6) + return "IPv6"; + + assert(proto == AVAHI_PROTO_UNSPEC); + return "UNSPEC"; +} diff --git a/avahi/address.h b/avahi/address.h new file mode 100644 index 0000000..7051ef1 --- /dev/null +++ b/avahi/address.h @@ -0,0 +1,119 @@ +#ifndef fooaddresshfoo +#define fooaddresshfoo + +/*** + 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. +***/ + +/** \file address.h Definitions and functions to manipulate IP addresses. */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** Protocol family specification, takes the values AVAHI_PROTO_INET, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC */ +typedef int AvahiProtocol; + +/** Numeric network interface index. Takes OS dependent values and the special constant AVAHI_IF_UNSPEC */ +typedef int AvahiIfIndex; + +/** Values for AvahiProtocol */ +enum { + AVAHI_PROTO_INET = 0, /**< IPv4 */ + AVAHI_PROTO_INET6 = 1, /**< IPv6 */ + AVAHI_PROTO_UNSPEC = -1 /**< Unspecified/all protocol(s) */ +}; + +/** Special values for AvahiIfIndex */ +enum { + AVAHI_IF_UNSPEC = -1 /**< Unspecified/all interface(s) */ +}; + +/** Maximum size of an address in string form */ +#define AVAHI_ADDRESS_STR_MAX 40 /* IPv6 Max = 4*8 + 7 + 1 for NUL */ + +/** Return TRUE if the specified interface index is valid */ +#define AVAHI_IF_VALID(ifindex) (((ifindex) >= 0) || ((ifindex) == AVAHI_IF_UNSPEC)) + +/** Return TRUE if the specified protocol is valid */ +#define AVAHI_PROTO_VALID(protocol) (((protocol) == AVAHI_PROTO_INET) || ((protocol) == AVAHI_PROTO_INET6) || ((protocol) == AVAHI_PROTO_UNSPEC)) + +/** An IPv4 address */ +typedef struct AvahiIPv4Address { + uint32_t address; /**< Address data in network byte order. */ +} AvahiIPv4Address; + +/** An IPv6 address */ +typedef struct AvahiIPv6Address { + uint8_t address[16]; /**< Address data */ +} AvahiIPv6Address; + +/** Protocol (address family) independent address structure */ +typedef struct AvahiAddress { + AvahiProtocol proto; /**< Address family */ + + union { + AvahiIPv6Address ipv6; /**< Address when IPv6 */ + AvahiIPv4Address ipv4; /**< Address when IPv4 */ + uint8_t data[1]; /**< Type-independent data field */ + } data; +} AvahiAddress; + +/** @{ \name Comparison */ + +/** Compare two addresses. Returns 0 when equal, a negative value when a < b, a positive value when a > b. */ +int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); + +/** @} */ + +/** @{ \name String conversion */ + +/** Convert the specified address *a to a human readable character string, use AVAHI_ADDRESS_STR_MAX to allocate an array of the right size */ +char *avahi_address_snprint(char *ret_s, size_t length, const AvahiAddress *a); + +/** Convert the specified human readable character string to an + * address structure. Set af to AVAHI_UNSPEC for automatic address + * family detection. */ +AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol af, AvahiAddress *ret_addr); + +/** @} */ + +/** \cond fulldocs */ +/** Generate the DNS reverse lookup name for an IPv4 or IPv6 address. */ +char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length); +/** \endcond */ + +/** @{ \name Protocol/address family handling */ + +/** Map AVAHI_PROTO_xxx constants to Unix AF_xxx constants */ +int avahi_proto_to_af(AvahiProtocol proto); + +/** Map Unix AF_xxx constants to AVAHI_PROTO_xxx constants */ +AvahiProtocol avahi_af_to_proto(int af); + +/** Return a textual representation of the specified protocol number. i.e. "IPv4", "IPv6" or "UNSPEC" */ +const char* avahi_proto_to_string(AvahiProtocol proto); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/alternative-test.c b/avahi/alternative-test.c new file mode 100644 index 0000000..9255435 --- /dev/null +++ b/avahi/alternative-test.c @@ -0,0 +1,90 @@ +/*** + 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 "alternative.h" +#include "malloc.h" +#include "domain.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + const char* const test_strings[] = { + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXüüüüüüü", + "gurke", + "-", + " #", + "1", + "#0", + " #0", + " #1", + "#-1", + " #-1", + "-0", + "--0", + "-1", + "--1", + "-2", + "gurke1", + "gurke0", + "gurke-2", + "gurke #0", + "gurke #1", + "gurke #", + "gurke#1", + "gurke-", + "gurke---", + "gurke #", + "gurke ###", + NULL + }; + + char *r = NULL; + int i, j, k; + + for (k = 0; test_strings[k]; k++) { + + printf(">>>>>%s<<<<\n", test_strings[k]); + + for (j = 0; j < 2; j++) { + + for (i = 0; i <= 100; i++) { + char *n; + + n = i == 0 ? avahi_strdup(test_strings[k]) : (j ? avahi_alternative_service_name(r) : avahi_alternative_host_name(r)); + avahi_free(r); + r = n; + + if (j) + assert(avahi_is_valid_service_name(n)); + else + assert(avahi_is_valid_host_name(n)); + + printf("%s\n", r); + } + } + } + + avahi_free(r); + return 0; +} diff --git a/avahi/alternative.c b/avahi/alternative.c new file mode 100644 index 0000000..b3d39f0 --- /dev/null +++ b/avahi/alternative.c @@ -0,0 +1,182 @@ +/*** + 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 "alternative.h" +#include "malloc.h" +#include "domain.h" +#include "utf8.h" + +static void drop_incomplete_utf8(char *c) { + char *e; + + e = strchr(c, 0) - 1; + + while (e >= c) { + + if (avahi_utf8_valid(c)) + break; + + assert(*e & 128); + *e = 0; + + e--; + } +} + +char *avahi_alternative_host_name(const char *s) { + const char *e; + char *r; + + assert(s); + + if (!avahi_is_valid_host_name(s)) + return NULL; + + if ((e = strrchr(s, '-'))) { + const char *p; + + e++; + + for (p = e; *p; p++) + if (!isdigit(*p)) { + e = NULL; + break; + } + + if (e && (*e == '0' || *e == 0)) + e = NULL; + } + + if (e) { + char *c, *m; + size_t l; + int n; + + n = atoi(e)+1; + if (!(m = avahi_strdup_printf("%i", n))) + return NULL; + + l = e-s-1; + + if (l >= AVAHI_LABEL_MAX-1-strlen(m)-1) + l = AVAHI_LABEL_MAX-1-strlen(m)-1; + + if (!(c = avahi_strndup(s, l))) { + avahi_free(m); + return NULL; + } + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s-%s", c, m); + avahi_free(c); + avahi_free(m); + + } else { + char *c; + + if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-2))) + return NULL; + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s-2", c); + avahi_free(c); + } + + assert(avahi_is_valid_host_name(r)); + + return r; +} + +char *avahi_alternative_service_name(const char *s) { + const char *e; + char *r; + + assert(s); + + if (!avahi_is_valid_service_name(s)) + return NULL; + + if ((e = strstr(s, " #"))) { + const char *n, *p; + e += 2; + + while ((n = strstr(e, " #"))) + e = n + 2; + + for (p = e; *p; p++) + if (!isdigit(*p)) { + e = NULL; + break; + } + + if (e && (*e == '0' || *e == 0)) + e = NULL; + } + + if (e) { + char *c, *m; + size_t l; + int n; + + n = atoi(e)+1; + if (!(m = avahi_strdup_printf("%i", n))) + return NULL; + + l = e-s-2; + + if (l >= AVAHI_LABEL_MAX-1-strlen(m)-2) + l = AVAHI_LABEL_MAX-1-strlen(m)-2; + + if (!(c = avahi_strndup(s, l))) { + avahi_free(m); + return NULL; + } + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s #%s", c, m); + avahi_free(c); + avahi_free(m); + } else { + char *c; + + if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-3))) + return NULL; + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s #2", c); + avahi_free(c); + } + + assert(avahi_is_valid_service_name(r)); + + return r; +} diff --git a/avahi/alternative.h b/avahi/alternative.h new file mode 100644 index 0000000..c4a2846 --- /dev/null +++ b/avahi/alternative.h @@ -0,0 +1,43 @@ +#ifndef fooalternativehfoo +#define fooalternativehfoo + +/*** + 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. +***/ + +/** \file alternative.h Functions to find alternative names for hosts and services in the case of name collision */ + +#include + +AVAHI_C_DECL_BEGIN + +/** Find an alternative for the specified host name. If called with an + * original host name, "-2" is appended, afterwards the number is + * increased on each call. (i.e. "foo" becomes "foo-2" becomes "foo-3" + * and so on.) avahi_free() the result. */ +char *avahi_alternative_host_name(const char *s); + +/** Find an alternative for the specified service name. If called with + * an original service name, " #2" is appended. Afterwards the number + * is increased on each call (i.e. "foo" becomes "foo #2" becomes "foo + * #3" and so on.) avahi_free() the result. */ +char *avahi_alternative_service_name(const char *s); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/announce.c b/avahi/announce.c new file mode 100644 index 0000000..ac75ad3 --- /dev/null +++ b/avahi/announce.c @@ -0,0 +1,524 @@ +/*** + 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 "announce.h" +#include "log.h" +#include "rr-util.h" + +#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250 +#define AVAHI_PROBE_JITTER_MSEC 250 +#define AVAHI_PROBE_INTERVAL_MSEC 250 + +static void remove_announcer(AvahiServer *s, AvahiAnnouncer *a) { + assert(s); + assert(a); + + if (a->time_event) + avahi_time_event_free(a->time_event); + + AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_interface, a->interface->announcers, a); + AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_entry, a->entry->announcers, a); + + avahi_free(a); +} + +static void elapse_announce(AvahiTimeEvent *e, void *userdata); + +static void set_timeout(AvahiAnnouncer *a, const struct timeval *tv) { + assert(a); + + if (!tv) { + if (a->time_event) { + avahi_time_event_free(a->time_event); + a->time_event = NULL; + } + } else { + + if (a->time_event) + avahi_time_event_update(a->time_event, tv); + else + a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a); + } +} + +static void next_state(AvahiAnnouncer *a); + +void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) { + AvahiEntry *e; + assert(g); + assert(!g->dead); + + /* Check whether all group members have been probed */ + + if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) + return; + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED); + + if (g->dead) + return; + + for (e = g->entries; e; e = e->by_group_next) { + AvahiAnnouncer *a; + + for (a = e->announcers; a; a = a->by_entry_next) { + + if (a->state != AVAHI_WAITING) + continue; + + a->state = AVAHI_ANNOUNCING; + + if (immediately) { + /* Shortcut */ + + a->n_iteration = 1; + next_state(a); + } else { + struct timeval tv; + a->n_iteration = 0; + avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + set_timeout(a, &tv); + } + } + } +} + +static void next_state(AvahiAnnouncer *a) { + assert(a); + + if (a->state == AVAHI_WAITING) { + + assert(a->entry->group); + + avahi_s_entry_group_check_probed(a->entry->group, 1); + + } else if (a->state == AVAHI_PROBING) { + + if (a->n_iteration >= 4) { + /* Probing done */ + + if (a->entry->group) { + assert(a->entry->group->n_probing); + a->entry->group->n_probing--; + } + + if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING) + a->state = AVAHI_WAITING; + else { + a->state = AVAHI_ANNOUNCING; + a->n_iteration = 1; + } + + set_timeout(a, NULL); + next_state(a); + } else { + struct timeval tv; + + avahi_interface_post_probe(a->interface, a->entry->record, 0); + + avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0); + set_timeout(a, &tv); + + a->n_iteration++; + } + + } else if (a->state == AVAHI_ANNOUNCING) { + + if (a->entry->flags & AVAHI_PUBLISH_UNIQUE) + /* Send the whole rrset at once */ + avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0); + else + avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0); + + avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0); + + if (++a->n_iteration >= 4) { + /* Announcing done */ + + a->state = AVAHI_ESTABLISHED; + + set_timeout(a, NULL); + } else { + struct timeval tv; + avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + + if (a->n_iteration < 10) + a->sec_delay *= 2; + + set_timeout(a, &tv); + } + } +} + +static void elapse_announce(AvahiTimeEvent *e, void *userdata) { + assert(e); + + next_state(userdata); +} + +static AvahiAnnouncer *get_announcer(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + + for (a = e->announcers; a; a = a->by_entry_next) + if (a->interface == i) + return a; + + return NULL; +} + +static void go_to_initial_state(AvahiAnnouncer *a) { + AvahiEntry *e; + struct timeval tv; + + assert(a); + e = a->entry; + + if ((e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)) + a->state = AVAHI_PROBING; + else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) { + + if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED) + a->state = AVAHI_ANNOUNCING; + else + a->state = AVAHI_WAITING; + + } else + a->state = AVAHI_ESTABLISHED; + + a->n_iteration = 1; + a->sec_delay = 1; + + if (a->state == AVAHI_PROBING && e->group) + e->group->n_probing++; + + if (a->state == AVAHI_PROBING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC)); + else if (a->state == AVAHI_ANNOUNCING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC)); + else + set_timeout(a, NULL); +} + +static void new_announcer(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { + AvahiAnnouncer *a; + + assert(s); + assert(i); + assert(e); + assert(!e->dead); + + if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e)) + return; + + /* We don't want duplicate announcers */ + if (get_announcer(s, e, i)) + return; + + if ((!(a = avahi_new(AvahiAnnouncer, 1)))) { + avahi_log_error(__FILE__": Out of memory."); + return; + } + + a->server = s; + a->interface = i; + a->entry = e; + a->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_interface, i->announcers, a); + AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_entry, e->announcers, a); + + go_to_initial_state(a); +} + +void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { + AvahiEntry *e; + + assert(s); + assert(i); + + if (!i->announcing) + return; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + new_announcer(s, i, e); +} + +static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + AvahiEntry *e = userdata; + + assert(m); + assert(i); + assert(e); + assert(!e->dead); + + new_announcer(m->server, i, e); +} + +void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) { + assert(s); + assert(e); + assert(!e->dead); + + avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e); +} + +void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) { + AvahiEntry *e; + + assert(s); + assert(g); + + for (e = g->entries; e; e = e->by_group_next) + if (!e->dead) + avahi_announce_entry(s, e); +} + +int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + assert(!e->dead); + + if (!(a = get_announcer(s, e, i))) + return 0; + + return + a->state == AVAHI_ANNOUNCING || + a->state == AVAHI_ESTABLISHED || + (a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE)); +} + +int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + assert(!e->dead); + + if (!(a = get_announcer(s, e, i))) + return 0; + + return + a->state == AVAHI_PROBING || + (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE)); +} + +void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + + if (!(a = get_announcer(s, e, i))) + return; + + if (a->state == AVAHI_PROBING && a->entry->group) + a->entry->group->n_probing--; + + go_to_initial_state(a); +} + +static AvahiRecord *make_goodbye_record(AvahiRecord *r) { + AvahiRecord *g; + + assert(r); + + if (!(g = avahi_record_copy(r))) + return NULL; /* OOM */ + + assert(g->ref == 1); + g->ttl = 0; + + return g; +} + +static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) { + AvahiEntry *i; + + assert(s); + assert(e); + + for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) { + + if ((i == e) || (i->dead)) + continue; + + if (!avahi_record_equal_no_ttl(i->record, e->record)) + continue; + + return 1; + } + + return 0; +} + +static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + AvahiEntry *e = userdata; + AvahiRecord *g; + + assert(m); + assert(i); + assert(e); + assert(!e->dead); + + if (!avahi_interface_match(i, e->interface, e->protocol)) + return; + + if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE) + return; + + if (!avahi_entry_is_registered(m->server, e, i)) + return; + + if (is_duplicate_entry(m->server, e)) + return; + + if (!(g = make_goodbye_record(e->record))) + return; /* OOM */ + + avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1); + avahi_record_unref(g); +} + +static void reannounce(AvahiAnnouncer *a) { + AvahiEntry *e; + struct timeval tv; + + assert(a); + e = a->entry; + + /* If the group this entry belongs to is not even commited, there's nothing to reannounce */ + if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION)) + return; + + /* Because we might change state we decrease the probing counter first */ + if (a->state == AVAHI_PROBING && a->entry->group) + a->entry->group->n_probing--; + + if (a->state == AVAHI_PROBING || + (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))) + + /* We were probing or waiting after probe, so we restart probing from the beginning here */ + + a->state = AVAHI_PROBING; + else if (a->state == AVAHI_WAITING) + + /* We were waiting, but were not probing before, so we continue waiting */ + a->state = AVAHI_WAITING; + + else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE) + + /* No announcer needed */ + a->state = AVAHI_ESTABLISHED; + + else { + + /* Ok, let's restart announcing */ + a->state = AVAHI_ANNOUNCING; + } + + /* Now let's increase the probing counter again */ + if (a->state == AVAHI_PROBING && e->group) + e->group->n_probing++; + + a->n_iteration = 1; + a->sec_delay = 1; + + if (a->state == AVAHI_PROBING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC)); + else if (a->state == AVAHI_ANNOUNCING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC)); + else + set_timeout(a, NULL); +} + + +static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + AvahiEntry *e = userdata; + AvahiAnnouncer *a; + + assert(m); + assert(i); + assert(e); + assert(!e->dead); + + if (!(a = get_announcer(m->server, e, i))) + return; + + reannounce(a); +} + +void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) { + + assert(s); + assert(e); + assert(!e->dead); + + avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e); +} + +void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) { + assert(s); + assert(i); + + if (send_goodbye) + if (i->announcing) { + AvahiEntry *e; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + send_goodbye_callback(s->monitor, i, e); + } + + if (remove) + while (i->announcers) + remove_announcer(s, i->announcers); +} + +void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) { + assert(s); + assert(e); + + if (send_goodbye) + if (!e->dead) + avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e); + + if (remove) + while (e->announcers) + remove_announcer(s, e->announcers); +} + diff --git a/avahi/announce.h b/avahi/announce.h new file mode 100644 index 0000000..f57b9d7 --- /dev/null +++ b/avahi/announce.h @@ -0,0 +1,69 @@ +#ifndef fooannouncehfoo +#define fooannouncehfoo + +/*** + 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 struct AvahiAnnouncer AvahiAnnouncer; + +#include +#include "iface.h" +#include "internal.h" +#include "timeeventq.h" +#include "publish.h" + +typedef enum { + AVAHI_PROBING, /* probing phase */ + AVAHI_WAITING, /* wait for other records in group */ + AVAHI_ANNOUNCING, /* announcing phase */ + AVAHI_ESTABLISHED /* we'e established */ +} AvahiAnnouncerState; + +struct AvahiAnnouncer { + AvahiServer *server; + AvahiInterface *interface; + AvahiEntry *entry; + + AvahiTimeEvent *time_event; + + AvahiAnnouncerState state; + unsigned n_iteration; + unsigned sec_delay; + + AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_interface); + AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_entry); +}; + +void avahi_announce_interface(AvahiServer *s, AvahiInterface *i); +void avahi_announce_entry(AvahiServer *s, AvahiEntry *e); +void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g); + +void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + +void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately); + +int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); +int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + +void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int rem); +void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int rem); + +void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e); + +#endif diff --git a/avahi/avahi-reflector.c b/avahi/avahi-reflector.c new file mode 100644 index 0000000..97032c2 --- /dev/null +++ b/avahi/avahi-reflector.c @@ -0,0 +1,61 @@ +/*** + 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 + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) { + AvahiServer *server; + AvahiServerConfig config; + int error; + AvahiSimplePoll *simple_poll; + + simple_poll = avahi_simple_poll_new(); + + avahi_server_config_init(&config); + config.publish_hinfo = 0; + config.publish_addresses = 0; + config.publish_workstation = 0; + config.publish_domain = 0; + config.use_ipv6 = 0; + config.enable_reflector = 1; + + server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error); + avahi_server_config_free(&config); + + for (;;) + if (avahi_simple_poll_iterate(simple_poll, -1) != 0) + break; + + avahi_server_free(server); + avahi_simple_poll_free(simple_poll); + + return 0; +} diff --git a/avahi/avahi-test.c b/avahi/avahi-test.c new file mode 100644 index 0000000..31edd0c --- /dev/null +++ b/avahi/avahi-test.c @@ -0,0 +1,402 @@ +/*** + 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 + +static AvahiSEntryGroup *group = NULL; +static AvahiServer *server = NULL; +static char *service_name = NULL; + +static const AvahiPoll *poll_api; + +static void quit_timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void* userdata) { + AvahiSimplePoll *simple_poll = userdata; + + avahi_simple_poll_quit(simple_poll); +} + +static void dump_line(const char *text, AVAHI_GCC_UNUSED void* userdata) { + printf("%s\n", text); +} + +static void dump_timeout_callback(AvahiTimeout *timeout, void* userdata) { + struct timeval tv; + + AvahiServer *avahi = userdata; + avahi_server_dump(avahi, dump_line, NULL); + + avahi_elapse_time(&tv, 5000, 0); + poll_api->timeout_update(timeout, &tv); +} + +static const char *browser_event_to_string(AvahiBrowserEvent event) { + switch (event) { + case AVAHI_BROWSER_NEW : return "NEW"; + case AVAHI_BROWSER_REMOVE : return "REMOVE"; + case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED"; + case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW"; + case AVAHI_BROWSER_FAILURE : return "FAILURE"; + } + + abort(); +} + +static const char *resolver_event_to_string(AvahiResolverEvent event) { + switch (event) { + case AVAHI_RESOLVER_FOUND: return "FOUND"; + case AVAHI_RESOLVER_FAILURE: return "FAILURE"; + } + abort(); +} + +static void record_browser_callback( + AvahiSRecordBrowser *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + char *t; + + assert(r); + + if (record) { + avahi_log_debug("RB: record [%s] on %i.%i is %s", t = avahi_record_to_string(record), interface, protocol, browser_event_to_string(event)); + avahi_free(t); + } else + avahi_log_debug("RB: [%s]", browser_event_to_string(event)); + +} + +static void remove_entries(void); +static void create_entries(int new_name); + +static void entry_group_callback(AVAHI_GCC_UNUSED AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) { + avahi_log_debug("entry group state: %i", state); + + if (state == AVAHI_ENTRY_GROUP_COLLISION) { + remove_entries(); + create_entries(1); + avahi_log_debug("Service name conflict, retrying with <%s>", service_name); + } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) { + avahi_log_debug("Service established under name <%s>", service_name); + } +} + +static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) { + + server = s; + avahi_log_debug("server state: %i", state); + + if (state == AVAHI_SERVER_RUNNING) { + avahi_log_debug("Server startup complete. Host name is <%s>. Service cookie is %u", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); + create_entries(0); + } else if (state == AVAHI_SERVER_COLLISION) { + char *n; + remove_entries(); + + n = avahi_alternative_host_name(avahi_server_get_host_name(s)); + + avahi_log_debug("Host name conflict, retrying with <%s>", n); + avahi_server_set_host_name(s, n); + avahi_free(n); + } +} + +static void remove_entries(void) { + if (group) + avahi_s_entry_group_reset(group); +} + +static void create_entries(int new_name) { + AvahiAddress a; + AvahiRecord *r; + + remove_entries(); + + if (!group) + group = avahi_s_entry_group_new(server, entry_group_callback, NULL); + + assert(avahi_s_entry_group_is_empty(group)); + + if (!service_name) + service_name = avahi_strdup("Test Service"); + else if (new_name) { + char *n = avahi_alternative_service_name(service_name); + avahi_free(service_name); + service_name = n; + } + + if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, service_name, "_http._tcp", NULL, NULL, 80, "foo", NULL) < 0) { + avahi_log_error("Failed to add HTTP service"); + goto fail; + } + + if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, service_name, "_ftp._tcp", NULL, NULL, 21, "foo", NULL) < 0) { + avahi_log_error("Failed to add FTP service"); + goto fail; + } + + if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,service_name, "_webdav._tcp", NULL, NULL, 80, "foo", NULL) < 0) { + avahi_log_error("Failed to add WEBDAV service"); + goto fail; + } + + if (avahi_server_add_dns_server_address(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &a), 53) < 0) { + avahi_log_error("Failed to add new DNS Server address"); + goto fail; + } + + r = avahi_record_new_full("cname.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, AVAHI_DEFAULT_TTL); + r->data.cname.name = avahi_strdup("cocaine.local"); + + if (avahi_server_add(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, r) < 0) { + avahi_record_unref(r); + avahi_log_error("Failed to add CNAME record"); + goto fail; + } + avahi_record_unref(r); + + avahi_s_entry_group_commit(group); + return; + +fail: + if (group) + avahi_s_entry_group_free(group); + + group = NULL; +} + +static void hnr_callback( + AVAHI_GCC_UNUSED AvahiSHostNameResolver *r, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *hostname, + const AvahiAddress *a, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + char t[AVAHI_ADDRESS_STR_MAX]; + + if (a) + avahi_address_snprint(t, sizeof(t), a); + + avahi_log_debug("HNR: (%i.%i) <%s> -> %s [%s]", iface, protocol, hostname, a ? t : "n/a", resolver_event_to_string(event)); +} + +static void ar_callback( + AVAHI_GCC_UNUSED AvahiSAddressResolver *r, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const AvahiAddress *a, + const char *hostname, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + char t[AVAHI_ADDRESS_STR_MAX]; + + avahi_address_snprint(t, sizeof(t), a); + + avahi_log_debug("AR: (%i.%i) %s -> <%s> [%s]", iface, protocol, t, hostname ? hostname : "n/a", resolver_event_to_string(event)); +} + +static void db_callback( + AVAHI_GCC_UNUSED AvahiSDomainBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + + avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain ? domain : "NULL", browser_event_to_string(event)); +} + +static void stb_callback( + AVAHI_GCC_UNUSED AvahiSServiceTypeBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *service_type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + + avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type ? service_type : "NULL", domain ? domain : "NULL", browser_event_to_string(event)); +} + +static void sb_callback( + AVAHI_GCC_UNUSED AvahiSServiceBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *service_type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name ? name : "NULL", service_type ? service_type : "NULL", domain ? domain : "NULL", browser_event_to_string(event)); +} + +static void sr_callback( + AVAHI_GCC_UNUSED AvahiSServiceResolver *r, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char*service_type, + const char*domain_name, + const char*hostname, + const AvahiAddress *a, + uint16_t port, + AvahiStringList *txt, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + + if (event != AVAHI_RESOLVER_FOUND) + avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain_name, resolver_event_to_string(event)); + else { + char t[AVAHI_ADDRESS_STR_MAX], *s; + + avahi_address_snprint(t, sizeof(t), a); + + s = avahi_string_list_to_string(txt); + avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s>: %s/%s:%i (%s) [%s]", iface, protocol, name, service_type, domain_name, hostname, t, port, s, resolver_event_to_string(event)); + avahi_free(s); + } +} + +static void dsb_callback( + AVAHI_GCC_UNUSED AvahiSDNSServerBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char*hostname, + const AvahiAddress *a, + uint16_t port, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + + char t[AVAHI_ADDRESS_STR_MAX] = "n/a"; + + if (a) + avahi_address_snprint(t, sizeof(t), a); + + avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname ? hostname : "NULL", t, port, browser_event_to_string(event)); +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + AvahiSRecordBrowser *r; + AvahiSHostNameResolver *hnr; + AvahiSAddressResolver *ar; + AvahiKey *k; + AvahiServerConfig config; + AvahiAddress a; + AvahiSDomainBrowser *db; + AvahiSServiceTypeBrowser *stb; + AvahiSServiceBrowser *sb; + AvahiSServiceResolver *sr; + AvahiSDNSServerBrowser *dsb; + AvahiSimplePoll *simple_poll; + int error; + struct timeval tv; + + simple_poll = avahi_simple_poll_new(); + poll_api = avahi_simple_poll_get(simple_poll); + + avahi_server_config_init(&config); + + avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); + config.n_wide_area_servers = 1; + config.enable_wide_area = 1; + + server = avahi_server_new(poll_api, &config, server_callback, NULL, &error); + avahi_server_config_free(&config); + + k = avahi_key_new("_http._tcp.0pointer.de", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); + r = avahi_s_record_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, k, 0, record_browser_callback, NULL); + avahi_key_unref(k); + + hnr = avahi_s_host_name_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "cname.local", AVAHI_PROTO_UNSPEC, 0, hnr_callback, NULL); + + ar = avahi_s_address_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_address_parse("192.168.50.1", AVAHI_PROTO_INET, &a), 0, ar_callback, NULL); + + db = avahi_s_domain_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, db_callback, NULL); + + stb = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, 0, stb_callback, NULL); + + sb = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, 0, sb_callback, NULL); + + sr = avahi_s_service_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "Ecstasy HTTP", "_http._tcp", "local", AVAHI_PROTO_UNSPEC, 0, sr_callback, NULL); + + dsb = avahi_s_dns_server_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "local", AVAHI_DNS_SERVER_RESOLVE, AVAHI_PROTO_UNSPEC, 0, dsb_callback, NULL); + + avahi_elapse_time(&tv, 1000*5, 0); + poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, server); + + avahi_elapse_time(&tv, 1000*60, 0); + poll_api->timeout_new(poll_api, &tv, quit_timeout_callback, simple_poll); + + avahi_simple_poll_loop(simple_poll); + + avahi_s_record_browser_free(r); + avahi_s_host_name_resolver_free(hnr); + avahi_s_address_resolver_free(ar); + avahi_s_domain_browser_free(db); + avahi_s_service_type_browser_free(stb); + avahi_s_service_browser_free(sb); + avahi_s_service_resolver_free(sr); + avahi_s_dns_server_browser_free(dsb); + + if (group) + avahi_s_entry_group_free(group); + + if (server) + avahi_server_free(server); + + if (simple_poll) + avahi_simple_poll_free(simple_poll); + + avahi_free(service_name); + + return 0; +} diff --git a/avahi/browse-dns-server.c b/avahi/browse-dns-server.c new file mode 100644 index 0000000..579c51b --- /dev/null +++ b/avahi/browse-dns-server.c @@ -0,0 +1,322 @@ +/*** + 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 "browse.h" +#include "log.h" +#include "rr.h" + +typedef struct AvahiDNSServerInfo AvahiDNSServerInfo; + +struct AvahiDNSServerInfo { + AvahiSDNSServerBrowser *browser; + + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiRecord *srv_record; + AvahiSHostNameResolver *host_name_resolver; + AvahiAddress address; + AvahiLookupResultFlags flags; + + AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info); +}; + +struct AvahiSDNSServerBrowser { + AvahiServer *server; + + AvahiSRecordBrowser *record_browser; + AvahiSDNSServerBrowserCallback callback; + void* userdata; + AvahiProtocol aprotocol; + AvahiLookupFlags user_flags; + + unsigned n_info; + + AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser); + AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info); +}; + +static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) { + AvahiDNSServerInfo *i; + + assert(b); + assert(r); + + for (i = b->info; i; i = i->info_next) + if (i->interface == interface && + i->protocol == protocol && + avahi_record_equal_no_ttl(r, i->srv_record)) + return i; + + return NULL; +} + +static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) { + assert(b); + assert(i); + + avahi_record_unref(i->srv_record); + if (i->host_name_resolver) + avahi_s_host_name_resolver_free(i->host_name_resolver); + + AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i); + + assert(b->n_info >= 1); + b->n_info--; + + avahi_free(i); +} + +static void host_name_resolver_callback( + AvahiSHostNameResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *host_name, + const AvahiAddress *a, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiDNSServerInfo *i = userdata; + + assert(r); + assert(host_name); + assert(i); + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + i->address = *a; + + i->browser->callback( + i->browser, + i->interface, + i->protocol, + AVAHI_BROWSER_NEW, + i->srv_record->data.srv.name, + &i->address, + i->srv_record->data.srv.port, + i->flags | flags, + i->browser->userdata); + + break; + } + + case AVAHI_RESOLVER_FAILURE: + /* Ignore */ + break; + } + + avahi_s_host_name_resolver_free(i->host_name_resolver); + i->host_name_resolver = NULL; +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSDNSServerBrowser *b = userdata; + + assert(rr); + assert(b); + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + switch (event) { + case AVAHI_BROWSER_NEW: { + AvahiDNSServerInfo *i; + + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_SRV); + + if (get_server_info(b, interface, protocol, record)) + return; + + if (b->n_info >= 10) + return; + + if (!(i = avahi_new(AvahiDNSServerInfo, 1))) + return; /* OOM */ + + i->browser = b; + i->interface = interface; + i->protocol = protocol; + i->srv_record = avahi_record_ref(record); + i->host_name_resolver = avahi_s_host_name_resolver_new( + b->server, + interface, protocol, + record->data.srv.name, + b->aprotocol, + b->user_flags, + host_name_resolver_callback, i); + i->flags = flags; + + AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i); + + b->n_info++; + break; + } + + case AVAHI_BROWSER_REMOVE: { + AvahiDNSServerInfo *i; + + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_SRV); + + if (!(i = get_server_info(b, interface, protocol, record))) + return; + + if (!i->host_name_resolver) + b->callback( + b, + interface, + protocol, + event, + i->srv_record->data.srv.name, + &i->address, + i->srv_record->data.srv.port, + i->flags | flags, + b->userdata); + + server_info_free(b, i); + break; + } + + case AVAHI_BROWSER_FAILURE: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + + b->callback( + b, + interface, + protocol, + event, + NULL, + NULL, + 0, + flags, + b->userdata); + + break; + } +} + +AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSDNSServerBrowserCallback callback, + void* userdata) { + + static const char * const type_table[AVAHI_DNS_SERVER_MAX] = { + "_domain._udp", + "_dns-update._udp" + }; + + AvahiSDNSServerBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DNS_SERVER_MAX, AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), NULL, type_table[type], domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->server = server; + b->callback = callback; + b->userdata = userdata; + b->aprotocol = aprotocol; + b->n_info = 0; + b->user_flags = flags; + + AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info); + AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b); + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + return b; + +fail: + + if (k) + avahi_key_unref(k); + + avahi_s_dns_server_browser_free(b); + return NULL; +} + +void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) { + assert(b); + + while (b->info) + server_info_free(b, b->info); + + AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + avahi_free(b); +} + diff --git a/avahi/browse-domain.c b/avahi/browse-domain.c new file mode 100644 index 0000000..134c745 --- /dev/null +++ b/avahi/browse-domain.c @@ -0,0 +1,235 @@ +/*** + 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 "browse.h" +#include "log.h" + +struct AvahiSDomainBrowser { + int ref; + + AvahiServer *server; + + AvahiSRecordBrowser *record_browser; + + AvahiDomainBrowserType type; + AvahiSDomainBrowserCallback callback; + void* userdata; + + AvahiTimeEvent *defer_event; + + int all_for_now_scheduled; + + AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser); +}; + +static void inc_ref(AvahiSDomainBrowser *b) { + assert(b); + assert(b->ref >= 1); + + b->ref++; +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSDomainBrowser *b = userdata; + char *n = NULL; + + assert(rr); + assert(b); + + if (event == AVAHI_BROWSER_ALL_FOR_NOW && + b->defer_event) { + + b->all_for_now_scheduled = 1; + return; + } + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + if (record) { + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + n = record->data.ptr.name; + + if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) { + AvahiStringList *l; + + /* Filter out entries defined statically */ + + for (l = b->server->config.browse_domains; l; l = l->next) + if (avahi_domain_equal((char*) l->text, n)) + return; + } + + } + + b->callback(b, interface, protocol, event, n, flags, b->userdata); +} + +static void defer_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSDomainBrowser *b = userdata; + AvahiStringList *l; + + assert(e); + assert(b); + + assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE); + + avahi_time_event_free(b->defer_event); + b->defer_event = NULL; + + /* Increase ref counter */ + inc_ref(b); + + for (l = b->server->config.browse_domains; l; l = l->next) { + + /* Check whether this object still exists outside our own + * stack frame */ + if (b->ref <= 1) + break; + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata); + } + + if (b->ref > 1) { + /* If the ALL_FOR_NOW event has already been scheduled, execute it now */ + + if (b->all_for_now_scheduled) + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata); + } + + /* Decrease ref counter */ + avahi_s_domain_browser_free(b); +} + +AvahiSDomainBrowser *avahi_s_domain_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType type, + AvahiLookupFlags flags, + AvahiSDomainBrowserCallback callback, + void* userdata) { + + static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = { + "b", + "db", + "r", + "dr", + "lb" + }; + + AvahiSDomainBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSDomainBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->ref = 1; + b->server = server; + b->callback = callback; + b->userdata = userdata; + b->record_browser = NULL; + b->type = type; + b->all_for_now_scheduled = 0; + b->defer_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b); + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains) + b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); + + return b; + +fail: + + if (k) + avahi_key_unref(k); + + avahi_s_domain_browser_free(b); + + return NULL; +} + +void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) { + assert(b); + + assert(b->ref >= 1); + if (--b->ref > 0) + return; + + AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + if (b->defer_event) + avahi_time_event_free(b->defer_event); + + avahi_free(b); +} diff --git a/avahi/browse-service-type.c b/avahi/browse-service-type.c new file mode 100644 index 0000000..8eb42da --- /dev/null +++ b/avahi/browse-service-type.c @@ -0,0 +1,157 @@ +/*** + 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 "browse.h" +#include "log.h" + +struct AvahiSServiceTypeBrowser { + AvahiServer *server; + char *domain_name; + + AvahiSRecordBrowser *record_browser; + + AvahiSServiceTypeBrowserCallback callback; + void* userdata; + + AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser); +}; + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSServiceTypeBrowser *b = userdata; + + assert(rr); + assert(b); + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + if (record) { + char type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) { + avahi_log_warn("Invalid service type '%s'", record->key->name); + return; + } + + b->callback(b, interface, protocol, event, type, domain, flags, b->userdata); + } else + b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->userdata); +} + +AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, + AvahiSServiceTypeBrowserCallback callback, + void* userdata) { + + AvahiSServiceTypeBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), NULL, "_services._dns-sd._udp", domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->server = server; + b->callback = callback; + b->userdata = userdata; + b->record_browser = NULL; + + AVAHI_LLIST_PREPEND(AvahiSServiceTypeBrowser, browser, server->service_type_browsers, b); + + if (!(b->domain_name = avahi_normalize_name_strdup(domain))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + return b; + +fail: + if (k) + avahi_key_unref(k); + + avahi_s_service_type_browser_free(b); + + return NULL; +} + +void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b) { + assert(b); + + AVAHI_LLIST_REMOVE(AvahiSServiceTypeBrowser, browser, b->server->service_type_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + avahi_free(b->domain_name); + avahi_free(b); +} + + diff --git a/avahi/browse-service.c b/avahi/browse-service.c new file mode 100644 index 0000000..babe7a1 --- /dev/null +++ b/avahi/browse-service.c @@ -0,0 +1,167 @@ +/*** + 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 "browse.h" +#include "log.h" + +struct AvahiSServiceBrowser { + AvahiServer *server; + char *domain_name; + char *service_type; + + AvahiSRecordBrowser *record_browser; + + AvahiSServiceBrowserCallback callback; + void* userdata; + + AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser); +}; + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSServiceBrowser *b = userdata; + + assert(rr); + assert(b); + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + if (record) { + char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (event == AVAHI_BROWSER_NEW && avahi_server_is_service_local(b->server, interface, protocol, record->data.ptr.name)) + flags |= AVAHI_LOOKUP_RESULT_LOCAL; + + if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) { + avahi_log_warn("Failed to split '%s'", record->key->name); + return; + } + + b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata); + + } else + b->callback(b, interface, protocol, event, NULL, b->service_type, b->domain_name, flags, b->userdata); + +} + +AvahiSServiceBrowser *avahi_s_service_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *service_type, + const char *domain, + AvahiLookupFlags flags, + AvahiSServiceBrowserCallback callback, + void* userdata) { + + AvahiSServiceBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + assert(service_type); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), NULL, service_type, domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSServiceBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->server = server; + b->domain_name = b->service_type = NULL; + b->callback = callback; + b->userdata = userdata; + b->record_browser = NULL; + + AVAHI_LLIST_PREPEND(AvahiSServiceBrowser, browser, server->service_browsers, b); + + if (!(b->domain_name = avahi_normalize_name_strdup(domain)) || + !(b->service_type = avahi_normalize_name_strdup(service_type))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + return b; + +fail: + + if (k) + avahi_key_unref(k); + + avahi_s_service_browser_free(b); + return NULL; +} + +void avahi_s_service_browser_free(AvahiSServiceBrowser *b) { + assert(b); + + AVAHI_LLIST_REMOVE(AvahiSServiceBrowser, browser, b->server->service_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + avahi_free(b->domain_name); + avahi_free(b->service_type); + avahi_free(b); +} diff --git a/avahi/browse.c b/avahi/browse.c new file mode 100644 index 0000000..47d5f23 --- /dev/null +++ b/avahi/browse.c @@ -0,0 +1,613 @@ +/*** + 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 "browse.h" +#include "log.h" +#include "querier.h" +#include "domain-util.h" +#include "rr-util.h" + +#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15 + +struct AvahiSRBLookup { + AvahiSRecordBrowser *record_browser; + + unsigned ref; + + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupFlags flags; + + AvahiKey *key; + + AvahiWideAreaLookup *wide_area; + AvahiMulticastLookup *multicast; + + AvahiRList *cname_lookups; + + AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups); +}; + +static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); +static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); + +static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) { + assert(flags); + assert(domain); + + assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA))); + + if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA)) + return; + + if (!s->wide_area_lookup_engine || + !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) + *flags |= AVAHI_LOOKUP_USE_MULTICAST; + else + *flags |= AVAHI_LOOKUP_USE_WIDE_AREA; +} + +static AvahiSRBLookup* lookup_new( + AvahiSRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiLookupFlags flags, + AvahiKey *key) { + + AvahiSRBLookup *l; + + assert(b); + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + + if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX) + /* We don't like cyclic CNAMEs */ + return NULL; + + if (!(l = avahi_new(AvahiSRBLookup, 1))) + return NULL; + + l->ref = 1; + l->record_browser = b; + l->interface = interface; + l->protocol = protocol; + l->key = avahi_key_ref(key); + l->wide_area = NULL; + l->multicast = NULL; + l->cname_lookups = NULL; + l->flags = flags; + + transport_flags_from_domain(b->server, &l->flags, key->name); + + AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l); + + b->n_lookups ++; + + return l; +} + +static void lookup_unref(AvahiSRBLookup *l) { + assert(l); + assert(l->ref >= 1); + + if (--l->ref >= 1) + return; + + AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l); + l->record_browser->n_lookups --; + + if (l->wide_area) { + avahi_wide_area_lookup_free(l->wide_area); + l->wide_area = NULL; + } + + if (l->multicast) { + avahi_multicast_lookup_free(l->multicast); + l->multicast = NULL; + } + + while (l->cname_lookups) { + lookup_unref(l->cname_lookups->data); + l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups); + } + + avahi_key_unref(l->key); + avahi_free(l); +} + +static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) { + assert(l); + assert(l->ref >= 1); + + l->ref++; + return l; +} + +static AvahiSRBLookup *lookup_find( + AvahiSRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiLookupFlags flags, + AvahiKey *key) { + + AvahiSRBLookup *l; + + assert(b); + + for (l = b->lookups; l; l = l->lookups_next) { + + if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) && + (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) && + l->flags == flags && + avahi_key_equal(l->key, key)) + + return l; + } + + return NULL; +} + +static void browser_cancel(AvahiSRecordBrowser *b) { + assert(b); + + if (b->root_lookup) { + lookup_unref(b->root_lookup); + b->root_lookup = NULL; + } + + if (b->defer_time_event) { + avahi_time_event_free(b->defer_time_event); + b->defer_time_event = NULL; + } +} + +static void lookup_wide_area_callback( + AvahiWideAreaLookupEngine *e, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata) { + + AvahiSRBLookup *l = userdata; + AvahiSRecordBrowser *b; + + assert(e); + assert(l); + assert(l->ref >= 1); + + b = l->record_browser; + + if (b->dead) + return; + + lookup_ref(l); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */ + lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(r->key, l->key)); + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + /* Not defined for wide area DNS */ + abort(); + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_FAILURE: + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); + break; + } + + lookup_unref(l); + +} + +static void lookup_multicast_callback( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata) { + + AvahiSRBLookup *l = userdata; + AvahiSRecordBrowser *b; + + assert(e); + assert(l); + + b = l->record_browser; + + if (b->dead) + return; + + lookup_ref(l); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */ + lookup_handle_cname(l, interface, protocol, b->flags, r); + else { + /* It's a normal record, so let's call the user callback */ + + if (avahi_server_is_record_local(b->server, interface, protocol, r)) + flags |= AVAHI_LOOKUP_RESULT_LOCAL; + + b->callback(b, interface, protocol, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_REMOVE: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's drop that query! */ + lookup_drop_cname(l, interface, protocol, 0, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(b->key, l->key)); + + b->callback(b, interface, protocol, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_FAILURE: + /* Not defined for multicast DNS */ + abort(); + + } + + lookup_unref(l); +} + +static int lookup_start(AvahiSRBLookup *l) { + assert(l); + + assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); + assert(!l->wide_area && !l->multicast); + + if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { + + if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l))) + return -1; + + } else { + assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); + + if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l))) + return -1; + } + + return 0; +} + +static int lookup_scan_cache(AvahiSRBLookup *l) { + int n = 0; + + assert(l); + + assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); + + + if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { + n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l); + + } else { + assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); + n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l); + } + + return n; +} + +static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) { + AvahiSRBLookup *l; + + assert(b); + assert(!b->dead); + + if ((l = lookup_find(b, interface, protocol, flags, key))) + return lookup_ref(l); + + if (!(l = lookup_new(b, interface, protocol, flags, key))) + return NULL; + + return l; +} + +static int lookup_go(AvahiSRBLookup *l) { + int n = 0; + assert(l); + + if (l->record_browser->dead) + return 0; + + lookup_ref(l); + + /* Browse the cache for the root request */ + n = lookup_scan_cache(l); + + /* Start the lookup */ + if (!l->record_browser->dead && l->ref > 1) { + + if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || n == 0) + /* We do no start a query if the cache contained entries and we're on wide area */ + + if (lookup_start(l) < 0) + n = -1; + } + + lookup_unref(l); + + return n; +} + +static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { + AvahiKey *k; + AvahiSRBLookup *n; + + assert(l); + assert(r); + + assert(r->key->clazz == AVAHI_DNS_CLASS_IN); + assert(r->key->type == AVAHI_DNS_TYPE_CNAME); + + k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); + n = lookup_add(l->record_browser, interface, protocol, flags, k); + avahi_key_unref(k); + + if (!n) { + avahi_log_debug(__FILE__": Failed to create SRBLookup."); + return; + } + + l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n)); + + lookup_go(n); + lookup_unref(n); +} + +static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { + AvahiKey *k; + AvahiSRBLookup *n = NULL; + AvahiRList *rl; + + assert(r->key->clazz == AVAHI_DNS_CLASS_IN); + assert(r->key->type == AVAHI_DNS_TYPE_CNAME); + + k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); + + for (rl = l->cname_lookups; rl; rl = rl->rlist_next) { + n = rl->data; + + assert(n); + + if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) && + (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) && + n->flags == flags && + avahi_key_equal(n->key, k)) + break; + } + + avahi_key_unref(k); + + if (rl) { + l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl); + lookup_unref(n); + } +} + +static void defer_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { + AvahiSRecordBrowser *b = userdata; + int n; + + assert(b); + assert(!b->dead); + + /* Remove the defer timeout */ + if (b->defer_time_event) { + avahi_time_event_free(b->defer_time_event); + b->defer_time_event = NULL; + } + + /* Create initial query */ + assert(!b->root_lookup); + b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key); + assert(b->root_lookup); + + n = lookup_go(b->root_lookup); + + if (b->dead) + return; + + if (n < 0) { + /* sending of the initial query failed */ + + avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE); + + b->callback( + b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL, + b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST, + b->userdata); + + browser_cancel(b); + return; + } + + /* Tell the client that we're done with the cache */ + b->callback( + b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL, + b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST, + b->userdata); + + if (!b->dead && b->root_lookup && b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA && n > 0) { + + /* If we do wide area lookups and the the cache contained + * entries, we assume that it is complete, and tell the user + * so by firing ALL_FOR_NOW. */ + + b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_RESULT_WIDE_AREA, b->userdata); + } +} + +void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) { + assert(b); + assert(!b->dead); + + browser_cancel(b); + + /* Request a new iteration of the cache scanning */ + if (!b->defer_time_event) { + b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b); + assert(b->defer_time_event); + } +} + +AvahiSRecordBrowser *avahi_s_record_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiLookupFlags flags, + AvahiSRecordBrowserCallback callback, + void* userdata) { + + AvahiSRecordBrowser *b; + + assert(server); + assert(key); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!(b = avahi_new(AvahiSRecordBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->dead = 0; + b->server = server; + b->interface = interface; + b->protocol = protocol; + b->key = avahi_key_ref(key); + b->flags = flags; + b->callback = callback; + b->userdata = userdata; + b->n_lookups = 0; + AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups); + b->root_lookup = NULL; + + AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b); + + /* The currently cached entries are scanned a bit later, and than we will start querying, too */ + b->defer_time_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); + assert(b->defer_time_event); + + return b; +} + +void avahi_s_record_browser_free(AvahiSRecordBrowser *b) { + assert(b); + assert(!b->dead); + + b->dead = 1; + b->server->need_browser_cleanup = 1; + + browser_cancel(b); +} + +void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) { + assert(b); + + browser_cancel(b); + + AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b); + + avahi_key_unref(b->key); + + avahi_free(b); +} + +void avahi_browser_cleanup(AvahiServer *server) { + AvahiSRecordBrowser *b; + AvahiSRecordBrowser *n; + + assert(server); + + while (server->need_browser_cleanup) { + server->need_browser_cleanup = 0; + + for (b = server->record_browsers; b; b = n) { + n = b->browser_next; + + if (b->dead) + avahi_s_record_browser_destroy(b); + } + } + + if (server->wide_area_lookup_engine) + avahi_wide_area_cleanup(server->wide_area_lookup_engine); + avahi_multicast_lookup_engine_cleanup(server->multicast_lookup_engine); +} + diff --git a/avahi/browse.h b/avahi/browse.h new file mode 100644 index 0000000..2592b80 --- /dev/null +++ b/avahi/browse.h @@ -0,0 +1,60 @@ +#ifndef foobrowsehfoo +#define foobrowsehfoo + +/*** + 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 "core.h" +#include "timeeventq.h" +#include "internal.h" +#include "dns.h" +#include "lookup.h" + +typedef struct AvahiSRBLookup AvahiSRBLookup; + +struct AvahiSRecordBrowser { + AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser); + int dead; + AvahiServer *server; + + AvahiKey *key; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupFlags flags; + + AvahiTimeEvent *defer_time_event; + + AvahiSRecordBrowserCallback callback; + void* userdata; + + /* Lookup data */ + AVAHI_LLIST_HEAD(AvahiSRBLookup, lookups); + unsigned n_lookups; + + AvahiSRBLookup *root_lookup; +}; + +void avahi_browser_cleanup(AvahiServer *server); + +void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b); +void avahi_s_record_browser_restart(AvahiSRecordBrowser *b); + +#endif diff --git a/avahi/cache.c b/avahi/cache.c new file mode 100644 index 0000000..fa4a24a --- /dev/null +++ b/avahi/cache.c @@ -0,0 +1,513 @@ +/*** + 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 "cache.h" +#include "log.h" +#include "rr-util.h" + +static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { + AvahiCacheEntry *t; + + assert(c); + assert(e); + +/* avahi_log_debug("removing from cache: %p %p", c, e); */ + + /* Remove from hash table */ + t = avahi_hashmap_lookup(c->hashmap, e->record->key); + AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e); + if (t) + avahi_hashmap_replace(c->hashmap, t->record->key, t); + else + avahi_hashmap_remove(c->hashmap, e->record->key); + + /* Remove from linked list */ + AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e); + + if (e->time_event) + avahi_time_event_free(e->time_event); + + avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_REMOVE); + + avahi_record_unref(e->record); + + avahi_free(e); + + assert(c->n_entries >= 1); + --c->n_entries; +} + +AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) { + AvahiCache *c; + assert(server); + + if (!(c = avahi_new(AvahiCache, 1))) { + avahi_log_error(__FILE__": Out of memory."); + return NULL; /* OOM */ + } + + c->server = server; + c->interface = iface; + + if (!(c->hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL))) { + avahi_log_error(__FILE__": Out of memory."); + avahi_free(c); + return NULL; /* OOM */ + } + + AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries); + c->n_entries = 0; + + c->last_rand_timestamp = 0; + + return c; +} + +void avahi_cache_free(AvahiCache *c) { + assert(c); + + while (c->entries) + remove_entry(c, c->entries); + assert(c->n_entries == 0); + + avahi_hashmap_free(c->hashmap); + + avahi_free(c); +} + +static AvahiCacheEntry *lookup_key(AvahiCache *c, AvahiKey *k) { + assert(c); + assert(k); + + assert(!avahi_key_is_pattern(k)); + + return avahi_hashmap_lookup(c->hashmap, k); +} + +void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata) { + void* ret; + + assert(c); + assert(pattern); + assert(cb); + + if (avahi_key_is_pattern(pattern)) { + AvahiCacheEntry *e, *n; + + for (e = c->entries; e; e = n) { + n = e->entry_next; + + if (avahi_key_pattern_match(pattern, e->record->key)) + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + + } else { + AvahiCacheEntry *e, *n; + + for (e = lookup_key(c, pattern); e; e = n) { + n = e->by_key_next; + + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + } + + return NULL; +} + +static void* lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { + assert(c); + assert(pattern); + assert(e); + + if (avahi_record_equal_no_ttl(e->record, userdata)) + return e; + + return NULL; +} + +static AvahiCacheEntry *lookup_record(AvahiCache *c, AvahiRecord *r) { + assert(c); + assert(r); + + return avahi_cache_walk(c, r->key, lookup_record_callback, r); +} + +static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent); + +static void elapse_func(AvahiTimeEvent *t, void *userdata) { + AvahiCacheEntry *e = userdata; +/* char *txt; */ + unsigned percent = 0; + + assert(t); + assert(e); + +/* txt = avahi_record_to_string(e->record); */ + + switch (e->state) { + + case AVAHI_CACHE_EXPIRY_FINAL: + case AVAHI_CACHE_POOF_FINAL: + case AVAHI_CACHE_GOODBYE_FINAL: + case AVAHI_CACHE_REPLACE_FINAL: + + remove_entry(e->cache, e); + + e = NULL; +/* avahi_log_debug("Removing entry from cache due to expiration (%s)", txt); */ + break; + + case AVAHI_CACHE_VALID: + case AVAHI_CACHE_POOF: + e->state = AVAHI_CACHE_EXPIRY1; + percent = 85; + break; + + case AVAHI_CACHE_EXPIRY1: + e->state = AVAHI_CACHE_EXPIRY2; + percent = 90; + break; + case AVAHI_CACHE_EXPIRY2: + e->state = AVAHI_CACHE_EXPIRY3; + percent = 95; + break; + + case AVAHI_CACHE_EXPIRY3: + e->state = AVAHI_CACHE_EXPIRY_FINAL; + percent = 100; + break; + } + + if (e) { + + assert(percent > 0); + + /* Request a cache update if we are subscribed to this entry */ + if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key)) + avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL); + + /* Check again later */ + next_expiry(e->cache, e, percent); + + } + +/* avahi_free(txt); */ +} + +static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) { + assert(c); + assert(e); + + if (e->time_event) + avahi_time_event_update(e->time_event, &e->expiry); + else + e->time_event = avahi_time_event_new(c->server->time_event_queue, &e->expiry, elapse_func, e); +} + +static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent) { + AvahiUsec usec, left, right; + time_t now; + + assert(c); + assert(e); + assert(percent > 0 && percent <= 100); + + usec = (AvahiUsec) e->record->ttl * 10000; + + left = usec * percent; + right = usec * (percent+2); /* 2% jitter */ + + now = time(NULL); + + if (now >= c->last_rand_timestamp + 10) { + c->last_rand = rand(); + c->last_rand_timestamp = now; + } + + usec = left + (AvahiUsec) ((double) (right-left) * c->last_rand / (RAND_MAX+1.0)); + + e->expiry = e->timestamp; + avahi_timeval_add(&e->expiry, usec); + +/* g_message("wake up in +%lu seconds", e->expiry.tv_sec - e->timestamp.tv_sec); */ + + update_time_event(c, e); +} + +static void expire_in_one_second(AvahiCache *c, AvahiCacheEntry *e, AvahiCacheEntryState state) { + assert(c); + assert(e); + + e->state = state; + gettimeofday(&e->expiry, NULL); + avahi_timeval_add(&e->expiry, 1000000); /* 1s */ + update_time_event(c, e); +} + +void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) { +/* char *txt; */ + + assert(c); + assert(r && r->ref >= 1); + +/* txt = avahi_record_to_string(r); */ + + if (r->ttl == 0) { + /* This is a goodbye request */ + + AvahiCacheEntry *e; + + if ((e = lookup_record(c, r))) + expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL); + + } else { + AvahiCacheEntry *e = NULL, *first; + struct timeval now; + + gettimeofday(&now, NULL); + + /* This is an update request */ + + if ((first = lookup_key(c, r->key))) { + + if (cache_flush) { + + /* For unique entries drop all entries older than one second */ + for (e = first; e; e = e->by_key_next) { + AvahiUsec t; + + t = avahi_timeval_diff(&now, &e->timestamp); + + if (t > 1000000) + expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL); + } + } + + /* Look for exactly the same entry */ + for (e = first; e; e = e->by_key_next) + if (avahi_record_equal_no_ttl(e->record, r)) + break; + } + + if (e) { + +/* avahi_log_debug("found matching cache entry"); */ + + /* We need to update the hash table key if we replace the + * record */ + if (e->by_key_prev == NULL) + avahi_hashmap_replace(c->hashmap, r->key, e); + + /* Update the record */ + avahi_record_unref(e->record); + e->record = avahi_record_ref(r); + +/* avahi_log_debug("cache: updating %s", txt); */ + + } else { + /* No entry found, therefore we create a new one */ + +/* avahi_log_debug("cache: couldn't find matching cache entry for %s", txt); */ + + if (c->n_entries >= c->server->config.n_cache_entries_max) + return; + + if (!(e = avahi_new(AvahiCacheEntry, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return; + } + + e->cache = c; + e->time_event = NULL; + e->record = avahi_record_ref(r); + + /* Append to hash table */ + AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e); + avahi_hashmap_replace(c->hashmap, e->record->key, first); + + /* Append to linked list */ + AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); + + c->n_entries++; + + /* Notify subscribers */ + avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW); + } + + e->origin = *a; + e->timestamp = now; + next_expiry(c, e, 80); + e->state = AVAHI_CACHE_VALID; + e->cache_flush = cache_flush; + } + +/* avahi_free(txt); */ +} + +struct dump_data { + AvahiDumpCallback callback; + void* userdata; +}; + +static void dump_callback(void* key, void* data, void* userdata) { + AvahiCacheEntry *e = data; + AvahiKey *k = key; + struct dump_data *dump_data = userdata; + + assert(k); + assert(e); + assert(data); + + for (; e; e = e->by_key_next) { + char *t; + + if (!(t = avahi_record_to_string(e->record))) + continue; /* OOM */ + + dump_data->callback(t, dump_data->userdata); + avahi_free(t); + } +} + +int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata) { + struct dump_data data; + + assert(c); + assert(callback); + + callback(";;; CACHE DUMP FOLLOWS ;;;", userdata); + + data.callback = callback; + data.userdata = userdata; + + avahi_hashmap_foreach(c->hashmap, dump_callback, &data); + + return 0; +} + +int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { + struct timeval now; + unsigned age; + + assert(c); + assert(e); + + gettimeofday(&now, NULL); + + age = (unsigned) (avahi_timeval_diff(&now, &e->timestamp)/1000000); + +/* avahi_log_debug("age: %lli, ttl/2: %u", age, e->record->ttl); */ + + return age >= e->record->ttl/2; +} + +void avahi_cache_flush(AvahiCache *c) { + assert(c); + + while (c->entries) + remove_entry(c, c->entries); +} + +/*** Passive observation of failure ***/ + +static void* start_poof_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { + AvahiAddress *a = userdata; + struct timeval now; + + assert(c); + assert(pattern); + assert(e); + assert(a); + + gettimeofday(&now, NULL); + + switch (e->state) { + case AVAHI_CACHE_VALID: + + /* The entry was perfectly valid till, now, so let's enter + * POOF mode */ + + e->state = AVAHI_CACHE_POOF; + e->poof_address = *a; + e->poof_timestamp = now; + e->poof_num = 0; + + break; + + case AVAHI_CACHE_POOF: + if (avahi_timeval_diff(&now, &e->poof_timestamp) < 1000000) + break; + + e->poof_timestamp = now; + e->poof_address = *a; + e->poof_num ++; + + /* This is the 4th time we got no response, so let's + * fucking remove this entry. */ + if (e->poof_num > 3) + expire_in_one_second(c, e, AVAHI_CACHE_POOF_FINAL); + break; + + default: + ; + } + + return NULL; +} + +void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a) { + assert(c); + assert(key); + + avahi_cache_walk(c, key, start_poof_callback, (void*) a); +} + +void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a) { + AvahiCacheEntry *e; + + assert(c); + assert(record); + assert(a); + + if (!(e = lookup_record(c, record))) + return; + + /* This function is called for each response suppression + record. If the matching cache entry is in POOF state and the + query address is the same, we put it back into valid mode */ + + if (e->state == AVAHI_CACHE_POOF || e->state == AVAHI_CACHE_POOF_FINAL) + if (avahi_address_cmp(a, &e->poof_address) == 0) { + e->state = AVAHI_CACHE_VALID; + next_expiry(c, e, 80); + } +} diff --git a/avahi/cache.h b/avahi/cache.h new file mode 100644 index 0000000..a1c8db3 --- /dev/null +++ b/avahi/cache.h @@ -0,0 +1,101 @@ +#ifndef foocachehfoo +#define foocachehfoo + +/*** + 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 struct AvahiCache AvahiCache; + +#include +#include "prioq.h" +#include "internal.h" +#include "timeeventq.h" +#include "hashmap.h" + +typedef enum { + AVAHI_CACHE_VALID, + AVAHI_CACHE_EXPIRY1, + AVAHI_CACHE_EXPIRY2, + AVAHI_CACHE_EXPIRY3, + AVAHI_CACHE_EXPIRY_FINAL, + AVAHI_CACHE_POOF, /* Passive observation of failure */ + AVAHI_CACHE_POOF_FINAL, + AVAHI_CACHE_GOODBYE_FINAL, + AVAHI_CACHE_REPLACE_FINAL +} AvahiCacheEntryState; + +typedef struct AvahiCacheEntry AvahiCacheEntry; + +struct AvahiCacheEntry { + AvahiCache *cache; + AvahiRecord *record; + struct timeval timestamp; + struct timeval poof_timestamp; + struct timeval expiry; + int cache_flush; + int poof_num; + + AvahiAddress origin; + + AvahiCacheEntryState state; + AvahiTimeEvent *time_event; + + AvahiAddress poof_address; + + AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry); +}; + +struct AvahiCache { + AvahiServer *server; + + AvahiInterface *interface; + + AvahiHashmap *hashmap; + + AVAHI_LLIST_HEAD(AvahiCacheEntry, entries); + + unsigned n_entries; + + int last_rand; + time_t last_rand_timestamp; +}; + +AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface); +void avahi_cache_free(AvahiCache *c); + +void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a); + +int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata); + +typedef void* AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata); +void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata); + +int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e); + +/** Start the "Passive observation of Failure" algorithm for all + * records of the specified key. The specified address is */ +void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a); + +/* Stop a previously started POOF algorithm for a record. (Used for response suppresions records */ +void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a); + +void avahi_cache_flush(AvahiCache *c); + +#endif diff --git a/avahi/cdecl.h b/avahi/cdecl.h new file mode 100644 index 0000000..aef6aba --- /dev/null +++ b/avahi/cdecl.h @@ -0,0 +1,38 @@ +#ifndef foocdeclhfoo +#define foocdeclhfoo + +/*** + 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. +***/ + +/** \file cdecl.h C++ compatibility */ +#ifdef __cplusplus +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN extern "C" { +/** If using C++ this macro switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END } + +#else +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN +/** If using C++ this macro switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END + +#endif + +#endif diff --git a/avahi/conformance-test.c b/avahi/conformance-test.c new file mode 100644 index 0000000..d2b40d4 --- /dev/null +++ b/avahi/conformance-test.c @@ -0,0 +1,158 @@ +/*** + 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 + +static char *name = NULL; +static AvahiSEntryGroup *group = NULL; +static int try = 0; +static AvahiServer *avahi = NULL; +static const AvahiPoll *poll_api; + +static void dump_line(const char *text, AVAHI_GCC_UNUSED void* userdata) { + printf("%s\n", text); +} + +static void dump_timeout_callback(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void* userdata) { + struct timeval tv; + + avahi_server_dump(avahi, dump_line, NULL); + + avahi_elapse_time(&tv, 5000, 0); + poll_api->timeout_update(timeout, &tv); +} + +static void entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); + +static void create_service(const char *t) { + char *n; + + assert(t || name); + + n = t ? avahi_strdup(t) : avahi_alternative_service_name(name); + avahi_free(name); + name = n; + + if (group) + avahi_s_entry_group_reset(group); + else + group = avahi_s_entry_group_new(avahi, entry_group_callback, NULL); + + avahi_server_add_service(avahi, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, "_http._tcp", NULL, NULL, 80, "foo", NULL); + avahi_s_entry_group_commit(group); + + try++; +} + +static void rename_timeout_callback(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void *userdata) { + struct timeval tv; + + if (access("flag", F_OK) == 0) { + create_service("New - Bonjour Service Name"); + return; + } + + avahi_elapse_time(&tv, 5000, 0); + poll_api->timeout_update(timeout, &tv); +} + +static void entry_group_callback(AVAHI_GCC_UNUSED AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) { + if (state == AVAHI_ENTRY_GROUP_COLLISION) + create_service(NULL); + else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) { + avahi_log_debug("ESTABLISHED !!!!"); + try = 0; + } +} + +static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) { + avahi_log_debug("server state: %i", state); + + if (state == AVAHI_SERVER_RUNNING) { + avahi_server_dump(avahi, dump_line, NULL); + } else if (state == AVAHI_SERVER_COLLISION) { + char *n; + + n = avahi_alternative_host_name(avahi_server_get_host_name(s)); + avahi_log_warn("Host name conflict, retrying with <%s>", n); + avahi_server_set_host_name(s, n); + avahi_free(n); + + } +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + int error; + AvahiSimplePoll *simple_poll; + struct timeval tv; + struct AvahiServerConfig config; + + simple_poll = avahi_simple_poll_new(); + poll_api = avahi_simple_poll_get(simple_poll); + + avahi_server_config_init(&config); + config.publish_workstation = 0; + config.use_ipv6 = 0; + config.publish_domain = 0; + config.publish_hinfo = 0; + avahi = avahi_server_new(poll_api, &config, server_callback, NULL, &error); + avahi_server_config_free(&config); + + avahi_elapse_time(&tv, 5000, 0); + poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, avahi); + + avahi_elapse_time(&tv, 5000, 0); + poll_api->timeout_new(poll_api, &tv, rename_timeout_callback, avahi); + + /* Evil, but the conformace test requires that*/ + create_service("gurke"); + + + avahi_simple_poll_loop(simple_poll); + + if (group) + avahi_s_entry_group_free(group); + avahi_server_free(avahi); + + avahi_simple_poll_free(simple_poll); + + return 0; +} diff --git a/avahi/core.h b/avahi/core.h new file mode 100644 index 0000000..491def7 --- /dev/null +++ b/avahi/core.h @@ -0,0 +1,166 @@ +#ifndef foocorehfoo +#define foocorehfoo + +/*** + 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. +***/ + +/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */ + +/** An mDNS responder object */ +typedef struct AvahiServer AvahiServer; + +#include +#include +#include +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Maximum number of defined DNS servers for wide area DNS */ +#define AVAHI_WIDE_AREA_SERVERS_MAX 4 + +/** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */ +typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState state, void* userdata); + +/** Stores configuration options for a server instance */ +typedef struct AvahiServerConfig { + char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */ + char *domain_name; /**< Default domain name. If left empty defaults to .local */ + int use_ipv4; /**< Enable IPv4 support */ + int use_ipv6; /**< Enable IPv6 support */ + AvahiStringList *allow_interfaces;/**< Allow specific interface to be used for Avahi */ + AvahiStringList *deny_interfaces; /**< Deny specific interfaces to be used for Avahi */ + int publish_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */ + int publish_addresses; /**< Register A, AAAA and PTR records for all local IP addresses */ + int publish_no_reverse; /**< Do not register PTR records */ + int publish_workstation; /**< Register a _workstation._tcp service */ + int publish_domain; /**< Announce the local domain for browsing */ + int check_response_ttl; /**< If enabled the server ignores all incoming responses with IP TTL != 255. Newer versions of the RFC do no longer contain this check, so it is disabled by default. */ + int use_iff_running; /**< Require IFF_RUNNING on local network interfaces. This is the official way to check for link beat. Unfortunately this doesn't work with all drivers. So bettere leave this off. */ + int enable_reflector; /**< Reflect incoming mDNS traffic to all local networks. This allows mDNS based network browsing beyond ethernet borders */ + int reflect_ipv; /**< if enable_reflector is 1, enable/disable reflecting between IPv4 and IPv6 */ + int add_service_cookie; /**< Add magic service cookie to all locally generated records implicitly */ + int enable_wide_area; /**< Enable wide area support */ + AvahiAddress wide_area_servers[AVAHI_WIDE_AREA_SERVERS_MAX]; /** Unicast DNS server to use for wide area lookup */ + unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */ + int disallow_other_stacks; /**< Make sure that only one mDNS responder is run at the same time on the local machine. If this is enable Avahi will not set SO_REUSADDR on its sockets, effectively preventing other stacks from running on the local machine */ + 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 */ + unsigned n_cache_entries_max; /**< Maximum number of cache entries per interface */ + AvahiUsec ratelimit_interval; /**< If non-zero, rate-limiting interval parameter. */ + unsigned ratelimit_burst; /**< If ratelimit_interval is non-zero, rate-limiting burst parameter. */ +} AvahiServerConfig; + +/** Allocate a new mDNS responder object. */ +AvahiServer *avahi_server_new( + const AvahiPoll *api, /**< The main loop adapter */ + const AvahiServerConfig *sc, /**< If non-NULL a pointer to a configuration structure for the server. The server makes an internal deep copy of this structure, so you may free it using avahi_server_config_done() immediately after calling this function. */ + AvahiServerCallback callback, /**< A callback which is called whenever the state of the server changes */ + void* userdata, /**< An opaque pointer which is passed to the callback function */ + int *error); + +/** Free an mDNS responder object */ +void avahi_server_free(AvahiServer* s); + +/** Fill in default values for a server configuration structure. If you + * make use of an AvahiServerConfig structure be sure to initialize + * it with this function for the sake of upwards library + * compatibility. This call may allocate strings on the heap. To + * release this memory make sure to call + * avahi_server_config_done(). If you want to replace any strings in + * the structure be sure to free the strings filled in by this + * function with avahi_free() first and allocate the replacements with + * g_malloc() (or g_strdup()).*/ +AvahiServerConfig* avahi_server_config_init( + AvahiServerConfig *c /**< A structure which shall be filled in */ ); + +/** Make a deep copy of the configuration structure *c to *ret. */ +AvahiServerConfig* avahi_server_config_copy( + AvahiServerConfig *ret /**< destination */, + const AvahiServerConfig *c /**< source */); + +/** Free the data in a server configuration structure. */ +void avahi_server_config_free(AvahiServerConfig *c); + +/** Return the currently chosen domain name of the server object. The + * return value points to an internally allocated string. Be sure to + * make a copy of the string before calling any other library + * functions. */ +const char* avahi_server_get_domain_name(AvahiServer *s); + +/** Return the currently chosen host name. The return value points to a internally allocated string. */ +const char* avahi_server_get_host_name(AvahiServer *s); + +/** Return the currently chosen host name as a FQDN ("fully qualified + * domain name", i.e. the concatenation of the host and domain + * name). The return value points to a internally allocated string. */ +const char* avahi_server_get_host_name_fqdn(AvahiServer *s); + +/** Change the host name of a running mDNS responder. This will drop +all automicatilly generated RRs and readd them with the new +name. Since the responder has to probe for the new RRs this function +takes some time to take effect altough it returns immediately. This +function is intended to be called when a host name conflict is +reported using AvahiServerCallback. The caller should readd all user +defined RRs too since they otherwise continue to point to the outdated +host name..*/ +int avahi_server_set_host_name(AvahiServer *s, const char *host_name); + +/** Change the domain name of a running mDNS responder. The same rules + * as with avahi_server_set_host_name() apply. */ +int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name); + +/** Return the opaque user data pointer attached to a server object */ +void* avahi_server_get_data(AvahiServer *s); + +/** Change the opaque user data pointer attached to a server object */ +void avahi_server_set_data(AvahiServer *s, void* userdata); + +/** Return the current state of the server object */ +AvahiServerState avahi_server_get_state(AvahiServer *s); + +/** Callback prototype for avahi_server_dump() */ +typedef void (*AvahiDumpCallback)(const char *text, void* userdata); + +/** Dump the current server status by calling "callback" for each line. */ +int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata); + +/** Return the last error code */ +int avahi_server_errno(AvahiServer *s); + +/** Return the local service cookie */ +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); + +/** Set the browsing domains */ +int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains); + +/** 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/defs.h b/avahi/defs.h new file mode 100644 index 0000000..8c66700 --- /dev/null +++ b/avahi/defs.h @@ -0,0 +1,356 @@ +#ifndef foodefshfoo +#define foodefshfoo + +/*** + 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. +***/ + +/** \file defs.h Some common definitions */ + +#include + +/** \mainpage + * + * \section choose_api Choosing an API + * + * Avahi provides three programming APIs for integration of + * mDNS/DNS-SD features into your C progams: + * + * \li avahi-core: an API for embedding a complete mDNS/DNS-SD stack + * into your software. This is intended for developers of embedded + * appliances only. We dissuade from using this API in normal desktop + * applications since it is not a good idea to run multiple mDNS + * stacks simultaneously on the same host. + * \li the D-Bus API: an extensive D-Bus interface for browsing and + * registering mDNS/DNS-SD services using avahi-daemon. We recommend + * using this API for software written in any language other than + * C (e.g. Python). + * \li avahi-client: a simplifying C wrapper around the D-Bus API. We + * recommend using this API in C or C++ progams. The D-Bus internals + * are hidden completely. + * \li avahi-gobject: an object-oriented C wrapper based on + * GLib's GObject. We recommd using this API for GNOME/Gtk programs. + * + * All three APIs are very similar, however avahi-core is the most powerful. + * + * In addition to the three APIs described above Avahi supports two + * compatibility libraries: + * + * \li avahi-compat-libdns_sd: the original Bonjour API as documented + * in the header file "dns_sd.h" by Apple Computer, Inc. + * + * \li avahi-compat-howl: the HOWL API as released with HOWL 0.9.8 by + * Porchdog Software. + * + * Please note that these compatibility layers are incomplete and + * generally a waste of resources. We strongly encourage everyone to + * use our native APIs for newly written programs and to port older + * programs to avahi-client! + * + * The native APIs (avahi-client and avahi-core) can be integrated + * into external event loops. We provide adapters for the following + * event loop implementations: + * + * \li avahi-glib: The GLIB main loop as used by GTk+/GNOME + * + * \li avahi-qt: The Qt main loop as used by Qt/KDE + * + * Finally, we provide a high-level Gtk+ GUI dialog called + * avahi-ui for user-friendly browsing for services. + * + * The doxygen-generated API documentation covers avahi-client + * (including its auxiliary APIs), the event loop adapters and + * avahi-ui. For the other APIs please consult the original + * documentation (for the compatibility APIs) or the header files. + * + * Please note that the doxygen-generated API documentation of the + * native Avahi API is not complete. A few definitions that are part + * of the Avahi API have been removed from this documentation, either + * because they are only relevant in a very few low-level applications + * or because they are considered obsolete. Please consult the C header + * files for all definitions that are part of the Avahi API. Please + * note that these hidden definitions are considered part of the Avahi + * API and will stay available in the API in the future. + * + * \section error_reporting Error Reporting + * + * Some notes on the Avahi error handling: + * + * - Error codes are negative integers and defined as AVAHI_ERR_xx + * - If a function returns some kind of non-negative integer value on + * success, a failure is indicated by returning the error code + * directly. + * - If a function returns a pointer of some kind on success, a + * failure is indicated by returning NULL + * - The last error number may be retrieved by calling + * avahi_client_errno() + * - Just like the libc errno variable the Avahi errno is NOT reset to + * AVAHI_OK if a function call succeeds. + * - You may convert a numeric error code into a human readable string + * using avahi_strerror() + * - The constructor function avahi_client_new() returns the error + * code in a call-by-reference argument + * + * \section event_loop Event Loop Abstraction + * + * Avahi uses a simple event loop abstraction layer. A table AvahiPoll + * which contains function pointers for user defined timeout and I/O + * condition event source implementations needs to be passed to + * avahi_client_new(). An adapter for this abstraction layer is + * available for the GLib main loop in the object AvahiGLibPoll. A + * simple stand-alone implementation is available under the name + * AvahiSimplePoll. An adpater for the Qt main loop is available from + * avahi_qt_poll_get(). + * + * \section good_publish How to Register Services + * + * - Subscribe to server state changes. Pass a callback function + * pointer to avahi_client_new(). It will be called + * whenever the server state changes. + * - Only register your services when the server is in state + * AVAHI_SERVER_RUNNING. If you register your services in other server + * states they might not be accessible since the local host name might not necessarily + * be established. + * - Remove your services when the server enters + * AVAHI_SERVER_COLLISION or AVAHI_SERVER_REGISTERING state. Your + * services may not be reachable anymore since the local host name is + * no longer established or is currently in the process of being + * established. + * - When registering services, use the following algorithm: + * - Create a new entry group (i.e. avahi_entry_group_new()) + * - Add your service(s)/additional RRs/subtypes (e.g. avahi_entry_group_add_service()) + * - Commit the entry group (i.e. avahi_entry_group_commit()) + * - Subscribe to entry group state changes. + * - If the entry group enters AVAHI_ENTRY_GROUP_COLLISION state the + * services of the entry group are automatically removed from the + * server. You may immediately add your services back to the entry + * group (but with new names, perhaps using + * avahi_alternative_service_name()) and commit again. Please do not + * free the entry group and create a new one. This would inhibit some + * traffic limiting algorithms in mDNS. + * - When you need to modify your services (i.e. change the TXT data + * or the port number), use the AVAHI_PUBLISH_UPDATE flag. Please do + * not free the entry group and create a new one. This would inhibit + * some traffic limiting algorithms in mDNS. When changing just the + * TXT data avahi_entry_group_update_txt() is a shortcut for + * AVAHI_PUBLISH_UPDATE. Please note that you cannot use + * AVAHI_PUBLISH_UPDATE when changing the service name! Renaming a + * DNS-SD service is identical to deleting and creating a new one, and + * that's exactly what you should do in that case. First call + * avahi_entry_group_reset() to remove it and then read it normally. + * + * \section good_browse How to Browse for Services + * + * - For normal applications you need to call avahi_service_browser_new() + * for the service type you want to browse for. Use + * avahi_service_resolver_new() to acquire service data for a service + * name. + * - You can use avahi_domain_browser_new() to get a list of announced + * browsing domains. Please note that not all domains whith services + * on the LAN are mandatorily announced. + * - There is no need to subscribe to server state changes. + * + * \section daemon_dies How to Write a Client That Can Deal with Daemon Restarts + * + * With Avahi it is possible to write client applications that can + * deal with Avahi daemon restarts. To accomplish that make sure to + * pass AVAHI_CLIENT_NO_FAIL to avahi_client_new()'s flags + * parameter. That way avahi_client_new() will succeed even when the + * daemon is not running. In that case the object will enter + * AVAHI_CLIENT_CONNECTING state. As soon as the daemon becomes + * available the object will enter one of the AVAHI_CLIENT_S_xxx + * states. Make sure to not create browsers or entry groups before the + * client object has entered one of those states. As usual you will be + * informed about state changes with the callback function supplied to + * avahi_client_new(). If the client is forced to disconnect from the + * server it will enter AVAHI_CLIENT_FAILURE state with + * avahi_client_errno() == AVAHI_ERR_DISCONNECTED. Free the + * AvahiClient object in that case (and all its associated objects + * such as entry groups and browser objects prior to that) and + * reconnect to the server anew - again with passing + * AVAHI_CLIENT_NO_FAIL to avahi_client_new(). + * + * We encourage implementing this in all software where service + * discovery is not an integral part of application. e.g. use it in + * all kinds of background daemons, but not necessarily in software + * like iChat compatible IM software. + * + * For now AVAHI_CLIENT_NO_FAIL cannot deal with D-Bus daemon restarts. + * + * \section domains How to Deal Properly with Browsing Domains + * + * Due to the introduction of wide-area DNS-SD the correct handling of + * domains becomes more important for Avahi enabled applications. All + * applications that offer the user a list of services discovered with + * Avahi should offer some kind of editable drop down box where the + * user can either enter his own domain or select one of those offered + * by AvahiDomainBrowser. The default domain to browse should be the + * one returned by avahi_client_get_domain_name(). The list of domains + * returned by AvahiDomainBrowser is assembled by the browsing domains + * configured in the daemon's configuration file, the domains + * announced inside the default domain, the domains set with the + * environment variable $AVAHI_BROWSE_DOMAINS (colon-seperated) on the + * client side and the domains set in the XDG configuration file + * ~/.config/avahi/browse-domains on the client side (seperated by + * newlines). File managers offering some kind of "Network + * Neighborhood" folder should show the entries of the default domain + * right inside that and offer subfolders for the browsing domains + * returned by AvahiDomainBrowser. + */ + +AVAHI_C_DECL_BEGIN + +/** @{ \name States */ + +/** States of a server object */ +typedef enum { + AVAHI_SERVER_INVALID, /**< Invalid state (initial) */ + AVAHI_SERVER_REGISTERING, /**< Host RRs are being registered */ + AVAHI_SERVER_RUNNING, /**< All host RRs have been established */ + AVAHI_SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */ + AVAHI_SERVER_FAILURE /**< Some fatal failure happened, the server is unable to proceed */ +} AvahiServerState; + +/** States of an entry group object */ +typedef enum { + AVAHI_ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been commited, the user must still call avahi_entry_group_commit() */ + AVAHI_ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */ + AVAHI_ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */ + AVAHI_ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */ + AVAHI_ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */ +} AvahiEntryGroupState; + +/** @} */ + +/** @{ \name Flags */ + +/** Some flags for publishing functions */ +typedef enum { + AVAHI_PUBLISH_UNIQUE = 1, /**< For raw records: The RRset is intended to be unique */ + AVAHI_PUBLISH_NO_PROBE = 2, /**< For raw records: Though the RRset is intended to be unique no probes shall be sent */ + AVAHI_PUBLISH_NO_ANNOUNCE = 4, /**< For raw records: Do not announce this RR to other hosts */ + AVAHI_PUBLISH_ALLOW_MULTIPLE = 8, /**< For raw records: Allow multiple local records of this type, even if they are intended to be unique */ +/** \cond fulldocs */ + AVAHI_PUBLISH_NO_REVERSE = 16, /**< For address records: don't create a reverse (PTR) entry */ + AVAHI_PUBLISH_NO_COOKIE = 32, /**< For service records: do not implicitly add the local service cookie to TXT data */ +/** \endcond */ + AVAHI_PUBLISH_UPDATE = 64, /**< Update existing records instead of adding new ones */ +/** \cond fulldocs */ + AVAHI_PUBLISH_USE_WIDE_AREA = 128, /**< Register the record using wide area DNS (i.e. unicast DNS update) */ + AVAHI_PUBLISH_USE_MULTICAST = 256 /**< Register the record using multicast DNS */ +/** \endcond */ +} AvahiPublishFlags; + +/** Some flags for lookup functions */ +typedef enum { +/** \cond fulldocs */ + AVAHI_LOOKUP_USE_WIDE_AREA = 1, /**< Force lookup via wide area DNS */ + AVAHI_LOOKUP_USE_MULTICAST = 2, /**< Force lookup via multicast DNS */ +/** \endcond */ + AVAHI_LOOKUP_NO_TXT = 4, /**< When doing service resolving, don't lookup TXT record */ + AVAHI_LOOKUP_NO_ADDRESS = 8 /**< When doing service resolving, don't lookup A/AAAA record */ +} AvahiLookupFlags; + +/** Some flags for lookup callback functions */ +typedef enum { + AVAHI_LOOKUP_RESULT_CACHED = 1, /**< This response originates from the cache */ + AVAHI_LOOKUP_RESULT_WIDE_AREA = 2, /**< This response originates from wide area DNS */ + AVAHI_LOOKUP_RESULT_MULTICAST = 4, /**< This response originates from multicast DNS */ + AVAHI_LOOKUP_RESULT_LOCAL = 8, /**< This record/service resides on and was announced by the local host. Only available in service and record browsers and only on AVAHI_BROWSER_NEW. */ + AVAHI_LOOKUP_RESULT_OUR_OWN = 16, /**< This service belongs to the same local client as the browser object. Only available in avahi-client, and only for service browsers and only on AVAHI_BROWSER_NEW. */ + AVAHI_LOOKUP_RESULT_STATIC = 32 /**< The returned data has been defined statically by some configuration option */ +} AvahiLookupResultFlags; + +/** @} */ + +/** @{ \name Events */ + +/** Type of callback event when browsing */ +typedef enum { + AVAHI_BROWSER_NEW, /**< The object is new on the network */ + AVAHI_BROWSER_REMOVE, /**< The object has been removed from the network */ + AVAHI_BROWSER_CACHE_EXHAUSTED, /**< One-time event, to notify the user that all entries from the caches have been sent */ + AVAHI_BROWSER_ALL_FOR_NOW, /**< One-time event, to notify the user that more records will probably not show up in the near future, i.e. all cache entries have been read and all static servers been queried */ + AVAHI_BROWSER_FAILURE /**< Browsing failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ +} AvahiBrowserEvent; + +/** Type of callback event when resolving */ +typedef enum { + AVAHI_RESOLVER_FOUND, /**< RR found, resolving successful */ + AVAHI_RESOLVER_FAILURE /**< Resolving failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ +} AvahiResolverEvent; + +/** @} */ + +/** @{ \name Other definitions */ + +/** The type of domain to browse for */ +typedef enum { + AVAHI_DOMAIN_BROWSER_BROWSE, /**< Browse for a list of available browsing domains */ + AVAHI_DOMAIN_BROWSER_BROWSE_DEFAULT, /**< Browse for the default browsing domain */ + AVAHI_DOMAIN_BROWSER_REGISTER, /**< Browse for a list of available registering domains */ + AVAHI_DOMAIN_BROWSER_REGISTER_DEFAULT, /**< Browse for the default registering domain */ + AVAHI_DOMAIN_BROWSER_BROWSE_LEGACY, /**< Legacy browse domain - see DNS-SD spec for more information */ + AVAHI_DOMAIN_BROWSER_MAX +} AvahiDomainBrowserType; + +/** @} */ + +/** \cond fulldocs */ +/** For every service a special TXT item is implicitly added, which + * contains a random cookie which is private to the local daemon. This + * can be used by clients to determine if two services on two + * different subnets are effectively the same. */ +#define AVAHI_SERVICE_COOKIE "org.freedesktop.Avahi.cookie" + +/** In invalid cookie as special value */ +#define AVAHI_SERVICE_COOKIE_INVALID (0) +/** \endcond fulldocs */ + +/** @{ \name DNS RR definitions */ + +/** DNS record types, see RFC 1035 */ +enum { + AVAHI_DNS_TYPE_A = 0x01, + AVAHI_DNS_TYPE_NS = 0x02, + AVAHI_DNS_TYPE_CNAME = 0x05, + AVAHI_DNS_TYPE_SOA = 0x06, + AVAHI_DNS_TYPE_PTR = 0x0C, + AVAHI_DNS_TYPE_HINFO = 0x0D, + AVAHI_DNS_TYPE_MX = 0x0F, + AVAHI_DNS_TYPE_TXT = 0x10, + AVAHI_DNS_TYPE_AAAA = 0x1C, + AVAHI_DNS_TYPE_SRV = 0x21 +}; + +/** DNS record classes, see RFC 1035 */ +enum { + AVAHI_DNS_CLASS_IN = 0x01 /**< Probably the only class we will ever use */ +}; + +/** @} */ + +/** The default TTL for RRs which contain a host name of some kind. */ +#define AVAHI_DEFAULT_TTL_HOST_NAME (120) + +/** The default TTL for all other records. */ +#define AVAHI_DEFAULT_TTL (75*60) + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/dns-spin-test.c b/avahi/dns-spin-test.c new file mode 100644 index 0000000..f50e691 --- /dev/null +++ b/avahi/dns-spin-test.c @@ -0,0 +1,122 @@ +/*** + 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. +***/ + +/* Regression test for Avahi bug #84. + * This program tests whether the avahi_dns_packet_consume_name function + * returns (rather than spinning forever). For a function as simple as + * avahi_dns_packet_consume_name, we assume that 1 second of CPU time ≈ forever + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +#define MAX_CPU_SECONDS 1 + +#define TEST_NAME "dns-spin-test" + +static void fail(const char *fmt, ...) __attribute__((format(printf, 1, 2), noreturn)); +static void unresolved(const char *fmt, ...) __attribute__((format(printf, 1, 2), noreturn)); +static void stdlib_fail(const char *msg) __attribute__((noreturn)); +static void handle(int sig) __attribute__((noreturn)); + +void stdlib_fail(const char *msg) { + perror(msg); + + printf("UNRESOLVED: " TEST_NAME " (stdlib failure)\n"); + + exit(77); +} + +void unresolved(const char *fmt, ...) { + va_list ap; + + printf("UNRESOLVED: " TEST_NAME ": "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + + exit(77); +} + +void fail(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + + exit(EXIT_FAILURE); +} + +void handle(AVAHI_GCC_UNUSED int sig) { + fail("Interrupted after %d second of CPU time", MAX_CPU_SECONDS); +} + +#define TRY_EXCEPT(cmd, badresult) \ + do { \ + if ((cmd) == (badresult)) \ + unresolved("%s returned %s", #cmd, #badresult); \ + } while (0) + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + struct itimerval itval; + AvahiDnsPacket *packet; + char name[512]; + int ret; + uint8_t badrr[] = { + 0xC0, AVAHI_DNS_PACKET_HEADER_SIZE, /* self-referential QNAME pointer */ + 0, 1, /* QTYPE A (host addr) */ + 0, 1, /* QCLASS IN (internet/ipv4) */ + }; + + if (signal(SIGVTALRM, handle) == SIG_ERR) + stdlib_fail("signal(SIGVTALRM)"); + + memset(&itval, 0, sizeof(itval)); + itval.it_value.tv_sec = MAX_CPU_SECONDS; + + if (setitimer(ITIMER_VIRTUAL, &itval, NULL) == -1) + stdlib_fail("setitimer()"); + + TRY_EXCEPT(packet = avahi_dns_packet_new_query(512), NULL); + TRY_EXCEPT(avahi_dns_packet_append_bytes(packet, badrr, sizeof(badrr)), NULL); + + /* This is expected to fail (if it returns) */ + ret = avahi_dns_packet_consume_name(packet, name, sizeof(name)); + + if (ret != -1) + fail("avahi_dns_packet_consume_name() returned %d; -1 was expected", ret); + + return EXIT_SUCCESS; +} + +/* vim:ts=4:sw=4:et + */ diff --git a/avahi/dns-srv-rr.h b/avahi/dns-srv-rr.h new file mode 100644 index 0000000..c52111b --- /dev/null +++ b/avahi/dns-srv-rr.h @@ -0,0 +1,87 @@ +#ifndef foodnssrvhfoo +#define foodnssrvhfoo + +/*** + 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. +***/ + +/** \file avahi/dns-srv-rr.h Functions for announcing and browsing for unicast DNS servers via mDNS */ + +/** A domain service browser object. Use this to browse for + * conventional unicast DNS servers which may be used to resolve + * conventional domain names */ +typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser; + +#include +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** The type of DNS server */ +typedef enum { + AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/ + AVAHI_DNS_SERVER_UPDATE, /**< Unicast DNS servers for updates (_dns-update._udp)*/ + AVAHI_DNS_SERVER_MAX +} AvahiDNSServerType; + +/** Publish the specified unicast DNS server address via mDNS. You may + * browse for records create this way wit + * avahi_s_dns_server_browser_new(). */ +int avahi_server_add_dns_server_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *domain, + AvahiDNSServerType type, + const AvahiAddress *address, + uint16_t port /** should be 53 */); + +/** Callback prototype for AvahiSDNSServerBrowser events */ +typedef void (*AvahiSDNSServerBrowserCallback)( + AvahiSDNSServerBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *host_name, /**< Host name of the DNS server, probably useless */ + const AvahiAddress *a, /**< Address of the DNS server */ + uint16_t port, /**< Port number of the DNS servers, probably 53 */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSDNSServerBrowser object */ +AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + AvahiProtocol aprotocol, /**< Address protocol for the DNS server */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSDNSServerBrowserCallback callback, + void* userdata); + +/** Free an AvahiSDNSServerBrowser object */ +void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/dns-test.c b/avahi/dns-test.c new file mode 100644 index 0000000..dd95487 --- /dev/null +++ b/avahi/dns-test.c @@ -0,0 +1,113 @@ +/*** + 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 "dns.h" +#include "log.h" +#include "util.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + char t[AVAHI_DOMAIN_NAME_MAX], *m; + const char *a, *b, *c, *d; + AvahiDnsPacket *p; + AvahiRecord *r, *r2; + uint8_t rdata[AVAHI_DNS_RDATA_MAX]; + size_t l; + + p = avahi_dns_packet_new(0); + + assert(avahi_dns_packet_append_name(p, a = "Ahello.hello.hello.de.")); + assert(avahi_dns_packet_append_name(p, b = "Bthis is a test.hello.de.")); + assert(avahi_dns_packet_append_name(p, c = "Cthis\\.is\\.a\\.test\\.with\\.dots.hello.de.")); + assert(avahi_dns_packet_append_name(p, d = "Dthis\\\\is another test.hello.de.")); + + avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size); + + assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); + avahi_log_debug(">%s<", t); + assert(avahi_domain_equal(a, t)); + + assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); + avahi_log_debug(">%s<", t); + assert(avahi_domain_equal(b, t)); + + assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); + avahi_log_debug(">%s<", t); + assert(avahi_domain_equal(c, t)); + + assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); + avahi_log_debug(">%s<", t); + assert(avahi_domain_equal(d, t)); + + avahi_dns_packet_free(p); + + /* RDATA PARSING AND SERIALIZATION */ + + /* Create an AvahiRecord with some usful data */ + r = avahi_record_new_full("foobar.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL); + assert(r); + r->data.hinfo.cpu = avahi_strdup("FOO"); + r->data.hinfo.os = avahi_strdup("BAR"); + + /* Serialize it into a blob */ + assert((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) != (size_t) -1); + + /* Print it */ + avahi_hexdump(rdata, l); + + /* Create a new record and fill in the data from the blob */ + r2 = avahi_record_new(r->key, AVAHI_DEFAULT_TTL); + assert(r2); + assert(avahi_rdata_parse(r2, rdata, l) >= 0); + + /* Compare both versions */ + assert(avahi_record_equal_no_ttl(r, r2)); + + /* Free the records */ + avahi_record_unref(r); + avahi_record_unref(r2); + + r = avahi_record_new_full("foobar", 77, 77, AVAHI_DEFAULT_TTL); + assert(r); + + assert(r->data.generic.data = avahi_memdup("HALLO", r->data.generic.size = 5)); + + m = avahi_record_to_string(r); + assert(m); + + avahi_log_debug(">%s<", m); + + avahi_free(m); + avahi_record_unref(r); + + return 0; +} diff --git a/avahi/dns.c b/avahi/dns.c new file mode 100644 index 0000000..84a0df3 --- /dev/null +++ b/avahi/dns.c @@ -0,0 +1,877 @@ +/*** + 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 "dns.h" +#include "log.h" + +AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) { + AvahiDnsPacket *p; + size_t max_size; + + if (mtu <= 0) + max_size = AVAHI_DNS_PACKET_SIZE_MAX; + else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE) + max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE; + else + max_size = 0; + + if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) + max_size = AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size))) + return p; + + p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + p->max_size = max_size; + p->name_table = NULL; + p->data = NULL; + + memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) { + AvahiDnsPacket *p; + + if (!(p = avahi_dns_packet_new(mtu))) + return NULL; + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) { + AvahiDnsPacket *p; + + if (!(p = avahi_dns_packet_new(mtu))) + return NULL; + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) { + AvahiDnsPacket *r; + assert(p); + + if (!(r = avahi_dns_packet_new_response(mtu, aa))) + return NULL; + + if (copy_queries) { + unsigned saved_rindex; + uint32_t n; + + saved_rindex = p->rindex; + p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) { + AvahiKey *k; + int unicast_response; + + if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) { + avahi_dns_packet_append_key(r, k, unicast_response); + avahi_key_unref(k); + } + } + + p->rindex = saved_rindex; + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT)); + } + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS, + (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) | + (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE)); + + return r; +} + + +void avahi_dns_packet_free(AvahiDnsPacket *p) { + assert(p); + + if (p->name_table) + avahi_hashmap_free(p->name_table); + + avahi_free(p); +} + +void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) { + assert(p); + assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); + + ((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v); +} + +uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) { + assert(p); + assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); + + return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]); +} + +void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) { + assert(p); + assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); + + avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1); +} + + +static void name_table_cleanup(void *key, void *value, void *user_data) { + AvahiDnsPacket *p = user_data; + + if ((uint8_t*) value >= AVAHI_DNS_PACKET_DATA(p) + p->size) + avahi_hashmap_remove(p->name_table, key); +} + +void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p) { + if (p->name_table) + avahi_hashmap_foreach(p->name_table, name_table_cleanup, p); +} + +uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) { + uint8_t *d, *saved_ptr = NULL; + size_t saved_size; + + assert(p); + assert(name); + + saved_size = p->size; + saved_ptr = avahi_dns_packet_extend(p, 0); + + while (*name) { + uint8_t* prev; + const char *pname; + char label[64], *u; + + /* Check whether we can compress this name. */ + + if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) { + unsigned idx; + + assert(prev >= AVAHI_DNS_PACKET_DATA(p)); + idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p)); + + assert(idx < p->size); + + if (idx < 0x4000) { + uint8_t *t; + if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t)))) + return NULL; + + t[0] = (uint8_t) ((0xC000 | idx) >> 8); + t[1] = (uint8_t) idx; + return saved_ptr; + } + } + + pname = name; + + if (!(avahi_unescape_label(&name, label, sizeof(label)))) + goto fail; + + if (!(d = avahi_dns_packet_append_string(p, label))) + goto fail; + + if (!p->name_table) + /* This works only for normalized domain names */ + p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL); + + if (!(u = avahi_strdup(pname))) + avahi_log_error("avahi_strdup() failed."); + else + avahi_hashmap_insert(p->name_table, u, d); + } + + if (!(d = avahi_dns_packet_extend(p, 1))) + goto fail; + + *d = 0; + + return saved_ptr; + +fail: + p->size = saved_size; + avahi_dns_packet_cleanup_name_table(p); + + return NULL; +} + +uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) { + uint8_t *d; + assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t)))) + return NULL; + + d[0] = (uint8_t) (v >> 8); + d[1] = (uint8_t) v; + return d; +} + +uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) { + uint8_t *d; + assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t)))) + return NULL; + + d[0] = (uint8_t) (v >> 24); + d[1] = (uint8_t) (v >> 16); + d[2] = (uint8_t) (v >> 8); + d[3] = (uint8_t) v; + + return d; +} + +uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *b, size_t l) { + uint8_t* d; + + assert(p); + assert(b); + assert(l); + + if (!(d = avahi_dns_packet_extend(p, l))) + return NULL; + + memcpy(d, b, l); + return d; +} + +uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) { + uint8_t* d; + size_t k; + + assert(p); + assert(s); + + if ((k = strlen(s)) >= 255) + k = 255; + + if (!(d = avahi_dns_packet_extend(p, k+1))) + return NULL; + + *d = (uint8_t) k; + memcpy(d+1, s, k); + + return d; +} + +uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) { + uint8_t *d; + + assert(p); + + if (p->size+l > p->max_size) + return NULL; + + d = AVAHI_DNS_PACKET_DATA(p) + p->size; + p->size += l; + + return d; +} + +int avahi_dns_packet_check_valid(AvahiDnsPacket *p) { + uint16_t flags; + assert(p); + + if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_OPCODE) + return -1; + + return 0; +} + +int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) { + uint16_t flags; + assert(p); + + if (avahi_dns_packet_check_valid(p) < 0) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_RCODE) + return -1; + + return 0; +} + +int avahi_dns_packet_is_query(AvahiDnsPacket *p) { + assert(p); + + return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); +} + +static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) { + int ret = 0; + int compressed = 0; + int first_label = 1; + unsigned label_ptr; + int i; + assert(p && ret_name && l); + + for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) { + uint8_t n; + + if (idx+1 > p->size) + return -1; + + n = AVAHI_DNS_PACKET_DATA(p)[idx]; + + if (!n) { + idx++; + if (!compressed) + ret++; + + if (l < 1) + return -1; + *ret_name = 0; + + return ret; + + } else if (n <= 63) { + /* Uncompressed label */ + idx++; + if (!compressed) + ret++; + + if (idx + n > p->size) + return -1; + + if ((size_t) n + 1 > l) + return -1; + + if (!first_label) { + *(ret_name++) = '.'; + l--; + } else + first_label = 0; + + if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l))) + return -1; + + idx += n; + + if (!compressed) + ret += n; + } else if ((n & 0xC0) == 0xC0) { + /* Compressed label */ + + if (idx+2 > p->size) + return -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; + + compressed = 1; + } else + return -1; + } + + return -1; +} + +int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) { + int r; + + if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) + return -1; + + p->rindex += r; + return 0; +} + +int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) { + uint8_t *d; + + assert(p); + assert(ret_v); + + if (p->rindex + sizeof(uint16_t) > p->size) + return -1; + + d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); + *ret_v = (d[0] << 8) | d[1]; + p->rindex += sizeof(uint16_t); + + return 0; +} + +int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) { + uint8_t* d; + + assert(p); + assert(ret_v); + + if (p->rindex + sizeof(uint32_t) > p->size) + return -1; + + d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); + *ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; + p->rindex += sizeof(uint32_t); + + return 0; +} + +int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) { + assert(p); + assert(ret_data); + assert(l > 0); + + if (p->rindex + l > p->size) + return -1; + + memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); + p->rindex += l; + + return 0; +} + +int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) { + size_t k; + + assert(p); + assert(ret_string); + assert(l > 0); + + if (p->rindex >= p->size) + return -1; + + k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; + + if (p->rindex+1+k > p->size) + return -1; + + if (l > k+1) + l = k+1; + + memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); + ret_string[l-1] = 0; + + p->rindex += 1+k; + + return 0; +} + +const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { + assert(p); + + if (p->rindex > p->size) + return NULL; + + return AVAHI_DNS_PACKET_DATA(p) + p->rindex; +} + +int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) { + assert(p); + + if (p->rindex + length > p->size) + return -1; + + p->rindex += length; + return 0; +} + +static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) { + char buf[AVAHI_DOMAIN_NAME_MAX]; + const void* start; + + assert(p); + assert(r); + + start = avahi_dns_packet_get_rptr(p); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + + if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.ptr.name = avahi_strdup(buf); + break; + + + case AVAHI_DNS_TYPE_SRV: + + if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || + avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.srv.name = avahi_strdup(buf); + break; + + case AVAHI_DNS_TYPE_HINFO: + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.hinfo.cpu = avahi_strdup(buf); + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.hinfo.os = avahi_strdup(buf); + break; + + case AVAHI_DNS_TYPE_TXT: + + if (rdlength > 0) { + if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0) + return -1; + + if (avahi_dns_packet_skip(p, rdlength) < 0) + return -1; + } else + r->data.txt.string_list = NULL; + + break; + + case AVAHI_DNS_TYPE_A: + +/* avahi_log_debug("A"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) + return -1; + + break; + + case AVAHI_DNS_TYPE_AAAA: + +/* avahi_log_debug("aaaa"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) + return -1; + + break; + + default: + +/* avahi_log_debug("generic"); */ + + if (rdlength > 0) { + + r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength); + r->data.generic.size = rdlength; + + if (avahi_dns_packet_skip(p, rdlength) < 0) + return -1; + } + + break; + } + + /* Check if we read enough data */ + if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength) + return -1; + + return 0; +} + +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) { + char name[AVAHI_DOMAIN_NAME_MAX]; + uint16_t type, class; + uint32_t ttl; + uint16_t rdlength; + AvahiRecord *r = NULL; + + assert(p); + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0 || + avahi_dns_packet_consume_uint32(p, &ttl) < 0 || + avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || + p->rindex + rdlength > p->size) + goto fail; + + if (ret_cache_flush) + *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); + class &= ~AVAHI_DNS_CACHE_FLUSH; + + if (!(r = avahi_record_new_full(name, class, type, ttl))) + goto fail; + + if (parse_rdata(p, r, rdlength) < 0) + goto fail; + + if (!avahi_record_is_valid(r)) + goto fail; + + return r; + +fail: + if (r) + avahi_record_unref(r); + + return NULL; +} + +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) { + char name[256]; + uint16_t type, class; + AvahiKey *k; + + assert(p); + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0) + return NULL; + + if (ret_unicast_response) + *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); + + class &= ~AVAHI_DNS_UNICAST_RESPONSE; + + if (!(k = avahi_key_new(name, class, type))) + return NULL; + + if (!avahi_key_is_valid(k)) { + avahi_key_unref(k); + return NULL; + } + + return k; +} + +uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) { + uint8_t *t; + size_t size; + + assert(p); + assert(k); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, k->name)) || + !avahi_dns_packet_append_uint16(p, k->type) || + !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { + p->size = size; + avahi_dns_packet_cleanup_name_table(p); + + return NULL; + } + + return t; +} + +static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) { + assert(p); + assert(r); + + switch (r->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + + if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) + return -1; + + break; + + case AVAHI_DNS_TYPE_SRV: + + if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || + !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || + !avahi_dns_packet_append_uint16(p, r->data.srv.port) || + !avahi_dns_packet_append_name(p, r->data.srv.name)) + return -1; + + break; + + case AVAHI_DNS_TYPE_HINFO: + if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || + !avahi_dns_packet_append_string(p, r->data.hinfo.os)) + return -1; + + break; + + case AVAHI_DNS_TYPE_TXT: { + + uint8_t *data; + size_t n; + + n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + + if (!(data = avahi_dns_packet_extend(p, n))) + return -1; + + avahi_string_list_serialize(r->data.txt.string_list, data, n); + break; + } + + + case AVAHI_DNS_TYPE_A: + + if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) + return -1; + + break; + + case AVAHI_DNS_TYPE_AAAA: + + if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) + return -1; + + break; + + default: + + if (r->data.generic.size) + if (!avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) + return -1; + + break; + } + + return 0; +} + + +uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) { + uint8_t *t, *l, *start; + size_t size; + + assert(p); + assert(r); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || + !avahi_dns_packet_append_uint16(p, r->key->type) || + !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) || + !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) || + !(l = avahi_dns_packet_append_uint16(p, 0))) + goto fail; + + start = avahi_dns_packet_extend(p, 0); + + if (append_rdata(p, r) < 0) + goto fail; + + size = avahi_dns_packet_extend(p, 0) - start; + assert(size <= AVAHI_DNS_RDATA_MAX); + +/* avahi_log_debug("appended %u", size); */ + + l[0] = (uint8_t) ((uint16_t) size >> 8); + l[1] = (uint8_t) ((uint16_t) size); + + return t; + + +fail: + p->size = size; + avahi_dns_packet_cleanup_name_table(p); + + return NULL; +} + +int avahi_dns_packet_is_empty(AvahiDnsPacket *p) { + assert(p); + + return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; +} + +size_t avahi_dns_packet_space(AvahiDnsPacket *p) { + assert(p); + + assert(p->size <= p->max_size); + + return p->max_size - p->size; +} + +int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) { + int ret; + AvahiDnsPacket p; + + assert(record); + assert(rdata); + + p.data = (void*) rdata; + p.max_size = p.size = size; + p.rindex = 0; + p.name_table = NULL; + + ret = parse_rdata(&p, record, size); + + assert(!p.name_table); + + return ret; +} + +size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) { + int ret; + AvahiDnsPacket p; + + assert(record); + assert(rdata); + assert(max_size > 0); + + p.data = (void*) rdata; + p.max_size = max_size; + p.size = p.rindex = 0; + p.name_table = NULL; + + ret = append_rdata(&p, record); + + if (p.name_table) + avahi_hashmap_free(p.name_table); + + if (ret < 0) + return (size_t) -1; + + return p.size; +} diff --git a/avahi/dns.h b/avahi/dns.h new file mode 100644 index 0000000..52e8d88 --- /dev/null +++ b/avahi/dns.h @@ -0,0 +1,111 @@ +#ifndef foodnshfoo +#define foodnshfoo + +/*** + 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 "rr.h" +#include "hashmap.h" + +#define AVAHI_DNS_PACKET_HEADER_SIZE 12 +#define AVAHI_DNS_PACKET_EXTRA_SIZE 48 +#define AVAHI_DNS_LABELS_MAX 127 +#define AVAHI_DNS_RDATA_MAX 0xFFFF +#define AVAHI_DNS_PACKET_SIZE_MAX (AVAHI_DNS_PACKET_HEADER_SIZE + 256 + 2 + 2 + 4 + 2 + AVAHI_DNS_RDATA_MAX) + +typedef struct AvahiDnsPacket { + size_t size, rindex, max_size; + AvahiHashmap *name_table; /* for name compression */ + uint8_t *data; +} AvahiDnsPacket; + +#define AVAHI_DNS_PACKET_DATA(p) ((p)->data ? (p)->data : ((uint8_t*) p) + sizeof(AvahiDnsPacket)) + +AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu); +AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu); +AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa); + +AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa); + +void avahi_dns_packet_free(AvahiDnsPacket *p); +void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v); +uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx); +void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx); + +uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l); + +void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p); + +uint8_t *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v); +uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v); +uint8_t *avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name); +uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *d, size_t l); +uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response); +uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl); +uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s); + +int avahi_dns_packet_is_query(AvahiDnsPacket *p); +int avahi_dns_packet_check_valid(AvahiDnsPacket *p); +int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p); + +int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v); +int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v); +int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l); +int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void* ret_data, size_t l); +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response); +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush); +int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l); + +const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p); + +int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length); + +int avahi_dns_packet_is_empty(AvahiDnsPacket *p); +size_t avahi_dns_packet_space(AvahiDnsPacket *p); + +#define AVAHI_DNS_FIELD_ID 0 +#define AVAHI_DNS_FIELD_FLAGS 1 +#define AVAHI_DNS_FIELD_QDCOUNT 2 +#define AVAHI_DNS_FIELD_ANCOUNT 3 +#define AVAHI_DNS_FIELD_NSCOUNT 4 +#define AVAHI_DNS_FIELD_ARCOUNT 5 + +#define AVAHI_DNS_FLAG_QR (1 << 15) +#define AVAHI_DNS_FLAG_OPCODE (15 << 11) +#define AVAHI_DNS_FLAG_RCODE (15) +#define AVAHI_DNS_FLAG_TC (1 << 9) +#define AVAHI_DNS_FLAG_AA (1 << 10) + +#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ + (((uint16_t) !!qr << 15) | \ + ((uint16_t) (opcode & 15) << 11) | \ + ((uint16_t) !!aa << 10) | \ + ((uint16_t) !!tc << 9) | \ + ((uint16_t) !!rd << 8) | \ + ((uint16_t) !!ra << 7) | \ + ((uint16_t) !!ad << 5) | \ + ((uint16_t) !!cd << 4) | \ + ((uint16_t) (rcode & 15))) + +#define AVAHI_MDNS_SUFFIX_LOCAL "local" +#define AVAHI_MDNS_SUFFIX_ADDR_IPV4 "254.169.in-addr.arpa" +#define AVAHI_MDNS_SUFFIX_ADDR_IPV6 "0.8.e.f.ip6.arpa" + +#endif + diff --git a/avahi/domain-test.c b/avahi/domain-test.c new file mode 100644 index 0000000..cf763ec --- /dev/null +++ b/avahi/domain-test.c @@ -0,0 +1,123 @@ +/*** + 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 "domain.h" +#include "malloc.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + char *s; + char t[256], r[256]; + const char *p; + size_t size; + char name[64], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; + + printf("%s\n", s = avahi_normalize_name_strdup("foo.foo\\046.")); + avahi_free(s); + + printf("%s\n", s = avahi_normalize_name_strdup("foo.foo\\.foo.")); + avahi_free(s); + + + printf("%s\n", s = avahi_normalize_name_strdup("fo\\\\o\\..f oo.")); + avahi_free(s); + + printf("%i\n", avahi_domain_equal("\\065aa bbb\\.\\046cc.cc\\\\.dee.fff.", "Aaa BBB\\.\\.cc.cc\\\\.dee.fff")); + printf("%i\n", avahi_domain_equal("A", "a")); + + printf("%i\n", avahi_domain_equal("a", "aaa")); + + printf("%u = %u\n", avahi_domain_hash("ccc\\065aa.aa\\.b\\\\."), avahi_domain_hash("cccAaa.aa\\.b\\\\")); + + + avahi_service_name_join(t, sizeof(t), "foo.foo.foo \\.", "_http._tcp", "test.local"); + printf("<%s>\n", t); + + avahi_service_name_split(t, name, sizeof(name), type, sizeof(type), domain, sizeof(domain)); + printf("name: <%s>; type: <%s>; domain <%s>\n", name, type, domain); + + avahi_service_name_join(t, sizeof(t), NULL, "_http._tcp", "one.two\\. .local"); + printf("<%s>\n", t); + + avahi_service_name_split(t, NULL, 0, type, sizeof(type), domain, sizeof(domain)); + printf("name: <>; type: <%s>; domain <%s>\n", type, domain); + + + p = "--:---\\\\\\123\\065_äöü\\064\\.\\\\sjöödfhh.sdfjhskjdf"; + printf("unescaped: <%s>, rest: %s\n", avahi_unescape_label(&p, t, sizeof(t)), p); + + size = sizeof(r); + s = r; + + printf("escaped: <%s>\n", avahi_escape_label(t, strlen(t), &s, &size)); + + p = r; + printf("unescaped: <%s>\n", avahi_unescape_label(&p, t, sizeof(t))); + + assert(avahi_is_valid_service_type_generic("_foo._bar._waldo")); + assert(!avahi_is_valid_service_type_strict("_foo._bar._waldo")); + assert(!avahi_is_valid_service_subtype("_foo._bar._waldo")); + + assert(avahi_is_valid_service_type_generic("_foo._tcp")); + assert(avahi_is_valid_service_type_strict("_foo._tcp")); + assert(!avahi_is_valid_service_subtype("_foo._tcp")); + + assert(!avahi_is_valid_service_type_generic("_foo._bar.waldo")); + assert(!avahi_is_valid_service_type_strict("_foo._bar.waldo")); + assert(!avahi_is_valid_service_subtype("_foo._bar.waldo")); + + assert(!avahi_is_valid_service_type_generic("")); + assert(!avahi_is_valid_service_type_strict("")); + assert(!avahi_is_valid_service_subtype("")); + + assert(avahi_is_valid_service_type_generic("_foo._sub._bar._tcp")); + assert(!avahi_is_valid_service_type_strict("_foo._sub._bar._tcp")); + assert(avahi_is_valid_service_subtype("_foo._sub._bar._tcp")); + + printf("%s\n", avahi_get_type_from_subtype("_foo._sub._bar._tcp")); + + assert(!avahi_is_valid_host_name("sf.ooo.")); + assert(avahi_is_valid_host_name("sfooo.")); + assert(avahi_is_valid_host_name("sfooo")); + + assert(avahi_is_valid_domain_name(".")); + assert(avahi_is_valid_domain_name("")); + + assert(avahi_normalize_name(".", t, sizeof(t))); + assert(avahi_normalize_name("", t, sizeof(t))); + + assert(!avahi_is_valid_fqdn(".")); + assert(!avahi_is_valid_fqdn("")); + assert(!avahi_is_valid_fqdn("foo")); + assert(avahi_is_valid_fqdn("foo.bar")); + assert(avahi_is_valid_fqdn("foo.bar.")); + assert(avahi_is_valid_fqdn("gnurz.foo.bar.")); + assert(!avahi_is_valid_fqdn("192.168.50.1")); + assert(!avahi_is_valid_fqdn("::1")); + assert(!avahi_is_valid_fqdn(".192.168.50.1.")); + + return 0; +} diff --git a/avahi/domain-util.c b/avahi/domain-util.c new file mode 100644 index 0000000..7731dd1 --- /dev/null +++ b/avahi/domain-util.c @@ -0,0 +1,188 @@ +/*** + 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 "log.h" +#include "domain-util.h" +#include "util.h" + +static void strip_bad_chars(char *s) { + char *p, *d; + + s[strcspn(s, ".")] = 0; + + for (p = s, d = s; *p; p++) + if ((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '-') + *(d++) = *p; + + *d = 0; +} + +#ifdef __linux__ +static int load_lsb_distrib_id(char *ret_s, size_t size) { + FILE *f; + + assert(ret_s); + assert(size > 0); + + if (!(f = fopen("/etc/lsb-release", "r"))) + return -1; + + while (!feof(f)) { + char ln[256], *p; + + if (!fgets(ln, sizeof(ln), f)) + break; + + if (strncmp(ln, "DISTRIB_ID=", 11)) + continue; + + p = ln + 11; + p += strspn(p, "\""); + p[strcspn(p, "\"")] = 0; + + snprintf(ret_s, size, "%s", p); + + fclose(f); + return 0; + } + + fclose(f); + return -1; +} +#endif + +char *avahi_get_host_name(char *ret_s, size_t size) { + assert(ret_s); + assert(size > 0); + + if (gethostname(ret_s, size) >= 0) { + ret_s[size-1] = 0; + strip_bad_chars(ret_s); + } else + *ret_s = 0; + + if (strcmp(ret_s, "localhost") == 0 || strncmp(ret_s, "localhost.", 10) == 0) { + *ret_s = 0; + avahi_log_warn("System host name is set to 'localhost'. This is not a suitable mDNS host name, looking for alternatives."); + } + + if (*ret_s == 0) { + /* No hostname was set, so let's take the OS name */ + +#ifdef __linux__ + + /* Try LSB distribution name first */ + if (load_lsb_distrib_id(ret_s, size) >= 0) { + strip_bad_chars(ret_s); + avahi_strdown(ret_s); + } + + if (*ret_s == 0) +#endif + + { + /* Try uname() second */ + struct utsname utsname; + + if (uname(&utsname) >= 0) { + snprintf(ret_s, size, "%s", utsname.sysname); + strip_bad_chars(ret_s); + avahi_strdown(ret_s); + } + + /* Give up */ + if (*ret_s == 0) + snprintf(ret_s, size, "unnamed"); + } + } + + if (size >= AVAHI_LABEL_MAX) + ret_s[AVAHI_LABEL_MAX-1] = 0; + + return ret_s; +} + +char *avahi_get_host_name_strdup(void) { + char t[AVAHI_DOMAIN_NAME_MAX]; + + if (!(avahi_get_host_name(t, sizeof(t)))) + return NULL; + + return avahi_strdup(t); +} + +int avahi_binary_domain_cmp(const char *a, const char *b) { + assert(a); + assert(b); + + if (a == b) + return 0; + + for (;;) { + char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *p; + int r; + + p = avahi_unescape_label(&a, ca, sizeof(ca)); + assert(p); + p = avahi_unescape_label(&b, cb, sizeof(cb)); + assert(p); + + if ((r = strcmp(ca, cb))) + return r; + + if (!*a && !*b) + return 0; + } +} + +int avahi_domain_ends_with(const char *domain, const char *suffix) { + assert(domain); + assert(suffix); + + for (;;) { + char dummy[AVAHI_LABEL_MAX], *r; + + if (*domain == 0) + return 0; + + if (avahi_domain_equal(domain, suffix)) + return 1; + + r = avahi_unescape_label(&domain, dummy, sizeof(dummy)); + assert(r); + } +} + diff --git a/avahi/domain-util.h b/avahi/domain-util.h new file mode 100644 index 0000000..2b786df --- /dev/null +++ b/avahi/domain-util.h @@ -0,0 +1,45 @@ +#ifndef foodomainutilhfoo +#define foodomainutilhfoo + +/*** + 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 + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Return the local host name. */ +char *avahi_get_host_name(char *ret_s, size_t size); + +/** Return the local host name. avahi_free() the result! */ +char *avahi_get_host_name_strdup(void); + +/** Do a binary comparison of to specified domain names, return -1, 0, or 1, depending on the order. */ +int avahi_binary_domain_cmp(const char *a, const char *b); + +/** Returns 1 if the the end labels of domain are eqal to suffix */ +int avahi_domain_ends_with(const char *domain, const char *suffix); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/domain.c b/avahi/domain.c new file mode 100644 index 0000000..3b1ab68 --- /dev/null +++ b/avahi/domain.c @@ -0,0 +1,609 @@ +/*** + 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 "domain.h" +#include "malloc.h" +#include "error.h" +#include "address.h" +#include "utf8.h" + +/* Read the first label from string *name, unescape "\" and write it to dest */ +char *avahi_unescape_label(const char **name, char *dest, size_t size) { + unsigned i = 0; + char *d; + + assert(dest); + assert(size > 0); + assert(name); + + d = dest; + + for (;;) { + if (i >= size) + return NULL; + + if (**name == '.') { + (*name)++; + break; + } + + if (**name == 0) + break; + + if (**name == '\\') { + /* Escaped character */ + + (*name) ++; + + if (**name == 0) + /* Ending NUL */ + return NULL; + + else if (**name == '\\' || **name == '.') { + /* Escaped backslash or dot */ + *(d++) = *((*name) ++); + i++; + } else if (isdigit(**name)) { + int n; + + /* Escaped literal ASCII character */ + + if (!isdigit(*(*name+1)) || !isdigit(*(*name+2))) + return NULL; + + n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0')); + + if (n > 255 || n == 0) + return NULL; + + *(d++) = (char) n; + i++; + + (*name) += 3; + } else + return NULL; + + } else { + + /* Normal character */ + + *(d++) = *((*name) ++); + i++; + } + } + + assert(i < size); + + *d = 0; + + if (!avahi_utf8_valid(dest)) + return NULL; + + return dest; +} + +/* Escape "\" and ".", append \0 */ +char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) { + char *r; + + assert(src); + assert(ret_name); + assert(*ret_name); + assert(ret_size); + assert(*ret_size > 0); + + r = *ret_name; + + while (src_length > 0) { + if (*src == '.' || *src == '\\') { + + /* Dot or backslash */ + + if (*ret_size < 3) + return NULL; + + *((*ret_name) ++) = '\\'; + *((*ret_name) ++) = *src; + (*ret_size) -= 2; + + } else if ( + *src == '_' || + *src == '-' || + (*src >= '0' && *src <= '9') || + (*src >= 'a' && *src <= 'z') || + (*src >= 'A' && *src <= 'Z')) { + + /* Proper character */ + + if (*ret_size < 2) + return NULL; + + *((*ret_name)++) = *src; + (*ret_size) --; + + } else { + + /* Everything else */ + + if (*ret_size < 5) + return NULL; + + *((*ret_name) ++) = '\\'; + *((*ret_name) ++) = '0' + (char) ((uint8_t) *src / 100); + *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10); + *((*ret_name) ++) = '0' + (char) ((uint8_t) *src % 10); + + (*ret_size) -= 4; + } + + src_length --; + src++; + } + + **ret_name = 0; + + return r; +} + +char *avahi_normalize_name(const char *s, char *ret_s, size_t size) { + int empty = 1; + char *r; + + assert(s); + assert(ret_s); + assert(size > 0); + + r = ret_s; + *ret_s = 0; + + while (*s) { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&s, label, sizeof(label)))) + return NULL; + + if (label[0] == 0) { + + if (*s == 0 && empty) + return ret_s; + + return NULL; + } + + if (!empty) { + if (size < 1) + return NULL; + + *(r++) = '.'; + size--; + + } else + empty = 0; + + avahi_escape_label(label, strlen(label), &r, &size); + } + + return ret_s; +} + +char *avahi_normalize_name_strdup(const char *s) { + char t[AVAHI_DOMAIN_NAME_MAX]; + assert(s); + + if (!(avahi_normalize_name(s, t, sizeof(t)))) + return NULL; + + return avahi_strdup(t); +} + +int avahi_domain_equal(const char *a, const char *b) { + assert(a); + assert(b); + + if (a == b) + return 1; + + for (;;) { + char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r; + + r = avahi_unescape_label(&a, ca, sizeof(ca)); + assert(r); + r = avahi_unescape_label(&b, cb, sizeof(cb)); + assert(r); + + if (strcasecmp(ca, cb)) + return 0; + + if (!*a && !*b) + return 1; + } + + return 1; +} + +int avahi_is_valid_service_type_generic(const char *t) { + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + do { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strlen(label) <= 2 || label[0] != '_') + return 0; + + } while (*t); + + return 1; +} + +int avahi_is_valid_service_type_strict(const char *t) { + char label[AVAHI_LABEL_MAX]; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + /* Application name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strlen(label) <= 2 || label[0] != '_') + return 0; + + if (!*t) + return 0; + + /* _tcp or _udp boilerplate */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) + return 0; + + if (*t) + return 0; + + return 1; +} + +const char *avahi_get_type_from_subtype(const char *t) { + char label[AVAHI_LABEL_MAX]; + const char *ret; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return NULL; + + /* Subtype name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strlen(label) <= 2 || label[0] != '_') + return NULL; + + if (!*t) + return NULL; + + /* String "_sub" */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strcasecmp(label, "_sub")) + return NULL; + + if (!*t) + return NULL; + + ret = t; + + /* Application name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strlen(label) <= 2 || label[0] != '_') + return NULL; + + if (!*t) + return NULL; + + /* _tcp or _udp boilerplate */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) + return NULL; + + if (*t) + return NULL; + + return ret; +} + +int avahi_is_valid_service_subtype(const char *t) { + assert(t); + + return !!avahi_get_type_from_subtype(t); +} + +int avahi_is_valid_domain_name(const char *t) { + int is_first = 1; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) + return 0; + + do { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + /* Explicitly allow the root domain name */ + if (is_first && label[0] == 0 && *t == 0) + return 1; + + is_first = 0; + + if (label[0] == 0) + return 0; + + } while (*t); + + return 1; +} + +int avahi_is_valid_service_name(const char *t) { + assert(t); + + if (strlen(t) >= AVAHI_LABEL_MAX || !*t) + return 0; + + return 1; +} + +int avahi_is_valid_host_name(const char *t) { + char label[AVAHI_LABEL_MAX]; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strlen(label) < 1) + return 0; + + if (*t) + return 0; + + return 1; +} + +unsigned avahi_domain_hash(const char *s) { + unsigned hash = 0; + + while (*s) { + char c[AVAHI_LABEL_MAX], *p, *r; + + r = avahi_unescape_label(&s, c, sizeof(c)); + assert(r); + + for (p = c; *p; p++) + hash = 31 * hash + tolower(*p); + } + + return hash; +} + +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) { + char escaped_name[AVAHI_LABEL_MAX*4]; + char normalized_type[AVAHI_DOMAIN_NAME_MAX]; + char normalized_domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(p); + + /* Validity checks */ + + if ((name && !avahi_is_valid_service_name(name))) + return AVAHI_ERR_INVALID_SERVICE_NAME; + + if (!avahi_is_valid_service_type_generic(type)) + return AVAHI_ERR_INVALID_SERVICE_TYPE; + + if (!avahi_is_valid_domain_name(domain)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + /* Preparation */ + + if (name) { + size_t l = sizeof(escaped_name); + char *e = escaped_name, *r; + r = avahi_escape_label(name, strlen(name), &e, &l); + assert(r); + } + + if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type)))) + return AVAHI_ERR_INVALID_SERVICE_TYPE; + + if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain)))) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + /* Concatenation */ + + snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain); + + return AVAHI_OK; +} + +#ifndef HAVE_STRLCPY + +static size_t strlcpy(char *dest, const char *src, size_t n) { + assert(dest); + assert(src); + + if (n > 0) { + strncpy(dest, src, n-1); + dest[n-1] = 0; + } + + return strlen(src); +} + +#endif + +int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size) { + enum { + NAME, + TYPE, + DOMAIN + } state; + int type_empty = 1, domain_empty = 1; + + assert(p); + assert(type); + assert(type_size > 0); + assert(domain); + assert(domain_size > 0); + + if (name) { + assert(name_size > 0); + *name = 0; + state = NAME; + } else + state = TYPE; + + *type = *domain = 0; + + while (*p) { + char buf[64]; + + if (!(avahi_unescape_label(&p, buf, sizeof(buf)))) + return -1; + + switch (state) { + case NAME: + strlcpy(name, buf, name_size); + state = TYPE; + break; + + case TYPE: + + if (buf[0] == '_') { + + if (!type_empty) { + if (!type_size) + return AVAHI_ERR_NO_MEMORY; + + *(type++) = '.'; + type_size --; + + } else + type_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size))) + return AVAHI_ERR_NO_MEMORY; + + break; + } + + state = DOMAIN; + /* fall through */ + + case DOMAIN: + + if (!domain_empty) { + if (!domain_size) + return AVAHI_ERR_NO_MEMORY; + + *(domain++) = '.'; + domain_size --; + } else + domain_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size))) + return AVAHI_ERR_NO_MEMORY; + + break; + } + } + + return 0; +} + +int avahi_is_valid_fqdn(const char *t) { + char label[AVAHI_LABEL_MAX]; + char normalized[AVAHI_DOMAIN_NAME_MAX]; + const char *k = t; + AvahiAddress a; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) + return 0; + + if (!avahi_is_valid_domain_name(t)) + return 0; + + /* Check if there are at least two labels*/ + if (!(avahi_unescape_label(&k, label, sizeof(label)))) + return 0; + + if (label[0] == 0 || !k) + return 0; + + if (!(avahi_unescape_label(&k, label, sizeof(label)))) + return 0; + + if (label[0] == 0 || !k) + return 0; + + /* Make sure that the name is not an IP address */ + if (!(avahi_normalize_name(t, normalized, sizeof(normalized)))) + return 0; + + if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a)) + return 0; + + return 1; +} diff --git a/avahi/domain.h b/avahi/domain.h new file mode 100644 index 0000000..b3630af --- /dev/null +++ b/avahi/domain.h @@ -0,0 +1,129 @@ +#ifndef foodomainhfoo +#define foodomainhfoo + +/*** + 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. +***/ + +/** \file domain.h Domain name handling functions */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** The maximum length of a a fully escaped domain name C string. This + * is calculated like this: RFC1034 mandates maximum length of FQDNs + * is 255. The maximum label length is 63. To minimize the number of + * (non-escaped) dots, we comprise our maximum-length domain name of + * four labels á 63 characters plus three inner dots. Escaping the + * four labels quadruples their length at maximum. An escaped domain + * name has the therefore the maximum length of 63*4*4+3=1011. A + * trailing NUL and perhaps two unnecessary dots leading and trailing + * the string brings us to 1014. */ +#define AVAHI_DOMAIN_NAME_MAX 1014 + +/** Maximum size of an unescaped label */ +#define AVAHI_LABEL_MAX 64 + +/** @{ \name Normalization */ + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. */ +char *avahi_normalize_name(const char *s, char *ret_s, size_t size); + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. avahi_free() the + * result! */ +char *avahi_normalize_name_strdup(const char *s); + +/** @} */ + +/** @{ \name Comparison */ + +/** Return 1 when the specified domain names are equal, 0 otherwise */ +int avahi_domain_equal(const char *a, const char *b); + +/** Return some kind of hash value for the domain, useful for using domains as hash table keys. */ +unsigned avahi_domain_hash(const char *name); + +/** @} */ + +/** @{ \name Escaping */ + +/** Read the first label from the textual domain name *name, unescape + * it and write it to dest, *name is changed to point to the next label*/ +char *avahi_unescape_label(const char **name, char *dest, size_t size); + +/** Escape the domain name in *src and write it to *ret_name */ +char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size); + +/** @} */ + +/** @{ \name Validity Checks */ + +/** Return 1 when the specified string contains a valid generic DNS-SD + * service type (i.e. a series of words starting with "_"), 0 + * otherwise */ +int avahi_is_valid_service_type_generic(const char *t); + +/** Return 1 when the specified string contains a valid strict DNS-SD + * service type (i.e. consisting of only two words, the latter being + * either _udp or _tcp), 0 otherwise */ +int avahi_is_valid_service_type_strict(const char *t); + +/** Return 1 when the specified string contains a valid DNS-SD service + * subtype, 0 otherwise */ +int avahi_is_valid_service_subtype(const char *t); + +/** Return 1 when the specified string contains a valid domain name, 0 otherwise */ +int avahi_is_valid_domain_name(const char *t); + +/** Return 1 when the specified string contains a valid DNS-SD service name, 0 otherwise */ +int avahi_is_valid_service_name(const char *t); + +/** Return 1 when the specified string contains a valid non-FQDN host name (i.e. without dots), 0 otherwise */ +int avahi_is_valid_host_name(const char *t); + +/** Return 1 when the specified string contains a valid FQDN host name (i.e. with more than one label and non-numerical), 0 otherwise. \since 0.6.9 */ +int avahi_is_valid_fqdn(const char *t); + +/** @} */ + +/** @{ \name DNS-SD service name handling */ + +/** Construct a valid complete DNS-SD service name from a name, a type and a domain */ +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain); + +/** Split a full service name into name, type and domain */ +int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size); + +/** @} */ + +/** @{ \name DNS-SD Subtype handling */ + +/** Return a pointer to the type section of a subtype i.e. _foo._sub._bar._tcp => _bar._tcp */ +const char *avahi_get_type_from_subtype(const char *t); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/entry.c b/avahi/entry.c new file mode 100644 index 0000000..2fbc45a --- /dev/null +++ b/avahi/entry.c @@ -0,0 +1,1233 @@ +/*** + 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 "internal.h" +#include "iface.h" +#include "socket.h" +#include "browse.h" +#include "log.h" +#include "util.h" +#include "dns-srv-rr.h" +#include "rr-util.h" +#include "domain-util.h" + +static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain) { + assert(flags); + assert(domain); + + assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA))); + + if (*flags & (AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA)) + return; + + if (!s->wide_area_lookup_engine || + !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) + *flags |= AVAHI_PUBLISH_USE_MULTICAST; + else + *flags |= AVAHI_PUBLISH_USE_WIDE_AREA; +} + +void avahi_entry_free(AvahiServer*s, AvahiEntry *e) { + AvahiEntry *t; + + assert(s); + assert(e); + + avahi_goodbye_entry(s, e, 1, 1); + + /* Remove from linked list */ + AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e); + + /* Remove from hash table indexed by name */ + t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); + AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); + if (t) + avahi_hashmap_replace(s->entries_by_key, t->record->key, t); + else + avahi_hashmap_remove(s->entries_by_key, e->record->key); + + /* Remove from associated group */ + if (e->group) + AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); + + avahi_record_unref(e->record); + avahi_free(e); +} + +void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) { + assert(s); + assert(g); + + while (g->entries) + avahi_entry_free(s, g->entries); + + if (g->register_time_event) + avahi_time_event_free(g->register_time_event); + + AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g); + avahi_free(g); +} + +void avahi_cleanup_dead_entries(AvahiServer *s) { + assert(s); + + if (s->need_group_cleanup) { + AvahiSEntryGroup *g, *next; + + for (g = s->groups; g; g = next) { + next = g->groups_next; + + if (g->dead) + avahi_entry_group_free(s, g); + } + + s->need_group_cleanup = 0; + } + + if (s->need_entry_cleanup) { + AvahiEntry *e, *next; + + for (e = s->entries; e; e = next) { + next = e->entries_next; + + if (e->dead) + avahi_entry_free(s, e); + } + + s->need_entry_cleanup = 0; + } + + if (s->need_browser_cleanup) + avahi_browser_cleanup(s); + + if (s->cleanup_time_event) { + avahi_time_event_free(s->cleanup_time_event); + s->cleanup_time_event = NULL; + } +} + +static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) { + AvahiEntry *e; + + assert(s); + assert(r); + + for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { + if (e->dead) + continue; + + if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE)) + continue; + + if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) ) + continue; + + if (avahi_record_equal_no_ttl(r, e->record)) { + /* The records are the same, not a conflict in any case */ + continue; + } + + if ((interface <= 0 || + e->interface <= 0 || + e->interface == interface) && + (protocol == AVAHI_PROTO_UNSPEC || + e->protocol == AVAHI_PROTO_UNSPEC || + e->protocol == protocol)) + + return -1; + } + + return 0; +} + +static AvahiEntry * server_add_internal( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + AvahiRecord *r) { + + AvahiEntry *e; + + assert(s); + assert(r); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( + flags, + AVAHI_PUBLISH_NO_ANNOUNCE| + AVAHI_PUBLISH_NO_PROBE| + AVAHI_PUBLISH_UNIQUE| + AVAHI_PUBLISH_ALLOW_MULTIPLE| + AVAHI_PUBLISH_UPDATE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, + (r->key->type != 0) && + (r->key->type != AVAHI_DNS_TYPE_ANY) && + (r->key->type != AVAHI_DNS_TYPE_OPT) && + (r->key->type != AVAHI_DNS_TYPE_TKEY) && + (r->key->type != AVAHI_DNS_TYPE_TSIG) && + (r->key->type != AVAHI_DNS_TYPE_IXFR) && + (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE); + + transport_flags_from_domain(s, &flags, r->key->name); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, + !g || + (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) || + (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); + + if (flags & AVAHI_PUBLISH_UPDATE) { + AvahiRecord *old_record; + int is_first = 1; + + /* Update and existing record */ + + /* Find the first matching entry */ + for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { + if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) + break; + + is_first = 0; + } + + /* Hmm, nothing found? */ + if (!e) { + avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); + return NULL; + } + + /* Update the entry */ + old_record = e->record; + e->record = avahi_record_ref(r); + e->flags = flags; + + /* Announce our changes when needed */ + if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) { + + /* Remove the old entry from all caches, if needed */ + if (!(e->flags & AVAHI_PUBLISH_UNIQUE)) + avahi_goodbye_entry(s, e, 1, 0); + + /* Reannounce our updated entry */ + avahi_reannounce_entry(s, e); + } + + /* If we were the first entry in the list, we need to update the key */ + if (is_first) + avahi_hashmap_replace(s->entries_by_key, e->record->key, e); + + avahi_record_unref(old_record); + + } else { + AvahiEntry *t; + + /* Add a new record */ + + if (check_record_conflict(s, interface, protocol, r, flags) < 0) { + avahi_server_set_errno(s, AVAHI_ERR_COLLISION); + return NULL; + } + + if (!(e = avahi_new(AvahiEntry, 1))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + e->server = s; + e->record = avahi_record_ref(r); + e->group = g; + e->interface = interface; + e->protocol = protocol; + e->flags = flags; + e->dead = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers); + + AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); + + /* Insert into hash table indexed by name */ + t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); + AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); + avahi_hashmap_replace(s->entries_by_key, e->record->key, t); + + /* Insert into group list */ + if (g) + AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); + + avahi_announce_entry(s, e); + } + + return e; +} + +int avahi_server_add( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + AvahiRecord *r) { + + if (!server_add_internal(s, g, interface, protocol, flags, r)) + return avahi_server_errno(s); + + return AVAHI_OK; +} + +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) { + AvahiEntry **e = (AvahiEntry**) state; + assert(s); + assert(e); + + if (!*e) + *e = g ? g->entries : s->entries; + + while (*e && (*e)->dead) + *e = g ? (*e)->by_group_next : (*e)->entries_next; + + if (!*e) + return NULL; + + return avahi_record_ref((*e)->record); +} + +int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) { + AvahiEntry *e; + + assert(s); + assert(callback); + + callback(";;; ZONE DUMP FOLLOWS ;;;", userdata); + + for (e = s->entries; e; e = e->entries_next) { + char *t; + char ln[256]; + + if (e->dead) + continue; + + if (!(t = avahi_record_to_string(e->record))) + return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + + snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol); + avahi_free(t); + + callback(ln, userdata); + } + + avahi_dump_caches(s->monitor, callback, userdata); + + if (s->wide_area_lookup_engine) + avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata); + return AVAHI_OK; +} + +static AvahiEntry *server_add_ptr_internal( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + const char *dest) { + + AvahiRecord *r; + AvahiEntry *e; + + assert(s); + assert(dest); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME); + + if (!name) + name = s->host_name_fqdn; + + if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->data.ptr.name = avahi_normalize_name_strdup(dest); + e = server_add_internal(s, g, interface, protocol, flags, r); + avahi_record_unref(r); + return e; +} + +int avahi_server_add_ptr( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + const char *dest) { + + AvahiEntry *e; + + assert(s); + + if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest))) + return avahi_server_errno(s); + + return AVAHI_OK; +} + +int avahi_server_add_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + AvahiAddress *a) { + + char n[AVAHI_DOMAIN_NAME_MAX]; + int ret = AVAHI_OK; + AvahiEntry *entry = NULL, *reverse = NULL; + AvahiRecord *r; + + assert(s); + assert(a); + + AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, + AVAHI_PUBLISH_NO_REVERSE| + AVAHI_PUBLISH_NO_ANNOUNCE| + AVAHI_PUBLISH_NO_PROBE| + AVAHI_PUBLISH_UPDATE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); + + /* Prepare the host naem */ + + if (!name) + name = s->host_name_fqdn; + else { + AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n))); + name = n; + } + + transport_flags_from_domain(s, &flags, name); + AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + /* Create the A/AAAA record */ + + if (a->proto == AVAHI_PROTO_INET) { + + if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto finish; + } + + r->data.a.address = a->data.ipv4; + + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto finish; + } + + r->data.aaaa.address = a->data.ipv6; + } + + entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); + avahi_record_unref(r); + + if (!entry) { + ret = avahi_server_errno(s); + goto finish; + } + + /* Create the reverse lookup entry */ + + if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) { + char reverse_n[AVAHI_DOMAIN_NAME_MAX]; + avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n)); + + if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) { + ret = avahi_server_errno(s); + goto finish; + } + } + +finish: + + if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { + if (entry) + avahi_entry_free(s, entry); + if (reverse) + avahi_entry_free(s, reverse); + } + + return ret; +} + +static AvahiEntry *server_add_txt_strlst_nocopy( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + AvahiStringList *strlst) { + + AvahiRecord *r; + AvahiEntry *e; + + assert(s); + + if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) { + avahi_string_list_free(strlst); + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->data.txt.string_list = strlst; + e = server_add_internal(s, g, interface, protocol, flags, r); + avahi_record_unref(r); + + return e; +} + +static AvahiStringList *add_magic_cookie( + AvahiServer *s, + AvahiStringList *strlst) { + + assert(s); + + if (!s->config.add_service_cookie) + return strlst; + + if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE)) + /* This string list already contains a magic cookie */ + return strlst; + + return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie); +} + +static int server_add_service_strlst_nocopy( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst) { + + char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL; + AvahiRecord *r = NULL; + int ret = AVAHI_OK; + AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL; + + assert(s); + assert(type); + assert(name); + + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, + AVAHI_PUBLISH_NO_COOKIE| + AVAHI_PUBLISH_UPDATE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME); + + if (!domain) + domain = s->domain_name; + + if (!host) + host = s->host_name_fqdn; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if (!(h = avahi_normalize_name_strdup(host))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || + (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 || + (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) { + avahi_server_set_errno(s, ret); + goto fail; + } + + /* Add service enumeration PTR record */ + + if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) { + ret = avahi_server_errno(s); + goto fail; + } + + /* Add SRV record */ + + if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + r->data.srv.priority = 0; + r->data.srv.weight = 0; + r->data.srv.port = port; + r->data.srv.name = h; + h = NULL; + srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r); + avahi_record_unref(r); + + if (!srv_entry) { + ret = avahi_server_errno(s); + goto fail; + } + + /* Add TXT record */ + + if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) + strlst = add_magic_cookie(s, strlst); + + txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst); + strlst = NULL; + + if (!txt_entry) { + ret = avahi_server_errno(s); + goto fail; + } + + /* Add service type enumeration record */ + + if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) { + ret = avahi_server_errno(s); + goto fail; + } + +fail: + if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { + if (srv_entry) + avahi_entry_free(s, srv_entry); + if (txt_entry) + avahi_entry_free(s, txt_entry); + if (ptr_entry) + avahi_entry_free(s, ptr_entry); + if (enum_entry) + avahi_entry_free(s, enum_entry); + } + + avahi_string_list_free(strlst); + avahi_free(h); + + return ret; +} + +int avahi_server_add_service_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst) { + + assert(s); + assert(type); + assert(name); + + return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst)); +} + +int avahi_server_add_service( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + ... ){ + + va_list va; + int ret; + + va_start(va, port); + ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va)); + va_end(va); + + return ret; +} + +static int server_update_service_txt_strlst_nocopy( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst) { + + char svc_name[AVAHI_DOMAIN_NAME_MAX]; + int ret = AVAHI_OK; + AvahiEntry *e; + + assert(s); + assert(type); + assert(name); + + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, + AVAHI_PUBLISH_NO_COOKIE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) { + avahi_server_set_errno(s, ret); + goto fail; + } + + /* Add TXT record */ + if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) + strlst = add_magic_cookie(s, strlst); + + e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst); + strlst = NULL; + + if (!e) + ret = avahi_server_errno(s); + +fail: + + avahi_string_list_free(strlst); + + return ret; +} + +int avahi_server_update_service_txt_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst) { + + return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst)); +} + +/** Update the TXT record for a service with the NULL termonate list of strings */ +int avahi_server_update_service_txt( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + ...) { + + va_list va; + int ret; + + va_start(va, domain); + ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va)); + va_end(va); + + return ret; +} + +int avahi_server_add_service_subtype( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *subtype) { + + int ret = AVAHI_OK; + char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX]; + + assert(name); + assert(type); + assert(subtype); + + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || + (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) { + avahi_server_set_errno(s, ret); + goto fail; + } + + if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0) + goto fail; + +fail: + + return ret; +} + +static void hexstring(char *s, size_t sl, const void *p, size_t pl) { + static const char hex[] = "0123456789abcdef"; + int b = 0; + const uint8_t *k = p; + + while (sl > 1 && pl > 0) { + *(s++) = hex[(b ? *k : *k >> 4) & 0xF]; + + if (b) { + k++; + pl--; + } + + b = !b; + + sl--; + } + + if (sl > 0) + *s = 0; +} + +static AvahiEntry *server_add_dns_server_name( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *domain, + AvahiDNSServerType type, + const char *name, + uint16_t port /** should be 53 */) { + + AvahiEntry *e; + char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n; + + AvahiRecord *r; + + assert(s); + assert(name); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if (!(n = avahi_normalize_name_strdup(name))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d))); + + snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d); + + if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + avahi_free(n); + return NULL; + } + + r->data.srv.priority = 0; + r->data.srv.weight = 0; + r->data.srv.port = port; + r->data.srv.name = n; + e = server_add_internal(s, g, interface, protocol, 0, r); + avahi_record_unref(r); + + return e; +} + +int avahi_server_add_dns_server_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *domain, + AvahiDNSServerType type, + const AvahiAddress *address, + uint16_t port /** should be 53 */) { + + AvahiRecord *r; + char n[64], h[64]; + AvahiEntry *a_entry, *s_entry; + + assert(s); + assert(address); + + AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT); + AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if (address->proto == AVAHI_PROTO_INET) { + hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address)); + snprintf(n, sizeof(n), "ip-%s.%s", h, domain); + r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME); + r->data.a.address = address->data.ipv4; + } else { + hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address)); + snprintf(n, sizeof(n), "ip6-%s.%s", h, domain); + r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME); + r->data.aaaa.address = address->data.ipv6; + } + + if (!r) + return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + + a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); + avahi_record_unref(r); + + if (!a_entry) + return avahi_server_errno(s); + + if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) { + if (!(flags & AVAHI_PUBLISH_UPDATE)) + avahi_entry_free(s, a_entry); + return avahi_server_errno(s); + } + + return AVAHI_OK; +} + +void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) { + assert(g); + + if (g->state == state) + return; + + assert(state <= AVAHI_ENTRY_GROUP_COLLISION); + + if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) { + + /* If the entry group was established for a time longer then + * 5s, reset the establishment trial counter */ + + if (avahi_age(&g->established_at) > 5000000) + g->n_register_try = 0; + } else if (g->state == AVAHI_ENTRY_GROUP_REGISTERING) { + if (g->register_time_event) { + avahi_time_event_free(g->register_time_event); + g->register_time_event = NULL; + } + } + + if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) + + /* If the entry group is now established, remember the time + * this happened */ + + gettimeofday(&g->established_at, NULL); + + g->state = state; + + if (g->callback) + g->callback(g->server, g, state, g->userdata); +} + +AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) { + AvahiSEntryGroup *g; + + assert(s); + + if (!(g = avahi_new(AvahiSEntryGroup, 1))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + g->server = s; + g->callback = callback; + g->userdata = userdata; + g->dead = 0; + g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; + g->n_probing = 0; + g->n_register_try = 0; + g->register_time_event = NULL; + g->register_time.tv_sec = 0; + g->register_time.tv_usec = 0; + AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); + + AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g); + return g; +} + +static void cleanup_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { + AvahiServer *s = userdata; + + assert(s); + + avahi_cleanup_dead_entries(s); +} + +static void schedule_cleanup(AvahiServer *s) { + struct timeval tv; + + assert(s); + + if (!s->cleanup_time_event) + s->cleanup_time_event = avahi_time_event_new(s->time_event_queue, avahi_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s); +} + +void avahi_s_entry_group_free(AvahiSEntryGroup *g) { + AvahiEntry *e; + + assert(g); + assert(g->server); + + for (e = g->entries; e; e = e->by_group_next) { + if (!e->dead) { + avahi_goodbye_entry(g->server, e, 1, 1); + e->dead = 1; + } + } + + if (g->register_time_event) { + avahi_time_event_free(g->register_time_event); + g->register_time_event = NULL; + } + + g->dead = 1; + + g->server->need_group_cleanup = 1; + g->server->need_entry_cleanup = 1; + + schedule_cleanup(g->server); +} + +static void entry_group_commit_real(AvahiSEntryGroup *g) { + assert(g); + + gettimeofday(&g->register_time, NULL); + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); + + if (g->dead) + return; + + avahi_announce_group(g->server, g); + avahi_s_entry_group_check_probed(g, 0); +} + +static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { + AvahiSEntryGroup *g = userdata; + assert(g); + + avahi_time_event_free(g->register_time_event); + g->register_time_event = NULL; + + /* Holdoff time passed, so let's start probing */ + entry_group_commit_real(g); +} + +int avahi_s_entry_group_commit(AvahiSEntryGroup *g) { + struct timeval now; + + assert(g); + assert(!g->dead); + + if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION) + return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE); + + if (avahi_s_entry_group_is_empty(g)) + return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY); + + g->n_register_try++; + + avahi_timeval_add(&g->register_time, + 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ? + AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT : + AVAHI_RR_HOLDOFF_MSEC)); + + gettimeofday(&now, NULL); + + if (avahi_timeval_compare(&g->register_time, &now) <= 0) { + + /* Holdoff time passed, so let's start probing */ + entry_group_commit_real(g); + } else { + + /* Holdoff time has not yet passed, so let's wait */ + assert(!g->register_time_event); + g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g); + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); + } + + return AVAHI_OK; +} + +void avahi_s_entry_group_reset(AvahiSEntryGroup *g) { + AvahiEntry *e; + assert(g); + + for (e = g->entries; e; e = e->by_group_next) { + if (!e->dead) { + avahi_goodbye_entry(g->server, e, 1, 1); + e->dead = 1; + } + } + g->server->need_entry_cleanup = 1; + + g->n_probing = 0; + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED); + + schedule_cleanup(g->server); +} + +int avahi_entry_is_commited(AvahiEntry *e) { + assert(e); + assert(!e->dead); + + return !e->group || + e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || + e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; +} + +AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) { + assert(g); + assert(!g->dead); + + return g->state; +} + +void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) { + assert(g); + + g->userdata = userdata; +} + +void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) { + assert(g); + + return g->userdata; +} + +int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) { + AvahiEntry *e; + assert(g); + + /* Look for an entry that is not dead */ + for (e = g->entries; e; e = e->by_group_next) + if (!e->dead) + return 0; + + return 1; +} diff --git a/avahi/error.c b/avahi/error.c new file mode 100644 index 0000000..63c5033 --- /dev/null +++ b/avahi/error.c @@ -0,0 +1,97 @@ +/*** + 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 "error.h" +#include "i18n.h" + +const char *avahi_strerror(int error) { + + const char * const msg[- AVAHI_ERR_MAX] = { + N_("OK"), + N_("Operation failed"), + N_("Bad state"), + N_("Invalid host name"), + N_("Invalid domain name"), + N_("No suitable network protocol available"), + N_("Invalid DNS TTL"), + N_("Resource record key is pattern"), + N_("Local name collision"), + N_("Invalid record"), + + N_("Invalid service name"), + N_("Invalid service type"), + N_("Invalid port number"), + N_("Invalid record key"), + N_("Invalid address"), + N_("Timeout reached"), + N_("Too many clients"), + N_("Too many objects"), + N_("Too many entries"), + N_("OS Error"), + + N_("Access denied"), + N_("Invalid operation"), + N_("An unexpected D-Bus error occurred"), + N_("Daemon connection failed"), + N_("Memory exhausted"), + N_("The object passed in was not valid"), + N_("Daemon not running"), + N_("Invalid interface index"), + N_("Invalid protocol specification"), + N_("Invalid flags"), + + N_("Not found"), + N_("Invalid configuration"), + N_("Version mismatch"), + N_("Invalid service subtype"), + N_("Invalid packet"), + N_("Invalid DNS return code"), + N_("DNS failure: FORMERR"), + N_("DNS failure: SERVFAIL"), + N_("DNS failure: NXDOMAIN"), + N_("DNS failure: NOTIMP"), + + N_("DNS failure: REFUSED"), + N_("DNS failure: YXDOMAIN"), + N_("DNS failure: YXRRSET"), + N_("DNS failure: NXRRSET"), + N_("DNS failure: NOTAUTH"), + N_("DNS failure: NOTZONE"), + N_("Invalid RDATA"), + N_("Invalid DNS type"), + N_("Invalid DNS class"), + N_("Not supported"), + + N_("Not permitted"), + N_("Invalid argument"), + N_("Is empty"), + N_("The requested operation is invalid because redundant") + }; + + avahi_init_i18n(); + + if (-error < 0 || -error >= -AVAHI_ERR_MAX) + return _("Invalid Error Code"); + + return _(msg[-error]); +} diff --git a/avahi/error.h b/avahi/error.h new file mode 100644 index 0000000..bfab2d3 --- /dev/null +++ b/avahi/error.h @@ -0,0 +1,107 @@ +#ifndef fooerrorhfoo +#define fooerrorhfoo + +/*** + 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. +***/ + +/** \file error.h Error codes and auxiliary functions */ + +#include + +AVAHI_C_DECL_BEGIN + +/** Error codes used by avahi */ +enum { + AVAHI_OK = 0, /**< OK */ + AVAHI_ERR_FAILURE = -1, /**< Generic error code */ + AVAHI_ERR_BAD_STATE = -2, /**< Object was in a bad state */ + AVAHI_ERR_INVALID_HOST_NAME = -3, /**< Invalid host name */ + AVAHI_ERR_INVALID_DOMAIN_NAME = -4, /**< Invalid domain name */ + AVAHI_ERR_NO_NETWORK = -5, /**< No suitable network protocol available */ + AVAHI_ERR_INVALID_TTL = -6, /**< Invalid DNS TTL */ + AVAHI_ERR_IS_PATTERN = -7, /**< RR key is pattern */ + AVAHI_ERR_COLLISION = -8, /**< Name collision */ + AVAHI_ERR_INVALID_RECORD = -9, /**< Invalid RR */ + + AVAHI_ERR_INVALID_SERVICE_NAME = -10, /**< Invalid service name */ + AVAHI_ERR_INVALID_SERVICE_TYPE = -11, /**< Invalid service type */ + AVAHI_ERR_INVALID_PORT = -12, /**< Invalid port number */ + AVAHI_ERR_INVALID_KEY = -13, /**< Invalid key */ + AVAHI_ERR_INVALID_ADDRESS = -14, /**< Invalid address */ + AVAHI_ERR_TIMEOUT = -15, /**< Timeout reached */ + AVAHI_ERR_TOO_MANY_CLIENTS = -16, /**< Too many clients */ + AVAHI_ERR_TOO_MANY_OBJECTS = -17, /**< Too many objects */ + AVAHI_ERR_TOO_MANY_ENTRIES = -18, /**< Too many entries */ + AVAHI_ERR_OS = -19, /**< OS error */ + + AVAHI_ERR_ACCESS_DENIED = -20, /**< Access denied */ + AVAHI_ERR_INVALID_OPERATION = -21, /**< Invalid operation */ + AVAHI_ERR_DBUS_ERROR = -22, /**< An unexpected D-Bus error occurred */ + 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 */ + AVAHI_ERR_NO_DAEMON = -26, /**< Daemon not running */ + AVAHI_ERR_INVALID_INTERFACE = -27, /**< Invalid interface */ + AVAHI_ERR_INVALID_PROTOCOL = -28, /**< Invalid protocol */ + AVAHI_ERR_INVALID_FLAGS = -29, /**< Invalid flags */ + + AVAHI_ERR_NOT_FOUND = -30, /**< Not found */ + AVAHI_ERR_INVALID_CONFIG = -31, /**< Configuration error */ + AVAHI_ERR_VERSION_MISMATCH = -32, /**< Verson mismatch */ + AVAHI_ERR_INVALID_SERVICE_SUBTYPE = -33, /**< Invalid service subtype */ + AVAHI_ERR_INVALID_PACKET = -34, /**< Invalid packet */ + AVAHI_ERR_INVALID_DNS_ERROR = -35, /**< Invlaid DNS return code */ + AVAHI_ERR_DNS_FORMERR = -36, /**< DNS Error: Form error */ + AVAHI_ERR_DNS_SERVFAIL = -37, /**< DNS Error: Server Failure */ + AVAHI_ERR_DNS_NXDOMAIN = -38, /**< DNS Error: No such domain */ + AVAHI_ERR_DNS_NOTIMP = -39, /**< DNS Error: Not implemented */ + + AVAHI_ERR_DNS_REFUSED = -40, /**< DNS Error: Operation refused */ + AVAHI_ERR_DNS_YXDOMAIN = -41, + AVAHI_ERR_DNS_YXRRSET = -42, + AVAHI_ERR_DNS_NXRRSET = -43, + AVAHI_ERR_DNS_NOTAUTH = -44, /**< DNS Error: Not authorized */ + AVAHI_ERR_DNS_NOTZONE = -45, + AVAHI_ERR_INVALID_RDATA = -46, /**< Invalid RDATA */ + AVAHI_ERR_INVALID_DNS_CLASS = -47, /**< Invalid DNS class */ + AVAHI_ERR_INVALID_DNS_TYPE = -48, /**< Invalid DNS type */ + AVAHI_ERR_NOT_SUPPORTED = -49, /**< Not supported */ + + 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 it is redundant */ + + /**** + **** IF YOU ADD A NEW ERROR CODE HERE, PLEASE DON'T FORGET TO ADD + **** IT TO THE STRING ARRAY IN avahi_strerror() IN error.c AND + **** TO THE ARRAY IN dbus.c AND FINALLY TO dbus.h! + **** + **** Also remember to update the MAX value below. + ****/ + + AVAHI_ERR_MAX = -54 +}; + +/** Return a human readable error string for the specified error code */ +const char *avahi_strerror(int error); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/fdutil.c b/avahi/fdutil.c new file mode 100644 index 0000000..c294754 --- /dev/null +++ b/avahi/fdutil.c @@ -0,0 +1,72 @@ +/*** + 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 "fdutil.h" + +int avahi_set_cloexec(int fd) { + int n; + + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFD)) < 0) + return -1; + + if (n & FD_CLOEXEC) + return 0; + + return fcntl(fd, F_SETFD, n|FD_CLOEXEC); +} + +int avahi_set_nonblock(int fd) { + int n; + + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +int avahi_wait_for_write(int fd) { + fd_set fds; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) + return -1; + + assert(r > 0); + + return 0; +} diff --git a/avahi/fdutil.h b/avahi/fdutil.h new file mode 100644 index 0000000..a0e72df --- /dev/null +++ b/avahi/fdutil.h @@ -0,0 +1,33 @@ +#ifndef foofdutilhfoo +#define foofdutilhfoo + +/*** + 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 + +AVAHI_C_DECL_BEGIN + +int avahi_set_cloexec(int fd); +int avahi_set_nonblock(int fd); +int avahi_wait_for_write(int fd); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/findstatic.pl b/avahi/findstatic.pl new file mode 100755 index 0000000..43a4916 --- /dev/null +++ b/avahi/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"; + } +} + diff --git a/avahi/gccmacro.h b/avahi/gccmacro.h new file mode 100644 index 0000000..08b9980 --- /dev/null +++ b/avahi/gccmacro.h @@ -0,0 +1,74 @@ +#ifndef foogccmacrohfoo +#define foogccmacrohfoo + +/*** + 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. +***/ + +/** \file gccmacro.h Defines some macros for GCC extensions */ + +#include + +AVAHI_C_DECL_BEGIN + +#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) +#define AVAHI_GCC_ALLOC_SIZE(x) __attribute__ ((__alloc_size__(x))) +#define AVAHI_GCC_ALLOC_SIZE2(x,y) __attribute__ ((__alloc_size__(x,y))) +#else +/** Macro for usage of GCC's alloc_size attribute */ +#define AVAHI_GCC_ALLOC_SIZE(x) +#define AVAHI_GCC_ALLOC_SIZE2(x,y) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define AVAHI_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define AVAHI_GCC_SENTINEL +#endif + +#ifdef __GNUC__ +#define AVAHI_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** Macro for usage of GCC's printf compilation warnings */ +#define AVAHI_GCC_PRINTF_ATTR(a,b) +#endif + +/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 1 and 2 */ +#define AVAHI_GCC_PRINTF_ATTR12 AVAHI_GCC_PRINTF_ATTR(1,2) + +/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 2 and 3 */ +#define AVAHI_GCC_PRINTF_ATTR23 AVAHI_GCC_PRINTF_ATTR(2,3) + +#ifdef __GNUC__ +#define AVAHI_GCC_NORETURN __attribute__((noreturn)) +#else +/** Macro for no-return functions */ +#define AVAHI_GCC_NORETURN +#endif + +#ifdef __GNUC__ +#define AVAHI_GCC_UNUSED __attribute__ ((unused)) +#else +/** Macro for not used parameter */ +#define AVAHI_GCC_UNUSED +#endif + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/hashmap-test.c b/avahi/hashmap-test.c new file mode 100644 index 0000000..54ac1b0 --- /dev/null +++ b/avahi/hashmap-test.c @@ -0,0 +1,62 @@ +/*** + 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 "hashmap.h" +#include "util.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + unsigned n; + AvahiHashmap *m; + const char *t; + + m = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, avahi_free); + + avahi_hashmap_insert(m, avahi_strdup("bla"), avahi_strdup("#1")); + avahi_hashmap_insert(m, avahi_strdup("bla2"), avahi_strdup("asdf")); + avahi_hashmap_insert(m, avahi_strdup("gurke"), avahi_strdup("ffsdf")); + avahi_hashmap_insert(m, avahi_strdup("blubb"), avahi_strdup("sadfsd")); + avahi_hashmap_insert(m, avahi_strdup("bla"), avahi_strdup("#2")); + + for (n = 0; n < 1000; n ++) + avahi_hashmap_insert(m, avahi_strdup_printf("key %u", n), avahi_strdup_printf("value %u", n)); + + printf("%s\n", (const char*) avahi_hashmap_lookup(m, "bla")); + + avahi_hashmap_replace(m, avahi_strdup("bla"), avahi_strdup("#3")); + + printf("%s\n", (const char*) avahi_hashmap_lookup(m, "bla")); + + avahi_hashmap_remove(m, "bla"); + + t = (const char*) avahi_hashmap_lookup(m, "bla"); + printf("%s\n", t ? t : "(null)"); + + avahi_hashmap_free(m); + + return 0; +} diff --git a/avahi/hashmap.c b/avahi/hashmap.c new file mode 100644 index 0000000..ae03416 --- /dev/null +++ b/avahi/hashmap.c @@ -0,0 +1,248 @@ +/*** + 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 "hashmap.h" +#include "util.h" + +#define HASH_MAP_SIZE 123 + +typedef struct Entry Entry; +struct Entry { + AvahiHashmap *hashmap; + void *key; + void *value; + + AVAHI_LLIST_FIELDS(Entry, bucket); + AVAHI_LLIST_FIELDS(Entry, entries); +}; + +struct AvahiHashmap { + AvahiHashFunc hash_func; + AvahiEqualFunc equal_func; + AvahiFreeFunc key_free_func, value_free_func; + + Entry *entries[HASH_MAP_SIZE]; + AVAHI_LLIST_HEAD(Entry, entries_list); +}; + +static Entry* entry_get(AvahiHashmap *m, const void *key) { + unsigned idx; + Entry *e; + + idx = m->hash_func(key) % HASH_MAP_SIZE; + + for (e = m->entries[idx]; e; e = e->bucket_next) + if (m->equal_func(key, e->key)) + return e; + + return NULL; +} + +static void entry_free(AvahiHashmap *m, Entry *e, int stolen) { + unsigned idx; + assert(m); + assert(e); + + idx = m->hash_func(e->key) % HASH_MAP_SIZE; + + AVAHI_LLIST_REMOVE(Entry, bucket, m->entries[idx], e); + AVAHI_LLIST_REMOVE(Entry, entries, m->entries_list, e); + + if (m->key_free_func) + m->key_free_func(e->key); + if (m->value_free_func && !stolen) + m->value_free_func(e->value); + + avahi_free(e); +} + +AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func) { + AvahiHashmap *m; + + assert(hash_func); + assert(equal_func); + + if (!(m = avahi_new0(AvahiHashmap, 1))) + return NULL; + + m->hash_func = hash_func; + m->equal_func = equal_func; + m->key_free_func = key_free_func; + m->value_free_func = value_free_func; + + AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list); + + return m; +} + +void avahi_hashmap_free(AvahiHashmap *m) { + assert(m); + + while (m->entries_list) + entry_free(m, m->entries_list, 0); + + avahi_free(m); +} + +void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key) { + Entry *e; + + assert(m); + + if (!(e = entry_get(m, key))) + return NULL; + + return e->value; +} + +int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value) { + unsigned idx; + Entry *e; + + assert(m); + + if ((e = entry_get(m, key))) { + if (m->key_free_func) + m->key_free_func(key); + if (m->value_free_func) + m->value_free_func(value); + + return 1; + } + + if (!(e = avahi_new(Entry, 1))) + return -1; + + e->hashmap = m; + e->key = key; + e->value = value; + + AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e); + + idx = m->hash_func(key) % HASH_MAP_SIZE; + AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e); + + return 0; +} + + +int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value) { + unsigned idx; + Entry *e; + + assert(m); + + if ((e = entry_get(m, key))) { + if (m->key_free_func) + m->key_free_func(e->key); + if (m->value_free_func) + m->value_free_func(e->value); + + e->key = key; + e->value = value; + + return 1; + } + + if (!(e = avahi_new(Entry, 1))) + return -1; + + e->hashmap = m; + e->key = key; + e->value = value; + + AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e); + + idx = m->hash_func(key) % HASH_MAP_SIZE; + AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e); + + return 0; +} + +void avahi_hashmap_remove(AvahiHashmap *m, const void *key) { + Entry *e; + + assert(m); + + if (!(e = entry_get(m, key))) + return; + + entry_free(m, e, 0); +} + +void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata) { + Entry *e, *next; + assert(m); + assert(callback); + + for (e = m->entries_list; e; e = next) { + next = e->entries_next; + + callback(e->key, e->value, userdata); + } +} + +unsigned avahi_string_hash(const void *data) { + const char *p = data; + unsigned hash = 0; + + assert(p); + + for (; *p; p++) + hash = 31 * hash + *p; + + return hash; +} + +int avahi_string_equal(const void *a, const void *b) { + const char *p = a, *q = b; + + assert(p); + assert(q); + + return strcmp(p, q) == 0; +} + +unsigned avahi_int_hash(const void *data) { + const int *i = data; + + assert(i); + + return (unsigned) *i; +} + +int avahi_int_equal(const void *a, const void *b) { + const int *_a = a, *_b = b; + + assert(_a); + assert(_b); + + return *_a == *_b; +} diff --git a/avahi/hashmap.h b/avahi/hashmap.h new file mode 100644 index 0000000..16adf6f --- /dev/null +++ b/avahi/hashmap.h @@ -0,0 +1,53 @@ +#ifndef foohashmaphfoo +#define foohashmaphfoo + +/*** + 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 + +AVAHI_C_DECL_BEGIN + +typedef struct AvahiHashmap AvahiHashmap; + +typedef unsigned (*AvahiHashFunc)(const void *data); +typedef int (*AvahiEqualFunc)(const void *a, const void *b); +typedef void (*AvahiFreeFunc)(void *p); + +AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func); + +void avahi_hashmap_free(AvahiHashmap *m); +void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key); +int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value); +int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value); +void avahi_hashmap_remove(AvahiHashmap *m, const void *key); + +typedef void (*AvahiHashmapForeachCallback)(void *key, void *value, void *userdata); + +void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata); + +unsigned avahi_string_hash(const void *data); +int avahi_string_equal(const void *a, const void *b); + +unsigned avahi_int_hash(const void *data); +int avahi_int_equal(const void *a, const void *b); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/i18n.c b/avahi/i18n.c new file mode 100644 index 0000000..509abbe --- /dev/null +++ b/avahi/i18n.c @@ -0,0 +1,42 @@ +/*** + 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 "i18n.h" + +void avahi_init_i18n(void) { +#ifdef ENABLE_NLS + + /* Not really thread safe, but this doesn't matter much since + * bindtextdomain is supposed to be idempotent anyway. */ + /* FIXME: but is bindtextdomain reentrant!? */ + + static int done = 0; + + if (!done) { + bindtextdomain(GETTEXT_PACKAGE, AVAHI_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + done = 1; + } + +#endif /* ENABLE_NLS */ +} diff --git a/avahi/i18n.h b/avahi/i18n.h new file mode 100644 index 0000000..f46931d --- /dev/null +++ b/avahi/i18n.h @@ -0,0 +1,53 @@ +#ifndef fooi18nhfoo +#define fooi18nhfoo + +/*** + 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 ENABLE_NLS + +#if !defined(GETTEXT_PACKAGE) +#error "Something is very wrong here, config.h needs to be included first" +#endif + +#include + +#define _(String) dgettext(GETTEXT_PACKAGE, String) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) +#endif + +#else /* NLS is disabled */ + +#define _(String) (String) +#define N_(String) (String) +#define textdomain(String) (String) +#define gettext(String) (String) +#define dgettext(Domain,String) (String) +#define dcgettext(Domain,String,Type) (String) +#define bindtextdomain(Domain,Directory) (Domain) +#define bind_textdomain_codeset(Domain,Codeset) (Codeset) + +#endif /* ENABLE_NLS */ + +void avahi_init_i18n(void); + +#endif diff --git a/avahi/iface-linux.c b/avahi/iface-linux.c new file mode 100644 index 0000000..37a371b --- /dev/null +++ b/avahi/iface-linux.c @@ -0,0 +1,391 @@ +/*** + 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 "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)))) +#endif + +#ifndef IFA_RTA +#include +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#endif + +static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) { + struct nlmsghdr *n; + struct rtgenmsg *gen; + uint8_t req[1024]; + + /* Issue a wild dump NETLINK request */ + + memset(&req, 0, sizeof(req)); + n = (struct nlmsghdr*) req; + n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + n->nlmsg_type = type; + n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + n->nlmsg_pid = 0; + + gen = NLMSG_DATA(n); + memset(gen, 0, sizeof(struct rtgenmsg)); + gen->rtgen_family = AF_UNSPEC; + + return avahi_netlink_send(nl, n, ret_seq); +} + +static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) { + AvahiInterfaceMonitor *m = userdata; + + /* This routine is called for every RTNETLINK response packet */ + + assert(m); + assert(n); + assert(m->osdep.netlink == nl); + + if (n->nlmsg_type == RTM_NEWLINK) { + + /* A new interface appeared or an existing one has been modified */ + + struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); + AvahiHwInterface *hw; + struct rtattr *a = NULL; + size_t l; + + /* A (superfluous?) sanity check */ + if (ifinfomsg->ifi_family != AF_UNSPEC) + return; + + /* Check whether there already is an AvahiHwInterface object + * for this link, so that we can update its data. Note that + * Netlink sends us an RTM_NEWLINK not only when a new + * interface appears, but when it changes, too */ + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index))) + + /* No object found, so let's create a new + * one. avahi_hw_interface_new() will call + * avahi_interface_new() internally twice for IPv4 and + * IPv6, so there is no need for us to do that + * ourselves */ + if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index))) + return; /* OOM */ + + /* Check whether the flags of this interface are OK for us */ + hw->flags_ok = + (ifinfomsg->ifi_flags & IFF_UP) && + (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) && + !(ifinfomsg->ifi_flags & IFF_LOOPBACK) && + (ifinfomsg->ifi_flags & IFF_MULTICAST) && + (m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT)); + + /* Handle interface attributes */ + l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)); + a = IFLA_RTA(ifinfomsg); + + while (RTA_OK(a, l)) { + switch(a->rta_type) { + case IFLA_IFNAME: + + /* Fill in interface name */ + avahi_free(hw->name); + hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a)); + break; + + case IFLA_MTU: + + /* Fill in MTU */ + assert(RTA_PAYLOAD(a) == sizeof(unsigned int)); + hw->mtu = *((unsigned int*) RTA_DATA(a)); + break; + + case IFLA_ADDRESS: + + /* Fill in hardware (MAC) address */ + hw->mac_address_size = RTA_PAYLOAD(a); + if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX) + hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX; + + memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size); + break; + + default: + ; + } + + a = RTA_NEXT(a, l); + } + + /* Check whether this interface is now "relevant" for us. If + * it is Avahi will start to announce its records on this + * interface and send out queries for subscribed records on + * it */ + avahi_hw_interface_check_relevant(hw); + + /* Update any associated RRs of this interface. (i.e. the + * _workstation._tcp record containing the MAC address) */ + avahi_hw_interface_update_rrs(hw, 0); + + } else if (n->nlmsg_type == RTM_DELLINK) { + + /* An interface has been removed */ + + struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); + AvahiHwInterface *hw; + + /* A (superfluous?) sanity check */ + if (ifinfomsg->ifi_family != AF_UNSPEC) + return; + + /* Get a reference to our AvahiHwInterface object of this interface */ + if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index))) + return; + + /* Free our object */ + avahi_hw_interface_free(hw, 0); + + } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { + + /* An address has been added, modified or removed */ + + struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); + AvahiInterface *i; + struct rtattr *a = NULL; + size_t l; + AvahiAddress raddr, rlocal, *r; + int raddr_valid = 0, rlocal_valid = 0; + + /* We are only interested in IPv4 and IPv6 */ + if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) + return; + + /* Try to get a reference to our AvahiInterface object for the + * interface this address is assigned to. If ther is no object + * for this interface, we ignore this address. */ + if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family)))) + return; + + /* Fill in address family for our new address */ + rlocal.proto = raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family); + + l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)); + a = IFA_RTA(ifaddrmsg); + + while (RTA_OK(a, l)) { + + switch(a->rta_type) { + + case IFA_ADDRESS: + + if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || + (rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) + return; + + memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); + rlocal_valid = 1; + + break; + + case IFA_LOCAL: + + /* Fill in local address data. Usually this is + * preferable over IFA_ADDRESS if both are set, + * since this refers to the local address of a PPP + * link while IFA_ADDRESS refers to the other + * end. */ + + if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || + (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) + return; + + memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); + raddr_valid = 1; + + break; + + default: + ; + } + + a = RTA_NEXT(a, l); + } + + /* If there was no adress attached to this message, let's quit. */ + if (rlocal_valid) + r = &rlocal; + else if (raddr_valid) + r = &raddr; + else + return; + + if (n->nlmsg_type == RTM_NEWADDR) { + AvahiInterfaceAddress *addr; + + /* This address is new or has been modified, so let's get an object for it */ + if (!(addr = avahi_interface_monitor_get_address(m, i, r))) + + /* Mmm, no object existing yet, so let's create a new one */ + if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen))) + return; /* OOM */ + + /* Update the scope field for the address */ + addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; + addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED); + } else { + AvahiInterfaceAddress *addr; + assert(n->nlmsg_type == RTM_DELADDR); + + /* Try to get a reference to our AvahiInterfaceAddress object for this address */ + if (!(addr = avahi_interface_monitor_get_address(m, i, r))) + return; + + /* And free it */ + avahi_interface_address_free(addr); + } + + /* Avahi only considers interfaces with at least one address + * attached relevant. Since we migh have added or removed an + * address, let's have it check again whether the interface is + * now relevant */ + avahi_interface_check_relevant(i); + + /* Update any associated RRs, like A or AAAA for our new/removed address */ + avahi_interface_update_rrs(i, 0); + + } else if (n->nlmsg_type == NLMSG_DONE) { + + /* This wild dump request ended, so let's see what we do next */ + + if (m->osdep.list == LIST_IFACE) { + + /* Mmmm, interfaces have been wild dumped already, so + * let's go on with wild dumping the addresses */ + + if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) { + avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno)); + m->osdep.list = LIST_DONE; + } else + + /* Update state information */ + m->osdep.list = LIST_ADDR; + + } else + /* We're done. Tell avahi_interface_monitor_sync() to finish. */ + m->osdep.list = LIST_DONE; + + if (m->osdep.list == LIST_DONE) { + + /* Only after this boolean variable has been set, Avahi + * will start to announce or browse on all interfaces. It + * is originaly set to 0, which means that relevancy + * checks and RR updates are disabled during the wild + * dumps. */ + m->list_complete = 1; + + /* So let's check if any interfaces are relevant now */ + avahi_interface_monitor_check_relevant(m); + + /* And update all RRs attached to any interface */ + avahi_interface_monitor_update_rrs(m, 0); + + /* Tell the user that the wild dump is complete */ + avahi_log_info("Network interface enumeration completed."); + } + + } else if (n->nlmsg_type == NLMSG_ERROR && + (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) { + struct nlmsgerr *e = NLMSG_DATA (n); + + /* Some kind of error happened. Let's just tell the user and + * ignore it otherwise */ + + if (e->error) + avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error)); + } +} + +int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { + assert(m); + + /* Initialize our own data */ + + m->osdep.netlink = NULL; + m->osdep.query_addr_seq = m->osdep.query_link_seq = 0; + + /* Create a netlink object for us. It abstracts some things and + * makes netlink easier to use. It will attach to the main loop + * for us and call netlink_callback() whenever an event + * happens. */ + if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m))) + goto fail; + + /* Set the initial state. */ + m->osdep.list = LIST_IFACE; + + /* Start the wild dump for the interfaces */ + if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0) + goto fail; + + return 0; + +fail: + + if (m->osdep.netlink) { + avahi_netlink_free(m->osdep.netlink); + m->osdep.netlink = NULL; + } + + return -1; +} + +void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { + assert(m); + + if (m->osdep.netlink) { + avahi_netlink_free(m->osdep.netlink); + m->osdep.netlink = NULL; + } +} + +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { + assert(m); + + /* Let's handle netlink events until we are done with wild + * dumping */ + + while (!m->list_complete) + if (!avahi_netlink_work(m->osdep.netlink, 1) == 0) + break; + + /* At this point Avahi knows about all local interfaces and + * addresses in existance. */ +} diff --git a/avahi/iface-linux.h b/avahi/iface-linux.h new file mode 100644 index 0000000..677f86d --- /dev/null +++ b/avahi/iface-linux.h @@ -0,0 +1,40 @@ +#ifndef fooifacelinuxhfoo +#define fooifacelinuxhfoo + +/*** + 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 struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; + +#include "netlink.h" + +struct AvahiInterfaceMonitorOSDep { + AvahiNetlink *netlink; + + unsigned query_addr_seq, query_link_seq; + + enum { + LIST_IFACE, + LIST_ADDR, + LIST_DONE + } list; +}; + + +#endif diff --git a/avahi/iface-none.c b/avahi/iface-none.c new file mode 100644 index 0000000..715e497 --- /dev/null +++ b/avahi/iface-none.c @@ -0,0 +1,30 @@ +/*** + 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 "iface.h" + +int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { + return 0; +} + +void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { +} + +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { +} diff --git a/avahi/iface-pfroute.c b/avahi/iface-pfroute.c new file mode 100644 index 0000000..7a885ab --- /dev/null +++ b/avahi/iface-pfroute.c @@ -0,0 +1,543 @@ +/*** + 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 +#ifdef HAVE_SYS_SYSCTL_H +#include +#else +#include +#endif + +#include +#include +#include +#include + +#include "log.h" +#include "iface.h" +#include "iface-pfroute.h" +#include "util.h" + +static int bitcount (unsigned int n) +{ + int count=0 ; + while (n) + { + count++ ; + n &= (n - 1) ; + } + return count ; +} + +static void rtm_info(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) +{ + AvahiHwInterface *hw; + struct if_msghdr *ifm = (struct if_msghdr *)rtm; + struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1); + + if (sdl->sdl_family != AF_LINK) + return; + + if (ifm->ifm_addrs == 0 && ifm->ifm_index > 0) { + if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifm->ifm_index))) + return; + avahi_hw_interface_free(hw, 0); + return; + } + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifm->ifm_index))) + if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifm->ifm_index))) + return; /* OOM */ + + hw->flags_ok = + (ifm->ifm_flags & IFF_UP) && + (!m->server->config.use_iff_running || (ifm->ifm_flags & IFF_RUNNING)) && + !(ifm->ifm_flags & IFF_LOOPBACK) && + (ifm->ifm_flags & IFF_MULTICAST) && + (m->server->config.allow_point_to_point || !(ifm->ifm_flags & IFF_POINTOPOINT)); + + avahi_free(hw->name); + hw->name = avahi_strndup(sdl->sdl_data, sdl->sdl_nlen); + + hw->mtu = ifm->ifm_data.ifi_mtu; + + hw->mac_address_size = sdl->sdl_alen; + if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX) + hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX; + + memcpy(hw->mac_address, sdl->sdl_data + sdl->sdl_nlen, hw->mac_address_size); + +/* { */ +/* char mac[256]; */ +/* avahi_log_debug("======\n name: %s\n index:%d\n mtu:%d\n mac:%s\n flags_ok:%d\n======", */ +/* hw->name, hw->index, */ +/* hw->mtu, */ +/* avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size), */ +/* hw->flags_ok); */ +/* } */ + + avahi_hw_interface_check_relevant(hw); + avahi_hw_interface_update_rrs(hw, 0); +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#ifdef HAVE_SYS_SYSCTL_H +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) +#else +#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr))) +#endif + +static void rtm_addr(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) +{ + AvahiInterface *iface; + AvahiAddress raddr; + int raddr_valid = 0; + struct ifa_msghdr *ifam = (struct ifa_msghdr *) rtm; + char *cp = (char *)(ifam + 1); + char *cp0; + int i; + int prefixlen = 0; + struct sockaddr *sa =NULL; + +#if defined(__NetBSD__) || defined(__OpenBSD__) + if(((struct sockaddr *)cp)->sa_family == AF_UNSPEC) + ((struct sockaddr *)cp)->sa_family = AF_INET; +#endif + + for (cp0 = cp, i = 0; i < RTAX_MAX; i++) { + if (!(ifam->ifam_addrs & (1<sa_family != AF_INET && sa->sa_family != AF_INET6) + return; + + if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifam->ifam_index, avahi_af_to_proto(sa->sa_family)))) + return; + + raddr.proto = avahi_af_to_proto(sa->sa_family); + + for(cp = cp0, i = 0; i < RTAX_MAX; i++) + { + if (!(ifam->ifam_addrs & (1<sa_len == 0) + continue; +#endif + switch(sa->sa_family) { + case AF_INET: + switch (1<sin_addr.s_addr); + break; + case RTA_IFA: + memcpy(raddr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr)); + raddr_valid = 1; + default: + break; + } + break; + case AF_INET6: + switch (1<sin6_addr.s6_addr); + break; + case RTA_IFA: + memcpy(raddr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr)); +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)raddr.data.data)) + { + ((struct in6_addr *)raddr.data.data)->s6_addr[2] = 0; + ((struct in6_addr *)raddr.data.data)->s6_addr[3] = 0; + } +#endif + raddr_valid = 1; + default: + break; + } + break; + default: + break; + } +#ifdef SA_SIZE + cp += SA_SIZE(sa); +#else + ADVANCE(cp, sa); +#endif + } + + if (!raddr_valid) + return; + + if(rtm->rtm_type == RTM_NEWADDR) + { + AvahiInterfaceAddress *addriface; + if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) + if (!(addriface = avahi_interface_address_new(m, iface, &raddr, prefixlen))) + return; /* OOM */ + if (raddr.proto == AVAHI_PROTO_INET6) + { + addriface->global_scope = !(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)raddr.data.data) || IN6_IS_ADDR_MULTICAST((struct in6_addr *)raddr.data.data)); + } + else + addriface->global_scope = 1; + } + else + { + AvahiInterfaceAddress *addriface; + assert(rtm->rtm_type == RTM_DELADDR); + if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) + return; + avahi_interface_address_free(addriface); + } + + avahi_interface_check_relevant(iface); + avahi_interface_update_rrs(iface, 0); +} + +static void parse_rtmsg(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) +{ + assert(m); + assert(rtm); + + if (rtm->rtm_version != RTM_VERSION) { + avahi_log_warn("routing message version %d not understood", + rtm->rtm_version); + return; + } + + switch (rtm->rtm_type) { + case RTM_IFINFO: + rtm_info(rtm,m); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + rtm_addr(rtm,m); + break; + default: + break; + } +} + +static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event,void *userdata) { + AvahiInterfaceMonitor *m = (AvahiInterfaceMonitor *)userdata; + AvahiPfRoute *nl = m->osdep.pfroute; + ssize_t bytes; + char msg[2048]; + + assert(m); + assert(w); + assert(nl); + assert(fd == nl->fd); + + do { + if((bytes = recv(nl->fd, msg, 2048, MSG_DONTWAIT)) < 0) { + if (errno == EAGAIN || errno == EINTR) + return; + avahi_log_error(__FILE__": recv() failed: %s", strerror(errno)); + return; + } + parse_rtmsg((struct rt_msghdr *)msg, m); + } + while (bytes > 0); +} + +int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { + int fd = -1; + + assert(m); + + m->osdep.pfroute = NULL; + + if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { + avahi_log_error(__FILE__": socket(PF_ROUTE): %s", strerror(errno)); + goto fail; + } + + if (!(m->osdep.pfroute = avahi_new(AvahiPfRoute , 1))) { + avahi_log_error(__FILE__": avahi_new() failed."); + goto fail; + } + m->osdep.pfroute->fd = fd; + + if (!(m->osdep.pfroute->watch = m->server->poll_api->watch_new(m->server->poll_api, + m->osdep.pfroute->fd, + AVAHI_WATCH_IN, + socket_event, + m))) { + avahi_log_error(__FILE__": Failed to create watch."); + goto fail; + } + + return 0; + +fail: + + if (m->osdep.pfroute) { + if (m->osdep.pfroute->watch) + m->server->poll_api->watch_free(m->osdep.pfroute->watch); + + if (fd >= 0) + close(fd); + + m->osdep.pfroute = NULL; + } + + return -1; +} + +void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { + assert(m); + + if (m->osdep.pfroute) { + if (m->osdep.pfroute->watch) + m->server->poll_api->watch_free(m->osdep.pfroute->watch); + + if (m->osdep.pfroute->fd >= 0) + close(m->osdep.pfroute->fd); + + avahi_free(m->osdep.pfroute); + m->osdep.pfroute = NULL; + } +} + +#if defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */ +/* + * I got this function from GNU zsbra + */ +static int ip6_masklen (struct in6_addr netmask) { + int len = 0; + unsigned char val; + unsigned char *pnt; + + pnt = (unsigned char *) & netmask; + + while ((*pnt == 0xff) && len < 128) { + len += 8; + pnt++; + } + + if (len < 128) { + val = *pnt; + while (val) { + len++; + val <<= 1; + } + } + return len; +} + +static void if_add_interface(struct lifreq *lifreq, AvahiInterfaceMonitor *m, int fd, int count) +{ + AvahiHwInterface *hw; + AvahiAddress addr; + struct lifreq lifrcopy; + unsigned int index; + int flags; + int mtu; + int prefixlen; + AvahiInterfaceAddress *addriface; + AvahiInterface *iface; + struct sockaddr_in mask; + struct sockaddr_in6 mask6; + char caddr[AVAHI_ADDRESS_STR_MAX]; + + lifrcopy = *lifreq; + + if (ioctl(fd, SIOCGLIFFLAGS, &lifrcopy) < 0) { + avahi_log_error(__FILE__": ioctl(SIOCGLIFFLAGS) %s", strerror(errno)); + return; + } + flags = lifrcopy.lifr_flags; + + if (ioctl(fd, SIOCGLIFMTU, &lifrcopy) < 0) { + avahi_log_error(__FILE__": ioctl(SIOCGLIFMTU) %s", strerror(errno)); + return; + } + mtu = lifrcopy.lifr_metric; + + if (ioctl(fd, SIOCGLIFADDR, &lifrcopy) < 0) { + avahi_log_error(__FILE__": ioctl(SIOCGLIFADDR) %s", strerror(errno)); + return; + } + addr.proto = avahi_af_to_proto(lifreq->lifr_addr.ss_family); + if (ioctl(fd, SIOCGLIFNETMASK, &lifrcopy) < 0) { + avahi_log_error(__FILE__": ioctl(SIOCGLIFNETMASK) %s", strerror(errno)); + return; + } + switch (lifreq->lifr_addr.ss_family) { + case AF_INET: + memcpy(addr.data.data, &((struct sockaddr_in *)&lifreq->lifr_addr)->sin_addr, sizeof(struct in_addr)); + memcpy(&mask, &((struct sockaddr_in *)&lifrcopy.lifr_addr)->sin_addr, sizeof(struct in_addr)); + prefixlen = bitcount((unsigned int) mask.sin_addr.s_addr); + break; + case AF_INET6: + memcpy(addr.data.data, &((struct sockaddr_in6 *)&lifreq->lifr_addr)->sin6_addr, sizeof(struct in6_addr)); + memcpy(&mask6, &((struct sockaddr_in6 *)&lifrcopy.lifr_addr)->sin6_addr, sizeof(struct in6_addr)); + prefixlen = lifrcopy.lifr_addrlen; + break; + default: + break; + } + index = if_nametoindex(lifreq->lifr_name); + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) index))) { + if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) index))) + return; /* OOM */ + + hw->flags_ok = + (flags & IFF_UP) && + (!m->server->config.use_iff_running || (flags & IFF_RUNNING)) && + !(flags & IFF_LOOPBACK) && + (flags & IFF_MULTICAST) && + (m->server->config.allow_point_to_point || !(flags & IFF_POINTOPOINT)); + hw->name = avahi_strdup(lifreq->lifr_name); + hw->mtu = mtu; + /* TODO get mac address */ + } + + if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex)index, addr.proto))) + return; + + if (!(addriface = avahi_interface_monitor_get_address(m, iface, &addr))) + if (!(addriface = avahi_interface_address_new(m, iface, &addr, prefixlen))) + return; /* OOM */ + + addriface->global_scope = 1; + + avahi_hw_interface_check_relevant(hw); + avahi_hw_interface_update_rrs(hw, 0); +} +#endif + +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { +#ifndef HAVE_STRUCT_LIFCONF + size_t needed; + int mib[6]; + char *buf, *lim, *next, count = 0; + struct rt_msghdr *rtm; + + assert(m); + + retry2: + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + { + avahi_log_error("sysctl failed: %s", strerror(errno)); + avahi_log_error("route-sysctl-estimate"); + return; + } + if ((buf = avahi_malloc(needed)) == NULL) + { + avahi_log_error("malloc failed in avahi_interface_monitor_sync"); + return; + } + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + avahi_log_warn("sysctl failed: %s", strerror(errno)); + if (errno == ENOMEM && count++ < 10) { + avahi_log_warn("Routing table grew, retrying"); + sleep(1); + avahi_free(buf); + goto retry2; + } + } + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + parse_rtmsg(rtm, m); + } + + m->list_complete = 1; + avahi_interface_monitor_check_relevant(m); + avahi_interface_monitor_update_rrs(m, 0); + avahi_log_info("Network interface enumeration completed."); +#elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */ + int sockfd; + int ret; + int n; + struct lifnum lifn; + struct lifconf lifc; + struct lifreq *lifreq; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + avahi_log_error(__FILE__": socket(PFROUTE): %s", strerror(errno)); + return; + } + lifc.lifc_buf = NULL; + lifn.lifn_family = AF_UNSPEC; + lifn.lifn_flags = 0; + if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0) { + avahi_log_error(__FILE__": ioctl(SIOCGLIFNUM): %s", strerror(errno)); + goto end; + } + lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); + if ((lifc.lifc_buf = avahi_malloc(lifc.lifc_len)) == NULL) { + avahi_log_error("malloc failed in avahi_interface_monitor_sync"); + goto end; + } + lifc.lifc_family = NULL; + lifc.lifc_flags = 0; + if(ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0) { + avahi_log_error(__FILE__": ioctl(SIOCGLIFCONF): %s", strerror(errno)); + goto end; + } + lifreq = lifc.lifc_req; + + for (n = 0; n < lifc.lifc_len; n += sizeof(struct lifreq)) { + if_add_interface(lifreq, m, sockfd, lifn.lifn_count); + lifreq++; + } + m->list_complete = 1; + avahi_interface_monitor_check_relevant(m); + avahi_interface_monitor_update_rrs(m, 0); +end: + close(sockfd); + avahi_free(lifc.lifc_buf); + + avahi_log_info("Network interface enumeration completed."); +#endif +} diff --git a/avahi/iface-pfroute.h b/avahi/iface-pfroute.h new file mode 100644 index 0000000..15c2dcb --- /dev/null +++ b/avahi/iface-pfroute.h @@ -0,0 +1,37 @@ +#ifndef fooifacepfroutehfoo +#define fooifacepfroutehfoo + +/*** + 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 + +typedef struct AvahiPfRoute AvahiPfRoute; +struct AvahiPfRoute { + int fd; + AvahiWatch *watch; + AvahiInterfaceMonitor *m; +}; + +typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; + +struct AvahiInterfaceMonitorOSDep { + AvahiPfRoute *pfroute; +}; + +#endif diff --git a/avahi/iface.c b/avahi/iface.c new file mode 100644 index 0000000..c17f994 --- /dev/null +++ b/avahi/iface.c @@ -0,0 +1,865 @@ +/*** + 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 "iface.h" +#include "dns.h" +#include "socket.h" +#include "announce.h" +#include "util.h" +#include "log.h" +#include "multicast-lookup.h" +#include "querier.h" + +void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs) { + AvahiInterfaceMonitor *m; + + assert(a); + m = a->monitor; + + 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 || + m->server->state == AVAHI_SERVER_REGISTERING)) { + + /* Fill the entry group */ + if (!a->entry_group) + a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL); + + if (!a->entry_group) /* OOM */ + return; + + if (avahi_s_entry_group_is_empty(a->entry_group)) { + char t[AVAHI_ADDRESS_STR_MAX]; + AvahiProtocol p; + + 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, p, m->server->config.publish_no_reverse ? AVAHI_PUBLISH_NO_REVERSE : 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; + return; + } + + avahi_s_entry_group_commit(a->entry_group); + } + } else { + + /* Clear the entry group */ + + if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) { + char t[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(t, sizeof(t), &a->address); + + avahi_log_info("Withdrawing address record for %s on %s.", t, a->interface->hardware->name); + + if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING && + m->server->state == AVAHI_SERVER_REGISTERING) + avahi_server_decrease_host_rr_pending(m->server); + + avahi_s_entry_group_reset(a->entry_group); + } + } +} + +void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs) { + AvahiInterfaceAddress *a; + + assert(i); + + for (a = i->addresses; a; a = a->address_next) + avahi_interface_address_update_rrs(a, remove_rrs); +} + +void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs) { + AvahiInterface *i; + AvahiInterfaceMonitor *m; + + assert(hw); + m = hw->monitor; + + for (i = hw->interfaces; i; i = i->by_hardware_next) + avahi_interface_update_rrs(i, remove_rrs); + + if (m->list_complete && + !remove_rrs && + m->server->config.publish_workstation && + (m->server->state == AVAHI_SERVER_RUNNING)) { + + if (!hw->entry_group) + hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL); + + if (!hw->entry_group) + return; /* OOM */ + + if (avahi_s_entry_group_is_empty(hw->entry_group)) { + char name[AVAHI_LABEL_MAX], unescaped[AVAHI_LABEL_MAX], mac[256]; + const char *p = m->server->host_name; + + avahi_unescape_label(&p, unescaped, sizeof(unescaped)); + avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size); + snprintf(name, sizeof(name), "%s [%s]", unescaped, mac); + + if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, 0, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) { + avahi_log_warn(__FILE__": avahi_server_add_service() failed: %s", avahi_strerror(m->server->error)); + avahi_s_entry_group_free(hw->entry_group); + hw->entry_group = NULL; + } else + avahi_s_entry_group_commit(hw->entry_group); + } + + } else { + + if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) { + + avahi_log_info("Withdrawing workstation service for %s.", hw->name); + + if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING && + m->server->state == AVAHI_SERVER_REGISTERING) + avahi_server_decrease_host_rr_pending(m->server); + + avahi_s_entry_group_reset(hw->entry_group); + } + } +} + +void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs) { + AvahiHwInterface *hw; + + assert(m); + + for (hw = m->hw_interfaces; hw; hw = hw->hardware_next) + avahi_hw_interface_update_rrs(hw, remove_rrs); +} + +static int interface_mdns_mcast_join(AvahiInterface *i, int join) { + char at[AVAHI_ADDRESS_STR_MAX]; + int r; + assert(i); + + 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; + + /* Look if there's an address with global scope */ + for (a = i->addresses; a; a = a->address_next) + if (a->global_scope) + break; + + /* No address with a global scope has been found, so let's use + * any. */ + if (!a) + a = i->addresses; + + /* Hmm, there is no address available. */ + if (!a) + return -1; + + i->local_mcast_address = a->address; + } + + avahi_log_info("%s mDNS multicast group on interface %s.%s with address %s.", + join ? "Joining" : "Leaving", + i->hardware->name, + avahi_proto_to_string(i->protocol), + avahi_address_snprint(at, sizeof(at), &i->local_mcast_address)); + + if (i->protocol == AVAHI_PROTO_INET6) + r = avahi_mdns_mcast_join_ipv6(i->monitor->server->fd_ipv6, &i->local_mcast_address.data.ipv6, i->hardware->index, join); + else { + assert(i->protocol == AVAHI_PROTO_INET); + + r = avahi_mdns_mcast_join_ipv4(i->monitor->server->fd_ipv4, &i->local_mcast_address.data.ipv4, i->hardware->index, join); + } + + if (r < 0) + i->mcast_joined = 0; + else + i->mcast_joined = join; + + return 0; +} + +static int interface_mdns_mcast_rejoin(AvahiInterface *i) { + AvahiInterfaceAddress *a, *usable = NULL, *found = NULL; + assert(i); + + if (!i->mcast_joined) + return 0; + + /* Check whether old address we joined with is still available. If + * not, rejoin using an other address. */ + + for (a = i->addresses; a; a = a->address_next) { + if (a->global_scope && !usable) + usable = a; + + if (avahi_address_cmp(&a->address, &i->local_mcast_address) == 0) { + + if (a->global_scope) + /* No action necessary: the address still exists and + * has global scope. */ + return 0; + + found = a; + } + } + + if (found && !usable) + /* No action necessary: the address still exists and no better one has been found */ + return 0; + + interface_mdns_mcast_join(i, 0); + return interface_mdns_mcast_join(i, 1); +} + +void avahi_interface_address_free(AvahiInterfaceAddress *a) { + assert(a); + assert(a->interface); + + avahi_interface_address_update_rrs(a, 1); + AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a); + + if (a->entry_group) + avahi_s_entry_group_free(a->entry_group); + + interface_mdns_mcast_rejoin(a->interface); + + avahi_free(a); +} + +void avahi_interface_free(AvahiInterface *i, int send_goodbye) { + assert(i); + + /* Handle goodbyes and remove announcers */ + avahi_goodbye_interface(i->monitor->server, i, send_goodbye, 1); + avahi_response_scheduler_force(i->response_scheduler); + assert(!i->announcers); + + if (i->mcast_joined) + interface_mdns_mcast_join(i, 0); + + /* Remove queriers */ + avahi_querier_free_all(i); + avahi_hashmap_free(i->queriers_by_key); + + /* Remove local RRs */ + avahi_interface_update_rrs(i, 1); + + while (i->addresses) + avahi_interface_address_free(i->addresses); + + avahi_response_scheduler_free(i->response_scheduler); + avahi_query_scheduler_free(i->query_scheduler); + avahi_probe_scheduler_free(i->probe_scheduler); + avahi_cache_free(i->cache); + + AVAHI_LLIST_REMOVE(AvahiInterface, interface, i->monitor->interfaces, i); + AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i); + + avahi_free(i); +} + +void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye) { + assert(hw); + + avahi_hw_interface_update_rrs(hw, 1); + + while (hw->interfaces) + avahi_interface_free(hw->interfaces, send_goodbye); + + if (hw->entry_group) + avahi_s_entry_group_free(hw->entry_group); + + AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, hw->monitor->hw_interfaces, hw); + avahi_hashmap_remove(hw->monitor->hashmap, &hw->index); + + avahi_free(hw->name); + avahi_free(hw); +} + +AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) { + AvahiInterface *i; + + assert(m); + assert(hw); + assert(AVAHI_PROTO_VALID(protocol)); + + if (!(i = avahi_new(AvahiInterface, 1))) + goto fail; /* OOM */ + + i->monitor = m; + i->hardware = hw; + i->protocol = protocol; + i->announcing = 0; + i->mcast_joined = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); + AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, i->announcers); + + AVAHI_LLIST_HEAD_INIT(AvahiQuerier, i->queriers); + i->queriers_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + + i->cache = avahi_cache_new(m->server, i); + i->response_scheduler = avahi_response_scheduler_new(i); + i->query_scheduler = avahi_query_scheduler_new(i); + i->probe_scheduler = avahi_probe_scheduler_new(i); + + if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler) + goto fail; /* OOM */ + + AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i); + AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i); + + return i; + +fail: + + if (i) { + if (i->cache) + avahi_cache_free(i->cache); + if (i->response_scheduler) + avahi_response_scheduler_free(i->response_scheduler); + if (i->query_scheduler) + avahi_query_scheduler_free(i->query_scheduler); + if (i->probe_scheduler) + avahi_probe_scheduler_free(i->probe_scheduler); + } + + return NULL; +} + +AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx) { + AvahiHwInterface *hw; + + assert(m); + assert(AVAHI_IF_VALID(idx)); + + if (!(hw = avahi_new(AvahiHwInterface, 1))) + return NULL; + + hw->monitor = m; + hw->name = NULL; + hw->flags_ok = 0; + hw->mtu = 1500; + hw->index = idx; + hw->mac_address_size = 0; + hw->entry_group = NULL; + hw->ratelimit_begin.tv_sec = 0; + hw->ratelimit_begin.tv_usec = 0; + hw->ratelimit_counter = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces); + AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw); + + avahi_hashmap_insert(m->hashmap, &hw->index, hw); + + 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 || m->server->config.publish_aaaa_on_ipv4) + avahi_interface_new(m, hw, AVAHI_PROTO_INET6); + + return hw; +} + +AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len) { + AvahiInterfaceAddress *a; + + assert(m); + assert(i); + + if (!(a = avahi_new(AvahiInterfaceAddress, 1))) + return NULL; + + a->interface = i; + a->monitor = m; + a->address = *addr; + a->prefix_len = prefix_len; + a->global_scope = 0; + a->deprecated = 0; + a->entry_group = NULL; + + AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, a); + + return a; +} + +void avahi_interface_check_relevant(AvahiInterface *i) { + int b; + AvahiInterfaceMonitor *m; + + assert(i); + m = i->monitor; + + b = avahi_interface_is_relevant(i); + + if (m->list_complete && b && !i->announcing) { + interface_mdns_mcast_join(i, 1); + + 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)); + + interface_mdns_mcast_join(i, 0); + + avahi_goodbye_interface(m->server, i, 0, 1); + avahi_querier_free_all(i); + + avahi_response_scheduler_clear(i->response_scheduler); + avahi_query_scheduler_clear(i->query_scheduler); + avahi_probe_scheduler_clear(i->probe_scheduler); + avahi_cache_flush(i->cache); + + i->announcing = 0; + + } else + interface_mdns_mcast_rejoin(i); +} + +void avahi_hw_interface_check_relevant(AvahiHwInterface *hw) { + AvahiInterface *i; + + assert(hw); + + for (i = hw->interfaces; i; i = i->by_hardware_next) + avahi_interface_check_relevant(i); +} + +void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m) { + AvahiInterface *i; + + assert(m); + + for (i = m->interfaces; i; i = i->interface_next) + avahi_interface_check_relevant(i); +} + +AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) { + AvahiInterfaceMonitor *m = NULL; + + if (!(m = avahi_new0(AvahiInterfaceMonitor, 1))) + return NULL; /* OOM */ + + m->server = s; + m->list_complete = 0; + m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL); + + AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces); + AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces); + + if (avahi_interface_monitor_init_osdep(m) < 0) + goto fail; + + return m; + +fail: + avahi_interface_monitor_free(m); + return NULL; +} + +void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { + assert(m); + + while (m->hw_interfaces) + avahi_hw_interface_free(m->hw_interfaces, 1); + + assert(!m->interfaces); + + avahi_interface_monitor_free_osdep(m); + + if (m->hashmap) + avahi_hashmap_free(m->hashmap); + + avahi_free(m); +} + + +AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) { + AvahiHwInterface *hw; + AvahiInterface *i; + + assert(m); + assert(idx >= 0); + assert(protocol != AVAHI_PROTO_UNSPEC); + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx))) + return NULL; + + for (i = hw->interfaces; i; i = i->by_hardware_next) + if (i->protocol == protocol) + return i; + + return NULL; +} + +AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) { + assert(m); + assert(idx >= 0); + + return avahi_hashmap_lookup(m->hashmap, &idx); +} + +AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) { + AvahiInterfaceAddress *ia; + + assert(m); + assert(i); + assert(raddr); + + for (ia = i->addresses; ia; ia = ia->address_next) + if (avahi_address_cmp(&ia->address, raddr) == 0) + return ia; + + return NULL; +} + +void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) { + assert(i); + assert(p); + + if (!i->announcing) + return; + + assert(!a || a->proto == i->protocol); + + if (i->monitor->server->config.ratelimit_interval > 0) { + struct timeval now, end; + + gettimeofday(&now, NULL); + + end = i->hardware->ratelimit_begin; + avahi_timeval_add(&end, i->monitor->server->config.ratelimit_interval); + + if (i->hardware->ratelimit_begin.tv_sec <= 0 || + avahi_timeval_compare(&end, &now) < 0) { + + i->hardware->ratelimit_begin = now; + i->hardware->ratelimit_counter = 0; + } + + if (i->hardware->ratelimit_counter > i->monitor->server->config.ratelimit_burst) + return; + + i->hardware->ratelimit_counter++; + } + + if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0) + avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port); + else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0) + avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv6 : NULL, a ? &a->data.ipv6 : NULL, port); +} + +void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { + assert(i); + assert(p); + + avahi_interface_send_packet_unicast(i, p, NULL, 0); +} + +int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) { + assert(i); + assert(key); + + 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) { + + return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id); +} + +int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { + assert(i); + assert(record); + + 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 (!i->announcing) + return 0; + + return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately); +} + +int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) { + AvahiInterface *i; + assert(m); + + for (i = m->interfaces; i; i = i->interface_next) { + if (avahi_interface_is_relevant(i)) { + char ln[256]; + snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol)); + callback(ln, userdata); + if (avahi_cache_dump(i->cache, callback, userdata) < 0) + return -1; + } + } + + return 0; +} + +static int avahi_interface_is_relevant_internal(AvahiInterface *i) { + AvahiInterfaceAddress *a; + + assert(i); + + if (!i->hardware->flags_ok) + return 0; + + for (a = i->addresses; a; a = a->address_next) + if (avahi_interface_address_is_relevant(a)) + return 1; + + return 0; +} + +int avahi_interface_is_relevant(AvahiInterface *i) { + AvahiStringList *l; + assert(i); + + for (l = i->monitor->server->config.deny_interfaces; l; l = l->next) + if (strcasecmp((char*) l->text, i->hardware->name) == 0) + return 0; + + if (i->monitor->server->config.allow_interfaces) { + + for (l = i->monitor->server->config.allow_interfaces; l; l = l->next) + if (strcasecmp((char*) l->text, i->hardware->name) == 0) + goto good; + + return 0; + } + +good: + return avahi_interface_is_relevant_internal(i); +} + +int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) { + AvahiInterfaceAddress *b; + assert(a); + + /* Publish public and non-deprecated IP addresses */ + if (a->global_scope && !a->deprecated) + return 1; + + /* Publish link-local and deprecated IP addresses only if they are + * the only ones on the link */ + for (b = a->interface->addresses; b; b = b->address_next) { + if (b == a) + continue; + + if (b->global_scope && !b->deprecated) + return 0; + } + + return 1; +} + +int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) { + assert(i); + + if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index) + return 0; + + if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol) + return 0; + + return 1; +} + +void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) { + assert(m); + assert(callback); + + if (interface != AVAHI_IF_UNSPEC) { + if (protocol != AVAHI_PROTO_UNSPEC) { + AvahiInterface *i; + + if ((i = avahi_interface_monitor_get_interface(m, interface, protocol))) + callback(m, i, userdata); + + } else { + AvahiHwInterface *hw; + AvahiInterface *i; + + if ((hw = avahi_interface_monitor_get_hw_interface(m, interface))) + for (i = hw->interfaces; i; i = i->by_hardware_next) + if (avahi_interface_match(i, interface, protocol)) + callback(m, i, userdata); + } + + } else { + AvahiInterface *i; + + for (i = m->interfaces; i; i = i->interface_next) + if (avahi_interface_match(i, interface, protocol)) + callback(m, i, userdata); + } +} + + +int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) { + AvahiInterface *i; + AvahiInterfaceAddress *ia; + assert(m); + assert(a); + + for (i = m->interfaces; i; i = i->interface_next) + for (ia = i->addresses; ia; ia = ia->address_next) + if (avahi_address_cmp(a, &ia->address) == 0) + return 1; + + return 0; +} + +int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) { + AvahiInterfaceAddress *ia; + + assert(i); + assert(a); + + if (a->proto != i->protocol) + return 0; + + for (ia = i->addresses; ia; ia = ia->address_next) { + + if (a->proto == AVAHI_PROTO_INET) { + uint32_t m; + + m = ~(((uint32_t) -1) >> ia->prefix_len); + + if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m)) + return 1; + } else { + unsigned j; + unsigned char pl; + assert(a->proto == AVAHI_PROTO_INET6); + + pl = ia->prefix_len; + + for (j = 0; j < 16; j++) { + uint8_t m; + + if (pl == 0) + return 1; + + if (pl >= 8) { + m = 0xFF; + pl -= 8; + } else { + m = ~(0xFF >> pl); + pl = 0; + } + + if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m)) + break; + } + } + } + + return 0; +} + +int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) { + AvahiInterface *i; + AvahiInterfaceAddress *j; + + assert(m); + assert(iface != AVAHI_IF_UNSPEC); + assert(a); + + if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto))) + return 0; + + for (j = i->addresses; j; j = j->address_next) + if (avahi_address_cmp(a, &j->address) == 0) + return 1; + + return 0; +} + +AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a) { + AvahiInterface *i; + assert(m); + + /* Some stupid OS don't support passing the interface index when a + * packet is received. We have to work around that limitation by + * looking for an interface that has the incoming address + * attached. This is sometimes ambiguous, but we have to live with + * it. */ + + for (i = m->interfaces; i; i = i->interface_next) { + AvahiInterfaceAddress *ai; + + if (i->protocol != a->proto) + continue; + + for (ai = i->addresses; ai; ai = ai->address_next) + if (avahi_address_cmp(a, &ai->address) == 0) + return i->hardware->index; + } + + return AVAHI_IF_UNSPEC; +} diff --git a/avahi/iface.h b/avahi/iface.h new file mode 100644 index 0000000..88d75c2 --- /dev/null +++ b/avahi/iface.h @@ -0,0 +1,195 @@ +#ifndef fooifacehfoo +#define fooifacehfoo + +/*** + 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 struct AvahiInterfaceMonitor AvahiInterfaceMonitor; +typedef struct AvahiInterfaceAddress AvahiInterfaceAddress; +typedef struct AvahiInterface AvahiInterface; +typedef struct AvahiHwInterface AvahiHwInterface; + +#include +#include + +#include "internal.h" +#include "cache.h" +#include "response-sched.h" +#include "query-sched.h" +#include "probe-sched.h" +#include "dns.h" +#include "announce.h" +#include "browse.h" +#include "querier.h" + +#ifdef HAVE_NETLINK +#include "iface-linux.h" +#elif defined(HAVE_PF_ROUTE) +#include "iface-pfroute.h" +#else +typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; +struct AvahiInterfaceMonitorOSDep { + + unsigned query_addr_seq, query_link_seq; + + enum { + LIST_IFACE, + LIST_ADDR, + LIST_DONE + } list; +}; +#endif + +#define AVAHI_MAC_ADDRESS_MAX 32 + +struct AvahiInterfaceMonitor { + AvahiServer *server; + AvahiHashmap *hashmap; + + AVAHI_LLIST_HEAD(AvahiInterface, interfaces); + AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces); + + int list_complete; + AvahiInterfaceMonitorOSDep osdep; +}; + +struct AvahiHwInterface { + AvahiInterfaceMonitor *monitor; + + AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware); + + char *name; + AvahiIfIndex index; + int flags_ok; + + unsigned mtu; + + uint8_t mac_address[AVAHI_MAC_ADDRESS_MAX]; + size_t mac_address_size; + + AvahiSEntryGroup *entry_group; + + /* Packet rate limiting */ + struct timeval ratelimit_begin; + unsigned ratelimit_counter; + + AVAHI_LLIST_HEAD(AvahiInterface, interfaces); +}; + +struct AvahiInterface { + AvahiInterfaceMonitor *monitor; + AvahiHwInterface *hardware; + + AVAHI_LLIST_FIELDS(AvahiInterface, interface); + AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware); + + AvahiProtocol protocol; + int announcing; + AvahiAddress local_mcast_address; + int mcast_joined; + + AvahiCache *cache; + + AvahiQueryScheduler *query_scheduler; + AvahiResponseScheduler * response_scheduler; + AvahiProbeScheduler *probe_scheduler; + + AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses); + AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers); + + AvahiHashmap *queriers_by_key; + AVAHI_LLIST_HEAD(AvahiQuerier, queriers); +}; + +struct AvahiInterfaceAddress { + AvahiInterfaceMonitor *monitor; + AvahiInterface *interface; + + AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address); + + AvahiAddress address; + unsigned prefix_len; + + int global_scope; + int deprecated; + + AvahiSEntryGroup *entry_group; +}; + +AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server); +void avahi_interface_monitor_free(AvahiInterfaceMonitor *m); + +int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m); +void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m); +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m); + +typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata); +void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata); +int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata); + +void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs); +int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a); +void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m); + +/* AvahiHwInterface */ + +AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx); +void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye); + +void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs); +void avahi_hw_interface_check_relevant(AvahiHwInterface *hw); + +AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, int idx); + +/* AvahiInterface */ + +AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol); +void avahi_interface_free(AvahiInterface *i, int send_goodbye); + +void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs); +void avahi_interface_check_relevant(AvahiInterface *i); +int avahi_interface_is_relevant(AvahiInterface *i); + +void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); +void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port); + +int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id); +int avahi_interface_withraw_query(AvahiInterface *i, unsigned id); +int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); +int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately); + +int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol); +int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a); +int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a); + +AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol); + +/* AvahiInterfaceAddress */ + +AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len); +void avahi_interface_address_free(AvahiInterfaceAddress *a); + +void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs); +int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a); + +AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr); + +AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a); + +#endif diff --git a/avahi/internal.h b/avahi/internal.h new file mode 100644 index 0000000..5ddaade --- /dev/null +++ b/avahi/internal.h @@ -0,0 +1,227 @@ +#ifndef foointernalhfoo +#define foointernalhfoo + +/*** + 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. +***/ + +/** A locally registered DNS resource record */ +typedef struct AvahiEntry AvahiEntry; + +#include +#include +#include + +#include "core.h" +#include "iface.h" +#include "prioq.h" +#include "timeeventq.h" +#include "announce.h" +#include "browse.h" +#include "dns.h" +#include "rrlist.h" +#include "hashmap.h" +#include "wide-area.h" +#include "multicast-lookup.h" +#include "dns-srv-rr.h" + +#define AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX 100 + +#define AVAHI_FLAGS_VALID(flags, max) (!((flags) & ~(max))) + +#define AVAHI_RR_HOLDOFF_MSEC 1000 +#define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 20000 +#define AVAHI_RR_RATE_LIMIT_COUNT 15 + +typedef struct AvahiLegacyUnicastReflectSlot AvahiLegacyUnicastReflectSlot; + +struct AvahiLegacyUnicastReflectSlot { + AvahiServer *server; + + uint16_t id, original_id; + AvahiAddress address; + uint16_t port; + int interface; + struct timeval elapse_time; + AvahiTimeEvent *time_event; +}; + +struct AvahiEntry { + AvahiServer *server; + AvahiSEntryGroup *group; + + int dead; + + AvahiPublishFlags flags; + AvahiRecord *record; + AvahiIfIndex interface; + AvahiProtocol protocol; + + AVAHI_LLIST_FIELDS(AvahiEntry, entries); + AVAHI_LLIST_FIELDS(AvahiEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiEntry, by_group); + + AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers); +}; + +struct AvahiSEntryGroup { + AvahiServer *server; + int dead; + + AvahiEntryGroupState state; + void* userdata; + AvahiSEntryGroupCallback callback; + + unsigned n_probing; + + unsigned n_register_try; + struct timeval register_time; + AvahiTimeEvent *register_time_event; + + struct timeval established_at; + + AVAHI_LLIST_FIELDS(AvahiSEntryGroup, groups); + AVAHI_LLIST_HEAD(AvahiEntry, entries); +}; + +struct AvahiServer { + const AvahiPoll *poll_api; + + AvahiInterfaceMonitor *monitor; + AvahiServerConfig config; + + AVAHI_LLIST_HEAD(AvahiEntry, entries); + AvahiHashmap *entries_by_key; + + AVAHI_LLIST_HEAD(AvahiSEntryGroup, groups); + + AVAHI_LLIST_HEAD(AvahiSRecordBrowser, record_browsers); + AvahiHashmap *record_browser_hashmap; + AVAHI_LLIST_HEAD(AvahiSHostNameResolver, host_name_resolvers); + AVAHI_LLIST_HEAD(AvahiSAddressResolver, address_resolvers); + AVAHI_LLIST_HEAD(AvahiSDomainBrowser, domain_browsers); + AVAHI_LLIST_HEAD(AvahiSServiceTypeBrowser, service_type_browsers); + AVAHI_LLIST_HEAD(AvahiSServiceBrowser, service_browsers); + AVAHI_LLIST_HEAD(AvahiSServiceResolver, service_resolvers); + AVAHI_LLIST_HEAD(AvahiSDNSServerBrowser, dns_server_browsers); + + int need_entry_cleanup, need_group_cleanup, need_browser_cleanup; + + /* Used for scheduling RR cleanup */ + AvahiTimeEvent *cleanup_time_event; + + AvahiTimeEventQueue *time_event_queue; + + char *host_name, *host_name_fqdn, *domain_name; + + int fd_ipv4, fd_ipv6, + /* The following two sockets two are used for reflection only */ + fd_legacy_unicast_ipv4, fd_legacy_unicast_ipv6; + + AvahiWatch *watch_ipv4, *watch_ipv6, + *watch_legacy_unicast_ipv4, *watch_legacy_unicast_ipv6; + + AvahiServerState state; + AvahiServerCallback callback; + void* userdata; + + AvahiSEntryGroup *hinfo_entry_group; + AvahiSEntryGroup *browse_domain_entry_group; + unsigned n_host_rr_pending; + + /* Used for assembling responses */ + AvahiRecordList *record_list; + + /* Used for reflection of legacy unicast packets */ + AvahiLegacyUnicastReflectSlot **legacy_unicast_reflect_slots; + uint16_t legacy_unicast_reflect_id; + + /* The last error code */ + int error; + + /* The local service cookie */ + uint32_t local_service_cookie; + + AvahiMulticastLookupEngine *multicast_lookup_engine; + AvahiWideAreaLookupEngine *wide_area_lookup_engine; +}; + +void avahi_entry_free(AvahiServer*s, AvahiEntry *e); +void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g); + +void avahi_cleanup_dead_entries(AvahiServer *s); + +void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary); +void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response); +void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int is_probe); + +void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state); + +int avahi_entry_is_commited(AvahiEntry *e); + +void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata); + +void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata); + +void avahi_server_decrease_host_rr_pending(AvahiServer *s); + +int avahi_server_set_errno(AvahiServer *s, int error); + +int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name); +int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record); + +int avahi_server_add_ptr( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + const char *dest); + +#define AVAHI_CHECK_VALIDITY(server, expression, error) { \ + if (!(expression)) \ + return avahi_server_set_errno((server), (error)); \ +} + +#define AVAHI_CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \ + if (!(expression)) { \ + avahi_server_set_errno((server), (error)); \ + return NULL; \ + } \ +} + +#define AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(server, expression, error) {\ + if (!(expression)) { \ + ret = avahi_server_set_errno((server), (error)); \ + goto fail; \ + } \ +} + +#define AVAHI_ASSERT_TRUE(expression) { \ + int __tmp = !!(expression); \ + assert(__tmp); \ +} + +#define AVAHI_ASSERT_SUCCESS(expression) { \ + int __tmp = (expression); \ + assert(__tmp == 0); \ +} + +#endif diff --git a/avahi/llist.h b/avahi/llist.h new file mode 100644 index 0000000..5101353 --- /dev/null +++ b/avahi/llist.h @@ -0,0 +1,75 @@ +#ifndef foollistfoo +#define foollistfoo + +/*** + 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. +***/ + +/** \file llist.h A simple macro based linked list implementation */ + +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define AVAHI_LLIST_HEAD(t,name) t *name + +/** The pointers in the linked list's items. Use this in the item structure */ +#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev + +/** Initialize the list's head */ +#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0) + +/** Initialize a list item */ +#define AVAHI_LLIST_INIT(t,name,item) do { \ + t *_item = (item); \ + assert(_item); \ + _item->name##_prev = _item->name##_next = NULL; \ + } while(0) + +/** Prepend an item to the list */ +#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->name##_next = *_head)) \ + _item->name##_next->name##_prev = _item; \ + _item->name##_prev = NULL; \ + *_head = _item; \ + } while (0) + +/** Remove an item from the list */ +#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->name##_next) \ + _item->name##_next->name##_prev = _item->name##_prev; \ + if (_item->name##_prev) \ + _item->name##_prev->name##_next = _item->name##_next; \ + else {\ + assert(*_head == _item); \ + *_head = _item->name##_next; \ + } \ + _item->name##_next = _item->name##_prev = NULL; \ + } while(0) + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/log.c b/avahi/log.c new file mode 100644 index 0000000..d110765 --- /dev/null +++ b/avahi/log.c @@ -0,0 +1,86 @@ +/*** + 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 "log.h" + +static AvahiLogFunction log_function = NULL; + +void avahi_set_log_function(AvahiLogFunction function) { + log_function = function; +} + +void avahi_log_ap(AvahiLogLevel level, const char*format, va_list ap) { + char txt[256]; + + vsnprintf(txt, sizeof(txt), format, ap); + + if (log_function) + log_function(level, txt); + else + fprintf(stderr, "%s\n", txt); +} + +void avahi_log(AvahiLogLevel level, const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(level, format, ap); + va_end(ap); +} + +void avahi_log_error(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_ERROR, format, ap); + va_end(ap); +} + +void avahi_log_warn(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_WARN, format, ap); + va_end(ap); +} + +void avahi_log_notice(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_NOTICE, format, ap); + va_end(ap); +} + +void avahi_log_info(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_INFO, format, ap); + va_end(ap); +} + +void avahi_log_debug(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_DEBUG, format, ap); + va_end(ap); +} diff --git a/avahi/log.h b/avahi/log.h new file mode 100644 index 0000000..df9b6f1 --- /dev/null +++ b/avahi/log.h @@ -0,0 +1,73 @@ +#ifndef foologhfoo +#define foologhfoo + +/*** + 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 +#include + +/** \file log.h Extensible logging subsystem */ + +AVAHI_C_DECL_BEGIN + +/** Log level for avahi_log_xxx() */ +typedef enum { + AVAHI_LOG_ERROR = 0, /**< Error messages */ + AVAHI_LOG_WARN = 1, /**< Warning messages */ + AVAHI_LOG_NOTICE = 2, /**< Notice messages */ + AVAHI_LOG_INFO = 3, /**< Info messages */ + AVAHI_LOG_DEBUG = 4, /**< Debug messages */ + AVAHI_LOG_LEVEL_MAX +} AvahiLogLevel; + +/** Prototype for a user supplied log function */ +typedef void (*AvahiLogFunction)(AvahiLogLevel level, const char *txt); + +/** Set a user supplied log function, replacing the default which + * prints to log messages unconditionally to STDERR. Pass NULL for + * resetting to the default log function */ +void avahi_set_log_function(AvahiLogFunction function); + +/** Issue a log message using a va_list object */ +void avahi_log_ap(AvahiLogLevel level, const char *format, va_list ap); + +/** Issue a log message by passing a log level and a format string */ +void avahi_log(AvahiLogLevel level, const char*format, ...) AVAHI_GCC_PRINTF_ATTR23; + +/** Shortcut for avahi_log(AVAHI_LOG_ERROR, ...) */ +void avahi_log_error(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_WARN, ...) */ +void avahi_log_warn(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_NOTICE, ...) */ +void avahi_log_notice(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_INFO, ...) */ +void avahi_log_info(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_DEBUG, ...) */ +void avahi_log_debug(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/lookup.h b/avahi/lookup.h new file mode 100644 index 0000000..5a04ff9 --- /dev/null +++ b/avahi/lookup.h @@ -0,0 +1,235 @@ +#ifndef foolookuphfoo +#define foolookuphfoo + +/*** + 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. +***/ + +/** \file avahi/lookup.h Functions for browsing/resolving services and other RRs */ + +/** \example core-browse-services.c Example how to browse for DNS-SD + * services using an embedded mDNS stack. */ + +/** A browsing object for arbitrary RRs */ +typedef struct AvahiSRecordBrowser AvahiSRecordBrowser; + +/** A host name to IP adddress resolver object */ +typedef struct AvahiSHostNameResolver AvahiSHostNameResolver; + +/** An IP address to host name resolver object ("reverse lookup") */ +typedef struct AvahiSAddressResolver AvahiSAddressResolver; + +/** A local domain browsing object. May be used to enumerate domains used on the local LAN */ +typedef struct AvahiSDomainBrowser AvahiSDomainBrowser; + +/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */ +typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser; + +/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */ +typedef struct AvahiSServiceBrowser AvahiSServiceBrowser; + +/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */ +typedef struct AvahiSServiceResolver AvahiSServiceResolver; + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Callback prototype for AvahiSRecordBrowser events */ +typedef void (*AvahiSRecordBrowserCallback)( + AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */ + AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */ + AvahiProtocol protocol, /**< Protocol number the record was found. */ + AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */ + AvahiRecord *record, /**< The record that was found */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ ); + +/** Create a new browsing object for arbitrary RRs */ +AvahiSRecordBrowser *avahi_s_record_browser_new( + AvahiServer *server, /**< The server object to which attach this query */ + AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */ + AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */ + AvahiKey *key, /**< The search key */ + AvahiLookupFlags flags, /**< Lookup flags. Must have set either AVAHI_LOOKUP_FORCE_WIDE_AREA or AVAHI_LOOKUP_FORCE_MULTICAST, since domain based detection is not available here. */ + AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */ + void* userdata /**< Arbitrary use suppliable data which is passed to the callback */); + +/** Free an AvahiSRecordBrowser object */ +void avahi_s_record_browser_free(AvahiSRecordBrowser *b); + +/** Callback prototype for AvahiSHostNameResolver events */ +typedef void (*AvahiSHostNameResolverCallback)( + AvahiSHostNameResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, /**< Resolving event */ + const char *host_name, /**< Host name which should be resolved. May differ in case from the query */ + const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */ +AvahiSHostNameResolver *avahi_s_host_name_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *host_name, /**< The host name to look for */ + AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSHostNameResolverCallback calback, + void* userdata); + +/** Free a AvahiSHostNameResolver object */ +void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r); + +/** Callback prototype for AvahiSAddressResolver events */ +typedef void (*AvahiSAddressResolverCallback)( + AvahiSAddressResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const AvahiAddress *a, + const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */ +AvahiSAddressResolver *avahi_s_address_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *address, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSAddressResolverCallback calback, + void* userdata); + +/** Free an AvahiSAddressResolver object */ +void avahi_s_address_resolver_free(AvahiSAddressResolver *r); + +/** Callback prototype for AvahiSDomainBrowser events */ +typedef void (*AvahiSDomainBrowserCallback)( + AvahiSDomainBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSDomainBrowser object */ +AvahiSDomainBrowser *avahi_s_domain_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType type, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSDomainBrowserCallback callback, + void* userdata); + +/** Free an AvahiSDomainBrowser object */ +void avahi_s_domain_browser_free(AvahiSDomainBrowser *b); + +/** Callback prototype for AvahiSServiceTypeBrowser events */ +typedef void (*AvahiSServiceTypeBrowserCallback)( + AvahiSServiceTypeBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceTypeBrowser object. */ +AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceTypeBrowserCallback callback, + void* userdata); + +/** Free an AvahiSServiceTypeBrowser object */ +void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b); + +/** Callback prototype for AvahiSServiceBrowser events */ +typedef void (*AvahiSServiceBrowserCallback)( + AvahiSServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name /**< Service name, e.g. "Lennart's Files" */, + const char *type /**< DNS-SD type, e.g. "_http._tcp" */, + const char *domain /**< Domain of this service, e.g. "local" */, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceBrowser object. */ +AvahiSServiceBrowser *avahi_s_service_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */, + const char *domain, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceBrowserCallback callback, + void* userdata); + +/** Free an AvahiSServiceBrowser object */ +void avahi_s_service_browser_free(AvahiSServiceBrowser *b); + +/** Callback prototype for AvahiSServiceResolver events */ +typedef void (*AvahiSServiceResolverCallback)( + AvahiSServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */ + const char *name, /**< Service name */ + const char *type, /**< Service Type */ + const char *domain, + const char *host_name, /**< Host name of the service */ + const AvahiAddress *a, /**< The resolved host name */ + uint16_t port, /**< Service name */ + AvahiStringList *txt, /**< TXT record data */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */ +AvahiSServiceResolver *avahi_s_service_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceResolverCallback calback, + void* userdata); + +/** Free an AvahiSServiceResolver object */ +void avahi_s_service_resolver_free(AvahiSServiceResolver *r); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/malloc.c b/avahi/malloc.c new file mode 100644 index 0000000..23b13a9 --- /dev/null +++ b/avahi/malloc.c @@ -0,0 +1,257 @@ +/*** + 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 "malloc.h" + +#ifndef va_copy +#ifdef __va_copy +#define va_copy(DEST,SRC) __va_copy((DEST),(SRC)) +#else +#define va_copy(DEST,SRC) memcpy(&(DEST), &(SRC), sizeof(va_list)) +#endif +#endif + +static const AvahiAllocator *allocator = NULL; + +static void oom(void) AVAHI_GCC_NORETURN; + +static void oom(void) { + + static const char msg[] = "Out of memory, aborting ...\n"; + const char *n = msg; + + while (strlen(n) > 0) { + ssize_t r; + + if ((r = write(2, n, strlen(n))) < 0) + break; + + n += r; + } + + abort(); +} + +/* Default implementation for avahi_malloc() */ +static void* xmalloc(size_t size) { + void *p; + + if (size == 0) + return NULL; + + if (!(p = malloc(size))) + oom(); + + return p; +} + +/* Default implementation for avahi_realloc() */ +static void *xrealloc(void *p, size_t size) { + + if (size == 0) { + free(p); + return NULL; + } + + if (!(p = realloc(p, size))) + oom(); + + return p; +} + +/* Default implementation for avahi_calloc() */ +static void *xcalloc(size_t nmemb, size_t size) { + void *p; + + if (size == 0 || nmemb == 0) + return NULL; + + if (!(p = calloc(nmemb, size))) + oom(); + + return p; +} + +void *avahi_malloc(size_t size) { + + if (size <= 0) + return NULL; + + if (!allocator) + return xmalloc(size); + + assert(allocator->malloc); + return allocator->malloc(size); +} + +void *avahi_malloc0(size_t size) { + void *p; + + if (size <= 0) + return NULL; + + if (!allocator) + return xcalloc(1, size); + + if (allocator->calloc) + return allocator->calloc(1, size); + + assert(allocator->malloc); + if ((p = allocator->malloc(size))) + memset(p, 0, size); + + return p; +} + +void avahi_free(void *p) { + + if (!p) + return; + + if (!allocator) { + free(p); + return; + } + + assert(allocator->free); + allocator->free(p); +} + +void *avahi_realloc(void *p, size_t size) { + + if (size == 0) { + avahi_free(p); + return NULL; + } + + if (!allocator) + return xrealloc(p, size); + + assert(allocator->realloc); + return allocator->realloc(p, size); +} + +char *avahi_strdup(const char *s) { + char *r; + size_t size; + + if (!s) + return NULL; + + size = strlen(s); + if (!(r = avahi_malloc(size+1))) + return NULL; + + memcpy(r, s, size+1); + return r; +} + +char *avahi_strndup(const char *s, size_t max) { + char *r; + size_t size; + const char *p; + + if (!s) + return NULL; + + for (p = s, size = 0; + size < max && *p; + p++, size++); + + if (!(r = avahi_new(char, size+1))) + return NULL; + + memcpy(r, s, size); + r[size] = 0; + return r; +} + +/* Change the allocator */ +void avahi_set_allocator(const AvahiAllocator *a) { + allocator = a; +} + +char *avahi_strdup_vprintf(const char *fmt, va_list ap) { + size_t len = 80; + char *buf; + + assert(fmt); + + if (!(buf = avahi_malloc(len))) + return NULL; + + for (;;) { + int n; + char *nbuf; + va_list ap2; + + va_copy (ap2, ap); + n = vsnprintf(buf, len, fmt, ap2); + va_end (ap2); + + if (n >= 0 && n < (int) len) + return buf; + + if (n >= 0) + len = n+1; + else + len *= 2; + + if (!(nbuf = avahi_realloc(buf, len))) { + avahi_free(buf); + return NULL; + } + + buf = nbuf; + } +} + +char *avahi_strdup_printf(const char *fmt, ... ) { + char *s; + va_list ap; + + assert(fmt); + + va_start(ap, fmt); + s = avahi_strdup_vprintf(fmt, ap); + va_end(ap); + + return s; +} + +void *avahi_memdup(const void *s, size_t l) { + void *p; + assert(s); + + if (!(p = avahi_malloc(l))) + return NULL; + + memcpy(p, s, l); + return p; +} diff --git a/avahi/malloc.h b/avahi/malloc.h new file mode 100644 index 0000000..3467cc2 --- /dev/null +++ b/avahi/malloc.h @@ -0,0 +1,96 @@ +#ifndef foomallochfoo +#define foomallochfoo + +/*** + 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. +***/ + +/** \file malloc.h Memory allocation */ + +#include +#include +#include +#include + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Allocate some memory, just like the libc malloc() */ +void *avahi_malloc(size_t size) AVAHI_GCC_ALLOC_SIZE(1); + +/** Similar to avahi_malloc() but set the memory to zero */ +void *avahi_malloc0(size_t size) AVAHI_GCC_ALLOC_SIZE(1); + +/** Free some memory */ +void avahi_free(void *p); + +/** Similar to libc's realloc() */ +void *avahi_realloc(void *p, size_t size) AVAHI_GCC_ALLOC_SIZE(2); + +/** Internal helper for avahi_new() */ +static inline void* AVAHI_GCC_ALLOC_SIZE2(1,2) avahi_new_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return avahi_malloc(n*k); +} + +/** Allocate n new structures of the specified type. */ +#define avahi_new(type, n) ((type*) avahi_new_internal((n), sizeof(type))) + +/** Internal helper for avahi_new0() */ +static inline void* AVAHI_GCC_ALLOC_SIZE2(1,2) avahi_new0_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return avahi_malloc0(n*k); +} + +/** Same as avahi_new() but set the memory to zero */ +#define avahi_new0(type, n) ((type*) avahi_new0_internal((n), sizeof(type))) + +/** Just like libc's strdup() */ +char *avahi_strdup(const char *s); + +/** Just like libc's strndup() */ +char *avahi_strndup(const char *s, size_t l); + +/** Duplicate the given memory block into a new one allocated with avahi_malloc() */ +void *avahi_memdup(const void *s, size_t l) AVAHI_GCC_ALLOC_SIZE(2); + +/** Wraps allocator functions */ +typedef struct AvahiAllocator { + void* (*malloc)(size_t size) AVAHI_GCC_ALLOC_SIZE(1); + void (*free)(void *p); + void* (*realloc)(void *p, size_t size) AVAHI_GCC_ALLOC_SIZE(2); + void* (*calloc)(size_t nmemb, size_t size) AVAHI_GCC_ALLOC_SIZE2(1,2); /**< May be NULL */ +} AvahiAllocator; + +/** Change the allocator. May be NULL to return to default (libc) + * allocators. The structure is not copied! */ +void avahi_set_allocator(const AvahiAllocator *a); + +/** Like sprintf() but store the result in a freshly allocated buffer. Free this with avahi_free() */ +char *avahi_strdup_printf(const char *fmt, ... ) AVAHI_GCC_PRINTF_ATTR12; + +/** \cond fulldocs */ +/** Same as avahi_strdup_printf() but take a va_list instead of varargs */ +char *avahi_strdup_vprintf(const char *fmt, va_list ap); +/** \endcond */ + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/multicast-lookup.c b/avahi/multicast-lookup.c new file mode 100644 index 0000000..21f2545 --- /dev/null +++ b/avahi/multicast-lookup.c @@ -0,0 +1,350 @@ +/*** + 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 "internal.h" +#include "browse.h" +#include "socket.h" +#include "log.h" +#include "hashmap.h" +#include "multicast-lookup.h" +#include "rr-util.h" + +struct AvahiMulticastLookup { + AvahiMulticastLookupEngine *engine; + int dead; + + AvahiKey *key, *cname_key; + + AvahiMulticastLookupCallback callback; + void *userdata; + + AvahiIfIndex interface; + AvahiProtocol protocol; + + int queriers_added; + + AvahiTimeEvent *all_for_now_event; + + AVAHI_LLIST_FIELDS(AvahiMulticastLookup, lookups); + AVAHI_LLIST_FIELDS(AvahiMulticastLookup, by_key); +}; + +struct AvahiMulticastLookupEngine { + AvahiServer *server; + + /* Lookups */ + AVAHI_LLIST_HEAD(AvahiMulticastLookup, lookups); + AvahiHashmap *lookups_by_key; + + int cleanup_dead; +}; + +static void all_for_now_callback(AvahiTimeEvent *e, void* userdata) { + AvahiMulticastLookup *l = userdata; + + assert(e); + assert(l); + + avahi_time_event_free(l->all_for_now_event); + l->all_for_now_event = NULL; + + l->callback(l->engine, l->interface, l->protocol, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_RESULT_MULTICAST, NULL, l->userdata); +} + +AvahiMulticastLookup *avahi_multicast_lookup_new( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiMulticastLookupCallback callback, + void *userdata) { + + AvahiMulticastLookup *l, *t; + struct timeval tv; + + assert(e); + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + assert(key); + assert(callback); + + l = avahi_new(AvahiMulticastLookup, 1); + l->engine = e; + l->dead = 0; + l->key = avahi_key_ref(key); + l->cname_key = avahi_key_new_cname(l->key); + l->callback = callback; + l->userdata = userdata; + l->interface = interface; + l->protocol = protocol; + l->all_for_now_event = NULL; + l->queriers_added = 0; + + t = avahi_hashmap_lookup(e->lookups_by_key, l->key); + AVAHI_LLIST_PREPEND(AvahiMulticastLookup, by_key, t, l); + avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); + + AVAHI_LLIST_PREPEND(AvahiMulticastLookup, lookups, e->lookups, l); + + avahi_querier_add_for_all(e->server, interface, protocol, l->key, &tv); + l->queriers_added = 1; + + /* Add a second */ + avahi_timeval_add(&tv, 1000000); + + /* Issue the ALL_FOR_NOW event one second after the querier was initially created */ + l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, all_for_now_callback, l); + + return l; +} + +static void lookup_stop(AvahiMulticastLookup *l) { + assert(l); + + l->callback = NULL; + + if (l->queriers_added) { + avahi_querier_remove_for_all(l->engine->server, l->interface, l->protocol, l->key); + l->queriers_added = 0; + } + + if (l->all_for_now_event) { + avahi_time_event_free(l->all_for_now_event); + l->all_for_now_event = NULL; + } +} + +static void lookup_destroy(AvahiMulticastLookup *l) { + AvahiMulticastLookup *t; + assert(l); + + lookup_stop(l); + + t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); + AVAHI_LLIST_REMOVE(AvahiMulticastLookup, by_key, t, l); + if (t) + avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); + else + avahi_hashmap_remove(l->engine->lookups_by_key, l->key); + + AVAHI_LLIST_REMOVE(AvahiMulticastLookup, lookups, l->engine->lookups, l); + + if (l->key) + avahi_key_unref(l->key); + + if (l->cname_key) + avahi_key_unref(l->cname_key); + + avahi_free(l); +} + +void avahi_multicast_lookup_free(AvahiMulticastLookup *l) { + assert(l); + + if (l->dead) + return; + + l->dead = 1; + l->engine->cleanup_dead = 1; + lookup_stop(l); +} + +void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e) { + AvahiMulticastLookup *l, *n; + assert(e); + + while (e->cleanup_dead) { + e->cleanup_dead = 0; + + for (l = e->lookups; l; l = n) { + n = l->lookups_next; + + if (l->dead) + lookup_destroy(l); + } + } +} + +struct cbdata { + AvahiMulticastLookupEngine *engine; + AvahiMulticastLookupCallback callback; + void *userdata; + AvahiKey *key, *cname_key; + AvahiInterface *interface; + unsigned n_found; +}; + +static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(c); + assert(pattern); + assert(e); + assert(cbdata); + + cbdata->callback( + cbdata->engine, + cbdata->interface->hardware->index, + cbdata->interface->protocol, + AVAHI_BROWSER_NEW, + AVAHI_LOOKUP_RESULT_CACHED|AVAHI_LOOKUP_RESULT_MULTICAST, + e->record, + cbdata->userdata); + + cbdata->n_found ++; + + return NULL; +} + +static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(m); + assert(i); + assert(cbdata); + + cbdata->interface = i; + + avahi_cache_walk(i->cache, cbdata->key, scan_cache_callback, cbdata); + + if (cbdata->cname_key) + avahi_cache_walk(i->cache, cbdata->cname_key, scan_cache_callback, cbdata); + + cbdata->interface = NULL; +} + +unsigned avahi_multicast_lookup_engine_scan_cache( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiMulticastLookupCallback callback, + void *userdata) { + + struct cbdata cbdata; + + assert(e); + assert(key); + assert(callback); + + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + + cbdata.engine = e; + cbdata.key = key; + cbdata.cname_key = avahi_key_new_cname(key); + cbdata.callback = callback; + cbdata.userdata = userdata; + cbdata.interface = NULL; + cbdata.n_found = 0; + + avahi_interface_monitor_walk(e->server->monitor, interface, protocol, scan_interface_callback, &cbdata); + + if (cbdata.cname_key) + avahi_key_unref(cbdata.cname_key); + + return cbdata.n_found; +} + +void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i) { + AvahiMulticastLookup *l; + + assert(e); + assert(i); + + for (l = e->lookups; l; l = l->lookups_next) { + + if (l->dead || !l->callback) + continue; + + if (l->queriers_added && avahi_interface_match(i, l->interface, l->protocol)) + avahi_querier_add(i, l->key, NULL); + } +} + +void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) { + AvahiMulticastLookup *l; + + assert(e); + assert(record); + assert(i); + + for (l = avahi_hashmap_lookup(e->lookups_by_key, record->key); l; l = l->by_key_next) { + if (l->dead || !l->callback) + continue; + + if (avahi_interface_match(i, l->interface, l->protocol)) + l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata); + } + + + if (record->key->clazz == AVAHI_DNS_CLASS_IN && record->key->type == AVAHI_DNS_TYPE_CNAME) { + /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ + + for (l = e->lookups; l; l = l->lookups_next) { + AvahiKey *key; + + if (l->dead || !l->callback) + continue; + + if ((key = avahi_key_new_cname(l->key))) { + if (avahi_key_equal(record->key, key)) + l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata); + + avahi_key_unref(key); + } + } + } +} + +AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s) { + AvahiMulticastLookupEngine *e; + + assert(s); + + e = avahi_new(AvahiMulticastLookupEngine, 1); + e->server = s; + e->cleanup_dead = 0; + + /* Initialize lookup list */ + e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); + + return e; +} + +void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e) { + assert(e); + + while (e->lookups) + lookup_destroy(e->lookups); + + avahi_hashmap_free(e->lookups_by_key); + avahi_free(e); +} + diff --git a/avahi/multicast-lookup.h b/avahi/multicast-lookup.h new file mode 100644 index 0000000..2707666 --- /dev/null +++ b/avahi/multicast-lookup.h @@ -0,0 +1,51 @@ +#ifndef foomulticastlookuphfoo +#define foomulticastlookuphfoo + +/*** + 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 "lookup.h" +#include "browse.h" + +typedef struct AvahiMulticastLookupEngine AvahiMulticastLookupEngine; +typedef struct AvahiMulticastLookup AvahiMulticastLookup; + +typedef void (*AvahiMulticastLookupCallback)( + AvahiMulticastLookupEngine *e, + AvahiIfIndex idx, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata); + +AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s); +void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e); + +unsigned avahi_multicast_lookup_engine_scan_cache(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); +void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i); +void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e); +void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event); + +AvahiMulticastLookup *avahi_multicast_lookup_new(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); +void avahi_multicast_lookup_free(AvahiMulticastLookup *q); + + +#endif + diff --git a/avahi/netlink.c b/avahi/netlink.c new file mode 100644 index 0000000..8ccca2e --- /dev/null +++ b/avahi/netlink.c @@ -0,0 +1,208 @@ +/*** + 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 "netlink.h" +#include "log.h" + +struct AvahiNetlink { + int fd; + unsigned seq; + AvahiNetlinkCallback callback; + void* userdata; + uint8_t* buffer; + size_t buffer_length; + + const AvahiPoll *poll_api; + AvahiWatch *watch; +}; + +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); + + iov.iov_base = nl->buffer; + iov.iov_len = nl->buffer_length; + + smsg.msg_name = 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__": recvmsg() failed: %s", strerror(errno)); + return -1; + } + + cmsg = CMSG_FIRSTHDR(&smsg); + + if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { + avahi_log_warn("No sender credentials received, ignoring data."); + return -1; + } + + cred = (struct ucred*) CMSG_DATA(cmsg); + + if (cred->uid != 0) + return -1; + + p = (struct nlmsghdr *) nl->buffer; + + assert(nl->callback); + + for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) { + if (!NLMSG_OK(p, (size_t) bytes)) { + avahi_log_warn(__FILE__": packet truncated"); + return -1; + } + + nl->callback(nl, p, nl->userdata); + } + + return 0; +} + +static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, void *userdata) { + AvahiNetlink *nl = userdata; + + assert(w); + assert(nl); + assert(fd == nl->fd); + + avahi_netlink_work(nl, 0); +} + +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; + + assert(poll_api); + assert(cb); + + if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + avahi_log_error(__FILE__": socket(PF_NETLINK): %s", strerror(errno)); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = groups; + addr.nl_pid = 0; // use 0 instead of getpid() to allow multiple instances of avahi in one process + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + avahi_log_error(__FILE__": bind(): %s", strerror(errno)); + goto fail; + } + + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { + avahi_log_error(__FILE__": SO_PASSCRED: %s", strerror(errno)); + goto fail; + } + + if (!(nl = avahi_new(AvahiNetlink, 1))) { + avahi_log_error(__FILE__": avahi_new() failed."); + goto fail; + } + + nl->poll_api = poll_api; + nl->fd = fd; + nl->seq = 0; + nl->callback = cb; + nl->userdata = userdata; + + if (!(nl->buffer = avahi_new(uint8_t, nl->buffer_length = 64*1024))) { + avahi_log_error(__FILE__": avahi_new() failed."); + goto fail; + } + + if (!(nl->watch = poll_api->watch_new(poll_api, fd, AVAHI_WATCH_IN, socket_event, nl))) { + avahi_log_error(__FILE__": Failed to create watch."); + goto fail; + } + + return nl; + +fail: + + if (fd >= 0) + close(fd); + + if (nl) { + avahi_free(nl->buffer); + avahi_free(nl); + } + + return NULL; +} + +void avahi_netlink_free(AvahiNetlink *nl) { + assert(nl); + + if (nl->watch) + nl->poll_api->watch_free(nl->watch); + + if (nl->fd >= 0) + close(nl->fd); + + avahi_free(nl->buffer); + avahi_free(nl); +} + +int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, unsigned *ret_seq) { + assert(nl); + assert(m); + + m->nlmsg_seq = nl->seq++; + m->nlmsg_flags |= NLM_F_ACK; + + if (send(nl->fd, m, m->nlmsg_len, 0) < 0) { + avahi_log_error(__FILE__": send(): %s", strerror(errno)); + return -1; + } + + if (ret_seq) + *ret_seq = m->nlmsg_seq; + + return 0; +} diff --git a/avahi/netlink.h b/avahi/netlink.h new file mode 100644 index 0000000..b11e0a4 --- /dev/null +++ b/avahi/netlink.h @@ -0,0 +1,41 @@ +#ifndef foonetlinkhfoo +#define foonetlinkhfoo + +/*** + 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 +#include + +#include +#include + +#include + +typedef struct AvahiNetlink AvahiNetlink; + +typedef void (*AvahiNetlinkCallback)(AvahiNetlink *n, struct nlmsghdr *m, void* userdata); + +AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, AvahiNetlinkCallback callback, void* userdata); +void avahi_netlink_free(AvahiNetlink *n); +int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, unsigned *ret_seq); +int avahi_netlink_work(AvahiNetlink *n, int block); + +#endif diff --git a/avahi/prioq-test.c b/avahi/prioq-test.c new file mode 100644 index 0000000..3315494 --- /dev/null +++ b/avahi/prioq-test.c @@ -0,0 +1,120 @@ +/*** + 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 "prioq.h" + +#define POINTER_TO_INT(p) ((int) (long) (p)) +#define INT_TO_POINTER(i) ((void*) (long) (i)) + +static int compare_int(const void* a, const void* b) { + int i = POINTER_TO_INT(a), j = POINTER_TO_INT(b); + + return i < j ? -1 : (i > j ? 1 : 0); +} + +static int compare_ptr(const void* a, const void* b) { + return a < b ? -1 : (a > b ? 1 : 0); +} + +static void rec(AvahiPrioQueueNode *n) { + if (!n) + return; + + if (n->left) + assert(n->left->parent == n); + + if (n->right) + assert(n->right->parent == n); + + if (n->parent) { + assert(n->parent->left == n || n->parent->right == n); + + if (n->parent->left == n) + assert(n->next == n->parent->right); + } + + if (!n->next) { + assert(n->queue->last == n); + + if (n->parent && n->parent->left == n) + assert(n->parent->right == NULL); + } + + + if (n->parent) { + int a = POINTER_TO_INT(n->parent->data), b = POINTER_TO_INT(n->data); + if (a > b) { + printf("%i <= %i: NO\n", a, b); + abort(); + } + } + + rec(n->left); + rec(n->right); +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + AvahiPrioQueue *q, *q2; + int i; + + q = avahi_prio_queue_new(compare_int); + q2 = avahi_prio_queue_new(compare_ptr); + + srand(time(NULL)); + + for (i = 0; i < 10000; i++) + avahi_prio_queue_put(q2, avahi_prio_queue_put(q, INT_TO_POINTER(random() & 0xFFFF))); + + while (q2->root) { + rec(q->root); + rec(q2->root); + + assert(q->n_nodes == q2->n_nodes); + + printf("%i\n", POINTER_TO_INT(((AvahiPrioQueueNode*)q2->root->data)->data)); + + avahi_prio_queue_remove(q, q2->root->data); + avahi_prio_queue_remove(q2, q2->root); + } + + +/* prev = 0; */ +/* while (q->root) { */ +/* int v = GPOINTER_TO_INT(q->root->data); */ +/* rec(q->root); */ +/* printf("%i\n", v); */ +/* avahi_prio_queue_remove(q, q->root); */ +/* assert(v >= prev); */ +/* prev = v; */ +/* } */ + + avahi_prio_queue_free(q); + return 0; +} diff --git a/avahi/prioq.c b/avahi/prioq.c new file mode 100644 index 0000000..e4558ee --- /dev/null +++ b/avahi/prioq.c @@ -0,0 +1,388 @@ +/*** + 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 "prioq.h" + +AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare) { + AvahiPrioQueue *q; + assert(compare); + + if (!(q = avahi_new(AvahiPrioQueue, 1))) + return NULL; /* OOM */ + + q->root = q->last = NULL; + q->n_nodes = 0; + q->compare = compare; + + return q; +} + +void avahi_prio_queue_free(AvahiPrioQueue *q) { + assert(q); + + while (q->last) + avahi_prio_queue_remove(q, q->last); + + assert(!q->n_nodes); + avahi_free(q); +} + +static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, unsigned x, unsigned y) { + unsigned r; + AvahiPrioQueueNode *n; + assert(q); + + n = q->root; + assert(n); + + for (r = 0; r < y; r++) { + assert(n); + + if ((x >> (y-r-1)) & 1) + n = n->right; + else + n = n->left; + } + + assert(n->x == x); + assert(n->y == y); + + return n; +} + +static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) { + AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn; + unsigned t; + assert(q); + assert(a); + assert(b); + assert(a != b); + + /* Swap positions */ + t = a->x; a->x = b->x; b->x = t; + t = a->y; a->y = b->y; b->y = t; + + if (a->parent == b) { + /* B is parent of A */ + + p = b->parent; + b->parent = a; + + if ((a->parent = p)) { + if (a->parent->left == b) + a->parent->left = a; + else + a->parent->right = a; + } else + q->root = a; + + if (b->left == a) { + if ((b->left = a->left)) + b->left->parent = b; + a->left = b; + + r = a->right; + if ((a->right = b->right)) + a->right->parent = a; + if ((b->right = r)) + b->right->parent = b; + + } else { + if ((b->right = a->right)) + b->right->parent = b; + a->right = b; + + l = a->left; + if ((a->left = b->left)) + a->left->parent = a; + if ((b->left = l)) + b->left->parent = b; + } + } else if (b->parent == a) { + /* A ist parent of B */ + + p = a->parent; + a->parent = b; + + if ((b->parent = p)) { + if (b->parent->left == a) + b->parent->left = b; + else + b->parent->right = b; + } else + q->root = b; + + if (a->left == b) { + if ((a->left = b->left)) + a->left->parent = a; + b->left = a; + + r = a->right; + if ((a->right = b->right)) + a->right->parent = a; + if ((b->right = r)) + b->right->parent = b; + } else { + if ((a->right = b->right)) + a->right->parent = a; + b->right = a; + + l = a->left; + if ((a->left = b->left)) + a->left->parent = a; + if ((b->left = l)) + b->left->parent = b; + } + } else { + AvahiPrioQueueNode *apl = NULL, *bpl = NULL; + + /* Swap parents */ + ap = a->parent; + bp = b->parent; + + if (ap) + apl = ap->left; + if (bp) + bpl = bp->left; + + if ((a->parent = bp)) { + if (bpl == b) + bp->left = a; + else + bp->right = a; + } else + q->root = a; + + if ((b->parent = ap)) { + if (apl == a) + ap->left = b; + else + ap->right = b; + } else + q->root = b; + + /* Swap children */ + l = a->left; + r = a->right; + + if ((a->left = b->left)) + a->left->parent = a; + + if ((b->left = l)) + b->left->parent = b; + + if ((a->right = b->right)) + a->right->parent = a; + + if ((b->right = r)) + b->right->parent = b; + } + + /* Swap siblings */ + ap = a->prev; an = a->next; + bp = b->prev; bn = b->next; + + if (a->next == b) { + /* A is predecessor of B */ + a->prev = b; + b->next = a; + + if ((a->next = bn)) + a->next->prev = a; + else + q->last = a; + + if ((b->prev = ap)) + b->prev->next = b; + + } else if (b->next == a) { + /* B is predecessor of A */ + a->next = b; + b->prev = a; + + if ((a->prev = bp)) + a->prev->next = a; + + if ((b->next = an)) + b->next->prev = b; + else + q->last = b; + + } else { + /* A is no neighbour of B */ + + if ((a->prev = bp)) + a->prev->next = a; + + if ((a->next = bn)) + a->next->prev = a; + else + q->last = a; + + if ((b->prev = ap)) + b->prev->next = b; + + if ((b->next = an)) + b->next->prev = b; + else + q->last = b; + } +} + +/* Move a node to the correct position */ +void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { + assert(q); + assert(n); + assert(n->queue == q); + + /* Move up until the position is OK */ + while (n->parent && q->compare(n->parent->data, n->data) > 0) + exchange_nodes(q, n, n->parent); + + /* Move down until the position is OK */ + for (;;) { + AvahiPrioQueueNode *min; + + if (!(min = n->left)) { + /* No children */ + assert(!n->right); + break; + } + + if (n->right && q->compare(n->right->data, min->data) < 0) + min = n->right; + + /* min now contains the smaller one of our two children */ + + if (q->compare(n->data, min->data) <= 0) + /* Order OK */ + break; + + exchange_nodes(q, n, min); + } +} + +AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data) { + AvahiPrioQueueNode *n; + assert(q); + + if (!(n = avahi_new(AvahiPrioQueueNode, 1))) + return NULL; /* OOM */ + + n->queue = q; + n->data = data; + + if (q->last) { + assert(q->root); + assert(q->n_nodes); + + n->y = q->last->y; + n->x = q->last->x+1; + + if (n->x >= ((unsigned) 1 << n->y)) { + n->x = 0; + n->y++; + } + + q->last->next = n; + n->prev = q->last; + + assert(n->y > 0); + n->parent = get_node_at_xy(q, n->x/2, n->y-1); + + if (n->x & 1) + n->parent->right = n; + else + n->parent->left = n; + } else { + assert(!q->root); + assert(!q->n_nodes); + + n->y = n->x = 0; + q->root = n; + n->prev = n->parent = NULL; + } + + n->next = n->left = n->right = NULL; + q->last = n; + q->n_nodes++; + + avahi_prio_queue_shuffle(q, n); + + return n; +} + +void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { + assert(q); + assert(n); + assert(q == n->queue); + + if (n != q->last) { + AvahiPrioQueueNode *replacement = q->last; + exchange_nodes(q, replacement, n); + avahi_prio_queue_remove(q, n); + avahi_prio_queue_shuffle(q, replacement); + return; + } + + assert(n == q->last); + assert(!n->next); + assert(!n->left); + assert(!n->right); + + q->last = n->prev; + + if (n->prev) { + n->prev->next = NULL; + assert(n->parent); + } else + assert(!n->parent); + + if (n->parent) { + assert(n->prev); + if (n->parent->left == n) { + assert(n->parent->right == NULL); + n->parent->left = NULL; + } else { + assert(n->parent->right == n); + assert(n->parent->left != NULL); + n->parent->right = NULL; + } + } else { + assert(q->root == n); + assert(!n->prev); + assert(q->n_nodes == 1); + q->root = NULL; + } + + avahi_free(n); + + assert(q->n_nodes > 0); + q->n_nodes--; +} + diff --git a/avahi/prioq.h b/avahi/prioq.h new file mode 100644 index 0000000..b3d31eb --- /dev/null +++ b/avahi/prioq.h @@ -0,0 +1,49 @@ +#ifndef fooprioqhfoo +#define fooprioqhfoo + +/*** + 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 struct AvahiPrioQueue AvahiPrioQueue; +typedef struct AvahiPrioQueueNode AvahiPrioQueueNode; + +typedef int (*AvahiPQCompareFunc)(const void* a, const void* b); + +struct AvahiPrioQueue { + AvahiPrioQueueNode *root, *last; + unsigned n_nodes; + AvahiPQCompareFunc compare; +}; + +struct AvahiPrioQueueNode { + AvahiPrioQueue *queue; + void* data; + unsigned x, y; + AvahiPrioQueueNode *left, *right, *parent, *next, *prev; +}; + +AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare); +void avahi_prio_queue_free(AvahiPrioQueue *q); + +AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data); +void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n); + +void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n); + +#endif diff --git a/avahi/probe-sched.c b/avahi/probe-sched.c new file mode 100644 index 0000000..4a13b19 --- /dev/null +++ b/avahi/probe-sched.c @@ -0,0 +1,397 @@ +/*** + 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 "probe-sched.h" +#include "log.h" +#include "rr-util.h" + +#define AVAHI_PROBE_HISTORY_MSEC 150 +#define AVAHI_PROBE_DEFER_MSEC 50 + +typedef struct AvahiProbeJob AvahiProbeJob; + +struct AvahiProbeJob { + AvahiProbeScheduler *scheduler; + AvahiTimeEvent *time_event; + + int chosen; /* Use for packet assembling */ + int done; + struct timeval delivery; + + AvahiRecord *record; + + AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs); +}; + +struct AvahiProbeScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + AVAHI_LLIST_HEAD(AvahiProbeJob, jobs); + AVAHI_LLIST_HEAD(AvahiProbeJob, history); +}; + +static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + if (!(pj = avahi_new(AvahiProbeJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + pj->scheduler = s; + pj->record = avahi_record_ref(record); + pj->time_event = NULL; + pj->chosen = 0; + + if ((pj->done = done)) + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); + else + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj); + + return pj; +} + +static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) { + assert(pj); + + if (pj->time_event) + avahi_time_event_free(pj->time_event); + + if (pj->done) + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj); + else + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); + + avahi_record_unref(pj->record); + avahi_free(pj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(pj); + + avahi_elapse_time(&tv, msec, jitter); + + if (pj->time_event) + avahi_time_event_update(pj->time_event, &tv); + else + pj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, pj); +} + +static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) { + assert(s); + assert(pj); + + assert(!pj->done); + + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); + + pj->done = 1; + + job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0); + gettimeofday(&pj->delivery, NULL); +} + +AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) { + AvahiProbeScheduler *s; + + assert(i); + + if (!(s = avahi_new(AvahiProbeScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history); + + return s; +} + +void avahi_probe_scheduler_free(AvahiProbeScheduler *s) { + assert(s); + + avahi_probe_scheduler_clear(s); + avahi_free(s); +} + +void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); +} + +static int packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) { + size_t size; + AvahiKey *k; + int b; + + assert(s); + assert(p); + assert(pj); + + assert(!pj->chosen); + + /* Estimate the size for this record */ + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record); + + /* Too large */ + if (size > avahi_dns_packet_space(p)) + return 0; + + /* Create the probe query */ + if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) + return 0; /* OOM */ + + b = !!avahi_dns_packet_append_key(p, k, 0); + assert(b); + + /* Mark this job for addition to the packet */ + pj->chosen = 1; + + /* Scan for more jobs whith matching key pattern */ + for (pj = s->jobs; pj; pj = pj->jobs_next) { + if (pj->chosen) + continue; + + /* Does the record match the probe? */ + if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name)) + continue; + + /* This job wouldn't fit in */ + if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p)) + break; + + /* Mark this job for addition to the packet */ + pj->chosen = 1; + } + + avahi_key_unref(k); + + return 1; +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiProbeJob *pj = data, *next; + AvahiProbeScheduler *s; + AvahiDnsPacket *p; + unsigned n; + + assert(pj); + s = pj->scheduler; + + if (pj->done) { + /* Lets remove it from the history */ + job_free(s, pj); + return; + } + + if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) + return; /* OOM */ + n = 1; + + /* Add the import probe */ + if (!packet_add_probe_query(s, p, pj)) { + size_t size; + AvahiKey *k; + int b; + + avahi_dns_packet_free(p); + + /* The probe didn't fit in the package, so let's allocate a larger one */ + + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record) + + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(p = avahi_dns_packet_new_query(size + AVAHI_DNS_PACKET_EXTRA_SIZE))) + return; /* OOM */ + + if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) { + avahi_dns_packet_free(p); + return; /* OOM */ + } + + b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0); + avahi_key_unref(k); + + if (b) { + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1); + avahi_interface_send_packet(s->interface, p); + } else + avahi_log_warn("Probe record too large, cannot send"); + + avahi_dns_packet_free(p); + job_mark_done(s, pj); + + return; + } + + /* Try to fill up packet with more probes, if available */ + for (pj = s->jobs; pj; pj = pj->jobs_next) { + + if (pj->chosen) + continue; + + if (!packet_add_probe_query(s, p, pj)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + n = 0; + + /* Now add the chosen records to the authorative section */ + for (pj = s->jobs; pj; pj = next) { + + next = pj->jobs_next; + + if (!pj->chosen) + continue; + + if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) { +/* avahi_log_warn("Bad probe size estimate!"); */ + + /* Unmark all following jobs */ + for (; pj; pj = pj->jobs_next) + pj->chosen = 0; + + break; + } + + job_mark_done(s, pj); + + n ++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); + + /* Send it now */ + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + for (pj = s->jobs; pj; pj = pj->jobs_next) { + assert(!pj->done); + + if (avahi_record_equal_no_ttl(pj->record, record)) + return pj; + } + + return NULL; +} + +static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + for (pj = s->history; pj; pj = pj->jobs_next) { + assert(pj->done); + + if (avahi_record_equal_no_ttl(pj->record, record)) { + /* Check whether this entry is outdated */ + + if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, pj); + return NULL; + } + + return pj; + } + } + + return NULL; +} + +int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately) { + AvahiProbeJob *pj; + struct timeval tv; + + assert(s); + assert(record); + assert(!avahi_key_is_pattern(record->key)); + + if ((pj = find_history_job(s, record))) + return 0; + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0); + + if ((pj = find_scheduled_job(s, record))) { + + if (avahi_timeval_compare(&tv, &pj->delivery) < 0) { + /* If the new entry should be scheduled earlier, update the old entry */ + pj->delivery = tv; + avahi_time_event_update(pj->time_event, &pj->delivery); + } + + return 1; + } else { + /* Create a new job and schedule it */ + if (!(pj = job_new(s, record, 0))) + return 0; /* OOM */ + + pj->delivery = tv; + pj->time_event = avahi_time_event_new(s->time_event_queue, &pj->delivery, elapse_callback, pj); + + +/* avahi_log_debug("Accepted new probe job."); */ + + return 1; + } +} diff --git a/avahi/probe-sched.h b/avahi/probe-sched.h new file mode 100644 index 0000000..4193df8 --- /dev/null +++ b/avahi/probe-sched.h @@ -0,0 +1,34 @@ +#ifndef fooprobeschedhfoo +#define fooprobeschedhfoo + +/*** + 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 struct AvahiProbeScheduler AvahiProbeScheduler; + +#include +#include "iface.h" + +AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i); +void avahi_probe_scheduler_free(AvahiProbeScheduler *s); +void avahi_probe_scheduler_clear(AvahiProbeScheduler *s); + +int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately); + +#endif diff --git a/avahi/publish.h b/avahi/publish.h new file mode 100644 index 0000000..d877249 --- /dev/null +++ b/avahi/publish.h @@ -0,0 +1,175 @@ +#ifndef foopublishhfoo +#define foopublishhfoo + +/*** + 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. +***/ + +/** \file core/publish.h Functions for publising local services and RRs */ + +/** \example core-publish-service.c Example how to register a DNS-SD + * service using an embedded mDNS stack. It behaves like a network + * printer registering both an IPP and a BSD LPR service. */ + +/** A group of locally registered DNS RRs */ +typedef struct AvahiSEntryGroup AvahiSEntryGroup; + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */ +typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); + +/** Iterate through all local entries of the server. (when g is NULL) + * or of a specified entry group. At the first call state should point + * to a NULL initialized void pointer, That pointer is used to track + * the current iteration. It is not safe to call any other + * avahi_server_xxx() function during the iteration. If the last entry + * has been read, NULL is returned. */ +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state); + +/** Create a new entry group. The specified callback function is + * called whenever the state of the group changes. Use entry group + * objects to keep track of you RRs. Add new RRs to a group using + * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit() + * to start the registration process for your RRs */ +AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata); + +/** Free an entry group. All RRs assigned to the group are removed from the server */ +void avahi_s_entry_group_free(AvahiSEntryGroup *g); + +/** Commit an entry group. This starts the probing and registration process for all RRs in the group */ +int avahi_s_entry_group_commit(AvahiSEntryGroup *g); + +/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */ +void avahi_s_entry_group_reset(AvahiSEntryGroup *g); + +/** Return 1 if the entry group is empty, i.e. has no records attached. */ +int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g); + +/** Return the current state of the specified entry group */ +AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g); + +/** Change the opaque user data pointer attached to an entry group object */ +void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata); + +/** Return the opaque user data pointer currently set for the entry group object */ +void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g); + +/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */ +int avahi_server_add( + AvahiServer *s, /**< The server object to add this record to */ + AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */ + AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */ + AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */ + AvahiPublishFlags flags, /**< Special flags for this record */ + AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */); + +/** Add an IP address mapping to the server. This will add both the + * host-name-to-address and the reverse mapping to the server. See + * avahi_server_add() for more information. If adding one of the RRs + * fails, the function returns with an error, but it is not defined if + * the other RR is deleted from the server or not. Therefore, you have + * to free the AvahiSEntryGroup and create a new one before + * proceeding. */ +int avahi_server_add_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + AvahiAddress *a); + +/** Add an DNS-SD service to the Server. This will add all required + * RRs to the server. See avahi_server_add() for more information. If + * adding one of the RRs fails, the function returns with an error, + * but it is not defined if the other RR is deleted from the server or + * not. Therefore, you have to free the AvahiSEntryGroup and create a + * new one before proceeding. */ +int avahi_server_add_service( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, /**< Service name, e.g. "Lennart's Files" */ + const char *type, /**< DNS-SD type, e.g. "_http._tcp" */ + const char *domain, + const char *host, /**< Host name where this servcie resides, or NULL if on the local host */ + uint16_t port, /**< Port number of the service */ + ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL; + +/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */ +int avahi_server_add_service_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst); + +/** Add a subtype for an already existing service */ +int avahi_server_add_service_subtype( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, /**< Specify the name of main service you already added here */ + const char *type, /**< Specify the main type of the service you already added here */ + const char *domain, /**< Specify the main type of the service you already added here */ + const char *subtype /**< The new subtype for the specified service */ ); + +/** Update the TXT record for a service with the data from the specified string list */ +int avahi_server_update_service_txt_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst); + +/** Update the TXT record for a service with the NULL termonate list of strings */ +int avahi_server_update_service_txt( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + ...) AVAHI_GCC_SENTINEL; + +/** Check if there is a service locally defined and return the entry group it is attached to. Returns NULL if the service isn't local*/ +int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/querier-test.c b/avahi/querier-test.c new file mode 100644 index 0000000..a6f16ac --- /dev/null +++ b/avahi/querier-test.c @@ -0,0 +1,122 @@ +/*** + 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 + +#define DOMAIN NULL +#define SERVICE_TYPE "_http._tcp" + +static AvahiSServiceBrowser *service_browser1 = NULL, *service_browser2 = NULL; +static const AvahiPoll * poll_api = NULL; +static AvahiServer *server = NULL; +static AvahiSimplePoll *simple_poll; + +static const char *browser_event_to_string(AvahiBrowserEvent event) { + switch (event) { + case AVAHI_BROWSER_NEW : return "NEW"; + case AVAHI_BROWSER_REMOVE : return "REMOVE"; + case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED"; + case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW"; + case AVAHI_BROWSER_FAILURE : return "FAILURE"; + } + + abort(); +} + +static void sb_callback( + AvahiSServiceBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *service_type, + const char *domain, + AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + avahi_log_debug("SB%i: (%i.%s) <%s> as <%s> in <%s> [%s] cached=%i", b == service_browser1 ? 1 : 2, iface, avahi_proto_to_string(protocol), name, service_type, domain, browser_event_to_string(event), !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); +} + +static void create_second_service_browser(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void* userdata) { + + service_browser2 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL); + assert(service_browser2); + + poll_api->timeout_free(timeout); +} + +static void quit(AVAHI_GCC_UNUSED AvahiTimeout *timeout, AVAHI_GCC_UNUSED void *userdata) { + avahi_simple_poll_quit(simple_poll); +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + struct timeval tv; + AvahiServerConfig config; + + simple_poll = avahi_simple_poll_new(); + assert(simple_poll); + + poll_api = avahi_simple_poll_get(simple_poll); + assert(poll_api); + + avahi_server_config_init(&config); + config.publish_hinfo = 0; + config.publish_addresses = 0; + config.publish_workstation = 0; + config.publish_domain = 0; + + avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); + config.n_wide_area_servers = 1; + config.enable_wide_area = 1; + + server = avahi_server_new(poll_api, &config, NULL, NULL, NULL); + assert(server); + avahi_server_config_free(&config); + + service_browser1 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL); + assert(service_browser1); + + poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 10000, 0), create_second_service_browser, NULL); + + poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 60000, 0), quit, NULL); + + + for (;;) + if (avahi_simple_poll_iterate(simple_poll, -1) != 0) + break; + + avahi_server_free(server); + avahi_simple_poll_free(simple_poll); + + return 0; +} diff --git a/avahi/querier.c b/avahi/querier.c new file mode 100644 index 0000000..f4666b6 --- /dev/null +++ b/avahi/querier.c @@ -0,0 +1,268 @@ +/*** + 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 "querier.h" +#include "log.h" + +struct AvahiQuerier { + AvahiInterface *interface; + + AvahiKey *key; + int n_used; + + unsigned sec_delay; + + AvahiTimeEvent *time_event; + + struct timeval creation_time; + + unsigned post_id; + int post_id_valid; + + AVAHI_LLIST_FIELDS(AvahiQuerier, queriers); +}; + +void avahi_querier_free(AvahiQuerier *q) { + assert(q); + + AVAHI_LLIST_REMOVE(AvahiQuerier, queriers, q->interface->queriers, q); + avahi_hashmap_remove(q->interface->queriers_by_key, q->key); + + avahi_key_unref(q->key); + avahi_time_event_free(q->time_event); + + avahi_free(q); +} + +static void querier_elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { + AvahiQuerier *q = userdata; + struct timeval tv; + + assert(q); + + if (q->n_used <= 0) { + + /* We are not referenced by anyone anymore, so let's free + * ourselves. We should not send out any further queries from + * this querier object anymore. */ + + avahi_querier_free(q); + return; + } + + if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) { + + /* The queue accepted our query. We store the query id here, + * that allows us to drop the query at a later point if the + * query is very short-lived. */ + + q->post_id_valid = 1; + } + + q->sec_delay *= 2; + + if (q->sec_delay >= 60*60) /* 1h */ + q->sec_delay = 60*60; + + avahi_elapse_time(&tv, q->sec_delay*1000, 0); + avahi_time_event_update(q->time_event, &tv); +} + +void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime) { + AvahiQuerier *q; + struct timeval tv; + + assert(i); + assert(key); + + if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) { + + /* Someone is already browsing for records of this RR key */ + q->n_used++; + + /* Return the creation time. This is used for generating the + * ALL_FOR_NOW event one second after the querier was + * initially created. */ + if (ret_ctime) + *ret_ctime = q->creation_time; + return; + } + + /* No one is browsing for this RR key, so we add a new querier */ + if (!(q = avahi_new(AvahiQuerier, 1))) + return; /* OOM */ + + q->key = avahi_key_ref(key); + q->interface = i; + q->n_used = 1; + q->sec_delay = 1; + q->post_id_valid = 0; + gettimeofday(&q->creation_time, NULL); + + /* Do the initial query */ + if (avahi_interface_post_query(i, key, 0, &q->post_id)) + q->post_id_valid = 1; + + /* Schedule next queries */ + q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q); + + AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q); + avahi_hashmap_insert(i->queriers_by_key, q->key, q); + + /* Return the creation time. This is used for generating the + * ALL_FOR_NOW event one second after the querier was initially + * created. */ + if (ret_ctime) + *ret_ctime = q->creation_time; +} + +void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) { + AvahiQuerier *q; + + /* There was no querier for this RR key, or it wasn't referenced + * by anyone. */ + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0) + return; + + if ((--q->n_used) <= 0) { + + /* Nobody references us anymore. */ + + if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) { + + /* We succeeded in withdrawing our query from the queue, + * so let's drop dead. */ + + avahi_querier_free(q); + } + + /* If we failed to withdraw our query from the queue, we stay + * alive, in case someone else might recycle our querier at a + * later point. We are freed at our next expiry, in case + * nobody recycled us. */ + } +} + +static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + assert(m); + assert(i); + assert(userdata); + + if (i->announcing) + avahi_querier_remove(i, (AvahiKey*) userdata); +} + +void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key) { + assert(s); + assert(key); + + avahi_interface_monitor_walk(s->monitor, idx, protocol, remove_querier_callback, key); +} + +struct cbdata { + AvahiKey *key; + struct timeval *ret_ctime; +}; + +static void add_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(m); + assert(i); + assert(cbdata); + + if (i->announcing) { + struct timeval tv; + avahi_querier_add(i, cbdata->key, &tv); + + if (cbdata->ret_ctime && avahi_timeval_compare(&tv, cbdata->ret_ctime) > 0) + *cbdata->ret_ctime = tv; + } +} + +void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime) { + struct cbdata cbdata; + + assert(s); + assert(key); + + cbdata.key = key; + cbdata.ret_ctime = ret_ctime; + + if (ret_ctime) + ret_ctime->tv_sec = ret_ctime->tv_usec = 0; + + avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata); +} + +int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) { + AvahiQuerier *q; + + assert(i); + assert(key); + + /* Called by the cache maintainer */ + + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) + /* This key is currently not subscribed at all, so no cache + * refresh is needed */ + return 0; + + if (q->n_used <= 0) { + + /* If this is an entry nobody references right now, don't + * consider it "existing". */ + + /* Remove this querier since it is referenced by nobody + * and the cached data will soon be out of date */ + avahi_querier_free(q); + + /* Tell the cache that no refresh is needed */ + return 0; + + } else { + struct timeval tv; + + /* We can defer our query a little, since the cache will now + * issue a refresh query anyway. */ + avahi_elapse_time(&tv, q->sec_delay*1000, 0); + avahi_time_event_update(q->time_event, &tv); + + /* Tell the cache that a refresh should be issued */ + return 1; + } +} + +void avahi_querier_free_all(AvahiInterface *i) { + assert(i); + + while (i->queriers) + avahi_querier_free(i->queriers); +} diff --git a/avahi/querier.h b/avahi/querier.h new file mode 100644 index 0000000..6a32a3b --- /dev/null +++ b/avahi/querier.h @@ -0,0 +1,48 @@ +#ifndef fooquerierhfoo +#define fooquerierhfoo + +/*** + 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 struct AvahiQuerier AvahiQuerier; + +#include "iface.h" + +/** Add querier for the specified key to the specified interface */ +void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime); + +/** Remove a querier for the specified key from the specified interface */ +void avahi_querier_remove(AvahiInterface *i, AvahiKey *key); + +/** Add a querier for the specified key on all interfaces that mach */ +void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime); + +/** Remove a querier for the specified key on all interfaces that mach */ +void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key); + +/** Free all queriers */ +void avahi_querier_free(AvahiQuerier *q); + +/** Free all queriers on the specified interface */ +void avahi_querier_free_all(AvahiInterface *i); + +/** Return 1 if there is a querier for the specified key on the specified interface */ +int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key); + +#endif diff --git a/avahi/query-sched.c b/avahi/query-sched.c new file mode 100644 index 0000000..9c0e7e1 --- /dev/null +++ b/avahi/query-sched.c @@ -0,0 +1,450 @@ +/*** + 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 "query-sched.h" +#include "log.h" + +#define AVAHI_QUERY_HISTORY_MSEC 100 +#define AVAHI_QUERY_DEFER_MSEC 100 + +typedef struct AvahiQueryJob AvahiQueryJob; +typedef struct AvahiKnownAnswer AvahiKnownAnswer; + +struct AvahiQueryJob { + unsigned id; + int n_posted; + + AvahiQueryScheduler *scheduler; + AvahiTimeEvent *time_event; + + int done; + struct timeval delivery; + + AvahiKey *key; + + /* Jobs are stored in a simple linked list. It might turn out in + * the future that this list grows too long and we must switch to + * some other kind of data structure. This needs further + * investigation. I expect the list to be very short (< 20 + * entries) most of the time, but this might be a wrong + * assumption, especially on setups where traffic reflection is + * involved. */ + + AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs); +}; + +struct AvahiKnownAnswer { + AvahiQueryScheduler *scheduler; + AvahiRecord *record; + + AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer); +}; + +struct AvahiQueryScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + unsigned next_id; + + AVAHI_LLIST_HEAD(AvahiQueryJob, jobs); + AVAHI_LLIST_HEAD(AvahiQueryJob, history); + AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers); +}; + +static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + if (!(qj = avahi_new(AvahiQueryJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + qj->scheduler = s; + qj->key = avahi_key_ref(key); + qj->time_event = NULL; + qj->n_posted = 1; + qj->id = s->next_id++; + + if ((qj->done = done)) + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); + else + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->jobs, qj); + + return qj; +} + +static void job_free(AvahiQueryScheduler *s, AvahiQueryJob *qj) { + assert(s); + assert(qj); + + if (qj->time_event) + avahi_time_event_free(qj->time_event); + + if (qj->done) + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->history, qj); + else + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj); + + avahi_key_unref(qj->key); + avahi_free(qj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiQueryScheduler *s, AvahiQueryJob *qj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(qj); + + avahi_elapse_time(&tv, msec, jitter); + + if (qj->time_event) + avahi_time_event_update(qj->time_event, &tv); + else + qj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, qj); +} + +static void job_mark_done(AvahiQueryScheduler *s, AvahiQueryJob *qj) { + assert(s); + assert(qj); + + assert(!qj->done); + + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj); + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); + + qj->done = 1; + + job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); + gettimeofday(&qj->delivery, NULL); +} + +AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) { + AvahiQueryScheduler *s; + assert(i); + + if (!(s = avahi_new(AvahiQueryScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + s->next_id = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history); + AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers); + + return s; +} + +void avahi_query_scheduler_free(AvahiQueryScheduler *s) { + assert(s); + + assert(!s->known_answers); + avahi_query_scheduler_clear(s); + avahi_free(s); +} + +void avahi_query_scheduler_clear(AvahiQueryScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); +} + +static void* known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + AvahiQueryScheduler *s = userdata; + AvahiKnownAnswer *ka; + + assert(c); + assert(pattern); + assert(e); + assert(s); + + if (avahi_cache_entry_half_ttl(c, e)) + return NULL; + + if (!(ka = avahi_new0(AvahiKnownAnswer, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + ka->scheduler = s; + ka->record = avahi_record_ref(e->record); + + AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka); + return NULL; +} + +static int packet_add_query_job(AvahiQueryScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) { + assert(s); + assert(p); + assert(qj); + + if (!avahi_dns_packet_append_key(p, qj->key, 0)) + return 0; + + /* Add all matching known answers to the list */ + avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s); + + job_mark_done(s, qj); + + return 1; +} + +static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) { + AvahiKnownAnswer *ka; + unsigned n; + assert(s); + assert(p); + + n = 0; + + while ((ka = s->known_answers)) { + int too_large = 0; + + while (!avahi_dns_packet_append_record(p, ka->record, 0, 0)) { + + if (avahi_dns_packet_is_empty(p)) { + /* The record is too large to fit into one packet, so + there's no point in sending it. Better is letting + the owner of the record send it as a response. This + has the advantage of a cache refresh. */ + + too_large = 1; + break; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); + + p = avahi_dns_packet_new_query(s->interface->hardware->mtu); + n = 0; + } + + AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka); + avahi_record_unref(ka->record); + avahi_free(ka); + + if (!too_large) + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiQueryJob *qj = data; + AvahiQueryScheduler *s; + AvahiDnsPacket *p; + unsigned n; + int b; + + assert(qj); + s = qj->scheduler; + + if (qj->done) { + /* Lets remove it from the history */ + job_free(s, qj); + return; + } + + assert(!s->known_answers); + + if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) + return; /* OOM */ + + b = packet_add_query_job(s, p, qj); + assert(b); /* An query must always fit in */ + n = 1; + + /* Try to fill up packet with more queries, if available */ + while (s->jobs) { + + if (!packet_add_query_job(s, p, s->jobs)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + /* Now add known answers */ + append_known_answers_and_send(s, p); +} + +static AvahiQueryJob* find_scheduled_job(AvahiQueryScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + for (qj = s->jobs; qj; qj = qj->jobs_next) { + assert(!qj->done); + + if (avahi_key_equal(qj->key, key)) + return qj; + } + + return NULL; +} + +static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + for (qj = s->history; qj; qj = qj->jobs_next) { + assert(qj->done); + + if (avahi_key_equal(qj->key, key)) { + /* Check whether this entry is outdated */ + + if (avahi_age(&qj->delivery) > AVAHI_QUERY_HISTORY_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, qj); + return NULL; + } + + return qj; + } + } + + return NULL; +} + +int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) { + struct timeval tv; + AvahiQueryJob *qj; + + assert(s); + assert(key); + + if ((qj = find_history_job(s, key))) + return 0; + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0); + + if ((qj = find_scheduled_job(s, key))) { + /* Duplicate questions suppression */ + + if (avahi_timeval_compare(&tv, &qj->delivery) < 0) { + /* If the new entry should be scheduled earlier, + * update the old entry */ + qj->delivery = tv; + avahi_time_event_update(qj->time_event, &qj->delivery); + } + + qj->n_posted++; + + } else { + + if (!(qj = job_new(s, key, 0))) + return 0; /* OOM */ + + qj->delivery = tv; + qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj); + } + + if (ret_id) + *ret_id = qj->id; + + return 1; +} + +void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + /* This function is called whenever an incoming query was + * received. We drop scheduled queries that match. The keyword is + * "DUPLICATE QUESTION SUPPRESION". */ + + if ((qj = find_scheduled_job(s, key))) { + job_mark_done(s, qj); + return; + } + + /* Look if there's a history job for this key. If there is, just + * update the elapse time */ + if (!(qj = find_history_job(s, key))) + if (!(qj = job_new(s, key, 1))) + return; /* OOM */ + + gettimeofday(&qj->delivery, NULL); + job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); +} + +int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) { + AvahiQueryJob *qj; + + assert(s); + + /* Very short lived queries can withdraw an already scheduled item + * from the queue using this function, simply by passing the id + * returned by avahi_query_scheduler_post(). */ + + for (qj = s->jobs; qj; qj = qj->jobs_next) { + assert(!qj->done); + + if (qj->id == id) { + /* Entry found */ + + assert(qj->n_posted >= 1); + + if (--qj->n_posted <= 0) { + + /* We withdraw this job only if the calling object was + * the only remaining poster. (Usually this is the + * case since there should exist only one querier per + * key, but there are exceptions, notably reflected + * traffic.) */ + + job_free(s, qj); + return 1; + } + } + } + + return 0; +} diff --git a/avahi/query-sched.h b/avahi/query-sched.h new file mode 100644 index 0000000..9dfe3b5 --- /dev/null +++ b/avahi/query-sched.h @@ -0,0 +1,36 @@ +#ifndef fooqueryschedhfoo +#define fooqueryschedhfoo + +/*** + 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 struct AvahiQueryScheduler AvahiQueryScheduler; + +#include +#include "iface.h" + +AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i); +void avahi_query_scheduler_free(AvahiQueryScheduler *s); +void avahi_query_scheduler_clear(AvahiQueryScheduler *s); + +int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id); +int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id); +void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key); + +#endif diff --git a/avahi/resolve-address.c b/avahi/resolve-address.c new file mode 100644 index 0000000..f24bee9 --- /dev/null +++ b/avahi/resolve-address.c @@ -0,0 +1,268 @@ +/*** + 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 "browse.h" + +#define TIMEOUT_MSEC 5000 + +struct AvahiSAddressResolver { + AvahiServer *server; + AvahiAddress address; + + AvahiSRecordBrowser *record_browser; + + AvahiSAddressResolverCallback callback; + void* userdata; + + AvahiRecord *ptr_record; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupResultFlags flags; + + int retry_with_multicast; + AvahiKey *key; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver); +}; + +static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) { + assert(r); + + if (r->time_event) { + avahi_time_event_free(r->time_event); + r->time_event = NULL; + } + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata); + break; + + case AVAHI_RESOLVER_FOUND: + assert(r->ptr_record); + r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata); + break; + } +} + +static void time_event_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSAddressResolver *r = userdata; + + assert(e); + assert(r); + + avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); + finish(r, AVAHI_RESOLVER_FAILURE); +} + +static void start_timeout(AvahiSAddressResolver *r) { + struct timeval tv; + assert(r); + + if (r->time_event) + return; + + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSAddressResolver *r = userdata; + + assert(rr); + assert(r); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (r->interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + if (!r->ptr_record) { + r->ptr_record = avahi_record_ref(record); + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FOUND); + } + break; + + case AVAHI_BROWSER_REMOVE: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { + avahi_record_unref(r->ptr_record); + r->ptr_record = NULL; + r->flags = flags; + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser); + start_timeout(r); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; + + case AVAHI_BROWSER_FAILURE: + + if (r->retry_with_multicast) { + r->retry_with_multicast = 0; + + avahi_s_record_browser_free(r->record_browser); + r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r); + + if (r->record_browser) { + start_timeout(r); + break; + } + } + + r->flags = flags; + finish(r, AVAHI_RESOLVER_FAILURE); + break; + } +} + +AvahiSAddressResolver *avahi_s_address_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *address, + AvahiLookupFlags flags, + AvahiSAddressResolverCallback callback, + void* userdata) { + + AvahiSAddressResolver *r; + AvahiKey *k; + char n[AVAHI_DOMAIN_NAME_MAX]; + + assert(server); + assert(address); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + avahi_reverse_lookup_name(address, n, sizeof(n)); + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + if (!(r = avahi_new(AvahiSAddressResolver, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + avahi_key_unref(k); + return NULL; + } + + r->server = server; + r->address = *address; + r->callback = callback; + r->userdata = userdata; + r->ptr_record = NULL; + r->interface = interface; + r->protocol = protocol; + r->flags = 0; + r->retry_with_multicast = 0; + r->key = k; + + r->record_browser = NULL; + AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r); + + r->time_event = NULL; + + if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) { + + if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine)) + flags |= AVAHI_LOOKUP_USE_MULTICAST; + else { + flags |= AVAHI_LOOKUP_USE_WIDE_AREA; + r->retry_with_multicast = 1; + } + } + + r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); + + if (!r->record_browser) { + avahi_s_address_resolver_free(r); + return NULL; + } + + start_timeout(r); + + return r; +} + +void avahi_s_address_resolver_free(AvahiSAddressResolver *r) { + assert(r); + + AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r); + + if (r->record_browser) + avahi_s_record_browser_free(r->record_browser); + + if (r->time_event) + avahi_time_event_free(r->time_event); + + if (r->ptr_record) + avahi_record_unref(r->ptr_record); + + if (r->key) + avahi_key_unref(r->key); + + avahi_free(r); +} diff --git a/avahi/resolve-host-name.c b/avahi/resolve-host-name.c new file mode 100644 index 0000000..a577d48 --- /dev/null +++ b/avahi/resolve-host-name.c @@ -0,0 +1,297 @@ +/*** + 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 "browse.h" +#include "log.h" + +#define TIMEOUT_MSEC 5000 + +struct AvahiSHostNameResolver { + AvahiServer *server; + char *host_name; + + AvahiSRecordBrowser *record_browser_a; + AvahiSRecordBrowser *record_browser_aaaa; + + AvahiSHostNameResolverCallback callback; + void* userdata; + + AvahiRecord *address_record; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupResultFlags flags; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiSHostNameResolver, resolver); +}; + +static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) { + assert(r); + + if (r->time_event) { + avahi_time_event_free(r->time_event); + r->time_event = NULL; + } + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + AvahiAddress a; + + assert(r->address_record); + + switch (r->address_record->key->type) { + case AVAHI_DNS_TYPE_A: + a.proto = AVAHI_PROTO_INET; + a.data.ipv4 = r->address_record->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a.proto = AVAHI_PROTO_INET6; + a.data.ipv6 = r->address_record->data.aaaa.address; + break; + + default: + abort(); + } + + r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->flags, r->userdata); + break; + + } + + case AVAHI_RESOLVER_FAILURE: + + r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata); + break; + } +} + +static void time_event_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSHostNameResolver *r = userdata; + + assert(e); + assert(r); + + avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); + finish(r, AVAHI_RESOLVER_FAILURE); +} + +static void start_timeout(AvahiSHostNameResolver *r) { + struct timeval tv; + assert(r); + + if (r->time_event) + return; + + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSHostNameResolver *r = userdata; + + assert(rr); + assert(r); + + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); + + if (r->interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + if (!r->address_record) { + r->address_record = avahi_record_ref(record); + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FOUND); + } + + break; + + case AVAHI_BROWSER_REMOVE: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); + + if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { + avahi_record_unref(r->address_record); + r->address_record = NULL; + + r->flags = flags; + + + /** Look for a replacement */ + if (r->record_browser_aaaa) + avahi_s_record_browser_restart(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_restart(r->record_browser_a); + + start_timeout(r); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + /* Ignore */ + break; + + case AVAHI_BROWSER_FAILURE: + + /* Stop browsers */ + + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + + r->record_browser_a = r->record_browser_aaaa = NULL; + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FAILURE); + break; + } +} + +AvahiSHostNameResolver *avahi_s_host_name_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *host_name, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSHostNameResolverCallback callback, + void* userdata) { + + AvahiSHostNameResolver *r; + AvahiKey *k; + + assert(server); + assert(host_name); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_fqdn(host_name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!(r = avahi_new(AvahiSHostNameResolver, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->server = server; + r->host_name = avahi_normalize_name_strdup(host_name); + r->callback = callback; + r->userdata = userdata; + r->address_record = NULL; + r->interface = interface; + r->protocol = protocol; + r->flags = 0; + + r->record_browser_a = r->record_browser_aaaa = NULL; + + r->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiSHostNameResolver, resolver, server->host_name_resolvers, r); + + r->record_browser_aaaa = r->record_browser_a = NULL; + + if (aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_UNSPEC) { + k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); + r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_a) + goto fail; + } + + if (aprotocol == AVAHI_PROTO_INET6 || aprotocol == AVAHI_PROTO_UNSPEC) { + k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); + r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_aaaa) + goto fail; + } + + assert(r->record_browser_aaaa || r->record_browser_a); + + start_timeout(r); + + return r; + +fail: + avahi_s_host_name_resolver_free(r); + return NULL; +} + +void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r) { + assert(r); + + AVAHI_LLIST_REMOVE(AvahiSHostNameResolver, resolver, r->server->host_name_resolvers, r); + + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + + if (r->time_event) + avahi_time_event_free(r->time_event); + + if (r->address_record) + avahi_record_unref(r->address_record); + + avahi_free(r->host_name); + avahi_free(r); +} diff --git a/avahi/resolve-service.c b/avahi/resolve-service.c new file mode 100644 index 0000000..eaac8bb --- /dev/null +++ b/avahi/resolve-service.c @@ -0,0 +1,489 @@ +/*** + 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 "browse.h" +#include "log.h" + +#define TIMEOUT_MSEC 5000 + +struct AvahiSServiceResolver { + AvahiServer *server; + char *service_name; + char *service_type; + char *domain_name; + AvahiProtocol address_protocol; + + AvahiIfIndex interface; + AvahiProtocol protocol; + + AvahiSRecordBrowser *record_browser_srv; + AvahiSRecordBrowser *record_browser_txt; + AvahiSRecordBrowser *record_browser_a; + AvahiSRecordBrowser *record_browser_aaaa; + + AvahiRecord *srv_record, *txt_record, *address_record; + AvahiLookupResultFlags srv_flags, txt_flags, address_flags; + + AvahiSServiceResolverCallback callback; + void* userdata; + AvahiLookupFlags user_flags; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver); +}; + +static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) { + AvahiLookupResultFlags flags; + + assert(r); + + if (r->time_event) { + avahi_time_event_free(r->time_event); + r->time_event = NULL; + } + + flags = + r->txt_flags | + r->srv_flags | + r->address_flags; + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + + r->callback( + r, + r->interface, + r->protocol, + event, + r->service_name, + r->service_type, + r->domain_name, + NULL, + NULL, + 0, + NULL, + flags, + r->userdata); + + break; + + case AVAHI_RESOLVER_FOUND: { + AvahiAddress a; + + assert(event == AVAHI_RESOLVER_FOUND); + + assert(r->srv_record); + + if (r->address_record) { + switch (r->address_record->key->type) { + case AVAHI_DNS_TYPE_A: + a.proto = AVAHI_PROTO_INET; + a.data.ipv4 = r->address_record->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a.proto = AVAHI_PROTO_INET6; + a.data.ipv6 = r->address_record->data.aaaa.address; + break; + + default: + assert(0); + } + } + + r->callback( + r, + r->interface, + r->protocol, + event, + r->service_name, + r->service_type, + r->domain_name, + r->srv_record->data.srv.name, + r->address_record ? &a : NULL, + r->srv_record->data.srv.port, + r->txt_record ? r->txt_record->data.txt.string_list : NULL, + flags, + r->userdata); + + break; + } + } +} + +static void time_event_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSServiceResolver *r = userdata; + + assert(e); + assert(r); + + avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); + finish(r, AVAHI_RESOLVER_FAILURE); +} + +static void start_timeout(AvahiSServiceResolver *r) { + struct timeval tv; + assert(r); + + if (r->time_event) + return; + + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSServiceResolver *r = userdata; + + assert(rr); + assert(r); + + if (rr == r->record_browser_aaaa || rr == r->record_browser_a) + r->address_flags = flags; + else if (rr == r->record_browser_srv) + r->srv_flags = flags; + else if (rr == r->record_browser_txt) + r->txt_flags = flags; + + switch (event) { + + case AVAHI_BROWSER_NEW: { + int changed = 0; + assert(record); + + if (r->interface > 0 && interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + switch (record->key->type) { + case AVAHI_DNS_TYPE_SRV: + if (!r->srv_record) { + r->srv_record = avahi_record_ref(record); + changed = 1; + + if (r->record_browser_a) { + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + } + + if (r->record_browser_aaaa) { + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + } + + if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) { + + if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) { + AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); + r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + } + + if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) { + AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); + r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + } + } + } + break; + + case AVAHI_DNS_TYPE_TXT: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); + + if (!r->txt_record) { + r->txt_record = avahi_record_ref(record); + changed = 1; + } + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); + + if (!r->address_record) { + r->address_record = avahi_record_ref(record); + changed = 1; + } + break; + + default: + abort(); + } + + + if (changed && + r->srv_record && + (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) && + (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS))) + finish(r, AVAHI_RESOLVER_FOUND); + + break; + + } + + case AVAHI_BROWSER_REMOVE: + + assert(record); + + switch (record->key->type) { + case AVAHI_DNS_TYPE_SRV: + + if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) { + avahi_record_unref(r->srv_record); + r->srv_record = NULL; + + if (r->record_browser_a) { + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + } + + if (r->record_browser_aaaa) { + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + } + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser_srv); + start_timeout(r); + } + + break; + + case AVAHI_DNS_TYPE_TXT: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); + + if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) { + avahi_record_unref(r->txt_record); + r->txt_record = NULL; + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser_txt); + start_timeout(r); + } + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); + + if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { + avahi_record_unref(r->address_record); + r->address_record = NULL; + + /** Look for a replacement */ + if (r->record_browser_aaaa) + avahi_s_record_browser_restart(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_restart(r->record_browser_a); + start_timeout(r); + } + break; + + default: + abort(); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; + + case AVAHI_BROWSER_FAILURE: + + if (rr == r->record_browser_a && r->record_browser_aaaa) { + /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + break; + } + + if (rr == r->record_browser_aaaa && r->record_browser_a) { + /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + break; + } + + /* Hmm, everything's lost, tell the user */ + + if (r->record_browser_srv) + avahi_s_record_browser_free(r->record_browser_srv); + if (r->record_browser_txt) + avahi_s_record_browser_free(r->record_browser_txt); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + + r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL; + + finish(r, AVAHI_RESOLVER_FAILURE); + break; + } +} + +AvahiSServiceResolver *avahi_s_service_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSServiceResolverCallback callback, + void* userdata) { + + AvahiSServiceResolver *r; + AvahiKey *k; + char n[AVAHI_DOMAIN_NAME_MAX]; + int ret; + + assert(server); + assert(type); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !name || avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain)) < 0) { + avahi_server_set_errno(server, ret); + return NULL; + } + + if (!(r = avahi_new(AvahiSServiceResolver, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->server = server; + r->service_name = avahi_strdup(name); + r->service_type = avahi_normalize_name_strdup(type); + r->domain_name = avahi_normalize_name_strdup(domain); + r->callback = callback; + r->userdata = userdata; + r->address_protocol = aprotocol; + r->srv_record = r->txt_record = r->address_record = NULL; + r->srv_flags = r->txt_flags = r->address_flags = 0; + r->interface = interface; + r->protocol = protocol; + r->user_flags = flags; + r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL; + r->time_event = NULL; + AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r); + + k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); + r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_srv) { + avahi_s_service_resolver_free(r); + return NULL; + } + + if (!(flags & AVAHI_LOOKUP_NO_TXT)) { + k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); + r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_txt) { + avahi_s_service_resolver_free(r); + return NULL; + } + } + + start_timeout(r); + + return r; +} + +void avahi_s_service_resolver_free(AvahiSServiceResolver *r) { + assert(r); + + AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r); + + if (r->time_event) + avahi_time_event_free(r->time_event); + + if (r->record_browser_srv) + avahi_s_record_browser_free(r->record_browser_srv); + if (r->record_browser_txt) + avahi_s_record_browser_free(r->record_browser_txt); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + + if (r->srv_record) + avahi_record_unref(r->srv_record); + if (r->txt_record) + avahi_record_unref(r->txt_record); + if (r->address_record) + avahi_record_unref(r->address_record); + + avahi_free(r->service_name); + avahi_free(r->service_type); + avahi_free(r->domain_name); + avahi_free(r); +} diff --git a/avahi/response-sched.c b/avahi/response-sched.c new file mode 100644 index 0000000..1c8887d --- /dev/null +++ b/avahi/response-sched.c @@ -0,0 +1,511 @@ +/*** + 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 "response-sched.h" +#include "log.h" +#include "rr-util.h" + +/* Local packets are supressed this long after sending them */ +#define AVAHI_RESPONSE_HISTORY_MSEC 500 + +/* Local packets are deferred this long before sending them */ +#define AVAHI_RESPONSE_DEFER_MSEC 20 + +/* Additional jitter for deferred packets */ +#define AVAHI_RESPONSE_JITTER_MSEC 100 + +/* Remote packets can suppress local traffic as long as this value */ +#define AVAHI_RESPONSE_SUPPRESS_MSEC 700 + +typedef struct AvahiResponseJob AvahiResponseJob; + +typedef enum { + AVAHI_SCHEDULED, + AVAHI_DONE, + AVAHI_SUPPRESSED +} AvahiResponseJobState; + +struct AvahiResponseJob { + AvahiResponseScheduler *scheduler; + AvahiTimeEvent *time_event; + + AvahiResponseJobState state; + struct timeval delivery; + + AvahiRecord *record; + int flush_cache; + AvahiAddress querier; + int querier_valid; + + AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs); +}; + +struct AvahiResponseScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + AVAHI_LLIST_HEAD(AvahiResponseJob, jobs); + AVAHI_LLIST_HEAD(AvahiResponseJob, history); + AVAHI_LLIST_HEAD(AvahiResponseJob, suppressed); +}; + +static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + + if (!(rj = avahi_new(AvahiResponseJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + rj->scheduler = s; + rj->record = avahi_record_ref(record); + rj->time_event = NULL; + rj->flush_cache = 0; + rj->querier_valid = 0; + + if ((rj->state = state) == AVAHI_SCHEDULED) + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->jobs, rj); + else if (rj->state == AVAHI_DONE) + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj); + else /* rj->state == AVAHI_SUPPRESSED */ + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->suppressed, rj); + + return rj; +} + +static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) { + assert(s); + assert(rj); + + if (rj->time_event) + avahi_time_event_free(rj->time_event); + + if (rj->state == AVAHI_SCHEDULED) + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj); + else if (rj->state == AVAHI_DONE) + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj); + else /* rj->state == AVAHI_SUPPRESSED */ + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj); + + avahi_record_unref(rj->record); + avahi_free(rj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiResponseScheduler *s, AvahiResponseJob *rj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(rj); + + avahi_elapse_time(&tv, msec, jitter); + + if (rj->time_event) + avahi_time_event_update(rj->time_event, &tv); + else + rj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, rj); +} + +static void job_mark_done(AvahiResponseScheduler *s, AvahiResponseJob *rj) { + assert(s); + assert(rj); + + assert(rj->state == AVAHI_SCHEDULED); + + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj); + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj); + + rj->state = AVAHI_DONE; + + job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); + + gettimeofday(&rj->delivery, NULL); +} + +AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i) { + AvahiResponseScheduler *s; + assert(i); + + if (!(s = avahi_new(AvahiResponseScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->history); + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->suppressed); + + return s; +} + +void avahi_response_scheduler_free(AvahiResponseScheduler *s) { + assert(s); + + avahi_response_scheduler_clear(s); + avahi_free(s); +} + +void avahi_response_scheduler_clear(AvahiResponseScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); + while (s->suppressed) + job_free(s, s->suppressed); +} + +static void enumerate_aux_records_callback(AVAHI_GCC_UNUSED AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { + AvahiResponseJob *rj = userdata; + + assert(r); + assert(rj); + + avahi_response_scheduler_post(rj->scheduler, r, flush_cache, rj->querier_valid ? &rj->querier : NULL, 0); +} + +static int packet_add_response_job(AvahiResponseScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) { + assert(s); + assert(p); + assert(rj); + + /* Try to add this record to the packet */ + if (!avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0)) + return 0; + + /* Ok, this record will definitely be sent, so schedule the + * auxilliary packets, too */ + avahi_server_enumerate_aux_records(s->interface->monitor->server, s->interface, rj->record, enumerate_aux_records_callback, rj); + job_mark_done(s, rj); + + return 1; +} + +static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) { + AvahiDnsPacket *p; + unsigned n; + + assert(s); + assert(rj); + + if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1))) + return; /* OOM */ + n = 1; + + /* Put it in the packet. */ + if (packet_add_response_job(s, p, rj)) { + + /* Try to fill up packet with more responses, if available */ + while (s->jobs) { + + if (!packet_add_response_job(s, p, s->jobs)) + break; + + n++; + } + + } else { + size_t size; + + avahi_dns_packet_free(p); + + /* OK, the packet was too small, so create one that fits */ + size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(p = avahi_dns_packet_new_response(size + AVAHI_DNS_PACKET_EXTRA_SIZE, 1))) + return; /* OOM */ + + if (!packet_add_response_job(s, p, rj)) { + avahi_dns_packet_free(p); + + avahi_log_warn("Record too large, cannot send"); + job_mark_done(s, rj); + return; + } + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiResponseJob *rj = data; + + assert(rj); + + if (rj->state == AVAHI_DONE || rj->state == AVAHI_SUPPRESSED) + job_free(rj->scheduler, rj); /* Lets drop this entry */ + else + send_response_packet(rj->scheduler, rj); +} + +static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + + for (rj = s->jobs; rj; rj = rj->jobs_next) { + assert(rj->state == AVAHI_SCHEDULED); + + if (avahi_record_equal_no_ttl(rj->record, record)) + return rj; + } + + return NULL; +} + +static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + + for (rj = s->history; rj; rj = rj->jobs_next) { + assert(rj->state == AVAHI_DONE); + + if (avahi_record_equal_no_ttl(rj->record, record)) { + /* Check whether this entry is outdated */ + +/* avahi_log_debug("history age: %u", (unsigned) (avahi_age(&rj->delivery)/1000)); */ + + if (avahi_age(&rj->delivery)/1000 > AVAHI_RESPONSE_HISTORY_MSEC) { + /* it is outdated, so let's remove it */ + job_free(s, rj); + return NULL; + } + + return rj; + } + } + + return NULL; +} + +static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + assert(querier); + + for (rj = s->suppressed; rj; rj = rj->jobs_next) { + assert(rj->state == AVAHI_SUPPRESSED); + assert(rj->querier_valid); + + if (avahi_record_equal_no_ttl(rj->record, record) && + avahi_address_cmp(&rj->querier, querier) == 0) { + /* Check whether this entry is outdated */ + + if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_SUPPRESS_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, rj); + return NULL; + } + + return rj; + } + } + + return NULL; +} + +int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { + AvahiResponseJob *rj; + struct timeval tv; +/* char *t; */ + + assert(s); + assert(record); + + assert(!avahi_key_is_pattern(record->key)); + +/* t = avahi_record_to_string(record); */ +/* avahi_log_debug("post %i %s", immediately, t); */ +/* avahi_free(t); */ + + /* Check whether this response is suppressed */ + if (querier && + (rj = find_suppressed_job(s, record, querier)) && + avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && + rj->record->ttl >= record->ttl/2) { + +/* avahi_log_debug("Response suppressed by known answer suppression."); */ + return 0; + } + + /* Check if we already sent this response recently */ + if ((rj = find_history_job(s, record))) { + + if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && + rj->record->ttl >= record->ttl/2 && + (rj->flush_cache || !flush_cache)) { +/* avahi_log_debug("Response suppressed by local duplicate suppression (history)"); */ + return 0; + } + + /* Outdated ... */ + job_free(s, rj); + } + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC); + + if ((rj = find_scheduled_job(s, record))) { +/* avahi_log_debug("Response suppressed by local duplicate suppression (scheduled)"); */ + + /* Update a little ... */ + + /* Update the time if the new is prior to the old */ + if (avahi_timeval_compare(&tv, &rj->delivery) < 0) { + rj->delivery = tv; + avahi_time_event_update(rj->time_event, &rj->delivery); + } + + /* Update the flush cache bit */ + if (flush_cache) + rj->flush_cache = 1; + + /* Update the querier field */ + if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0)) + rj->querier_valid = 0; + + /* Update record data (just for the TTL) */ + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + + return 1; + } else { +/* avahi_log_debug("Accepted new response job."); */ + + /* Create a new job and schedule it */ + if (!(rj = job_new(s, record, AVAHI_SCHEDULED))) + return 0; /* OOM */ + + rj->delivery = tv; + rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj); + rj->flush_cache = flush_cache; + + if ((rj->querier_valid = !!querier)) + rj->querier = *querier; + + return 1; + } +} + +void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) { + AvahiResponseJob *rj; + assert(s); + + /* This function is called whenever an incoming response was + * receieved. We drop scheduled responses which match here. The + * keyword is "DUPLICATE ANSWER SUPPRESION". */ + + if ((rj = find_scheduled_job(s, record))) { + + if ((!rj->flush_cache || flush_cache) && /* flush cache bit was set correctly */ + avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */ + record->ttl >= rj->record->ttl/2) { /* sensible TTL */ + + /* A matching entry was found, so let's mark it done */ +/* avahi_log_debug("Response suppressed by distributed duplicate suppression"); */ + job_mark_done(s, rj); + } + + return; + } + + if ((rj = find_history_job(s, record))) { + /* Found a history job, let's update it */ + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + } else + /* Found no existing history job, so let's create a new one */ + if (!(rj = job_new(s, record, AVAHI_DONE))) + return; /* OOM */ + + rj->flush_cache = flush_cache; + rj->querier_valid = 0; + + gettimeofday(&rj->delivery, NULL); + job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); +} + +void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + assert(querier); + + if ((rj = find_scheduled_job(s, record))) { + + if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */ + avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */ + record->ttl >= rj->record->ttl/2) { /* sensible TTL */ + + /* A matching entry was found, so let's drop it */ +/* avahi_log_debug("Known answer suppression active!"); */ + job_free(s, rj); + } + } + + if ((rj = find_suppressed_job(s, record, querier))) { + + /* Let's update the old entry */ + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + + } else { + + /* Create a new entry */ + if (!(rj = job_new(s, record, AVAHI_SUPPRESSED))) + return; /* OOM */ + rj->querier_valid = 1; + rj->querier = *querier; + } + + gettimeofday(&rj->delivery, NULL); + job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0); +} + +void avahi_response_scheduler_force(AvahiResponseScheduler *s) { + assert(s); + + /* Send all scheduled responses immediately */ + while (s->jobs) + send_response_packet(s, s->jobs); +} diff --git a/avahi/response-sched.h b/avahi/response-sched.h new file mode 100644 index 0000000..a3eb608 --- /dev/null +++ b/avahi/response-sched.h @@ -0,0 +1,37 @@ +#ifndef fooresponseschedhfoo +#define fooresponseschedhfoo + +/*** + 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 struct AvahiResponseScheduler AvahiResponseScheduler; + +#include +#include "iface.h" + +AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i); +void avahi_response_scheduler_free(AvahiResponseScheduler *s); +void avahi_response_scheduler_clear(AvahiResponseScheduler *s); +void avahi_response_scheduler_force(AvahiResponseScheduler *s); + +int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); +void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache); +void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier); + +#endif diff --git a/avahi/rlist.c b/avahi/rlist.c new file mode 100644 index 0000000..17dcb24 --- /dev/null +++ b/avahi/rlist.c @@ -0,0 +1,62 @@ +/*** + 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 "rlist.h" +#include "malloc.h" + +AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data) { + AvahiRList *n; + + if (!(n = avahi_new(AvahiRList, 1))) + return NULL; + + n->data = data; + + AVAHI_LLIST_PREPEND(AvahiRList, rlist, r, n); + return r; +} + +AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data) { + AvahiRList *n; + + for (n = r; n; n = n->rlist_next) + + if (n->data == data) { + AVAHI_LLIST_REMOVE(AvahiRList, rlist, r, n); + avahi_free(n); + break; + } + + return r; +} + +AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n) { + assert(n); + + AVAHI_LLIST_REMOVE(AvahiRList, rlist, r, n); + avahi_free(n); + + return r; +} diff --git a/avahi/rlist.h b/avahi/rlist.h new file mode 100644 index 0000000..9bcc1d5 --- /dev/null +++ b/avahi/rlist.h @@ -0,0 +1,49 @@ +#ifndef foorlistfoo +#define foorlistfoo + +/*** + 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. +***/ + +/** \file rlist.h A simple linked list implementation */ + +#include "llist.h" + +AVAHI_C_DECL_BEGIN + +/** A doubly linked list type */ +typedef struct AvahiRList AvahiRList; + +/** A doubly linked list type */ +struct AvahiRList { + AVAHI_LLIST_FIELDS(AvahiRList, rlist); + void *data; +}; + +/** Prepend a new item to the beginning of the list and return the new beginning */ +AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data); + +/** Remove the first occurence of the specified item from the list and return the new beginning */ +AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data); + +/** Remove the specified item from the list and return the new beginning */ +AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/rr-util.h b/avahi/rr-util.h new file mode 100644 index 0000000..0eebc00 --- /dev/null +++ b/avahi/rr-util.h @@ -0,0 +1,62 @@ +#ifndef foorrutilhfoo +#define foorrutilhfoo + +/*** + 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 "rr.h" + +AVAHI_C_DECL_BEGIN + +/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */ +AvahiKey *avahi_key_new_cname(AvahiKey *key); + +/** Match a key to a key pattern. The pattern has a type of +AVAHI_DNS_CLASS_ANY, the classes are taken to be equal. Same for the +type. If the pattern has neither class nor type with ANY constants, +this function is identical to avahi_key_equal(). In contrast to +avahi_equal() this function is not commutative. */ +int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); + +/** Check whether a key is a pattern key, i.e. the class/type has a + * value of AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY */ +int avahi_key_is_pattern(const AvahiKey *k); + +/** Returns a maximum estimate for the space that is needed to store + * this key in a DNS packet. */ +size_t avahi_key_get_estimate_size(AvahiKey *k); + +/** Returns a maximum estimate for the space that is needed to store + * the record in a DNS packet. */ +size_t avahi_record_get_estimate_size(AvahiRecord *r); + +/** Do a mDNS spec conforming lexicographical comparison of the two + * records. Return a negative value if a < b, a positive if a > b, + * zero if equal. */ +int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); + +/** Return 1 if the specified record is an mDNS goodbye record. i.e. TTL is zero. */ +int avahi_record_is_goodbye(AvahiRecord *r); + +/** Make a deep copy of an AvahiRecord object */ +AvahiRecord *avahi_record_copy(AvahiRecord *r); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/rr.c b/avahi/rr.c new file mode 100644 index 0000000..0f516fd --- /dev/null +++ b/avahi/rr.c @@ -0,0 +1,733 @@ +/*** + 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 "rr.h" +#include "log.h" +#include "util.h" +#include "hashmap.h" +#include "domain-util.h" +#include "rr-util.h" +#include "addr-util.h" + +AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) { + AvahiKey *k; + assert(name); + + if (!(k = avahi_new(AvahiKey, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + if (!(k->name = avahi_normalize_name_strdup(name))) { + avahi_log_error("avahi_normalize_name() failed."); + avahi_free(k); + return NULL; + } + + k->ref = 1; + k->clazz = class; + k->type = type; + + return k; +} + +AvahiKey *avahi_key_new_cname(AvahiKey *key) { + assert(key); + + if (key->clazz != AVAHI_DNS_CLASS_IN) + return NULL; + + if (key->type == AVAHI_DNS_TYPE_CNAME) + return NULL; + + return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME); +} + +AvahiKey *avahi_key_ref(AvahiKey *k) { + assert(k); + assert(k->ref >= 1); + + k->ref++; + + return k; +} + +void avahi_key_unref(AvahiKey *k) { + assert(k); + assert(k->ref >= 1); + + if ((--k->ref) <= 0) { + avahi_free(k->name); + avahi_free(k); + } +} + +AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) { + AvahiRecord *r; + + assert(k); + + if (!(r = avahi_new(AvahiRecord, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + r->ref = 1; + r->key = avahi_key_ref(k); + + memset(&r->data, 0, sizeof(r->data)); + + r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL; + + return r; +} + +AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) { + AvahiRecord *r; + AvahiKey *k; + + assert(name); + + if (!(k = avahi_key_new(name, class, type))) { + avahi_log_error("avahi_key_new() failed."); + return NULL; + } + + r = avahi_record_new(k, ttl); + avahi_key_unref(k); + + if (!r) { + avahi_log_error("avahi_record_new() failed."); + return NULL; + } + + return r; +} + +AvahiRecord *avahi_record_ref(AvahiRecord *r) { + assert(r); + assert(r->ref >= 1); + + r->ref++; + return r; +} + +void avahi_record_unref(AvahiRecord *r) { + assert(r); + assert(r->ref >= 1); + + if ((--r->ref) <= 0) { + switch (r->key->type) { + + case AVAHI_DNS_TYPE_SRV: + avahi_free(r->data.srv.name); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + avahi_free(r->data.ptr.name); + break; + + case AVAHI_DNS_TYPE_HINFO: + avahi_free(r->data.hinfo.cpu); + avahi_free(r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_TXT: + avahi_string_list_free(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + break; + + default: + avahi_free(r->data.generic.data); + } + + avahi_key_unref(r->key); + avahi_free(r); + } +} + +const char *avahi_dns_class_to_string(uint16_t class) { + if (class & AVAHI_DNS_CACHE_FLUSH) + return "FLUSH"; + + switch (class) { + case AVAHI_DNS_CLASS_IN: + return "IN"; + case AVAHI_DNS_CLASS_ANY: + return "ANY"; + default: + return NULL; + } +} + +const char *avahi_dns_type_to_string(uint16_t type) { + switch (type) { + case AVAHI_DNS_TYPE_CNAME: + return "CNAME"; + case AVAHI_DNS_TYPE_A: + return "A"; + case AVAHI_DNS_TYPE_AAAA: + return "AAAA"; + case AVAHI_DNS_TYPE_PTR: + return "PTR"; + case AVAHI_DNS_TYPE_HINFO: + return "HINFO"; + case AVAHI_DNS_TYPE_TXT: + return "TXT"; + case AVAHI_DNS_TYPE_SRV: + return "SRV"; + case AVAHI_DNS_TYPE_ANY: + return "ANY"; + case AVAHI_DNS_TYPE_SOA: + return "SOA"; + case AVAHI_DNS_TYPE_NS: + return "NS"; + default: + return NULL; + } +} + +char *avahi_key_to_string(const AvahiKey *k) { + char class[16], type[16]; + const char *c, *t; + + assert(k); + assert(k->ref >= 1); + + /* According to RFC3597 */ + + if (!(c = avahi_dns_class_to_string(k->clazz))) { + snprintf(class, sizeof(class), "CLASS%u", k->clazz); + c = class; + } + + if (!(t = avahi_dns_type_to_string(k->type))) { + snprintf(type, sizeof(type), "TYPE%u", k->type); + t = type; + } + + return avahi_strdup_printf("%s\t%s\t%s", k->name, c, t); +} + +char *avahi_record_to_string(const AvahiRecord *r) { + char *p, *s; + char buf[1024], *t = NULL, *d = NULL; + + assert(r); + assert(r->ref >= 1); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_A: + inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_AAAA: + inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + + t = r->data.ptr.name; + break; + + case AVAHI_DNS_TYPE_TXT: + t = d = avahi_string_list_to_string(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_HINFO: + + snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_SRV: + + snprintf(t = buf, sizeof(buf), "%u %u %u %s", + r->data.srv.priority, + r->data.srv.weight, + r->data.srv.port, + r->data.srv.name); + + break; + + default: { + + uint8_t *c; + uint16_t n; + int i; + char *e; + + /* According to RFC3597 */ + + snprintf(t = buf, sizeof(buf), "\\# %u", r->data.generic.size); + + e = strchr(t, 0); + + for (c = r->data.generic.data, n = r->data.generic.size, i = 0; + n > 0 && i < 20; + c ++, n --, i++) { + + sprintf(e, " %02X", *c); + e = strchr(e, 0); + } + + break; + } + } + + p = avahi_key_to_string(r->key); + s = avahi_strdup_printf("%s %s ; ttl=%u", p, t, r->ttl); + avahi_free(p); + avahi_free(d); + + return s; +} + +int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { + assert(a); + assert(b); + + if (a == b) + return 1; + + return avahi_domain_equal(a->name, b->name) && + a->type == b->type && + a->clazz == b->clazz; +} + +int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { + assert(pattern); + assert(k); + + assert(!avahi_key_is_pattern(k)); + + if (pattern == k) + return 1; + + return avahi_domain_equal(pattern->name, k->name) && + (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && + (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY); +} + +int avahi_key_is_pattern(const AvahiKey *k) { + assert(k); + + return + k->type == AVAHI_DNS_TYPE_ANY || + k->clazz == AVAHI_DNS_CLASS_ANY; +} + +unsigned avahi_key_hash(const AvahiKey *k) { + assert(k); + + return + avahi_domain_hash(k->name) + + k->type + + k->clazz; +} + +static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { + assert(a); + assert(b); + assert(a->key->type == b->key->type); + + switch (a->key->type) { + case AVAHI_DNS_TYPE_SRV: + return + a->data.srv.priority == b->data.srv.priority && + a->data.srv.weight == b->data.srv.weight && + a->data.srv.port == b->data.srv.port && + avahi_domain_equal(a->data.srv.name, b->data.srv.name); + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_HINFO: + return + !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) && + !strcmp(a->data.hinfo.os, b->data.hinfo.os); + + case AVAHI_DNS_TYPE_TXT: + return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; + + default: + return a->data.generic.size == b->data.generic.size && + (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); + } + +} + +int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { + assert(a); + assert(b); + + if (a == b) + return 1; + + return + avahi_key_equal(a->key, b->key) && + rdata_equal(a, b); +} + + +AvahiRecord *avahi_record_copy(AvahiRecord *r) { + AvahiRecord *copy; + + if (!(copy = avahi_new(AvahiRecord, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + copy->ref = 1; + copy->key = avahi_key_ref(r->key); + copy->ttl = r->ttl; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name))) + goto fail; + break; + + case AVAHI_DNS_TYPE_SRV: + copy->data.srv.priority = r->data.srv.priority; + copy->data.srv.weight = r->data.srv.weight; + copy->data.srv.port = r->data.srv.port; + if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name))) + goto fail; + break; + + case AVAHI_DNS_TYPE_HINFO: + if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os))) + goto fail; + + if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) { + avahi_free(r->data.hinfo.os); + goto fail; + } + break; + + case AVAHI_DNS_TYPE_TXT: + copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + copy->data.a.address = r->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + copy->data.aaaa.address = r->data.aaaa.address; + break; + + default: + if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size))) + goto fail; + copy->data.generic.size = r->data.generic.size; + break; + + } + + return copy; + +fail: + avahi_log_error("Failed to allocate memory"); + + avahi_key_unref(copy->key); + avahi_free(copy); + + return NULL; +} + + +size_t avahi_key_get_estimate_size(AvahiKey *k) { + assert(k); + + return strlen(k->name)+1+4; +} + +size_t avahi_record_get_estimate_size(AvahiRecord *r) { + size_t n; + assert(r); + + n = avahi_key_get_estimate_size(r->key) + 4 + 2; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + n += strlen(r->data.ptr.name) + 1; + break; + + case AVAHI_DNS_TYPE_SRV: + n += 6 + strlen(r->data.srv.name) + 1; + break; + + case AVAHI_DNS_TYPE_HINFO: + n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; + break; + + case AVAHI_DNS_TYPE_TXT: + n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + break; + + case AVAHI_DNS_TYPE_A: + n += sizeof(AvahiIPv4Address); + break; + + case AVAHI_DNS_TYPE_AAAA: + n += sizeof(AvahiIPv6Address); + break; + + default: + n += r->data.generic.size; + } + + return n; +} + +static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) { + size_t c; + int ret; + + assert(a); + assert(b); + + c = al < bl ? al : bl; + if ((ret = memcmp(a, b, c))) + return ret; + + if (al == bl) + return 0; + else + return al == c ? 1 : -1; +} + +static int uint16_cmp(uint16_t a, uint16_t b) { + return a == b ? 0 : (a < b ? -1 : 1); +} + +int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { + int r; +/* char *t1, *t2; */ + + assert(a); + assert(b); + +/* t1 = avahi_record_to_string(a); */ +/* t2 = avahi_record_to_string(b); */ +/* g_message("lexicocmp: %s %s", t1, t2); */ +/* avahi_free(t1); */ +/* avahi_free(t2); */ + + if (a == b) + return 0; + + if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) || + (r = uint16_cmp(a->key->type, b->key->type))) + return r; + + switch (a->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_SRV: { + if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && + (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && + (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) + r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name); + + return r; + } + + case AVAHI_DNS_TYPE_HINFO: { + + if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) || + (r = strcmp(a->data.hinfo.os, b->data.hinfo.os))) + return r; + + return 0; + + } + + case AVAHI_DNS_TYPE_TXT: { + + uint8_t *ma = NULL, *mb = NULL; + size_t asize, bsize; + + asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0); + bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0); + + if (asize > 0 && !(ma = avahi_new(uint8_t, asize))) + goto fail; + + if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) { + avahi_free(ma); + goto fail; + } + + avahi_string_list_serialize(a->data.txt.string_list, ma, asize); + avahi_string_list_serialize(b->data.txt.string_list, mb, bsize); + + if (asize && bsize) + r = lexicographical_memcmp(ma, asize, mb, bsize); + else if (asize && !bsize) + r = 1; + else if (!asize && bsize) + r = -1; + else + r = 0; + + avahi_free(ma); + avahi_free(mb); + + return r; + } + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); + + default: + return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, + b->data.generic.data, b->data.generic.size); + } + + +fail: + avahi_log_error(__FILE__": Out of memory"); + return -1; /* or whatever ... */ +} + +int avahi_record_is_goodbye(AvahiRecord *r) { + assert(r); + + return r->ttl == 0; +} + +int avahi_key_is_valid(AvahiKey *k) { + assert(k); + + if (!avahi_is_valid_domain_name(k->name)) + return 0; + + return 1; +} + +int avahi_record_is_valid(AvahiRecord *r) { + assert(r); + + if (!avahi_key_is_valid(r->key)) + return 0; + + switch (r->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + return avahi_is_valid_domain_name(r->data.ptr.name); + + case AVAHI_DNS_TYPE_SRV: + return avahi_is_valid_domain_name(r->data.srv.name); + + case AVAHI_DNS_TYPE_HINFO: + return + strlen(r->data.hinfo.os) <= 255 && + strlen(r->data.hinfo.cpu) <= 255; + + case AVAHI_DNS_TYPE_TXT: { + + AvahiStringList *strlst; + + for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next) + if (strlst->size > 255 || strlst->size <= 0) + return 0; + + return 1; + } + } + + return 1; +} + +static AvahiAddress *get_address(const AvahiRecord *r, AvahiAddress *a) { + assert(r); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_A: + a->proto = AVAHI_PROTO_INET; + a->data.ipv4 = r->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a->proto = AVAHI_PROTO_INET6; + a->data.ipv6 = r->data.aaaa.address; + break; + + default: + return NULL; + } + + return a; +} + +int avahi_record_is_link_local_address(const AvahiRecord *r) { + AvahiAddress a; + + assert(r); + + if (!get_address(r, &a)) + return 0; + + return avahi_address_is_link_local(&a); +} diff --git a/avahi/rr.h b/avahi/rr.h new file mode 100644 index 0000000..3cf99dd --- /dev/null +++ b/avahi/rr.h @@ -0,0 +1,175 @@ +#ifndef foorrhfoo +#define foorrhfoo + +/*** + 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. +***/ + +/** \file rr.h Functions and definitions for manipulating DNS resource record (RR) data. */ + +#include +#include + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** DNS record types, see RFC 1035, in addition to those defined in defs.h */ +enum { + AVAHI_DNS_TYPE_ANY = 0xFF, /**< Special query type for requesting all records */ + AVAHI_DNS_TYPE_OPT = 41, /**< EDNS0 option */ + AVAHI_DNS_TYPE_TKEY = 249, + AVAHI_DNS_TYPE_TSIG = 250, + AVAHI_DNS_TYPE_IXFR = 251, + AVAHI_DNS_TYPE_AXFR = 252 +}; + +/** DNS record classes, see RFC 1035, in addition to those defined in defs.h */ +enum { + AVAHI_DNS_CLASS_ANY = 0xFF, /**< Special query type for requesting all records */ + AVAHI_DNS_CACHE_FLUSH = 0x8000, /**< Not really a class but a bit which may be set in response packets, see mDNS spec for more information */ + AVAHI_DNS_UNICAST_RESPONSE = 0x8000 /**< Not really a class but a bit which may be set in query packets, see mDNS spec for more information */ +}; + +/** Encapsulates a DNS query key consisting of class, type and + name. Use avahi_key_ref()/avahi_key_unref() for manipulating the + reference counter. The structure is intended to be treated as "immutable", no + changes should be imposed after creation */ +typedef struct AvahiKey { + int ref; /**< Reference counter */ + char *name; /**< Record name */ + uint16_t clazz; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */ + uint16_t type; /**< Record type, one of the AVAHI_DNS_TYPE_xxx constants */ +} AvahiKey; + +/** Encapsulates a DNS resource record. The structure is intended to + * be treated as "immutable", no changes should be imposed after + * creation. */ +typedef struct AvahiRecord { + int ref; /**< Reference counter */ + AvahiKey *key; /**< Reference to the query key of this record */ + + uint32_t ttl; /**< DNS TTL of this record */ + + union { + + struct { + void* data; + uint16_t size; + } generic; /**< Generic record data for unknown types */ + + struct { + uint16_t priority; + uint16_t weight; + uint16_t port; + char *name; + } srv; /**< Data for SRV records */ + + struct { + char *name; + } ptr, ns, cname; /**< Data for PTR, NS and CNAME records */ + + struct { + char *cpu; + char *os; + } hinfo; /**< Data for HINFO records */ + + struct { + AvahiStringList *string_list; + } txt; /**< Data for TXT records */ + + struct { + AvahiIPv4Address address; + } a; /**< Data for A records */ + + struct { + AvahiIPv6Address address; + } aaaa; /**< Data for AAAA records */ + + } data; /**< Record data */ + +} AvahiRecord; + +/** Create a new AvahiKey object. The reference counter will be set to 1. */ +AvahiKey *avahi_key_new(const char *name, uint16_t clazz, uint16_t type); + +/** Increase the reference counter of an AvahiKey object by one */ +AvahiKey *avahi_key_ref(AvahiKey *k); + +/** Decrease the reference counter of an AvahiKey object by one */ +void avahi_key_unref(AvahiKey *k); + +/** Check whether two AvahiKey object contain the same + * data. AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY are treated like any + * other class/type. */ +int avahi_key_equal(const AvahiKey *a, const AvahiKey *b); + +/** Return a numeric hash value for a key for usage in hash tables. */ +unsigned avahi_key_hash(const AvahiKey *k); + +/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ +AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl); + +/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ +AvahiRecord *avahi_record_new_full(const char *name, uint16_t clazz, uint16_t type, uint32_t ttl); + +/** Increase the reference counter of an AvahiRecord by one. */ +AvahiRecord *avahi_record_ref(AvahiRecord *r); + +/** Decrease the reference counter of an AvahiRecord by one. */ +void avahi_record_unref(AvahiRecord *r); + +/** Return a textual representation of the specified DNS class. The + * returned pointer points to a read only internal string. */ +const char *avahi_dns_class_to_string(uint16_t clazz); + +/** Return a textual representation of the specified DNS class. The + * returned pointer points to a read only internal string. */ +const char *avahi_dns_type_to_string(uint16_t type); + +/** Create a textual representation of the specified key. avahi_free() the + * result! */ +char *avahi_key_to_string(const AvahiKey *k); + +/** Create a textual representation of the specified record, similar + * in style to BIND zone file data. avahi_free() the result! */ +char *avahi_record_to_string(const AvahiRecord *r); + +/** Check whether two records are equal (regardless of the TTL */ +int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b); + +/** Check whether the specified key is valid */ +int avahi_key_is_valid(AvahiKey *k); + +/** Check whether the specified record is valid */ +int avahi_record_is_valid(AvahiRecord *r); + +/** Parse a binary rdata object and fill it into *record. This function is actually implemented in dns.c */ +int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size); + +/** Serialize an AvahiRecord object into binary rdata. This function is actually implemented in dns.c */ +size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size); + +/** Return TRUE if the AvahiRecord object is a link-local A or AAAA address */ +int avahi_record_is_link_local_address(const AvahiRecord *r); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/rrlist.c b/avahi/rrlist.c new file mode 100644 index 0000000..016edbf --- /dev/null +++ b/avahi/rrlist.c @@ -0,0 +1,188 @@ +/*** + 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 "rrlist.h" +#include "log.h" + +typedef struct AvahiRecordListItem AvahiRecordListItem; + +struct AvahiRecordListItem { + int read; + AvahiRecord *record; + int unicast_response; + int flush_cache; + int auxiliary; + AVAHI_LLIST_FIELDS(AvahiRecordListItem, items); +}; + +struct AvahiRecordList { + AVAHI_LLIST_HEAD(AvahiRecordListItem, read); + AVAHI_LLIST_HEAD(AvahiRecordListItem, unread); + + int all_flush_cache; +}; + +AvahiRecordList *avahi_record_list_new(void) { + AvahiRecordList *l; + + if (!(l = avahi_new(AvahiRecordList, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read); + AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread); + + l->all_flush_cache = 1; + return l; +} + +void avahi_record_list_free(AvahiRecordList *l) { + assert(l); + + avahi_record_list_flush(l); + avahi_free(l); +} + +static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) { + assert(l); + assert(i); + + if (i->read) + AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->read, i); + else + AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); + + avahi_record_unref(i->record); + avahi_free(i); +} + +void avahi_record_list_flush(AvahiRecordList *l) { + assert(l); + + while (l->read) + item_free(l, l->read); + while (l->unread) + item_free(l, l->unread); + + l->all_flush_cache = 1; +} + +AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) { + AvahiRecord *r; + AvahiRecordListItem *i; + + if (!(i = l->unread)) + return NULL; + + assert(!i->read); + + r = avahi_record_ref(i->record); + if (ret_unicast_response) + *ret_unicast_response = i->unicast_response; + if (ret_flush_cache) + *ret_flush_cache = i->flush_cache; + if (ret_auxiliary) + *ret_auxiliary = i->auxiliary; + + AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); + AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i); + + i->read = 1; + + return r; +} + +static AvahiRecordListItem *get(AvahiRecordList *l, AvahiRecord *r) { + AvahiRecordListItem *i; + + assert(l); + assert(r); + + for (i = l->read; i; i = i->items_next) + if (avahi_record_equal_no_ttl(i->record, r)) + return i; + + for (i = l->unread; i; i = i->items_next) + if (avahi_record_equal_no_ttl(i->record, r)) + return i; + + return NULL; +} + +void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary) { + AvahiRecordListItem *i; + + assert(l); + assert(r); + + if (get(l, r)) + return; + + if (!(i = avahi_new(AvahiRecordListItem, 1))) { + avahi_log_error("avahi_new() failed."); + return; + } + + i->unicast_response = unicast_response; + i->flush_cache = flush_cache; + i->auxiliary = auxiliary; + i->record = avahi_record_ref(r); + i->read = 0; + + l->all_flush_cache = l->all_flush_cache && flush_cache; + + AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i); +} + +void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) { + AvahiRecordListItem *i; + + assert(l); + assert(r); + + if (!(i = get(l, r))) + return; + + item_free(l, i); +} + +int avahi_record_list_is_empty(AvahiRecordList *l) { + assert(l); + + return !l->unread && !l->read; +} + +int avahi_record_list_all_flush_cache(AvahiRecordList *l) { + assert(l); + + /* Return TRUE if all entries in this list have flush_cache set */ + + return l->all_flush_cache; +} diff --git a/avahi/rrlist.h b/avahi/rrlist.h new file mode 100644 index 0000000..c7b5aba --- /dev/null +++ b/avahi/rrlist.h @@ -0,0 +1,40 @@ +#ifndef foorrlisthfoo +#define foorrlisthfoo + +/*** + 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 "rr.h" + +typedef struct AvahiRecordList AvahiRecordList; + +AvahiRecordList *avahi_record_list_new(void); +void avahi_record_list_free(AvahiRecordList *l); +void avahi_record_list_flush(AvahiRecordList *l); + +AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary); +void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary); +void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r); + +int avahi_record_list_all_flush_cache(AvahiRecordList *l); + +int avahi_record_list_is_empty(AvahiRecordList *l); + +#endif diff --git a/avahi/server.c b/avahi/server.c new file mode 100644 index 0000000..e0a0e44 --- /dev/null +++ b/avahi/server.c @@ -0,0 +1,1806 @@ +/*** + 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 "internal.h" +#include "iface.h" +#include "socket.h" +#include "browse.h" +#include "log.h" +#include "util.h" +#include "dns-srv-rr.h" +#include "addr-util.h" +#include "domain-util.h" +#include "rr-util.h" + +#define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096 + +static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { + assert(s); + assert(i); + assert(name); + assert(callback); + + if (type == AVAHI_DNS_TYPE_ANY) { + AvahiEntry *e; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead && + avahi_entry_is_registered(s, e, i) && + e->record->key->clazz == AVAHI_DNS_CLASS_IN && + avahi_domain_equal(name, e->record->key->name)) + callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); + + } else { + AvahiEntry *e; + AvahiKey *k; + + if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type))) + return; /** OOM */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next) + if (!e->dead && avahi_entry_is_registered(s, e, i)) + callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); + + avahi_key_unref(k); + } +} + +void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { + assert(s); + assert(i); + assert(r); + assert(callback); + + /* Call the specified callback far all records referenced by the one specified in *r */ + + if (r->key->clazz == AVAHI_DNS_CLASS_IN) { + if (r->key->type == AVAHI_DNS_TYPE_PTR) { + enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata); + enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata); + } else if (r->key->type == AVAHI_DNS_TYPE_SRV) { + enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata); + enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata); + } else if (r->key->type == AVAHI_DNS_TYPE_CNAME) + enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata); + } +} + +void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) { + assert(s); + assert(i); + assert(e); + + avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary); +} + +void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) { + assert(s); + assert(i); + assert(k); + + /* Push all records that match the specified key to the record list */ + + if (avahi_key_is_pattern(k)) { + AvahiEntry *e; + + /* Handle ANY query */ + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i)) + avahi_server_prepare_response(s, i, e, unicast_response, 0); + + } else { + AvahiEntry *e; + + /* Handle all other queries */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next) + if (!e->dead && avahi_entry_is_registered(s, e, i)) + avahi_server_prepare_response(s, i, e, unicast_response, 0); + } + + /* Look for CNAME records */ + + if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY) + && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) { + + AvahiKey *cname_key; + + if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME))) + return; + + avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response); + avahi_key_unref(cname_key); + } +} + +static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { + assert(s); + assert(e); + + /* Withdraw the specified entry, and if is part of an entry group, + * put that into COLLISION state */ + + if (e->dead) + return; + + if (e->group) { + AvahiEntry *k; + + for (k = e->group->entries; k; k = k->by_group_next) + if (!k->dead) { + avahi_goodbye_entry(s, k, 0, 1); + k->dead = 1; + } + + e->group->n_probing = 0; + + avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION); + } else { + avahi_goodbye_entry(s, e, 0, 1); + e->dead = 1; + } + + s->need_entry_cleanup = 1; +} + +static void withdraw_rrset(AvahiServer *s, AvahiKey *key) { + AvahiEntry *e; + + assert(s); + assert(key); + + /* Withdraw an entry RRSset */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) + withdraw_entry(s, e); +} + +static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { + AvahiEntry *e, *n; + int ours = 0, won = 0, lost = 0; + + assert(s); + assert(record); + assert(i); + + /* Handle incoming probes and check if they conflict our own probes */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) { + int cmp; + n = e->by_key_next; + + if (e->dead) + continue; + + if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) { + ours = 1; + break; + } else { + + if (avahi_entry_is_probing(s, e, i)) { + if (cmp > 0) + won = 1; + else /* cmp < 0 */ + lost = 1; + } + } + } + + if (!ours) { + char *t = avahi_record_to_string(record); + + if (won) + avahi_log_debug("Received conflicting probe [%s]. Local host won.", t); + else if (lost) { + avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t); + withdraw_rrset(s, record->key); + } + + avahi_free(t); + } +} + +static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) { + int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0; + AvahiEntry *e, *n, *conflicting_entry = NULL; + + assert(s); + assert(i); + assert(record); + + /* Check whether an incoming record conflicts with one of our own */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) { + n = e->by_key_next; + + if (e->dead) + continue; + + /* Check if the incoming is a goodbye record */ + if (avahi_record_is_goodbye(record)) { + + if (avahi_record_equal_no_ttl(e->record, record)) { + char *t; + + /* Refresh */ + t = avahi_record_to_string(record); + avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t); + avahi_server_prepare_matching_responses(s, i, e->record->key, 0); + + valid = 0; + avahi_free(t); + break; + } + + /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */ + continue; + } + + if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique) + continue; + + /* Either our entry or the other is intended to be unique, so let's check */ + + if (avahi_record_equal_no_ttl(e->record, record)) { + ours = 1; /* We have an identical record, so this is no conflict */ + + /* Check wheter there is a TTL conflict */ + if (record->ttl <= e->record->ttl/2 && + avahi_entry_is_registered(s, e, i)) { + char *t; + /* Refresh */ + t = avahi_record_to_string(record); + + avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t); + avahi_server_prepare_matching_responses(s, i, e->record->key, 0); + valid = 0; + + avahi_free(t); + } + + /* There's no need to check the other entries of this RRset */ + break; + + } else { + + if (avahi_entry_is_registered(s, e, i)) { + + /* A conflict => we have to return to probe mode */ + conflict = 1; + conflicting_entry = e; + + } else if (avahi_entry_is_probing(s, e, i)) { + + /* We are currently registering a matching record, but + * someone else already claimed it, so let's + * withdraw */ + conflict = 1; + withdraw_immediately = 1; + } + } + } + + if (!ours && conflict) { + char *t; + + valid = 0; + + t = avahi_record_to_string(record); + + if (withdraw_immediately) { + avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t); + withdraw_rrset(s, record->key); + } else { + assert(conflicting_entry); + avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t); + avahi_entry_return_to_initial_state(s, conflicting_entry, i); + + /* Local unique records are returned to probing + * state. Local shared records are reannounced. */ + } + + avahi_free(t); + } + + return valid; +} + +static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { + int *unicast_response = userdata; + + assert(s); + assert(r); + assert(unicast_response); + + avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1); +} + +static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) { + assert(s); + assert(r); + + avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response); +} + +void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) { + + assert(s); + assert(i); + assert(!legacy_unicast || (a && port > 0 && p)); + + if (legacy_unicast) { + AvahiDnsPacket *reply; + AvahiRecord *r; + + if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1))) + return; /* OOM */ + + while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) { + + append_aux_records_to_list(s, i, r, 0); + + if (avahi_dns_packet_append_record(reply, r, 0, 10)) + avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); + else { + char *t = avahi_record_to_string(r); + avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t); + avahi_free(t); + } + + avahi_record_unref(r); + } + + if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) + avahi_interface_send_packet_unicast(i, reply, a, port); + + avahi_dns_packet_free(reply); + + } else { + int unicast_response, flush_cache, auxiliary; + AvahiDnsPacket *reply = NULL; + AvahiRecord *r; + + /* In case the query packet was truncated never respond + immediately, because known answer suppression records might be + contained in later packets */ + int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC); + + while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) { + + int im = immediately; + + /* Only send the response immediately if it contains a + * unique entry AND it is not in reply to a truncated + * packet AND it is not an auxiliary record AND all other + * responses for this record are unique too. */ + + if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list)) + im = 1; + + if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) { + + /* Due to some reasons the record has not been scheduled. + * The client requested an unicast response in that + * case. Therefore we prepare such a response */ + + append_aux_records_to_list(s, i, r, unicast_response); + + for (;;) { + + if (!reply) { + assert(p); + + if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0))) + break; /* OOM */ + } + + if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { + + /* Appending this record succeeded, so incremeant + * the specific header field, and return to the caller */ + + avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); + break; + } + + if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) { + size_t size; + + /* The record is too large for one packet, so create a larger packet */ + + avahi_dns_packet_free(reply); + size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1))) + break; /* OOM */ + + if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { + + /* Appending this record succeeded, so incremeant + * the specific header field, and return to the caller */ + + avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); + break; + + } else { + + /* We completely fucked up, there's + * nothing we can do. The RR just doesn't + * fit in. Let's ignore it. */ + + char *t; + avahi_dns_packet_free(reply); + reply = NULL; + t = avahi_record_to_string(r); + avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t); + avahi_free(t); + break; + } + } + + /* Appending the record didn't succeeed, so let's send this packet, and create a new one */ + avahi_interface_send_packet_unicast(i, reply, a, port); + avahi_dns_packet_free(reply); + reply = NULL; + } + } + + avahi_record_unref(r); + } + + if (reply) { + if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) + avahi_interface_send_packet_unicast(i, reply, a, port); + avahi_dns_packet_free(reply); + } + } + + avahi_record_list_flush(s->record_list); +} + +static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) { + AvahiInterface *j; + + assert(s); + assert(i); + assert(r); + + if (!s->config.enable_reflector) + return; + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) + avahi_interface_post_response(j, r, flush_cache, NULL, 1); +} + +static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + AvahiServer *s = userdata; + AvahiRecord* r; + + assert(c); + assert(pattern); + assert(e); + assert(s); + + /* Don't reflect cache entry with ipv6 link-local addresses. */ + r = e->record; + if ((r->key->type == AVAHI_DNS_TYPE_AAAA) && + (r->data.aaaa.address.address[0] == 0xFE) && + (r->data.aaaa.address.address[1] == 0x80)) + return NULL; + + avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0); + return NULL; +} + +static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) { + AvahiInterface *j; + + assert(s); + assert(i); + assert(k); + + if (!s->config.enable_reflector) + return; + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) { + /* Post the query to other networks */ + avahi_interface_post_query(j, k, 1, NULL); + + /* Reply from caches of other network. This is needed to + * "work around" known answer suppression. */ + + avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s); + } +} + +static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) { + AvahiInterface *j; + + assert(s); + assert(i); + assert(r); + + if (!s->config.enable_reflector) + return; + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) + avahi_interface_post_probe(j, r, 1); +} + +static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) { + size_t n; + int is_probe; + + assert(s); + assert(p); + assert(i); + assert(a); + + assert(avahi_record_list_is_empty(s->record_list)); + + is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0; + + /* Handle the questions */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) { + AvahiKey *key; + int unicast_response = 0; + + if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { + avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); + goto fail; + } + + if (!legacy_unicast && !from_local_iface) { + reflect_query(s, i, key); + if (!unicast_response) + avahi_cache_start_poof(i->cache, key, a); + } + + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 && + !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC)) + /* Allow our own queries to be suppressed by incoming + * queries only when they do not include known answers */ + avahi_query_scheduler_incoming(i->query_scheduler, key); + + avahi_server_prepare_matching_responses(s, i, key, unicast_response); + avahi_key_unref(key); + } + + if (!legacy_unicast) { + + /* Known Answer Suppression */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { + AvahiRecord *record; + int unique = 0; + + if (!(record = avahi_dns_packet_consume_record(p, &unique))) { + avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)"); + goto fail; + } + + avahi_response_scheduler_suppress(i->response_scheduler, record, a); + avahi_record_list_drop(s->record_list, record); + avahi_cache_stop_poof(i->cache, record, a); + + avahi_record_unref(record); + } + + /* Probe record */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) { + AvahiRecord *record; + int unique = 0; + + if (!(record = avahi_dns_packet_consume_record(p, &unique))) { + avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)"); + goto fail; + } + + if (!avahi_key_is_pattern(record->key)) { + if (!from_local_iface) + reflect_probe(s, i, record); + incoming_probe(s, record, i); + } + + avahi_record_unref(record); + } + } + + if (!avahi_record_list_is_empty(s->record_list)) + avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe); + + return; + +fail: + avahi_record_list_flush(s->record_list); +} + +static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) { + unsigned n; + + assert(s); + assert(p); + assert(i); + assert(a); + + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) { + AvahiRecord *record; + int cache_flush = 0; + + if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) { + avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); + break; + } + + if (!avahi_key_is_pattern(record->key)) { + + if (handle_conflict(s, i, record, cache_flush)) { + if (!from_local_iface && !avahi_record_is_link_local_address(record)) + reflect_response(s, i, record, cache_flush); + avahi_cache_update(i->cache, record, cache_flush, a); + avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush); + } + } + + avahi_record_unref(record); + } + + /* If the incoming response contained a conflicting record, some + records have been scheduled for sending. We need to flush them + here. */ + if (!avahi_record_list_is_empty(s->record_list)) + avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1); +} + +static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) { + unsigned n, idx = (unsigned) -1; + AvahiLegacyUnicastReflectSlot *slot; + + assert(s); + + if (!s->legacy_unicast_reflect_slots) + s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX); + + for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) { + idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; + + if (!s->legacy_unicast_reflect_slots[idx]) + break; + } + + if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx]) + return NULL; + + if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1))) + return NULL; /* OOM */ + + s->legacy_unicast_reflect_slots[idx] = slot; + slot->id = s->legacy_unicast_reflect_id++; + slot->server = s; + + return slot; +} + +static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) { + unsigned idx; + + assert(s); + assert(slot); + + idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; + + assert(s->legacy_unicast_reflect_slots[idx] == slot); + + avahi_time_event_free(slot->time_event); + + avahi_free(slot); + s->legacy_unicast_reflect_slots[idx] = NULL; +} + +static void free_slots(AvahiServer *s) { + unsigned idx; + assert(s); + + if (!s->legacy_unicast_reflect_slots) + return; + + for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++) + if (s->legacy_unicast_reflect_slots[idx]) + deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]); + + avahi_free(s->legacy_unicast_reflect_slots); + s->legacy_unicast_reflect_slots = NULL; +} + +static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) { + unsigned idx; + + assert(s); + + if (!s->legacy_unicast_reflect_slots) + return NULL; + + idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; + + if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id) + return NULL; + + return s->legacy_unicast_reflect_slots[idx]; +} + +static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) { + AvahiLegacyUnicastReflectSlot *slot = userdata; + + assert(e); + assert(slot); + assert(slot->time_event == e); + + deallocate_slot(slot->server, slot); +} + +static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) { + AvahiLegacyUnicastReflectSlot *slot; + AvahiInterface *j; + + assert(s); + assert(p); + assert(i); + assert(a); + assert(port > 0); + assert(i->protocol == a->proto); + + if (!s->config.enable_reflector) + return; + + /* Reflecting legacy unicast queries is a little more complicated + than reflecting normal queries, since we must route the + responses back to the right client. Therefore we must store + some information for finding the right client contact data for + response packets. In contrast to normal queries legacy + unicast query and response packets are reflected untouched and + are not reassembled into larger packets */ + + if (!(slot = allocate_slot(s))) { + /* No slot available, we drop this legacy unicast query */ + avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet."); + return; + } + + slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID); + slot->address = *a; + slot->port = port; + slot->interface = i->hardware->index; + + avahi_elapse_time(&slot->elapse_time, 2000, 0); + slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot); + + /* Patch the packet with our new locally generatet id */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j->announcing && + j != i && + (s->config.reflect_ipv || j->protocol == i->protocol)) { + + if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) { + avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0); + } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) + avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0); + } + + /* Reset the id */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); +} + +static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) { + assert(s); + assert(address); + assert(port > 0); + + if (!s->config.enable_reflector) + return 0; + + if (!avahi_address_is_local(s->monitor, address)) + return 0; + + if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) { + struct sockaddr_in lsa; + socklen_t l = sizeof(lsa); + + if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0) + avahi_log_warn("getsockname(): %s", strerror(errno)); + else + return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; + + } + + if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) { + struct sockaddr_in6 lsa; + socklen_t l = sizeof(lsa); + + if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0) + avahi_log_warn("getsockname(): %s", strerror(errno)); + else + return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; + } + + return 0; +} + +static int is_mdns_mcast_address(const AvahiAddress *a) { + AvahiAddress b; + assert(a); + + avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b); + return avahi_address_cmp(a, &b) == 0; +} + +static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) { + assert(s); + assert(iface != AVAHI_IF_UNSPEC); + assert(a); + + /* If it isn't the MDNS port it can't be generated by us */ + if (port != AVAHI_MDNS_PORT) + return 0; + + return avahi_interface_has_address(s->monitor, iface, a); +} + +static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) { + AvahiInterface *i; + int from_local_iface = 0; + + assert(s); + assert(p); + assert(src_address); + assert(dst_address); + assert(iface > 0); + assert(src_address->proto == dst_address->proto); + + if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) || + !i->announcing) { + avahi_log_warn("Received packet from invalid interface."); + return; + } + + if (port <= 0) { + /* This fixes RHBZ #475394 */ + avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port); + return; + } + + if (avahi_address_is_ipv4_in_ipv6(src_address)) + /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ + return; + + if (originates_from_local_legacy_unicast_socket(s, src_address, port)) + /* This originates from our local reflector, so let's ignore it */ + return; + + /* We don't want to reflect local traffic, so we check if this packet is generated locally. */ + if (s->config.enable_reflector) + from_local_iface = originates_from_local_iface(s, iface, src_address, port); + + if (avahi_dns_packet_check_valid_multicast(p) < 0) { + avahi_log_warn("Received invalid packet."); + return; + } + + if (avahi_dns_packet_is_query(p)) { + int legacy_unicast = 0; + + /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the + * AR section completely here, so far. Until the day we add + * EDNS0 support. */ + + if (port != AVAHI_MDNS_PORT) { + /* Legacy Unicast */ + + if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) { + avahi_log_warn("Invalid legacy unicast query packet."); + return; + } + + legacy_unicast = 1; + } + + if (legacy_unicast) + reflect_legacy_unicast_query_packet(s, p, i, src_address, port); + + handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface); + + } else { + char t[AVAHI_ADDRESS_STR_MAX]; + + if (port != AVAHI_MDNS_PORT) { + avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol); + return; + } + + if (ttl != 255 && s->config.check_response_ttl) { + avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol); + return; + } + + if (!is_mdns_mcast_address(dst_address) && + !avahi_interface_address_on_link(i, src_address)) { + + avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol); + return; + } + + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) { + + avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address)); + return; + } + + handle_response_packet(s, p, i, src_address, from_local_iface); + } +} + +static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) { + AvahiInterface *j; + AvahiLegacyUnicastReflectSlot *slot; + + assert(s); + assert(p); + + if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { + avahi_log_warn("Received invalid packet."); + return; + } + + if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) { + avahi_log_warn("Received legacy unicast response with unknown id"); + return; + } + + if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) || + !j->announcing) + return; + + /* Patch the original ID into this response */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); + + /* Forward the response to the correct client */ + avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port); + + /* Undo changes to packet */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); +} + +static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { + AvahiServer *s = userdata; + AvahiAddress dest, src; + AvahiDnsPacket *p = NULL; + AvahiIfIndex iface; + uint16_t port; + uint8_t ttl; + + assert(w); + assert(fd >= 0); + assert(events & AVAHI_WATCH_IN); + + if (fd == s->fd_ipv4) { + dest.proto = src.proto = AVAHI_PROTO_INET; + p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl); + } else { + assert(fd == s->fd_ipv6); + dest.proto = src.proto = AVAHI_PROTO_INET6; + p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl); + } + + if (p) { + if (iface == AVAHI_IF_UNSPEC) + iface = avahi_find_interface_for_address(s->monitor, &dest); + + if (iface != AVAHI_IF_UNSPEC) + dispatch_packet(s, p, &src, port, &dest, iface, ttl); + else + avahi_log_error("Incoming packet received on address that isn't local."); + + avahi_dns_packet_free(p); + + avahi_cleanup_dead_entries(s); + } +} + +static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { + AvahiServer *s = userdata; + AvahiDnsPacket *p = NULL; + + assert(w); + assert(fd >= 0); + assert(events & AVAHI_WATCH_IN); + + if (fd == s->fd_legacy_unicast_ipv4) + p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL); + else { + assert(fd == s->fd_legacy_unicast_ipv6); + p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL); + } + + if (p) { + dispatch_legacy_unicast_packet(s, p); + avahi_dns_packet_free(p); + + avahi_cleanup_dead_entries(s); + } +} + +static void server_set_state(AvahiServer *s, AvahiServerState state) { + assert(s); + + if (s->state == state) + return; + + s->state = state; + + avahi_interface_monitor_update_rrs(s->monitor, 0); + + if (s->callback) + s->callback(s, state, s->userdata); +} + +static void withdraw_host_rrs(AvahiServer *s) { + assert(s); + + if (s->hinfo_entry_group) + avahi_s_entry_group_reset(s->hinfo_entry_group); + + if (s->browse_domain_entry_group) + avahi_s_entry_group_reset(s->browse_domain_entry_group); + + avahi_interface_monitor_update_rrs(s->monitor, 1); + s->n_host_rr_pending = 0; +} + +void avahi_server_decrease_host_rr_pending(AvahiServer *s) { + assert(s); + + assert(s->n_host_rr_pending > 0); + + if (--s->n_host_rr_pending == 0) + server_set_state(s, AVAHI_SERVER_RUNNING); +} + +void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { + assert(s); + assert(g); + + if (state == AVAHI_ENTRY_GROUP_REGISTERING && + s->state == AVAHI_SERVER_REGISTERING) + s->n_host_rr_pending ++; + + else if (state == AVAHI_ENTRY_GROUP_COLLISION && + (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) { + withdraw_host_rrs(s); + server_set_state(s, AVAHI_SERVER_COLLISION); + + } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED && + s->state == AVAHI_SERVER_REGISTERING) + avahi_server_decrease_host_rr_pending(s); +} + +static void register_hinfo(AvahiServer *s) { + struct utsname utsname; + AvahiRecord *r; + + assert(s); + + if (!s->config.publish_hinfo) + return; + + if (s->hinfo_entry_group) + assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group)); + else + s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL); + + if (!s->hinfo_entry_group) { + avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error)); + return; + } + + /* Fill in HINFO rr */ + if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) { + + if (uname(&utsname) < 0) + avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno)); + else { + + r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine)); + r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname)); + + avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os); + + if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) { + avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error)); + return; + } + } + + avahi_record_unref(r); + } + + if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0) + avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error)); + +} + +static void register_localhost(AvahiServer *s) { + AvahiAddress a; + assert(s); + + /* Add localhost entries */ + avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a); + avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a); + + avahi_address_parse("::1", AVAHI_PROTO_INET6, &a); + avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a); +} + +static void register_browse_domain(AvahiServer *s) { + assert(s); + + if (!s->config.publish_domain) + return; + + if (avahi_domain_equal(s->domain_name, "local")) + return; + + if (s->browse_domain_entry_group) + assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group)); + else + s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL); + + if (!s->browse_domain_entry_group) { + avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error)); + return; + } + + if (avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) { + avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error)); + return; + } + + if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0) + avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error)); +} + +static void register_stuff(AvahiServer *s) { + assert(s); + + server_set_state(s, AVAHI_SERVER_REGISTERING); + s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */ + + register_hinfo(s); + register_browse_domain(s); + avahi_interface_monitor_update_rrs(s->monitor, 0); + + assert(s->n_host_rr_pending > 0); + s->n_host_rr_pending --; + + if (s->n_host_rr_pending == 0) + server_set_state(s, AVAHI_SERVER_RUNNING); +} + +static void update_fqdn(AvahiServer *s) { + char *n; + + assert(s); + assert(s->host_name); + assert(s->domain_name); + + if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name))) + return; /* OOM */ + + avahi_free(s->host_name_fqdn); + s->host_name_fqdn = n; +} + +int avahi_server_set_host_name(AvahiServer *s, const char *host_name) { + char *hn = NULL; + assert(s); + + AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME); + + if (!host_name) + hn = avahi_get_host_name_strdup(); + else + hn = avahi_normalize_name_strdup(host_name); + + hn[strcspn(hn, ".")] = 0; + + if (avahi_domain_equal(s->host_name, hn) && 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 = hn; + + update_fqdn(s); + + register_stuff(s); + return AVAHI_OK; +} + +int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) { + char *dn = NULL; + assert(s); + + 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"); + else + dn = avahi_normalize_name_strdup(domain_name); + + 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 = dn; + update_fqdn(s); + + register_stuff(s); + + avahi_free(dn); + return AVAHI_OK; +} + +static int valid_server_config(const AvahiServerConfig *sc) { + AvahiStringList *l; + + assert(sc); + + if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX) + return AVAHI_ERR_INVALID_CONFIG; + + if (sc->host_name && !avahi_is_valid_host_name(sc->host_name)) + return AVAHI_ERR_INVALID_HOST_NAME; + + if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + for (l = sc->browse_domains; l; l = l->next) + if (!avahi_is_valid_domain_name((char*) l->text)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + return AVAHI_OK; +} + +static int setup_sockets(AvahiServer *s) { + assert(s); + + s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1; + s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1; + + if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) + return AVAHI_ERR_NO_NETWORK; + + if (s->fd_ipv4 < 0 && s->config.use_ipv4) + avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode"); + else if (s->fd_ipv6 < 0 && s->config.use_ipv6) + avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode"); + + s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1; + s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1; + + s->watch_ipv4 = + s->watch_ipv6 = + s->watch_legacy_unicast_ipv4 = + s->watch_legacy_unicast_ipv6 = NULL; + + if (s->fd_ipv4 >= 0) + s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s); + if (s->fd_ipv6 >= 0) + s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s); + + if (s->fd_legacy_unicast_ipv4 >= 0) + s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); + if (s->fd_legacy_unicast_ipv6 >= 0) + s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); + + return 0; +} + +AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) { + AvahiServer *s; + int e; + + if (sc && (e = valid_server_config(sc)) < 0) { + if (error) + *error = e; + return NULL; + } + + if (!(s = avahi_new(AvahiServer, 1))) { + if (error) + *error = AVAHI_ERR_NO_MEMORY; + + return NULL; + } + + s->poll_api = poll_api; + + if (sc) + avahi_server_config_copy(&s->config, sc); + else + avahi_server_config_init(&s->config); + + if ((e = setup_sockets(s)) < 0) { + if (error) + *error = e; + + avahi_server_config_free(&s->config); + avahi_free(s); + + return NULL; + } + + s->n_host_rr_pending = 0; + s->need_entry_cleanup = 0; + s->need_group_cleanup = 0; + s->need_browser_cleanup = 0; + s->cleanup_time_event = NULL; + s->hinfo_entry_group = NULL; + s->browse_domain_entry_group = NULL; + s->error = AVAHI_OK; + s->state = AVAHI_SERVER_INVALID; + + s->callback = callback; + s->userdata = userdata; + + s->time_event_queue = avahi_time_event_queue_new(poll_api); + + s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries); + AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups); + + s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers); + AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers); + AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers); + AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers); + + s->legacy_unicast_reflect_slots = NULL; + s->legacy_unicast_reflect_id = 0; + + s->record_list = avahi_record_list_new(); + + /* Get host name */ + s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup(); + s->host_name[strcspn(s->host_name, ".")] = 0; + s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local"); + s->host_name_fqdn = NULL; + update_fqdn(s); + + do { + s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand(); + } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID); + + if (s->config.enable_wide_area) { + s->wide_area_lookup_engine = avahi_wide_area_engine_new(s); + avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers); + } else + s->wide_area_lookup_engine = NULL; + + s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s); + + s->monitor = avahi_interface_monitor_new(s); + avahi_interface_monitor_sync(s->monitor); + + register_localhost(s); + register_stuff(s); + + return s; +} + +void avahi_server_free(AvahiServer* s) { + assert(s); + + /* Remove all browsers */ + + while (s->dns_server_browsers) + avahi_s_dns_server_browser_free(s->dns_server_browsers); + while (s->host_name_resolvers) + avahi_s_host_name_resolver_free(s->host_name_resolvers); + while (s->address_resolvers) + avahi_s_address_resolver_free(s->address_resolvers); + while (s->domain_browsers) + avahi_s_domain_browser_free(s->domain_browsers); + while (s->service_type_browsers) + avahi_s_service_type_browser_free(s->service_type_browsers); + while (s->service_browsers) + avahi_s_service_browser_free(s->service_browsers); + while (s->service_resolvers) + avahi_s_service_resolver_free(s->service_resolvers); + while (s->record_browsers) + avahi_s_record_browser_destroy(s->record_browsers); + + /* Remove all locally rgeistered stuff */ + + while(s->entries) + avahi_entry_free(s, s->entries); + + avahi_interface_monitor_free(s->monitor); + + while (s->groups) + avahi_entry_group_free(s, s->groups); + + free_slots(s); + + avahi_hashmap_free(s->entries_by_key); + avahi_record_list_free(s->record_list); + avahi_hashmap_free(s->record_browser_hashmap); + + if (s->wide_area_lookup_engine) + avahi_wide_area_engine_free(s->wide_area_lookup_engine); + avahi_multicast_lookup_engine_free(s->multicast_lookup_engine); + + if (s->cleanup_time_event) + avahi_time_event_free(s->cleanup_time_event); + + avahi_time_event_queue_free(s->time_event_queue); + + /* Free watches */ + + if (s->watch_ipv4) + s->poll_api->watch_free(s->watch_ipv4); + if (s->watch_ipv6) + s->poll_api->watch_free(s->watch_ipv6); + + if (s->watch_legacy_unicast_ipv4) + s->poll_api->watch_free(s->watch_legacy_unicast_ipv4); + if (s->watch_legacy_unicast_ipv6) + s->poll_api->watch_free(s->watch_legacy_unicast_ipv6); + + /* Free sockets */ + + if (s->fd_ipv4 >= 0) + close(s->fd_ipv4); + if (s->fd_ipv6 >= 0) + close(s->fd_ipv6); + + if (s->fd_legacy_unicast_ipv4 >= 0) + close(s->fd_legacy_unicast_ipv4); + if (s->fd_legacy_unicast_ipv6 >= 0) + close(s->fd_legacy_unicast_ipv6); + + /* Free other stuff */ + + avahi_free(s->host_name); + avahi_free(s->domain_name); + avahi_free(s->host_name_fqdn); + + avahi_server_config_free(&s->config); + + avahi_free(s); +} + +const char* avahi_server_get_domain_name(AvahiServer *s) { + assert(s); + + return s->domain_name; +} + +const char* avahi_server_get_host_name(AvahiServer *s) { + assert(s); + + return s->host_name; +} + +const char* avahi_server_get_host_name_fqdn(AvahiServer *s) { + assert(s); + + return s->host_name_fqdn; +} + +void* avahi_server_get_data(AvahiServer *s) { + assert(s); + + return s->userdata; +} + +void avahi_server_set_data(AvahiServer *s, void* userdata) { + assert(s); + + s->userdata = userdata; +} + +AvahiServerState avahi_server_get_state(AvahiServer *s) { + assert(s); + + return s->state; +} + +AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) { + assert(c); + + memset(c, 0, sizeof(AvahiServerConfig)); + c->use_ipv6 = 1; + c->use_ipv4 = 1; + c->allow_interfaces = NULL; + c->deny_interfaces = NULL; + c->host_name = NULL; + c->domain_name = NULL; + c->check_response_ttl = 0; + c->publish_hinfo = 0; + c->publish_addresses = 1; + c->publish_no_reverse = 0; + c->publish_workstation = 0; + c->publish_domain = 1; + c->use_iff_running = 0; + c->enable_reflector = 0; + c->reflect_ipv = 0; + c->add_service_cookie = 0; + c->enable_wide_area = 0; + c->n_wide_area_servers = 0; + c->disallow_other_stacks = 0; + 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; + c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX; + c->ratelimit_interval = 0; + c->ratelimit_burst = 0; + + return c; +} + +void avahi_server_config_free(AvahiServerConfig *c) { + assert(c); + + avahi_free(c->host_name); + avahi_free(c->domain_name); + avahi_string_list_free(c->browse_domains); + avahi_string_list_free(c->allow_interfaces); + avahi_string_list_free(c->deny_interfaces); +} + +AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) { + char *d = NULL, *h = NULL; + AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL; + assert(ret); + assert(c); + + if (c->host_name) + if (!(h = avahi_strdup(c->host_name))) + return NULL; + + if (c->domain_name) + if (!(d = avahi_strdup(c->domain_name))) { + avahi_free(h); + return NULL; + } + + if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) { + avahi_free(h); + avahi_free(d); + return NULL; + } + + if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) { + avahi_string_list_free(browse); + avahi_free(h); + avahi_free(d); + return NULL; + } + + if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) { + avahi_string_list_free(allow); + avahi_string_list_free(browse); + avahi_free(h); + avahi_free(d); + return NULL; + } + + *ret = *c; + ret->host_name = h; + ret->domain_name = d; + ret->browse_domains = browse; + ret->allow_interfaces = allow; + ret->deny_interfaces = deny; + + return ret; +} + +int avahi_server_errno(AvahiServer *s) { + assert(s); + + return s->error; +} + +/* Just for internal use */ +int avahi_server_set_errno(AvahiServer *s, int error) { + assert(s); + + return s->error = error; +} + +uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) { + assert(s); + + return s->local_service_cookie; +} + +static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) { + AvahiEntry *e; + + assert(s); + assert(key); + + for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) + + if ((e->interface == interface || e->interface <= 0 || interface <= 0) && + (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && + (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING)) + + return e; + + return NULL; +} + +int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) { + AvahiKey *key = NULL; + AvahiEntry *e; + int ret; + char n[AVAHI_DOMAIN_NAME_MAX]; + + assert(s); + assert(name); + assert(type); + assert(ret_group); + + AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0)) + return avahi_server_set_errno(s, ret); + + if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) + return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + + e = find_entry(s, interface, protocol, key); + avahi_key_unref(key); + + if (e) { + *ret_group = e->group; + return AVAHI_OK; + } + + return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); +} + +int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) { + AvahiKey *key = NULL; + AvahiEntry *e; + + assert(s); + assert(name); + + if (!s->host_name_fqdn) + return 0; + + if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) + return 0; + + e = find_entry(s, interface, protocol, key); + avahi_key_unref(key); + + if (!e) + return 0; + + return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name); +} + +int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) { + AvahiEntry *e; + + assert(s); + assert(record); + + for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next) + + if ((e->interface == interface || e->interface <= 0 || interface <= 0) && + (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && + (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) && + avahi_record_equal_no_ttl(record, e->record)) + return 1; + + return 0; +} + +/** Set the wide area DNS servers */ +int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) { + assert(s); + + if (!s->wide_area_lookup_engine) + return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG); + + 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; +} + +/** Set the browsing domains */ +int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) { + AvahiStringList *l; + + assert(s); + + for (l = s->config.browse_domains; l; l = l->next) + if (!avahi_is_valid_domain_name((char*) l->text)) + return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME); + + avahi_string_list_free(s->config.browse_domains); + s->config.browse_domains = avahi_string_list_copy(domains); + + return AVAHI_OK; +} diff --git a/avahi/simple-watch.c b/avahi/simple-watch.c new file mode 100644 index 0000000..8df18dd --- /dev/null +++ b/avahi/simple-watch.c @@ -0,0 +1,649 @@ +/*** + 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 "llist.h" +#include "malloc.h" +#include "timeval.h" +#include "simple-watch.h" + +struct AvahiWatch { + AvahiSimplePoll *simple_poll; + int dead; + + int idx; + struct pollfd pollfd; + + AvahiWatchCallback callback; + void *userdata; + + AVAHI_LLIST_FIELDS(AvahiWatch, watches); +}; + +struct AvahiTimeout { + AvahiSimplePoll *simple_poll; + int dead; + + int enabled; + struct timeval expiry; + + AvahiTimeoutCallback callback; + void *userdata; + + AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts); +}; + +struct AvahiSimplePoll { + AvahiPoll api; + AvahiPollFunc poll_func; + void *poll_func_userdata; + + struct pollfd* pollfds; + int n_pollfds, max_pollfds, rebuild_pollfds; + + int watch_req_cleanup, timeout_req_cleanup; + int quit; + int events_valid; + + int n_watches; + AVAHI_LLIST_HEAD(AvahiWatch, watches); + AVAHI_LLIST_HEAD(AvahiTimeout, timeouts); + + int wakeup_pipe[2]; + int wakeup_issued; + + int prepared_timeout; + + enum { + STATE_INIT, + STATE_PREPARING, + STATE_PREPARED, + STATE_RUNNING, + STATE_RAN, + STATE_DISPATCHING, + STATE_DISPATCHED, + STATE_QUIT, + STATE_FAILURE + } state; +}; + +void avahi_simple_poll_wakeup(AvahiSimplePoll *s) { + char c = 'W'; + assert(s); + + write(s->wakeup_pipe[1], &c, sizeof(c)); + s->wakeup_issued = 1; +} + +static void clear_wakeup(AvahiSimplePoll *s) { + char c[10]; /* Read ten at a time */ + + if (!s->wakeup_issued) + return; + + s->wakeup_issued = 0; + + for(;;) + if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c)) + break; +} + +static int set_nonblock(int fd) { + int n; + + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) { + AvahiWatch *w; + AvahiSimplePoll *s; + + assert(api); + assert(fd >= 0); + assert(callback); + + s = api->userdata; + assert(s); + + if (!(w = avahi_new(AvahiWatch, 1))) + return NULL; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); + + w->simple_poll = s; + w->dead = 0; + + w->pollfd.fd = fd; + w->pollfd.events = event; + w->pollfd.revents = 0; + + w->callback = callback; + w->userdata = userdata; + + w->idx = -1; + s->rebuild_pollfds = 1; + + AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w); + s->n_watches++; + + return w; +} + +static void watch_update(AvahiWatch *w, AvahiWatchEvent events) { + assert(w); + assert(!w->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(w->simple_poll); + + w->pollfd.events = events; + + if (w->idx != -1) { + assert(w->simple_poll); + w->simple_poll->pollfds[w->idx] = w->pollfd; + } else + w->simple_poll->rebuild_pollfds = 1; +} + +static AvahiWatchEvent watch_get_events(AvahiWatch *w) { + assert(w); + assert(!w->dead); + + if (w->idx != -1 && w->simple_poll->events_valid) + return w->simple_poll->pollfds[w->idx].revents; + + return 0; +} + +static void remove_pollfd(AvahiWatch *w) { + assert(w); + + if (w->idx == -1) + return; + + w->simple_poll->rebuild_pollfds = 1; +} + +static void watch_free(AvahiWatch *w) { + assert(w); + + assert(!w->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(w->simple_poll); + + remove_pollfd(w); + + w->dead = 1; + w->simple_poll->n_watches --; + w->simple_poll->watch_req_cleanup = 1; +} + +static void destroy_watch(AvahiWatch *w) { + assert(w); + + remove_pollfd(w); + AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w); + + if (!w->dead) + w->simple_poll->n_watches --; + + avahi_free(w); +} + +static void cleanup_watches(AvahiSimplePoll *s, int all) { + AvahiWatch *w, *next; + assert(s); + + for (w = s->watches; w; w = next) { + next = w->watches_next; + + if (all || w->dead) + destroy_watch(w); + } + + s->timeout_req_cleanup = 0; +} + +static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) { + AvahiTimeout *t; + AvahiSimplePoll *s; + + assert(api); + assert(callback); + + s = api->userdata; + assert(s); + + if (!(t = avahi_new(AvahiTimeout, 1))) + return NULL; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); + + t->simple_poll = s; + t->dead = 0; + + if ((t->enabled = !!tv)) + t->expiry = *tv; + + t->callback = callback; + t->userdata = userdata; + + AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t); + return t; +} + +static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { + assert(t); + assert(!t->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(t->simple_poll); + + if ((t->enabled = !!tv)) + t->expiry = *tv; +} + +static void timeout_free(AvahiTimeout *t) { + assert(t); + assert(!t->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(t->simple_poll); + + t->dead = 1; + t->simple_poll->timeout_req_cleanup = 1; +} + + +static void destroy_timeout(AvahiTimeout *t) { + assert(t); + + AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t); + + avahi_free(t); +} + +static void cleanup_timeouts(AvahiSimplePoll *s, int all) { + AvahiTimeout *t, *next; + assert(s); + + for (t = s->timeouts; t; t = next) { + next = t->timeouts_next; + + if (all || t->dead) + destroy_timeout(t); + } + + s->timeout_req_cleanup = 0; +} + +AvahiSimplePoll *avahi_simple_poll_new(void) { + AvahiSimplePoll *s; + + if (!(s = avahi_new(AvahiSimplePoll, 1))) + return NULL; + + if (pipe(s->wakeup_pipe) < 0) { + avahi_free(s); + return NULL; + } + + set_nonblock(s->wakeup_pipe[0]); + set_nonblock(s->wakeup_pipe[1]); + + s->api.userdata = s; + + s->api.watch_new = watch_new; + s->api.watch_free = watch_free; + s->api.watch_update = watch_update; + s->api.watch_get_events = watch_get_events; + + s->api.timeout_new = timeout_new; + s->api.timeout_free = timeout_free; + s->api.timeout_update = timeout_update; + + s->pollfds = NULL; + s->max_pollfds = s->n_pollfds = 0; + s->rebuild_pollfds = 1; + s->quit = 0; + s->n_watches = 0; + s->events_valid = 0; + + s->watch_req_cleanup = 0; + s->timeout_req_cleanup = 0; + + s->prepared_timeout = 0; + + s->state = STATE_INIT; + + s->wakeup_issued = 0; + + avahi_simple_poll_set_func(s, NULL, NULL); + + AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches); + AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts); + + return s; +} + +void avahi_simple_poll_free(AvahiSimplePoll *s) { + assert(s); + + cleanup_timeouts(s, 1); + cleanup_watches(s, 1); + assert(s->n_watches == 0); + + avahi_free(s->pollfds); + + if (s->wakeup_pipe[0] >= 0) + close(s->wakeup_pipe[0]); + + if (s->wakeup_pipe[1] >= 0) + close(s->wakeup_pipe[1]); + + avahi_free(s); +} + +static int rebuild(AvahiSimplePoll *s) { + AvahiWatch *w; + int idx; + + assert(s); + + if (s->n_watches+1 > s->max_pollfds) { + struct pollfd *n; + + s->max_pollfds = s->n_watches + 10; + + if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds))) + return -1; + + s->pollfds = n; + } + + + s->pollfds[0].fd = s->wakeup_pipe[0]; + s->pollfds[0].events = POLLIN; + s->pollfds[0].revents = 0; + + idx = 1; + + for (w = s->watches; w; w = w->watches_next) { + + if(w->dead) + continue; + + assert(w->idx < s->max_pollfds); + s->pollfds[w->idx = idx++] = w->pollfd; + } + + s->n_pollfds = idx; + s->events_valid = 0; + s->rebuild_pollfds = 0; + + return 0; +} + +static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) { + AvahiTimeout *t, *n = NULL; + assert(s); + + for (t = s->timeouts; t; t = t->timeouts_next) { + + if (t->dead || !t->enabled) + continue; + + if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0) + n = t; + } + + return n; +} + +static void timeout_callback(AvahiTimeout *t) { + assert(t); + assert(!t->dead); + assert(t->enabled); + + t->enabled = 0; + t->callback(t, t->userdata); +} + +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) { + AvahiTimeout *next_timeout; + + assert(s); + assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE); + s->state = STATE_PREPARING; + + /* Clear pending wakeup requests */ + clear_wakeup(s); + + /* Cleanup things first */ + if (s->watch_req_cleanup) + cleanup_watches(s, 0); + + if (s->timeout_req_cleanup) + cleanup_timeouts(s, 0); + + /* Check whether a quit was requested */ + if (s->quit) { + s->state = STATE_QUIT; + return 1; + } + + /* Do we need to rebuild our array of pollfds? */ + if (s->rebuild_pollfds) + if (rebuild(s) < 0) { + s->state = STATE_FAILURE; + return -1; + } + + /* Calculate the wakeup time */ + if ((next_timeout = find_next_timeout(s))) { + struct timeval now; + int t; + AvahiUsec usec; + + if (next_timeout->expiry.tv_sec == 0 && + next_timeout->expiry.tv_usec == 0) { + + /* Just a shortcut so that we don't need to call gettimeofday() */ + timeout = 0; + goto finish; + } + + gettimeofday(&now, NULL); + usec = avahi_timeval_diff(&next_timeout->expiry, &now); + + if (usec <= 0) { + /* Timeout elapsed */ + + timeout = 0; + goto finish; + } + + /* Calculate sleep time. We add 1ms because otherwise we'd + * wake up too early most of the time */ + t = (int) (usec / 1000) + 1; + + if (timeout < 0 || timeout > t) + timeout = t; + } + +finish: + s->prepared_timeout = timeout; + s->state = STATE_PREPARED; + return 0; +} + +int avahi_simple_poll_run(AvahiSimplePoll *s) { + assert(s); + assert(s->state == STATE_PREPARED || s->state == STATE_FAILURE); + + s->state = STATE_RUNNING; + + for (;;) { + errno = 0; + + if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) { + + if (errno == EINTR) + continue; + + s->state = STATE_FAILURE; + return -1; + } + + break; + } + + /* The poll events are now valid again */ + s->events_valid = 1; + + /* Update state */ + s->state = STATE_RAN; + return 0; +} + +int avahi_simple_poll_dispatch(AvahiSimplePoll *s) { + AvahiTimeout *next_timeout; + AvahiWatch *w; + + assert(s); + assert(s->state == STATE_RAN); + s->state = STATE_DISPATCHING; + + /* We execute only on callback in every iteration */ + + /* Check whether the wakeup time has been reached now */ + if ((next_timeout = find_next_timeout(s))) { + + if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) { + + /* Just a shortcut so that we don't need to call gettimeofday() */ + timeout_callback(next_timeout); + goto finish; + } + + if (avahi_age(&next_timeout->expiry) >= 0) { + + /* Timeout elapsed */ + timeout_callback(next_timeout); + goto finish; + } + } + + /* Look for some kind of I/O event */ + for (w = s->watches; w; w = w->watches_next) { + + if (w->dead) + continue; + + assert(w->idx >= 0); + assert(w->idx < s->n_pollfds); + + if (s->pollfds[w->idx].revents != 0) { + w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata); + goto finish; + } + } + +finish: + + s->state = STATE_DISPATCHED; + return 0; +} + +int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) { + int r; + + if ((r = avahi_simple_poll_prepare(s, timeout)) != 0) + return r; + + if ((r = avahi_simple_poll_run(s)) != 0) + return r; + + if ((r = avahi_simple_poll_dispatch(s)) != 0) + return r; + + return 0; +} + +void avahi_simple_poll_quit(AvahiSimplePoll *s) { + assert(s); + + s->quit = 1; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); +} + +const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) { + assert(s); + + return &s->api; +} + +static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, AVAHI_GCC_UNUSED void *userdata) { + return poll(ufds, nfds, timeout); +} + +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) { + assert(s); + + s->poll_func = func ? func : system_poll; + s->poll_func_userdata = func ? userdata : NULL; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); +} + +int avahi_simple_poll_loop(AvahiSimplePoll *s) { + int r; + + assert(s); + + for (;;) + if ((r = avahi_simple_poll_iterate(s, -1)) != 0) + if (r >= 0 || errno != EINTR) + return r; +} diff --git a/avahi/simple-watch.h b/avahi/simple-watch.h new file mode 100644 index 0000000..108cadf --- /dev/null +++ b/avahi/simple-watch.h @@ -0,0 +1,85 @@ +#ifndef foosimplewatchhfoo +#define foosimplewatchhfoo + +/*** + 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. +***/ + +/** \file simple-watch.h Simple poll() based main loop implementation */ + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** A main loop object. Main loops of this type aren't very flexible + * since they only support a single wakeup type. Nevertheless it + * should suffice for small test and example applications. */ +typedef struct AvahiSimplePoll AvahiSimplePoll; + +/** Create a new main loop object */ +AvahiSimplePoll *avahi_simple_poll_new(void); + +/** Free a main loop object */ +void avahi_simple_poll_free(AvahiSimplePoll *s); + +/** Return the abstracted poll API object for this main loop + * object. The is will return the same pointer each time it is + * called. */ +const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s); + +/** Run a single main loop iteration of this main loop. If sleep_time +is < 0 this will block until any of the registered events happens, +then it will execute the attached callback function. If sleep_time is +0 the routine just checks if any event is pending. If yes the attached +callback function is called, otherwise the function returns +immediately. If sleep_time > 0 the function will block for at most the +specified time in msecs. Returns -1 on error, 0 on success and 1 if a +quit request has been scheduled. Usually this function should be called +in a loop until it returns a non-zero value*/ +int avahi_simple_poll_iterate(AvahiSimplePoll *s, int sleep_time); + +/** Request that the main loop quits. If this is called the next + call to avahi_simple_poll_iterate() will return 1 */ +void avahi_simple_poll_quit(AvahiSimplePoll *s); + +/** Prototype for a poll() type function */ +typedef int (*AvahiPollFunc)(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata); + +/** Replace the internally used poll() function. By default the system's poll() will be used */ +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata); + +/** The first stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout); + +/** The second stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_run(AvahiSimplePoll *s); + +/** The third and final stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_dispatch(AvahiSimplePoll *s); + +/** Call avahi_simple_poll_iterate() in a loop and return if it returns non-zero */ +int avahi_simple_poll_loop(AvahiSimplePoll *s); + +/** Wakeup the main loop. (for threaded environments) */ +void avahi_simple_poll_wakeup(AvahiSimplePoll *s); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/socket.c b/avahi/socket.c new file mode 100644 index 0000000..17ab6e5 --- /dev/null +++ b/avahi/socket.c @@ -0,0 +1,993 @@ +/*** + 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 +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +#ifdef IP_RECVIF +#include +#endif + +#include "dns.h" +#include "fdutil.h" +#include "socket.h" +#include "log.h" +#include "addr-util.h" + +/* this is a portability hack */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif +#endif + +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif +#endif + +static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { + assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in)); + ret_sa->sin_family = AF_INET; + ret_sa->sin_port = htons(AVAHI_MDNS_PORT); + inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr); +} + +static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { + assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in6)); + ret_sa->sin6_family = AF_INET6; + ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); + inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr); +} + +static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) { + assert(ret_sa); + assert(a); + assert(port > 0); + + memset(ret_sa, 0, sizeof(struct sockaddr_in)); + ret_sa->sin_family = AF_INET; + ret_sa->sin_port = htons(port); + memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address)); +} + +static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) { + assert(ret_sa); + assert(a); + assert(port > 0); + + memset(ret_sa, 0, sizeof(struct sockaddr_in6)); + ret_sa->sin6_family = AF_INET6; + ret_sa->sin6_port = htons(port); + memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address)); +} + +int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { +#ifdef HAVE_STRUCT_IP_MREQN + struct ip_mreqn mreq; +#else + struct ip_mreq mreq; +#endif + struct sockaddr_in sa; + + assert(fd >= 0); + assert(idx >= 0); + assert(a); + + memset(&mreq, 0, sizeof(mreq)); +#ifdef HAVE_STRUCT_IP_MREQN + mreq.imr_ifindex = idx; + mreq.imr_address.s_addr = a->address; +#else + mreq.imr_interface.s_addr = a->address; +#endif + mdns_mcast_group_ipv4(&sa); + mreq.imr_multiaddr = sa.sin_addr; + + /* Some network drivers have issues with dropping membership of + * mcast groups when the iface is down, but don't allow rejoining + * when it comes back up. This is an ugly workaround */ + if (join) + setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + + if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno)); + return -1; + } + + return 0; +} + +int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) { + struct ipv6_mreq mreq6; + struct sockaddr_in6 sa6; + + assert(fd >= 0); + assert(idx >= 0); + assert(a); + + memset(&mreq6, 0, sizeof(mreq6)); + mdns_mcast_group_ipv6 (&sa6); + mreq6.ipv6mr_multiaddr = sa6.sin6_addr; + mreq6.ipv6mr_interface = idx; + + if (join) + setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); + + if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno)); + return -1; + } + + return 0; +} + +static int reuseaddr(int fd) { + int yes; + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno)); + return -1; + } + +#ifdef SO_REUSEPORT + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { + avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno)); + return -1; + } +#endif + + return 0; +} + +static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { + + assert(fd >= 0); + assert(sa); + assert(l > 0); + + if (bind(fd, sa, l) < 0) { + + if (errno != EADDRINUSE) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + return -1; + } + + avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***", + sa->sa_family == AF_INET ? "IPv4" : "IPv6"); + + /* Try again, this time with SO_REUSEADDR set */ + if (reuseaddr(fd) < 0) + return -1; + + if (bind(fd, sa, l) < 0) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + return -1; + } + } else { + + /* We enable SO_REUSEADDR afterwards, to make sure that the + * user may run other mDNS implementations if he really + * wants. */ + + if (reuseaddr(fd) < 0) + return -1; + } + + return 0; +} + +static int ipv4_pktinfo(int fd) { + int yes; + +#ifdef IP_PKTINFO + yes = 1; + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno)); + return -1; + } +#else + +#ifdef IP_RECVINTERFACE + yes = 1; + if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno)); + return -1; + } +#elif defined(IP_RECVIF) + yes = 1; + if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVIF failed: %s", strerror(errno)); + return -1; + } +#endif + +#ifdef IP_RECVDSTADDR + yes = 1; + if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno)); + return -1; + } +#endif + +#endif /* IP_PKTINFO */ + +#ifdef IP_RECVTTL + yes = 1; + if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno)); + return -1; + } +#endif + + return 0; +} + +static int ipv6_pktinfo(int fd) { + int yes; + +#ifdef IPV6_RECVPKTINFO + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno)); + return -1; + } +#elif defined(IPV6_PKTINFO) + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno)); + return -1; + } +#endif + +#ifdef IPV6_RECVHOPS + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno)); + return -1; + } +#elif defined(IPV6_RECVHOPLIMIT) + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno)); + return -1; + } +#elif defined(IPV6_HOPLIMIT) + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno)); + return -1; + } +#endif + + return 0; +} + +int avahi_open_socket_ipv4(int no_reuse) { + struct sockaddr_in local; + int fd = -1, r, ittl; + uint8_t ttl, cyes; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { + avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno)); + goto fail; + } + + ittl = 255; + if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) { + avahi_log_warn("IP_TTL failed: %s", strerror(errno)); + goto fail; + } + + cyes = 1; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) { + avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(AVAHI_MDNS_PORT); + + if (no_reuse) + r = bind(fd, (struct sockaddr*) &local, sizeof(local)); + else + r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); + + if (r < 0) + goto fail; + + if (ipv4_pktinfo (fd) < 0) + goto fail; + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +int avahi_open_socket_ipv6(int no_reuse) { + struct sockaddr_in6 sa, local; + int fd = -1, yes, r; + int ttl; + + mdns_mcast_group_ipv6(&sa); + + if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + avahi_log_warn("IPV6_UNICAST_HOPS 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; + } + + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin6_family = AF_INET6; + local.sin6_port = htons(AVAHI_MDNS_PORT); + + if (no_reuse) + r = bind(fd, (struct sockaddr*) &local, sizeof(local)); + else + r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); + + if (r < 0) + goto fail; + + if (ipv6_pktinfo(fd) < 0) + goto fail; + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { + assert(fd >= 0); + assert(msg); + + for (;;) { + + if (sendmsg(fd, msg, flags) >= 0) + break; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) { + char where[64]; + struct sockaddr_in *sin = msg->msg_name; + + inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where)); + avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno)); + return -1; + } + + if (avahi_wait_for_write(fd) < 0) + return -1; + } + + return 0; +} + +int avahi_send_dns_packet_ipv4( + int fd, + AvahiIfIndex interface, + AvahiDnsPacket *p, + const AvahiIPv4Address *src_address, + const AvahiIPv4Address *dst_address, + uint16_t dst_port) { + + struct sockaddr_in sa; + struct msghdr msg; + struct iovec io; +#ifdef IP_PKTINFO + struct cmsghdr *cmsg; + size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; +#elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR) + struct cmsghdr *cmsg; + size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1]; +#endif + + assert(fd >= 0); + assert(p); + assert(avahi_dns_packet_check_valid(p) >= 0); + assert(!dst_address || dst_port > 0); + + if (!dst_address) + mdns_mcast_group_ipv4(&sa); + else + ipv4_address_to_sockaddr(&sa, dst_address, dst_port); + + memset(&io, 0, sizeof(io)); + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + +#ifdef IP_PKTINFO + if (interface > 0 || src_address) { + struct in_pktinfo *pkti; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + msg.msg_control = cmsg_data; + msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); + + if (interface > 0) + pkti->ipi_ifindex = interface; + + if (src_address) + pkti->ipi_spec_dst.s_addr = src_address->address; + } +#elif defined(IP_MULTICAST_IF) + if (src_address) { + struct in_addr any = { INADDR_ANY }; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) { + avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno)); + return -1; + } + } +#elif defined(IP_SENDSRCADDR) + if (src_address) { + struct in_addr *addr; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + msg.msg_control = cmsg_data; + msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + + addr = (struct in_addr *)CMSG_DATA(cmsg); + addr->s_addr = src_address->address; + } +#elif defined(__GNUC__) +#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available" +#endif + + 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) { + + struct sockaddr_in6 sa; + struct msghdr msg; + struct iovec io; + struct cmsghdr *cmsg; + size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1]; + + assert(fd >= 0); + assert(p); + assert(avahi_dns_packet_check_valid(p) >= 0); + assert(!dst_address || dst_port > 0); + + if (!dst_address) + mdns_mcast_group_ipv6(&sa); + else + ipv6_address_to_sockaddr(&sa, dst_address, dst_port); + + memset(&io, 0, sizeof(io)); + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + if (interface > 0 || src_address) { + struct in6_pktinfo *pkti; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + msg.msg_control = cmsg_data; + msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg); + + if (interface > 0) + pkti->ipi6_ifindex = interface; + + if (src_address) + memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address)); + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + 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 *p= NULL; + struct msghdr msg; + struct iovec io; + size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */ + ssize_t l; + struct cmsghdr *cmsg; + int found_addr = 0; + int ms; + struct sockaddr_in sa; + + assert(fd >= 0); + + if (ioctl(fd, FIONREAD, &ms) < 0) { + avahi_log_warn("ioctl(): %s", strerror(errno)); + goto fail; + } + + if (ms < 0) { + avahi_log_warn("FIONREAD returned negative value."); + goto fail; + } + + p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); + + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->max_size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + if ((l = recvmsg(fd, &msg, 0)) < 0) { + /* Linux returns EAGAIN when an invalid IP packet has been + received. 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; + } + + /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So + * fail after having read them. */ + if (!ms) + goto fail; + + if (sa.sin_addr.s_addr == INADDR_ANY) + /* Linux 2.4 behaves very strangely sometimes! */ + goto fail; + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + p->size = (size_t) l; + + if (ret_src_port) + *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); + + if (ret_src_address) { + AvahiAddress a; + avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); + *ret_src_address = a.data.ipv4; + } + + if (ret_ttl) + *ret_ttl = 255; + + if (ret_iface) + *ret_iface = AVAHI_IF_UNSPEC; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IP) { + + switch (cmsg->cmsg_type) { +#ifdef IP_RECVTTL + case IP_RECVTTL: +#endif + case IP_TTL: + if (ret_ttl) + *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + + break; + +#ifdef IP_PKTINFO + case IP_PKTINFO: { + struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); + + if (ret_iface && i->ipi_ifindex > 0) + *ret_iface = (int) i->ipi_ifindex; + + if (ret_dst_address) + ret_dst_address->address = i->ipi_addr.s_addr; + + found_addr = 1; + + break; + } +#endif + +#ifdef IP_RECVIF + case IP_RECVIF: { + struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg); + + if (ret_iface) { +#ifdef __sun + if (*(uint_t*) sdl > 0) + *ret_iface = *(uint_t*) sdl; +#else + + if (sdl->sdl_index > 0) + *ret_iface = (int) sdl->sdl_index; +#endif + } + + break; + } +#endif + +#ifdef IP_RECVDSTADDR + case IP_RECVDSTADDR: + if (ret_dst_address) + memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4); + + found_addr = 1; + break; +#endif + + default: + avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type); + break; + } + } + } + + assert(found_addr); + + return p; + +fail: + if (p) + avahi_dns_packet_free(p); + + 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 *p = NULL; + struct msghdr msg; + struct iovec io; + size_t aux[1024 / sizeof(size_t)]; + ssize_t l; + int ms; + struct cmsghdr *cmsg; + int found_ttl = 0, found_iface = 0; + struct sockaddr_in6 sa; + + assert(fd >= 0); + + if (ioctl(fd, FIONREAD, &ms) < 0) { + avahi_log_warn("ioctl(): %s", strerror(errno)); + goto fail; + } + + if (ms < 0) { + avahi_log_warn("FIONREAD returned negative value."); + goto fail; + } + + p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); + + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->max_size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr*) &sa; + msg.msg_namelen = sizeof(sa); + + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + if ((l = recvmsg(fd, &msg, 0)) < 0) { + /* Linux returns EAGAIN when an invalid IP packet has been + received. 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; + } + + /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So + * fail after having read them. */ + if (!ms) + goto fail; + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + p->size = (size_t) l; + + if (ret_src_port) + *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); + + if (ret_src_address) { + AvahiAddress a; + avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); + *ret_src_address = a.data.ipv6; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + + switch (cmsg->cmsg_type) { + + case IPV6_HOPLIMIT: + + if (ret_ttl) + *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + + found_ttl = 1; + + break; + + case IPV6_PKTINFO: { + struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); + + if (ret_iface && i->ipi6_ifindex > 0) + *ret_iface = i->ipi6_ifindex; + + if (ret_dst_address) + memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16); + + found_iface = 1; + break; + } + + default: + avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type); + break; + } + } + } + + assert(found_iface); + assert(found_ttl); + + return p; + +fail: + if (p) + avahi_dns_packet_free(p); + + return NULL; +} + +int avahi_open_unicast_socket_ipv4(void) { + struct sockaddr_in local; + int fd = -1; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + goto fail; + } + + if (ipv4_pktinfo(fd) < 0) { + goto fail; + } + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +int avahi_open_unicast_socket_ipv6(void) { + struct sockaddr_in6 local; + 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; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + goto fail; + } + + if (ipv6_pktinfo(fd) < 0) + goto fail; + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} diff --git a/avahi/socket.h b/avahi/socket.h new file mode 100644 index 0000000..92f12d7 --- /dev/null +++ b/avahi/socket.h @@ -0,0 +1,47 @@ +#ifndef foosockethfoo +#define foosockethfoo + +/*** + 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 "dns.h" + +#define AVAHI_MDNS_PORT 5353 +#define AVAHI_DNS_PORT 53 +#define AVAHI_IPV4_MCAST_GROUP "224.0.0.251" +#define AVAHI_IPV6_MCAST_GROUP "ff02::fb" + +int avahi_open_socket_ipv4(int no_reuse); +int avahi_open_socket_ipv6(int no_reuse); + +int avahi_open_unicast_socket_ipv4(void); +int avahi_open_unicast_socket_ipv6(void); + +int avahi_send_dns_packet_ipv4(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv4Address *src_address, const AvahiIPv4Address *dst_address, uint16_t dst_port); +int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port); + +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_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl); + +int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *local_address, int iface, int join); +int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *local_address, int iface, int join); + +#endif diff --git a/avahi/strlst-test.c b/avahi/strlst-test.c new file mode 100644 index 0000000..0945b37 --- /dev/null +++ b/avahi/strlst-test.c @@ -0,0 +1,127 @@ +/*** + 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 "strlst.h" +#include "malloc.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + char *t, *v; + uint8_t data[1024]; + AvahiStringList *a = NULL, *b, *p; + size_t size, n; + int r; + + a = avahi_string_list_new("prefix", "a", "b", NULL); + + a = avahi_string_list_add(a, "start"); + a = avahi_string_list_add(a, "foo=99"); + a = avahi_string_list_add(a, "bar"); + a = avahi_string_list_add(a, ""); + a = avahi_string_list_add(a, ""); + a = avahi_string_list_add(a, "quux"); + a = avahi_string_list_add(a, ""); + a = avahi_string_list_add_arbitrary(a, (const uint8_t*) "null\0null", 9); + a = avahi_string_list_add_printf(a, "seven=%i %c", 7, 'x'); + a = avahi_string_list_add_pair(a, "blubb", "blaa"); + a = avahi_string_list_add_pair(a, "uxknurz", NULL); + a = avahi_string_list_add_pair_arbitrary(a, "uxknurz2", (const uint8_t*) "blafasel\0oerks", 14); + + a = avahi_string_list_add(a, "end"); + + t = avahi_string_list_to_string(a); + printf("--%s--\n", t); + avahi_free(t); + + n = avahi_string_list_serialize(a, NULL, 0); + size = avahi_string_list_serialize(a, data, sizeof(data)); + assert(size == n); + + printf("%zu\n", size); + + for (t = (char*) data, n = 0; n < size; n++, t++) { + if (*t <= 32) + printf("(%u)", *t); + else + printf("%c", *t); + } + + printf("\n"); + + assert(avahi_string_list_parse(data, size, &b) == 0); + + printf("equal: %i\n", avahi_string_list_equal(a, b)); + + t = avahi_string_list_to_string(b); + printf("--%s--\n", t); + avahi_free(t); + + avahi_string_list_free(b); + + b = avahi_string_list_copy(a); + + assert(avahi_string_list_equal(a, b)); + + t = avahi_string_list_to_string(b); + printf("--%s--\n", t); + avahi_free(t); + + p = avahi_string_list_find(a, "seven"); + assert(p); + + r = avahi_string_list_get_pair(p, &t, &v, NULL); + assert(r >= 0); + assert(t); + assert(v); + + printf("<%s>=<%s>\n", t, v); + avahi_free(t); + avahi_free(v); + + p = avahi_string_list_find(a, "quux"); + assert(p); + + r = avahi_string_list_get_pair(p, &t, &v, NULL); + assert(r >= 0); + assert(t); + assert(!v); + + printf("<%s>=<%s>\n", t, v); + avahi_free(t); + avahi_free(v); + + avahi_string_list_free(a); + avahi_string_list_free(b); + + n = avahi_string_list_serialize(NULL, NULL, 0); + size = avahi_string_list_serialize(NULL, data, sizeof(data)); + assert(size == 1); + assert(size == n); + + assert(avahi_string_list_parse(data, size, &a) == 0); + assert(!a); + + return 0; +} diff --git a/avahi/strlst.c b/avahi/strlst.c new file mode 100644 index 0000000..b861cf8 --- /dev/null +++ b/avahi/strlst.c @@ -0,0 +1,505 @@ +/*** + 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 "strlst.h" +#include "malloc.h" +#include "defs.h" + +AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size) { + AvahiStringList *n; + + if (!(n = avahi_malloc(sizeof(AvahiStringList) + size))) + return NULL; + + n->next = l; + n->size = size; + + /* NUL terminate strings, just to make sure */ + n->text[size] = 0; + + return n; +} + +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t*text, size_t size) { + AvahiStringList *n; + + assert(size == 0 || text); + + if (!(n = avahi_string_list_add_anonymous(l, size))) + return NULL; + + if (size > 0) + memcpy(n->text, text, size); + + return n; +} + +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text) { + assert(text); + + return avahi_string_list_add_arbitrary(l, (const uint8_t*) text, strlen(text)); +} + +int avahi_string_list_parse(const void* data, size_t size, AvahiStringList **ret) { + const uint8_t *c; + AvahiStringList *r = NULL; + + assert(data); + assert(ret); + + c = data; + while (size > 0) { + size_t k; + + k = *(c++); + size--; + + if (k > size) + goto fail; /* Overflow */ + + if (k > 0) { /* Ignore empty strings */ + AvahiStringList *n; + + if (!(n = avahi_string_list_add_arbitrary(r, c, k))) + goto fail; /* OOM */ + + r = n; + } + + c += k; + size -= k; + } + + *ret = r; + + return 0; + +fail: + avahi_string_list_free(r); + return -1; +} + +void avahi_string_list_free(AvahiStringList *l) { + AvahiStringList *n; + + while (l) { + n = l->next; + avahi_free(l); + l = n; + } +} + +AvahiStringList* avahi_string_list_reverse(AvahiStringList *l) { + AvahiStringList *r = NULL, *n; + + while (l) { + n = l->next; + l->next = r; + r = l; + l = n; + } + + return r; +} + +char* avahi_string_list_to_string(AvahiStringList *l) { + AvahiStringList *n; + size_t s = 0; + char *t, *e; + + for (n = l; n; n = n->next) { + if (n != l) + s ++; + + s += n->size+2; + } + + if (!(t = e = avahi_new(char, s+1))) + return NULL; + + l = avahi_string_list_reverse(l); + + for (n = l; n; n = n->next) { + if (n != l) + *(e++) = ' '; + + *(e++) = '"'; + strncpy(e, (char*) n->text, n->size); + e[n->size] = 0; + e = strchr(e, 0); + *(e++) = '"'; + + assert(e); + } + + l = avahi_string_list_reverse(l); + + *e = 0; + + return t; +} + +size_t avahi_string_list_serialize(AvahiStringList *l, void *data, size_t size) { + size_t used = 0; + + if (data) { + AvahiStringList *n; + uint8_t *c; + + l = avahi_string_list_reverse(l); + c = data; + + for (n = l; size > 1 && n; n = n->next) { + size_t k; + + if ((k = n->size) == 0) + /* Skip empty strings */ + continue; + + if (k > 255) + /* Truncate strings at 255 characters */ + k = 255; + + if (k > size-1) + /* Make sure this string fits in */ + k = size-1; + + *(c++) = (uint8_t) k; + memcpy(c, n->text, k); + c += k; + + used += 1 + k; + size -= 1 + k; + } + + l = avahi_string_list_reverse(l); + + if (used == 0 && size > 0) { + + /* Empty lists are treated specially. To comply with + * section 6.1 of the DNS-SD spec, we return a single + * empty string (i.e. a NUL byte)*/ + + *(uint8_t*) data = 0; + used = 1; + } + + } else { + AvahiStringList *n; + + for (n = l; n; n = n->next) { + size_t k; + + if ((k = n->size) == 0) + continue; + + if (k > 255) + k = 255; + + used += 1+k; + } + + if (used == 0) + used = 1; + } + + return used; +} + +int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b) { + + for (;;) { + if (!a && !b) + return 1; + + if (!a || !b) + return 0; + + if (a->size != b->size) + return 0; + + if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0) + return 0; + + a = a->next; + b = b->next; + } +} + +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) { + va_list va; + + va_start(va, r); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + + return r; +} + +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) { + const char *txt; + + while ((txt = va_arg(va, const char*))) + r = avahi_string_list_add(r, txt); + + return r; +} + +AvahiStringList *avahi_string_list_new(const char *txt, ...) { + va_list va; + AvahiStringList *r = NULL; + + if (txt) { + r = avahi_string_list_add(r, txt); + + va_start(va, txt); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + } + + return r; +} + +AvahiStringList *avahi_string_list_new_va(va_list va) { + return avahi_string_list_add_many_va(NULL, va); +} + +AvahiStringList *avahi_string_list_copy(const AvahiStringList *l) { + AvahiStringList *r = NULL; + + for (; l; l = l->next) + if (!(r = avahi_string_list_add_arbitrary(r, l->text, l->size))) { + avahi_string_list_free(r); + return NULL; + } + + return avahi_string_list_reverse(r); +} + +AvahiStringList *avahi_string_list_new_from_array(const char *array[], int length) { + AvahiStringList *r = NULL; + int i; + + assert(array); + + for (i = 0; length >= 0 ? i < length : !!array[i]; i++) + r = avahi_string_list_add(r, array[i]); + + return r; +} + +unsigned avahi_string_list_length(const AvahiStringList *l) { + unsigned n = 0; + + for (; l; l = l->next) + n++; + + return n; +} + +AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va) { + size_t len = 80; + AvahiStringList *r; + + assert(format); + + if (!(r = avahi_malloc(sizeof(AvahiStringList) + len))) + return NULL; + + for (;;) { + int n; + AvahiStringList *nr; + va_list va2; + + va_copy(va2, va); + n = vsnprintf((char*) r->text, len, format, va2); + va_end(va2); + + if (n >= 0 && n < (int) len) + break; + + if (n >= 0) + len = n+1; + else + len *= 2; + + if (!(nr = avahi_realloc(r, sizeof(AvahiStringList) + len))) { + avahi_free(r); + return NULL; + } + + r = nr; + } + + r->next = l; + r->size = strlen((char*) r->text); + + return r; +} + +AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) { + va_list va; + + assert(format); + + va_start(va, format); + l = avahi_string_list_add_vprintf(l, format, va); + va_end(va); + + return l; +} + +AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key) { + size_t n; + + assert(key); + n = strlen(key); + + for (; l; l = l->next) { + if (strcasecmp((char*) l->text, key) == 0) + return l; + + if (strncasecmp((char*) l->text, key, n) == 0 && l->text[n] == '=') + return l; + } + + return NULL; +} + +AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value) { + assert(key); + + if (value) + return avahi_string_list_add_printf(l, "%s=%s", key, value); + else + return avahi_string_list_add(l, key); +} + +AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size) { + size_t n; + assert(key); + + if (!value) + return avahi_string_list_add(l, key); + + n = strlen(key); + + if (!(l = avahi_string_list_add_anonymous(l, n + 1 + size))) + return NULL; + + memcpy(l->text, key, n); + l->text[n] = '='; + memcpy(l->text + n + 1, value, size); + + return l; +} + +int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size) { + char *e; + + assert(l); + + if (!(e = memchr(l->text, '=', l->size))) { + + if (key) + if (!(*key = avahi_strdup((char*) l->text))) + return -1; + + if (value) + *value = NULL; + + if (size) + *size = 0; + + } else { + size_t n; + + if (key) + if (!(*key = avahi_strndup((char*) l->text, e - (char *) l->text))) + return -1; + + e++; /* Advance after '=' */ + + n = l->size - (e - (char*) l->text); + + if (value) { + + if (!(*value = avahi_memdup(e, n+1))) { + if (key) + avahi_free(*key); + return -1; + } + + (*value)[n] = 0; + } + + if (size) + *size = n; + } + + return 0; +} + +AvahiStringList *avahi_string_list_get_next(AvahiStringList *l) { + assert(l); + return l->next; +} + +uint8_t *avahi_string_list_get_text(AvahiStringList *l) { + assert(l); + return l->text; +} + +size_t avahi_string_list_get_size(AvahiStringList *l) { + assert(l); + return l->size; +} + +uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l) { + AvahiStringList *f; + char *value = NULL, *end = NULL; + uint32_t ret; + + if (!(f = avahi_string_list_find(l, AVAHI_SERVICE_COOKIE))) + return AVAHI_SERVICE_COOKIE_INVALID; + + if (avahi_string_list_get_pair(f, NULL, &value, NULL) < 0 || !value) + return AVAHI_SERVICE_COOKIE_INVALID; + + ret = (uint32_t) strtoll(value, &end, 0); + + if (*value && end && *end != 0) { + avahi_free(value); + return AVAHI_SERVICE_COOKIE_INVALID; + } + + avahi_free(value); + + return ret; +} diff --git a/avahi/strlst.h b/avahi/strlst.h new file mode 100644 index 0000000..c726405 --- /dev/null +++ b/avahi/strlst.h @@ -0,0 +1,180 @@ +#ifndef footxtlisthfoo +#define footxtlisthfoo + +/*** + 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. +***/ + +/** \file strlst.h Implementation of a data type to store lists of strings */ + +#include +#include +#include + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Linked list of strings that can contain any number of binary + * characters, including NUL bytes. An empty list is created by + * assigning a NULL to a pointer to AvahiStringList. The string list + * is stored in reverse order, so that appending to the string list is + * effectively a prepending to the linked list. This object is used + * primarily for storing DNS TXT record data. */ +typedef struct AvahiStringList { + struct AvahiStringList *next; /**< Pointer to the next linked list element */ + size_t size; /**< Size of text[] */ + uint8_t text[1]; /**< Character data */ +} AvahiStringList; + +/** @{ \name Construction and destruction */ + +/** Create a new string list by taking a variable list of NUL + * terminated strings. The strings are copied using g_strdup(). The + * argument list must be terminated by a NULL pointer. */ +AvahiStringList *avahi_string_list_new(const char *txt, ...) AVAHI_GCC_SENTINEL; + +/** \cond fulldocs */ +/** Same as avahi_string_list_new() but pass a va_list structure */ +AvahiStringList *avahi_string_list_new_va(va_list va); +/** \endcond */ + +/** Create a new string list from a string array. The strings are + * copied using g_strdup(). length should contain the length of the + * array, or -1 if the array is NULL terminated*/ +AvahiStringList *avahi_string_list_new_from_array(const char **array, int length); + +/** Free a string list */ +void avahi_string_list_free(AvahiStringList *l); + +/** @} */ + +/** @{ \name Adding strings */ + +/** Append a NUL terminated string to the specified string list. The + * passed string is copied using g_strdup(). Returns the new list + * start. */ +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text); + +/** Append a new NUL terminated formatted string to the specified string list */ +AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) AVAHI_GCC_PRINTF_ATTR23; + +/** \cond fulldocs */ +/** Append a new NUL terminated formatted string to the specified string list */ +AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va); +/** \endcond */ + +/** Append an arbitrary length byte string to the list. Returns the + * new list start. */ +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t *text, size_t size); + +/** Append a new entry to the string list. The string is not filled +with data. The caller should fill in string data afterwards by writing +it to l->text, where l is the pointer returned by this function. This +function exists solely to optimize a few operations where otherwise +superfluous string copying would be necessary. */ +AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size); + +/** Same as avahi_string_list_add(), but takes a variable number of + * NUL terminated strings. The argument list must be terminated by a + * NULL pointer. Returns the new list start. */ +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) AVAHI_GCC_SENTINEL; + +/** \cond fulldocs */ +/** Same as avahi_string_list_add_many(), but use a va_list + * structure. Returns the new list start. */ +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); +/** \endcond */ + +/** @} */ + +/** @{ \name String list operations */ + +/** Convert the string list object to a single character string, + * seperated by spaces and enclosed in "". avahi_free() the result! This + * function doesn't work well with strings that contain NUL bytes. */ +char* avahi_string_list_to_string(AvahiStringList *l); + +/** \cond fulldocs */ +/** Serialize the string list object in a way that is compatible with + * the storing of DNS TXT records. Strings longer than 255 bytes are truncated. */ +size_t avahi_string_list_serialize(AvahiStringList *l, void * data, size_t size); + +/** Inverse of avahi_string_list_serialize() */ +int avahi_string_list_parse(const void *data, size_t size, AvahiStringList **ret); +/** \endcond */ + +/** Compare to string lists */ +int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b); + +/** Copy a string list */ +AvahiStringList *avahi_string_list_copy(const AvahiStringList *l); + +/** Reverse the string list. */ +AvahiStringList* avahi_string_list_reverse(AvahiStringList *l); + +/** Return the number of elements in the string list */ +unsigned avahi_string_list_length(const AvahiStringList *l); + +/** @} */ + +/** @{ \name Accessing items */ + +/** Returns the next item in the string list */ +AvahiStringList *avahi_string_list_get_next(AvahiStringList *l); + +/** Returns the text for the current item */ +uint8_t *avahi_string_list_get_text(AvahiStringList *l); + +/** Returns the size of the current text */ +size_t avahi_string_list_get_size(AvahiStringList *l); + +/** @} */ + +/** @{ \name DNS-SD TXT pair handling */ + +/** Find the string list entry for the given DNS-SD TXT key */ +AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key); + +/** Return the DNS-SD TXT key and value for the specified string list + * item. If size is not NULL it will be filled with the length of + * value. (for strings containing NUL bytes). If the entry doesn't + * contain a value *value will be set to NULL. You need to + * avahi_free() the strings returned in *key and *value. */ +int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size); + +/** Add a new DNS-SD TXT key value pair to the string list. value may + * be NULL in case you want to specify a key without a value */ +AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value); + +/** Same as avahi_string_list_add_pair() but allow strings containing NUL bytes in *value. */ +AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size); + +/** @} */ + +/** \cond fulldocs */ +/** Try to find a magic service cookie in the specified DNS-SD string + * list. Or return AVAHI_SERVICE_COOKIE_INVALID if none is found. */ +uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l); +/** \endcond */ + +AVAHI_C_DECL_END + +#endif + diff --git a/avahi/thread-watch.c b/avahi/thread-watch.c new file mode 100644 index 0000000..c0cadeb --- /dev/null +++ b/avahi/thread-watch.c @@ -0,0 +1,186 @@ +/*** + 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 "llist.h" +#include "malloc.h" +#include "timeval.h" +#include "simple-watch.h" +#include "thread-watch.h" + +struct AvahiThreadedPoll { + AvahiSimplePoll *simple_poll; + pthread_t thread_id; + pthread_mutex_t mutex; + int thread_running; + int retval; +}; + +static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { + pthread_mutex_t *mutex = userdata; + int r; + + /* Before entering poll() we unlock the mutex, so that + * avahi_simple_poll_quit() can succeed from another thread. */ + + pthread_mutex_unlock(mutex); + r = poll(ufds, nfds, timeout); + pthread_mutex_lock(mutex); + + return r; +} + +static void* thread(void *userdata){ + AvahiThreadedPoll *p = userdata; + sigset_t mask; + + /* Make sure that signals are delivered to the main thread */ + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + + pthread_mutex_lock(&p->mutex); + p->retval = avahi_simple_poll_loop(p->simple_poll); + pthread_mutex_unlock(&p->mutex); + + return NULL; +} + +AvahiThreadedPoll *avahi_threaded_poll_new(void) { + AvahiThreadedPoll *p; + + if (!(p = avahi_new(AvahiThreadedPoll, 1))) + goto fail; /* OOM */ + + if (!(p->simple_poll = avahi_simple_poll_new())) + goto fail; + + pthread_mutex_init(&p->mutex, NULL); + + avahi_simple_poll_set_func(p->simple_poll, poll_func, &p->mutex); + + p->thread_running = 0; + + return p; + +fail: + if (p) { + if (p->simple_poll) { + avahi_simple_poll_free(p->simple_poll); + pthread_mutex_destroy(&p->mutex); + } + + avahi_free(p); + } + + return NULL; +} + +void avahi_threaded_poll_free(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is not called from the helper thread */ + assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); + + if (p->thread_running) + avahi_threaded_poll_stop(p); + + if (p->simple_poll) + avahi_simple_poll_free(p->simple_poll); + + pthread_mutex_destroy(&p->mutex); + avahi_free(p); +} + +const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p) { + assert(p); + + return avahi_simple_poll_get(p->simple_poll); +} + +int avahi_threaded_poll_start(AvahiThreadedPoll *p) { + assert(p); + + assert(!p->thread_running); + + if (pthread_create(&p->thread_id, NULL, thread, p) < 0) + return -1; + + p->thread_running = 1; + + return 0; +} + +int avahi_threaded_poll_stop(AvahiThreadedPoll *p) { + assert(p); + + if (!p->thread_running) + return -1; + + /* Make sure that this function is not called from the helper thread */ + assert(!pthread_equal(pthread_self(), p->thread_id)); + + pthread_mutex_lock(&p->mutex); + avahi_simple_poll_quit(p->simple_poll); + pthread_mutex_unlock(&p->mutex); + + pthread_join(p->thread_id, NULL); + p->thread_running = 0; + + return p->retval; +} + +void avahi_threaded_poll_quit(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is called from the helper thread */ + assert(pthread_equal(pthread_self(), p->thread_id)); + + avahi_simple_poll_quit(p->simple_poll); +} + +void avahi_threaded_poll_lock(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is not called from the helper thread */ + assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); + + pthread_mutex_lock(&p->mutex); +} + +void avahi_threaded_poll_unlock(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is not called from the helper thread */ + assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); + + pthread_mutex_unlock(&p->mutex); +} diff --git a/avahi/thread-watch.h b/avahi/thread-watch.h new file mode 100644 index 0000000..855edbc --- /dev/null +++ b/avahi/thread-watch.h @@ -0,0 +1,80 @@ +#ifndef foothreadedwatchhfoo +#define foothreadedwatchhfoo + +/*** + 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. +***/ + +/** \file thread-watch.h Threaded poll() based main loop implementation */ + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** A main loop object that runs an AvahiSimplePoll in its own thread. \since 0.6.4 */ +typedef struct AvahiThreadedPoll AvahiThreadedPoll; + +/** Create a new event loop object. This will allocate the internal + * AvahiSimplePoll, but will not start the helper thread. \since 0.6.4 */ +AvahiThreadedPoll *avahi_threaded_poll_new(void); + +/** Free an event loop object. This will stop the associated event loop + * thread (if it is running). \since 0.6.4 */ +void avahi_threaded_poll_free(AvahiThreadedPoll *p); + +/** Return the abstracted poll API object for this event loop + * object. The will return the same pointer each time it is + * called. \since 0.6.4 */ +const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p); + +/** Start the event loop helper thread. After the thread has started + * you must make sure to access the event loop object + * (AvahiThreadedPoll, AvahiPoll and all its associated objects) + * synchronized, i.e. with proper locking. You may want to use + * avahi_threaded_poll_lock()/avahi_threaded_poll_unlock() for this, + * which will lock the the entire event loop. Please note that event + * loop callback functions are called from the event loop helper thread + * with that lock held, i.e. avahi_threaded_poll_lock() calls are not + * required from event callbacks. \since 0.6.4 */ +int avahi_threaded_poll_start(AvahiThreadedPoll *p); + +/** Request that the event loop quits and the associated thread + stops. Call this from outside the helper thread if you want to shut + it down. \since 0.6.4 */ +int avahi_threaded_poll_stop(AvahiThreadedPoll *p); + +/** Request that the event loop quits and the associated thread + stops. Call this from inside the helper thread if you want to shut it + down. \since 0.6.4 */ +void avahi_threaded_poll_quit(AvahiThreadedPoll *p); + +/** Lock the main loop object. Use this if you want to access the event + * loop objects (such as creating a new event source) from anything + * else but the event loop helper thread, i.e. from anything else but event + * loop callbacks \since 0.6.4 */ +void avahi_threaded_poll_lock(AvahiThreadedPoll *p); + +/** Unlock the event loop object, use this as counterpart to + * avahi_threaded_poll_lock() \since 0.6.4 */ +void avahi_threaded_poll_unlock(AvahiThreadedPoll *p); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/timeeventq-test.c b/avahi/timeeventq-test.c new file mode 100644 index 0000000..91116ff --- /dev/null +++ b/avahi/timeeventq-test.c @@ -0,0 +1,67 @@ +/*** + 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 "timeeventq.h" +#include "log.h" + +#define POINTER_TO_INT(p) ((int) (long) (p)) +#define INT_TO_POINTER(i) ((void*) (long) (i)) + +static AvahiTimeEventQueue *q = NULL; + +static void callback(AvahiTimeEvent*e, void* userdata) { + struct timeval tv = {0, 0}; + assert(e); + avahi_log_info("callback(%i)", POINTER_TO_INT(userdata)); + avahi_elapse_time(&tv, 1000, 100); + avahi_time_event_update(e, &tv); +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + struct timeval tv; + AvahiSimplePoll *s; + + s = avahi_simple_poll_new(); + + q = avahi_time_event_queue_new(avahi_simple_poll_get(s)); + + avahi_time_event_new(q, avahi_elapse_time(&tv, 5000, 100), callback, INT_TO_POINTER(1)); + avahi_time_event_new(q, avahi_elapse_time(&tv, 5000, 100), callback, INT_TO_POINTER(2)); + + avahi_log_info("starting"); + + for (;;) + if (avahi_simple_poll_iterate(s, -1) != 0) + break; + + avahi_time_event_queue_free(q); + avahi_simple_poll_free(s); + + return 0; +} diff --git a/avahi/timeeventq.c b/avahi/timeeventq.c new file mode 100644 index 0000000..2de3380 --- /dev/null +++ b/avahi/timeeventq.c @@ -0,0 +1,225 @@ +/*** + 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 "timeeventq.h" +#include "log.h" + +struct AvahiTimeEvent { + AvahiTimeEventQueue *queue; + AvahiPrioQueueNode *node; + struct timeval expiry; + struct timeval last_run; + AvahiTimeEventCallback callback; + void* userdata; +}; + +struct AvahiTimeEventQueue { + const AvahiPoll *poll_api; + AvahiPrioQueue *prioq; + AvahiTimeout *timeout; +}; + +static int compare(const void* _a, const void* _b) { + const AvahiTimeEvent *a = _a, *b = _b; + int ret; + + if ((ret = avahi_timeval_compare(&a->expiry, &b->expiry)) != 0) + return ret; + + /* If both exevents are scheduled for the same time, put the entry + * that has been run earlier the last time first. */ + return avahi_timeval_compare(&a->last_run, &b->last_run); +} + +static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) { + assert(q); + + return q->prioq->root ? q->prioq->root->data : NULL; +} + +static void update_timeout(AvahiTimeEventQueue *q) { + AvahiTimeEvent *e; + assert(q); + + if ((e = time_event_queue_root(q))) + q->poll_api->timeout_update(q->timeout, &e->expiry); + else + q->poll_api->timeout_update(q->timeout, NULL); +} + +static void expiration_event(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) { + AvahiTimeEventQueue *q = userdata; + AvahiTimeEvent *e; + + if ((e = time_event_queue_root(q))) { + struct timeval now; + + gettimeofday(&now, NULL); + + /* Check if expired */ + if (avahi_timeval_compare(&now, &e->expiry) >= 0) { + + /* Make sure to move the entry away from the front */ + e->last_run = now; + avahi_prio_queue_shuffle(q->prioq, e->node); + + /* Run it */ + assert(e->callback); + e->callback(e, e->userdata); + + update_timeout(q); + return; + } + } + + avahi_log_debug(__FILE__": Strange, expiration_event() called, but nothing really happened."); + update_timeout(q); +} + +static void fix_expiry_time(AvahiTimeEvent *e) { + struct timeval now; + assert(e); + + return; /*** DO WE REALLY NEED THIS? ***/ + + gettimeofday(&now, NULL); + + if (avahi_timeval_compare(&now, &e->expiry) > 0) + e->expiry = now; +} + +AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api) { + AvahiTimeEventQueue *q; + + if (!(q = avahi_new(AvahiTimeEventQueue, 1))) { + avahi_log_error(__FILE__": Out of memory"); + goto oom; + } + + q->poll_api = poll_api; + + if (!(q->prioq = avahi_prio_queue_new(compare))) + goto oom; + + if (!(q->timeout = poll_api->timeout_new(poll_api, NULL, expiration_event, q))) + goto oom; + + return q; + +oom: + + if (q) { + avahi_free(q); + + if (q->prioq) + avahi_prio_queue_free(q->prioq); + } + + return NULL; +} + +void avahi_time_event_queue_free(AvahiTimeEventQueue *q) { + AvahiTimeEvent *e; + + assert(q); + + while ((e = time_event_queue_root(q))) + avahi_time_event_free(e); + avahi_prio_queue_free(q->prioq); + + q->poll_api->timeout_free(q->timeout); + + avahi_free(q); +} + +AvahiTimeEvent* avahi_time_event_new( + AvahiTimeEventQueue *q, + const struct timeval *timeval, + AvahiTimeEventCallback callback, + void* userdata) { + + AvahiTimeEvent *e; + + assert(q); + assert(callback); + assert(userdata); + + if (!(e = avahi_new(AvahiTimeEvent, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + e->queue = q; + e->callback = callback; + e->userdata = userdata; + + if (timeval) + e->expiry = *timeval; + else { + e->expiry.tv_sec = 0; + e->expiry.tv_usec = 0; + } + + fix_expiry_time(e); + + e->last_run.tv_sec = 0; + e->last_run.tv_usec = 0; + + if (!(e->node = avahi_prio_queue_put(q->prioq, e))) { + avahi_free(e); + return NULL; + } + + update_timeout(q); + return e; +} + +void avahi_time_event_free(AvahiTimeEvent *e) { + AvahiTimeEventQueue *q; + assert(e); + + q = e->queue; + + avahi_prio_queue_remove(q->prioq, e->node); + avahi_free(e); + + update_timeout(q); +} + +void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval) { + assert(e); + assert(timeval); + + e->expiry = *timeval; + fix_expiry_time(e); + avahi_prio_queue_shuffle(e->queue->prioq, e->node); + + update_timeout(e->queue); +} + diff --git a/avahi/timeeventq.h b/avahi/timeeventq.h new file mode 100644 index 0000000..14ad393 --- /dev/null +++ b/avahi/timeeventq.h @@ -0,0 +1,46 @@ +#ifndef footimeeventqhfoo +#define footimeeventqhfoo + +/*** + 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 + +typedef struct AvahiTimeEventQueue AvahiTimeEventQueue; +typedef struct AvahiTimeEvent AvahiTimeEvent; + +#include + +#include "prioq.h" + +typedef void (*AvahiTimeEventCallback)(AvahiTimeEvent *e, void* userdata); + +AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api); +void avahi_time_event_queue_free(AvahiTimeEventQueue *q); + +AvahiTimeEvent* avahi_time_event_new( + AvahiTimeEventQueue *q, + const struct timeval *timeval, + AvahiTimeEventCallback callback, + void* userdata); + +void avahi_time_event_free(AvahiTimeEvent *e); +void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval); + +#endif diff --git a/avahi/timeval-test.c b/avahi/timeval-test.c new file mode 100644 index 0000000..387c180 --- /dev/null +++ b/avahi/timeval-test.c @@ -0,0 +1,43 @@ +/*** + 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 "gccmacro.h" +#include "timeval.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + + struct timeval a = { 5, 5 }, b; + + b = a; + + printf("%li.%li\n", a.tv_sec, a.tv_usec); + avahi_timeval_add(&a, -50); + + printf("%li.%li\n", a.tv_sec, a.tv_usec); + + printf("%lli\n", (long long) avahi_timeval_diff(&a, &b)); + + return 0; +} diff --git a/avahi/timeval.c b/avahi/timeval.c new file mode 100644 index 0000000..cdb0f09 --- /dev/null +++ b/avahi/timeval.c @@ -0,0 +1,123 @@ +/*** + 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 "timeval.h" + +int avahi_timeval_compare(const struct timeval *a, const struct timeval *b) { + assert(a); + assert(b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b) { + assert(a); + assert(b); + + if (avahi_timeval_compare(a, b) < 0) + return - avahi_timeval_diff(b, a); + + return ((AvahiUsec) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec; +} + +struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec) { + AvahiUsec u; + assert(a); + + u = usec + a->tv_usec; + + if (u < 0) { + a->tv_usec = (long) (1000000 + (u % 1000000)); + a->tv_sec += (long) (-1 + (u / 1000000)); + } else { + a->tv_usec = (long) (u % 1000000); + a->tv_sec += (long) (u / 1000000); + } + + return a; +} + +AvahiUsec avahi_age(const struct timeval *a) { + struct timeval now; + + assert(a); + + gettimeofday(&now, NULL); + + return avahi_timeval_diff(&now, a); +} + +struct timeval *avahi_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) { + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static int last_rand; + static time_t timestamp = 0; + + time_t now; + int r; + + now = time(NULL); + + pthread_mutex_lock(&mutex); + if (now >= timestamp + 10) { + timestamp = now; + last_rand = rand(); + } + + r = last_rand; + + pthread_mutex_unlock(&mutex); + + /* We use the same jitter for 10 seconds. That way our + * time events elapse in bursts which has the advantage that + * packet data can be aggregated better */ + + avahi_timeval_add(tv, (AvahiUsec) (jitter*1000.0*r/(RAND_MAX+1.0))); + } + + return tv; +} + diff --git a/avahi/timeval.h b/avahi/timeval.h new file mode 100644 index 0000000..618801a --- /dev/null +++ b/avahi/timeval.h @@ -0,0 +1,54 @@ +#ifndef footimevalhfoo +#define footimevalhfoo + +/*** + 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. +***/ + +/** \file timeval.h Functions to facilitate timeval handling */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** A numeric data type for storing microsecond values. (signed 64bit integer) */ +typedef int64_t AvahiUsec; + +/** Compare two timeval structures and return a negative value when a < b, 0 when a == b and a positive value otherwise */ +int avahi_timeval_compare(const struct timeval *a, const struct timeval *b); + +/** Calculate the difference between two timeval structures as microsecond value */ +AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b); + +/** Add a number of microseconds to the specified timeval structure and return it. *a is modified. */ +struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec); + +/** Return the difference between the current time and *a. Positive if *a was earlier */ +AvahiUsec avahi_age(const struct timeval *a); + +/** Fill *tv with the current time plus "ms" milliseconds plus an + * extra jitter of "j" milliseconds. Pass 0 for j if you don't want + * the jitter */ +struct timeval *avahi_elapse_time(struct timeval *tv, unsigned ms, unsigned j); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/update-test.c b/avahi/update-test.c new file mode 100644 index 0000000..dcbba80 --- /dev/null +++ b/avahi/update-test.c @@ -0,0 +1,91 @@ +/*** + 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 + +static AvahiSEntryGroup *group = NULL; + +static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) { + + avahi_log_debug("server state: %i", state); + + if (state == AVAHI_SERVER_RUNNING) { + int ret; + + group = avahi_s_entry_group_new(s, NULL, NULL); + assert(group); + + ret = avahi_server_add_service(s, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "foo", "_http._tcp", NULL, NULL, 80, "test1", NULL); + assert(ret == AVAHI_OK); + + avahi_s_entry_group_commit(group); + } +} + +static void modify_txt_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, void *userdata) { + int ret; + AvahiServer *s = userdata; + + avahi_log_debug("modifying"); + + ret = avahi_server_update_service_txt(s, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "foo", "_http._tcp", NULL, "test2", NULL); + assert(ret == AVAHI_OK); +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + AvahiSimplePoll *simple_poll; + const AvahiPoll *poll_api; + AvahiServer *server; + struct timeval tv; + AvahiServerConfig config; + + simple_poll = avahi_simple_poll_new(); + assert(simple_poll); + + poll_api = avahi_simple_poll_get(simple_poll); + assert(poll_api); + + avahi_server_config_init(&config); + config.publish_domain = config.publish_workstation = config.use_ipv6 = config.publish_hinfo = 0; + server = avahi_server_new(poll_api, &config, server_callback, NULL, NULL); + assert(server); + avahi_server_config_free(&config); + + poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 1000*10, 0), modify_txt_callback, server); + + avahi_simple_poll_loop(simple_poll); + return 0; +} diff --git a/avahi/utf8-test.c b/avahi/utf8-test.c new file mode 100644 index 0000000..ee928de --- /dev/null +++ b/avahi/utf8-test.c @@ -0,0 +1,37 @@ +/*** + 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 "utf8.h" + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + + assert(avahi_utf8_valid("hallo")); + assert(!avahi_utf8_valid("üxknürz")); + assert(avahi_utf8_valid("üxknürz")); + + return 0; +} diff --git a/avahi/utf8.c b/avahi/utf8.c new file mode 100644 index 0000000..ab10ba0 --- /dev/null +++ b/avahi/utf8.c @@ -0,0 +1,110 @@ +/* This file is based on the GLIB utf8 validation functions. The + * original license text follows. */ + +/* gutf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library 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. + * + * This library 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 this library; 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 "utf8.h" + +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFE) != 0xFFFE) + + +#define CONTINUATION_CHAR \ + do { \ + if ((*(const unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */ \ + goto error; \ + val <<= 6; \ + val |= (*(const unsigned char *)p) & 0x3f; \ + } while(0) + + +const char * +avahi_utf8_valid (const char *str) + +{ + unsigned val = 0; + unsigned min = 0; + const char *p; + + for (p = str; *p; p++) + { + if (*(const unsigned char *)p < 128) + /* done */; + else + { + if ((*(const unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */ + { + if ( ((*(const unsigned char *)p & 0x1e) == 0)) + goto error; + p++; + if ( ((*(const unsigned char *)p & 0xc0) != 0x80)) /* 10xxxxxx */ + goto error; + } + else + { + if ((*(const unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */ + { + min = (1 << 11); + val = *(const unsigned char *)p & 0x0f; + goto TWO_REMAINING; + } + else if ((*(const unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */ + { + min = (1 << 16); + val = *(const unsigned char *)p & 0x07; + } + else + goto error; + + p++; + CONTINUATION_CHAR; + TWO_REMAINING: + p++; + CONTINUATION_CHAR; + p++; + CONTINUATION_CHAR; + + if ( (val < min)) + goto error; + + if ( (!UNICODE_VALID(val))) + goto error; + } + + continue; + + error: + return NULL; + } + } + + return str; +} diff --git a/avahi/utf8.h b/avahi/utf8.h new file mode 100644 index 0000000..42ed288 --- /dev/null +++ b/avahi/utf8.h @@ -0,0 +1,33 @@ +#ifndef fooutf8hfoo +#define fooutf8hfoo + +/*** + 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 + +AVAHI_C_DECL_BEGIN + +const char *avahi_utf8_valid(const char *str); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/util.c b/avahi/util.c new file mode 100644 index 0000000..8f4c435 --- /dev/null +++ b/avahi/util.c @@ -0,0 +1,120 @@ +/*** + 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 "util.h" + +void avahi_hexdump(const void* p, size_t size) { + const uint8_t *c = p; + assert(p); + + printf("Dumping %lu bytes from %p:\n", (unsigned long) size, p); + + while (size > 0) { + unsigned i; + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%02x ", c[i]); + else + printf(" "); + } + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.'); + else + printf(" "); + } + + printf("\n"); + + c += 16; + + if (size <= 16) + break; + + size -= 16; + } +} + +char *avahi_format_mac_address(char *r, size_t l, const uint8_t* mac, size_t size) { + char *t = r; + unsigned i; + static const char hex[] = "0123456789abcdef"; + + assert(r); + assert(l > 0); + assert(mac); + + if (size <= 0) { + *r = 0; + return r; + } + + for (i = 0; i < size; i++) { + if (l < 3) + break; + + *(t++) = hex[*mac >> 4]; + *(t++) = hex[*mac & 0xF]; + *(t++) = ':'; + + l -= 3; + + mac++; + } + + if (t > r) + *(t-1) = 0; + else + *r = 0; + + return r; +} + +char *avahi_strup(char *s) { + char *c; + assert(s); + + for (c = s; *c; c++) + *c = (char) toupper(*c); + + return s; +} + +char *avahi_strdown(char *s) { + char *c; + assert(s); + + for (c = s; *c; c++) + *c = (char) tolower(*c); + + return s; +} diff --git a/avahi/util.h b/avahi/util.h new file mode 100644 index 0000000..787aa89 --- /dev/null +++ b/avahi/util.h @@ -0,0 +1,41 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/*** + 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 + +AVAHI_C_DECL_BEGIN + +void avahi_hexdump(const void *p, size_t size); + +char *avahi_format_mac_address(char *t, size_t l, const uint8_t* mac, size_t size); + +/** Change every character in the string to upper case (ASCII), return a pointer to the string */ +char *avahi_strup(char *s); + +/** Change every character in the string to lower case (ASCII), return a pointer to the string */ +char *avahi_strdown(char *s); + +AVAHI_C_DECL_END + +#endif diff --git a/avahi/watch-test.c b/avahi/watch-test.c new file mode 100644 index 0000000..6c178ba --- /dev/null +++ b/avahi/watch-test.c @@ -0,0 +1,115 @@ +/*** + 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 "watch.h" +#include "timeval.h" +#include "gccmacro.h" + +static const AvahiPoll *api = NULL; + +#ifndef USE_THREAD +#include "simple-watch.h" +static AvahiSimplePoll *simple_poll = NULL; +#else +#include "thread-watch.h" +static AvahiThreadedPoll *threaded_poll = NULL; +#endif + +static void callback(AvahiWatch *w, int fd, AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) { + + if (event & AVAHI_WATCH_IN) { + ssize_t r; + char c; + + if ((r = read(fd, &c, 1)) <= 0) { + fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + api->watch_free(w); + return; + } + + printf("Read: %c\n", c >= 32 && c < 127 ? c : '.'); + } +} + +static void wakeup(AvahiTimeout *t, AVAHI_GCC_UNUSED void *userdata) { + static int i = 0; + struct timeval tv; + + printf("Wakeup #%i\n", i++); + + if (i > 10) { +#ifndef USE_THREAD + avahi_simple_poll_quit(simple_poll); +#else + avahi_threaded_poll_quit(threaded_poll); +#endif + } else { + avahi_elapse_time(&tv, 1000, 0); + api->timeout_update(t, &tv); + } +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { + struct timeval tv; + +#ifndef USE_THREAD + simple_poll = avahi_simple_poll_new(); + assert(simple_poll); + api = avahi_simple_poll_get(simple_poll); + assert(api); +#else + threaded_poll = avahi_threaded_poll_new(); + assert(threaded_poll); + api = avahi_threaded_poll_get(threaded_poll); + assert(api); +#endif + + api->watch_new(api, 0, AVAHI_WATCH_IN, callback, NULL); + + avahi_elapse_time(&tv, 1000, 0); + api->timeout_new(api, &tv, wakeup, NULL); + +#ifndef USE_THREAD + /* Our main loop */ + avahi_simple_poll_loop(simple_poll); + avahi_simple_poll_free(simple_poll); + +#else + avahi_threaded_poll_start(threaded_poll); + + fprintf(stderr, "Now doing some stupid stuff ...\n"); + sleep(20); + fprintf(stderr, "... stupid stuff is done.\n"); + + avahi_threaded_poll_free(threaded_poll); + +#endif + + return 0; +} diff --git a/avahi/watch.h b/avahi/watch.h new file mode 100644 index 0000000..6cade5d --- /dev/null +++ b/avahi/watch.h @@ -0,0 +1,97 @@ +#ifndef foowatchhfoo +#define foowatchhfoo + +/*** + 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. +***/ + +/** \file watch.h Simplistic main loop abstraction */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** An I/O watch object */ +typedef struct AvahiWatch AvahiWatch; + +/** A timeout watch object */ +typedef struct AvahiTimeout AvahiTimeout; + +/** An event polling abstraction object */ +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 */ +} AvahiWatchEvent; + +/** Called whenever an I/O event happens on an I/O watch */ +typedef void (*AvahiWatchCallback)(AvahiWatch *w, int fd, AvahiWatchEvent event, void *userdata); + +/** Called when the timeout is reached */ +typedef void (*AvahiTimeoutCallback)(AvahiTimeout *t, void *userdata); + +/** Defines an abstracted event polling API. This may be used to + connect Avahi to other main loops. This is loosely based on Unix + poll(2). A consumer will call watch_new() for all file descriptors it + wants to listen for events on. In addition he can call timeout_new() + to define time based events .*/ +struct AvahiPoll { + + /** Some abstract user data usable by the provider of the API */ + void* userdata; + + /** Create a new watch for the specified file descriptor and for + * the specified events. The API will call the callback function + * whenever any of the events happens. */ + AvahiWatch* (*watch_new)(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata); + + /** Update the events to wait for. It is safe to call this function from an AvahiWatchCallback */ + void (*watch_update)(AvahiWatch *w, AvahiWatchEvent event); + + /** Return the events that happened. It is safe to call this function from an AvahiWatchCallback */ + AvahiWatchEvent (*watch_get_events)(AvahiWatch *w); + + /** Free a watch. It is safe to call this function from an AvahiWatchCallback */ + void (*watch_free)(AvahiWatch *w); + + /** Set a wakeup time for the polling loop. The API will call the + callback function when the absolute time *tv is reached. If tv is + NULL, the timeout is disabled. After the timeout expired the + callback function will be called and the timeout is disabled. You + can reenable it by calling timeout_update() */ + AvahiTimeout* (*timeout_new)(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata); + + /** Update the absolute expiration time for a timeout, If tv is + * NULL, the timeout is disabled. It is safe to call this function from an AvahiTimeoutCallback */ + void (*timeout_update)(AvahiTimeout *, const struct timeval *tv); + + /** Free a timeout. It is safe to call this function from an AvahiTimeoutCallback */ + void (*timeout_free)(AvahiTimeout *t); +}; + +AVAHI_C_DECL_END + +#endif + diff --git a/avahi/wide-area.c b/avahi/wide-area.c new file mode 100644 index 0000000..ab1369d --- /dev/null +++ b/avahi/wide-area.c @@ -0,0 +1,723 @@ +/*** + 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 "internal.h" +#include "browse.h" +#include "socket.h" +#include "log.h" +#include "hashmap.h" +#include "wide-area.h" +#include "addr-util.h" +#include "rr-util.h" + +#define CACHE_ENTRIES_MAX 500 + +typedef struct AvahiWideAreaCacheEntry AvahiWideAreaCacheEntry; + +struct AvahiWideAreaCacheEntry { + AvahiWideAreaLookupEngine *engine; + + AvahiRecord *record; + struct timeval timestamp; + struct timeval expiry; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, cache); +}; + +struct AvahiWideAreaLookup { + AvahiWideAreaLookupEngine *engine; + int dead; + + uint32_t id; /* effectively just an uint16_t, but we need it as an index for a hash table */ + AvahiTimeEvent *time_event; + + AvahiKey *key, *cname_key; + + int n_send; + AvahiDnsPacket *packet; + + AvahiWideAreaLookupCallback callback; + void *userdata; + + AvahiAddress dns_server_used; + + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, lookups); + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, by_key); +}; + +struct AvahiWideAreaLookupEngine { + AvahiServer *server; + + int fd_ipv4, fd_ipv6; + AvahiWatch *watch_ipv4, *watch_ipv6; + + uint16_t next_id; + + /* Cache */ + AVAHI_LLIST_HEAD(AvahiWideAreaCacheEntry, cache); + AvahiHashmap *cache_by_key; + unsigned cache_n_entries; + + /* Lookups */ + AVAHI_LLIST_HEAD(AvahiWideAreaLookup, lookups); + AvahiHashmap *lookups_by_id; + AvahiHashmap *lookups_by_key; + + int cleanup_dead; + + AvahiAddress dns_servers[AVAHI_WIDE_AREA_SERVERS_MAX]; + unsigned n_dns_servers; + unsigned current_dns_server; +}; + +static AvahiWideAreaLookup* find_lookup(AvahiWideAreaLookupEngine *e, uint16_t id) { + AvahiWideAreaLookup *l; + int i = (int) id; + + assert(e); + + if (!(l = avahi_hashmap_lookup(e->lookups_by_id, &i))) + return NULL; + + assert(l->id == id); + + if (l->dead) + return NULL; + + return l; +} + +static int send_to_dns_server(AvahiWideAreaLookup *l, AvahiDnsPacket *p) { + AvahiAddress *a; + + assert(l); + assert(p); + + if (l->engine->n_dns_servers <= 0) + return -1; + + assert(l->engine->current_dns_server < l->engine->n_dns_servers); + + a = &l->engine->dns_servers[l->engine->current_dns_server]; + l->dns_server_used = *a; + + if (a->proto == AVAHI_PROTO_INET) { + + if (l->engine->fd_ipv4 < 0) + return -1; + + return avahi_send_dns_packet_ipv4(l->engine->fd_ipv4, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT); + + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + if (l->engine->fd_ipv6 < 0) + return -1; + + return avahi_send_dns_packet_ipv6(l->engine->fd_ipv6, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv6, AVAHI_DNS_PORT); + } +} + +static void next_dns_server(AvahiWideAreaLookupEngine *e) { + assert(e); + + e->current_dns_server++; + + if (e->current_dns_server >= e->n_dns_servers) + e->current_dns_server = 0; +} + +static void lookup_stop(AvahiWideAreaLookup *l) { + assert(l); + + l->callback = NULL; + + if (l->time_event) { + avahi_time_event_free(l->time_event); + l->time_event = NULL; + } +} + +static void sender_timeout_callback(AvahiTimeEvent *e, void *userdata) { + AvahiWideAreaLookup *l = userdata; + struct timeval tv; + + assert(l); + + /* Try another DNS server after three retries */ + if (l->n_send >= 3 && avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) { + next_dns_server(l->engine); + + if (avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) + /* There is no other DNS server, fail */ + l->n_send = 1000; + } + + if (l->n_send >= 6) { + avahi_log_warn(__FILE__": Query timed out."); + avahi_server_set_errno(l->engine->server, AVAHI_ERR_TIMEOUT); + l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); + lookup_stop(l); + return; + } + + assert(l->packet); + send_to_dns_server(l, l->packet); + l->n_send++; + + avahi_time_event_update(e, avahi_elapse_time(&tv, 1000, 0)); +} + +AvahiWideAreaLookup *avahi_wide_area_lookup_new( + AvahiWideAreaLookupEngine *e, + AvahiKey *key, + AvahiWideAreaLookupCallback callback, + void *userdata) { + + struct timeval tv; + AvahiWideAreaLookup *l, *t; + uint8_t *p; + + assert(e); + assert(key); + assert(callback); + assert(userdata); + + l = avahi_new(AvahiWideAreaLookup, 1); + l->engine = e; + l->dead = 0; + l->key = avahi_key_ref(key); + l->cname_key = avahi_key_new_cname(l->key); + l->callback = callback; + l->userdata = userdata; + + /* If more than 65K wide area quries are issued simultaneously, + * this will break. This should be limited by some higher level */ + + for (;; e->next_id++) + if (!find_lookup(e, e->next_id)) + break; /* This ID is not yet used. */ + + l->id = e->next_id++; + + /* We keep the packet around in case we need to repeat our query */ + l->packet = avahi_dns_packet_new(0); + + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_ID, (uint16_t) l->id); + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0, 0)); + + p = avahi_dns_packet_append_key(l->packet, key, 0); + assert(p); + + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_QDCOUNT, 1); + + if (send_to_dns_server(l, l->packet) < 0) { + avahi_log_error(__FILE__": Failed to send packet."); + avahi_dns_packet_free(l->packet); + avahi_key_unref(l->key); + if (l->cname_key) + avahi_key_unref(l->cname_key); + avahi_free(l); + return NULL; + } + + l->n_send = 1; + + l->time_event = avahi_time_event_new(e->server->time_event_queue, avahi_elapse_time(&tv, 500, 0), sender_timeout_callback, l); + + avahi_hashmap_insert(e->lookups_by_id, &l->id, l); + + t = avahi_hashmap_lookup(e->lookups_by_key, l->key); + AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, by_key, t, l); + avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); + + AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, lookups, e->lookups, l); + + return l; +} + +static void lookup_destroy(AvahiWideAreaLookup *l) { + AvahiWideAreaLookup *t; + assert(l); + + lookup_stop(l); + + t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); + AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, by_key, t, l); + if (t) + avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); + else + avahi_hashmap_remove(l->engine->lookups_by_key, l->key); + + AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, lookups, l->engine->lookups, l); + + avahi_hashmap_remove(l->engine->lookups_by_id, &l->id); + avahi_dns_packet_free(l->packet); + + if (l->key) + avahi_key_unref(l->key); + + if (l->cname_key) + avahi_key_unref(l->cname_key); + + avahi_free(l); +} + +void avahi_wide_area_lookup_free(AvahiWideAreaLookup *l) { + assert(l); + + if (l->dead) + return; + + l->dead = 1; + l->engine->cleanup_dead = 1; + lookup_stop(l); +} + +void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e) { + AvahiWideAreaLookup *l, *n; + assert(e); + + while (e->cleanup_dead) { + e->cleanup_dead = 0; + + for (l = e->lookups; l; l = n) { + n = l->lookups_next; + + if (l->dead) + lookup_destroy(l); + } + } +} + +static void cache_entry_free(AvahiWideAreaCacheEntry *c) { + AvahiWideAreaCacheEntry *t; + assert(c); + + if (c->time_event) + avahi_time_event_free(c->time_event); + + AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, cache, c->engine->cache, c); + + t = avahi_hashmap_lookup(c->engine->cache_by_key, c->record->key); + AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, by_key, t, c); + if (t) + avahi_hashmap_replace(c->engine->cache_by_key, avahi_key_ref(c->record->key), t); + else + avahi_hashmap_remove(c->engine->cache_by_key, c->record->key); + + c->engine->cache_n_entries --; + + avahi_record_unref(c->record); + avahi_free(c); +} + +static void expiry_event(AvahiTimeEvent *te, void *userdata) { + AvahiWideAreaCacheEntry *e = userdata; + + assert(te); + assert(e); + + cache_entry_free(e); +} + +static AvahiWideAreaCacheEntry* find_record_in_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaCacheEntry *c; + + assert(e); + assert(r); + + for (c = avahi_hashmap_lookup(e->cache_by_key, r->key); c; c = c->by_key_next) + if (avahi_record_equal_no_ttl(r, c->record)) + return c; + + return NULL; +} + +static void run_callbacks(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaLookup *l; + + assert(e); + assert(r); + + for (l = avahi_hashmap_lookup(e->lookups_by_key, r->key); l; l = l->by_key_next) { + if (l->dead || !l->callback) + continue; + + l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA, r, l->userdata); + } + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && r->key->type == AVAHI_DNS_TYPE_CNAME) { + /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ + + for (l = e->lookups; l; l = l->lookups_next) { + AvahiKey *key; + + if (l->dead || !l->callback) + continue; + + if ((key = avahi_key_new_cname(l->key))) { + if (avahi_key_equal(r->key, key)) + l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA, r, l->userdata); + + avahi_key_unref(key); + } + } + } +} + +static void add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaCacheEntry *c; + int is_new; + + assert(e); + assert(r); + + if ((c = find_record_in_cache(e, r))) { + is_new = 0; + + /* Update the existing entry */ + avahi_record_unref(c->record); + } else { + AvahiWideAreaCacheEntry *t; + + is_new = 1; + + /* Enforce cache size */ + if (e->cache_n_entries >= CACHE_ENTRIES_MAX) + /* Eventually we should improve the caching algorithm here */ + goto finish; + + c = avahi_new(AvahiWideAreaCacheEntry, 1); + c->engine = e; + c->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c); + + /* Add the new entry to the cache entry hash table */ + t = avahi_hashmap_lookup(e->cache_by_key, r->key); + AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c); + avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t); + + e->cache_n_entries ++; + } + + c->record = avahi_record_ref(r); + + gettimeofday(&c->timestamp, NULL); + c->expiry = c->timestamp; + avahi_timeval_add(&c->expiry, r->ttl * 1000000); + + if (c->time_event) + avahi_time_event_update(c->time_event, &c->expiry); + else + c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c); + +finish: + + if (is_new) + run_callbacks(e, r); +} + +static int map_dns_error(uint16_t error) { + static const int table[16] = { + AVAHI_OK, + AVAHI_ERR_DNS_FORMERR, + AVAHI_ERR_DNS_SERVFAIL, + AVAHI_ERR_DNS_NXDOMAIN, + AVAHI_ERR_DNS_NOTIMP, + AVAHI_ERR_DNS_REFUSED, + AVAHI_ERR_DNS_YXDOMAIN, + AVAHI_ERR_DNS_YXRRSET, + AVAHI_ERR_DNS_NXRRSET, + AVAHI_ERR_DNS_NOTAUTH, + AVAHI_ERR_DNS_NOTZONE, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR + }; + + assert(error <= 15); + + return table[error]; +} + +static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) { + AvahiWideAreaLookup *l = NULL; + int i, r; + + AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW; + + assert(e); + assert(p); + + /* Some superficial validity tests */ + if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { + avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram."); + goto finish; + } + + /* Look for the lookup that issued this query */ + if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead) + goto finish; + + /* Check whether this a packet indicating a failure */ + if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) { + + avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r)); + /* Tell the user about the failure */ + final_event = AVAHI_BROWSER_FAILURE; + + /* We go on here, since some of the records contained in the + reply might be interesting in some way */ + } + + /* Skip over the question */ + for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) { + AvahiKey *k; + + if (!(k = avahi_dns_packet_consume_key(p, NULL))) { + avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); + avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); + final_event = AVAHI_BROWSER_FAILURE; + goto finish; + } + + avahi_key_unref(k); + } + + /* Process responses */ + for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) + + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) { + + AvahiRecord *rr; + + if (!(rr = avahi_dns_packet_consume_record(p, NULL))) { + avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); + avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); + final_event = AVAHI_BROWSER_FAILURE; + goto finish; + } + + add_to_cache(e, rr); + avahi_record_unref(rr); + } + +finish: + + if (l && !l->dead) { + if (l->callback) + l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); + + lookup_stop(l); + } +} + +static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) { + AvahiWideAreaLookupEngine *e = userdata; + AvahiDnsPacket *p = NULL; + + if (fd == e->fd_ipv4) + p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL); + else { + assert(fd == e->fd_ipv6); + p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL); + } + + if (p) { + handle_packet(e, p); + avahi_dns_packet_free(p); + } +} + +AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) { + AvahiWideAreaLookupEngine *e; + + assert(s); + + e = avahi_new(AvahiWideAreaLookupEngine, 1); + e->server = s; + e->cleanup_dead = 0; + + /* Create sockets */ + 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)); + + if (e->fd_ipv6 >= 0) + close(e->fd_ipv6); + + if (e->fd_ipv4 >= 0) + close(e->fd_ipv4); + + avahi_free(e); + return NULL; + } + + /* Create watches */ + + e->watch_ipv4 = e->watch_ipv6 = NULL; + + if (e->fd_ipv4 >= 0) + e->watch_ipv4 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv4, AVAHI_WATCH_IN, socket_event, e); + if (e->fd_ipv6 >= 0) + e->watch_ipv6 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv6, AVAHI_WATCH_IN, socket_event, e); + + e->n_dns_servers = e->current_dns_server = 0; + e->next_id = (uint16_t) rand(); + + /* Initialize cache */ + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaCacheEntry, e->cache); + e->cache_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + e->cache_n_entries = 0; + + /* Initialize lookup list */ + e->lookups_by_id = avahi_hashmap_new((AvahiHashFunc) avahi_int_hash, (AvahiEqualFunc) avahi_int_equal, NULL, NULL); + e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); + + return e; +} + +void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e) { + assert(e); + + avahi_wide_area_clear_cache(e); + + while (e->lookups) + lookup_destroy(e->lookups); + + avahi_hashmap_free(e->cache_by_key); + avahi_hashmap_free(e->lookups_by_id); + avahi_hashmap_free(e->lookups_by_key); + + if (e->watch_ipv4) + e->server->poll_api->watch_free(e->watch_ipv4); + + if (e->watch_ipv6) + e->server->poll_api->watch_free(e->watch_ipv6); + + if (e->fd_ipv6 >= 0) + close(e->fd_ipv6); + + if (e->fd_ipv4 >= 0) + close(e->fd_ipv4); + + avahi_free(e); +} + +void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e) { + assert(e); + + while (e->cache) + cache_entry_free(e->cache); + + assert(e->cache_n_entries == 0); +} + +void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n) { + assert(e); + + if (a) { + for (e->n_dns_servers = 0; n > 0 && e->n_dns_servers < AVAHI_WIDE_AREA_SERVERS_MAX; a++, n--) + if ((a->proto == AVAHI_PROTO_INET && e->fd_ipv4 >= 0) || (a->proto == AVAHI_PROTO_INET6 && e->fd_ipv6 >= 0)) + e->dns_servers[e->n_dns_servers++] = *a; + } else { + assert(n == 0); + e->n_dns_servers = 0; + } + + e->current_dns_server = 0; + + avahi_wide_area_clear_cache(e); +} + +void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata) { + AvahiWideAreaCacheEntry *c; + + assert(e); + assert(callback); + + callback(";; WIDE AREA CACHE ;;; ", userdata); + + for (c = e->cache; c; c = c->cache_next) { + char *t = avahi_record_to_string(c->record); + callback(t, userdata); + avahi_free(t); + } +} + +unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata) { + AvahiWideAreaCacheEntry *c; + AvahiKey *cname_key; + unsigned n = 0; + + assert(e); + assert(key); + assert(callback); + + for (c = avahi_hashmap_lookup(e->cache_by_key, key); c; c = c->by_key_next) { + callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_CACHED, c->record, userdata); + n++; + } + + if ((cname_key = avahi_key_new_cname(key))) { + + for (c = avahi_hashmap_lookup(e->cache_by_key, cname_key); c; c = c->by_key_next) { + callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_CACHED, c->record, userdata); + n++; + } + + avahi_key_unref(cname_key); + } + + return n; +} + +int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e) { + assert(e); + + return e->n_dns_servers > 0; +} + + + diff --git a/avahi/wide-area.h b/avahi/wide-area.h new file mode 100644 index 0000000..b1dc570 --- /dev/null +++ b/avahi/wide-area.h @@ -0,0 +1,52 @@ +#ifndef foowideareahfoo +#define foowideareahfoo + +/*** + 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 "lookup.h" +#include "browse.h" + +typedef struct AvahiWideAreaLookupEngine AvahiWideAreaLookupEngine; +typedef struct AvahiWideAreaLookup AvahiWideAreaLookup; + +typedef void (*AvahiWideAreaLookupCallback)( + AvahiWideAreaLookupEngine *e, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata); + +AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s); +void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e); + +unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); +void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata); +void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n); +void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e); +void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e); +int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e); + +AvahiWideAreaLookup *avahi_wide_area_lookup_new(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); +void avahi_wide_area_lookup_free(AvahiWideAreaLookup *q); + + + +#endif + diff --git a/configure.ac b/configure.ac index 6abc045..5dcefae 100644 --- a/configure.ac +++ b/configure.ac @@ -20,15 +20,14 @@ AC_PREREQ(2.63) AC_INIT([avahi],[0.6.31],[avahi (at) lists (dot) freedesktop (dot) org]) -AC_CONFIG_SRCDIR([avahi-core/server.c]) +AC_CONFIG_SRCDIR([avahi/server.c]) AC_CONFIG_MACRO_DIR([common]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax]) AC_SUBST(PACKAGE_URL, [http://avahi.org/]) -AC_SUBST(LIBAVAHI_COMMON_VERSION_INFO, [8:3:5]) -AC_SUBST(LIBAVAHI_CORE_VERSION_INFO, [7:2:0]) +AC_SUBST(LIBAVAHI_VERSION_INFO, [7:2:0]) AC_CANONICAL_HOST @@ -324,8 +323,7 @@ AM_CONDITIONAL([ENABLE_TESTS], [test "x$ENABLE_TESTS" = "xyes"]) # ========================================================================== AC_CONFIG_FILES([ Makefile -avahi-common/Makefile -avahi-core/Makefile +avahi/Makefile examples/Makefile common/Makefile ]) @@ -344,6 +342,5 @@ echo " " echo "\ - Building libavahi-core yes Building tests: ${ENABLE_TESTS} " diff --git a/docs/MALLOC b/docs/MALLOC index 7c452af..742e409 100644 --- a/docs/MALLOC +++ b/docs/MALLOC @@ -1,5 +1,5 @@ Avahi supports pluggable memory allocator implemenations. See - for more information. + for more information. Currently, Avahi does not deal well with out-of-memory situations. Therefore we recommend the usage of memory allocators that diff --git a/examples/Makefile.am b/examples/Makefile.am index 2235000..aff7d3c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -25,8 +25,8 @@ endif core_publish_service_SOURCES = core-publish-service.c core_publish_service_CFLAGS = $(AM_CFLAGS) -core_publish_service_LDADD = $(AM_LDADD) ../avahi-core/libavahi-core.la ../avahi-common/libavahi-common.la +core_publish_service_LDADD = $(AM_LDADD) ../avahi/libavahi.la core_browse_services_SOURCES = core-browse-services.c core_browse_services_CFLAGS = $(AM_CFLAGS) -core_browse_services_LDADD = $(AM_LDADD) ../avahi-core/libavahi-core.la ../avahi-common/libavahi-common.la +core_browse_services_LDADD = $(AM_LDADD) ../avahi/libavahi.la diff --git a/examples/core-browse-services.c b/examples/core-browse-services.c index a36668a..563125b 100644 --- a/examples/core-browse-services.c +++ b/examples/core-browse-services.c @@ -37,11 +37,11 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include static AvahiSimplePoll *simple_poll = NULL; static AvahiServer *server = NULL; diff --git a/examples/core-publish-service.c b/examples/core-publish-service.c index 8029eb8..e1afe51 100644 --- a/examples/core-publish-service.c +++ b/examples/core-publish-service.c @@ -26,12 +26,12 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include static AvahiSEntryGroup *group = NULL; static AvahiSimplePoll *simple_poll = NULL;