nss-mdns is an extension to glibc to support resolution of link-local
(.local) addresses via GNU Name Service Switch (NSS) which redirects
the queries to a running avahi-daemon, which in turn does the resolution
via multicast DNS (mdns).

However, this does not work for uClibc, as it does not support NSS.

This patch integrates the nss-mdns approach into uClibc's resolver,
so getaddrinfo() and getnameinfo() calls get redirected to a running
avahi-daemon before trying to resolve via unicast DNS.

This increases the size of the library by 50 bytes on ARM.

Signed-off-by: Nikolaus Voss <[email protected]>
---
 extra/Configs/Config.in                          |   19 +++
 libc/inet/Makefile.in                            |    1 +
 libc/inet/avahi.c                                |  183 ++++++++++++++++++++++
 libc/inet/avahi.h                                |   33 ++++
 libc/inet/resolv.c                               |   23 +++-
 libc/sysdeps/linux/common/bits/kernel-features.h |    7 +-
 6 files changed, 259 insertions(+), 7 deletions(-)
 create mode 100644 libc/inet/avahi.c
 create mode 100644 libc/inet/avahi.h

diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in
index e41adc4..6053a55 100644
--- a/extra/Configs/Config.in
+++ b/extra/Configs/Config.in
@@ -1268,6 +1268,25 @@ config UCLIBC_HAS_EXTRA_COMPAT_RES_STATE
          Answer Y if selecting UCLIBC_HAS_COMPAT_RES_STATE is not enough.
          As far as I can say, this should never be needed.
 
+config UCLIBC_HAS_AVAHI_RES
+       bool "Try to resolve link-local IP addresses via avahi-daemon"
+       default n
+       depends on UCLIBC_HAS_IPV4 || UCLIBC_HAS_IPV6
+       help
+         Answer Y if you want to resolve .local addresses via multicast-dns.
+         The queries are forwarded to a running avahi-daemon via a domain
+         socket interface.
+         Note that this might interfere with a .local domain on your DNS.
+         Answering N saves around 50 bytes.
+
+config UCLIBC_AVAHI_SOCKET_PATH
+       string "Path to avahi unix domain socket"
+       default "/var/run/avahi-daemon/socket"
+       depends on UCLIBC_HAS_AVAHI_RES
+       help
+         A running avahi-daemon creates a socket to listen for queries.
+         Enter the path you configured your avahi-daemon to.
+
 config UCLIBC_HAS_LIBRESOLV_STUB
        bool "Provide libresolv stub"
        default n
diff --git a/libc/inet/Makefile.in b/libc/inet/Makefile.in
index d588220..7f436c9 100644
--- a/libc/inet/Makefile.in
+++ b/libc/inet/Makefile.in
@@ -33,6 +33,7 @@ CSRC-$(findstring y,$(UCLIBC_HAS_IPV4)$(UCLIBC_HAS_IPV6)) += \
        dnslookup.c opennameservers.c closenameservers.c \
        getnameinfo.c \
        gethostent.c gethostent_r.c
+CSRC-$(UCLIBC_HAS_AVAHI_RES) += avahi.c
 CSRC-$(findstring y,$(UCLIBC_HAS_IPV4)$(UCLIBC_HAS_IPV6)) += \
        get_hosts_byaddr_r.c get_hosts_byname_r.c \
        gethostbyaddr_r.c gethostbyname_r.c gethostbyname2_r.c \
diff --git a/libc/inet/avahi.c b/libc/inet/avahi.c
new file mode 100644
index 0000000..11ce055
--- /dev/null
+++ b/libc/inet/avahi.c
@@ -0,0 +1,183 @@
+/*
+ *
+ * (C) 2011 Weinmann GmbH, Hamburg, Germany
+ *
+ * Author: Nikolaus Voss <[email protected]>
+ *
+ * Derived from avahi.c / nss-mdns by Lennart Poettering.
+ * Copyright 2004-2007 Lennart Poettering <mzaffzqaf (at) 0pointer (dot) de>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <not-cancel.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "avahi.h"
+
+#define WHITESPACE " \t"
+
+static FILE *avahi_open_socket(void)
+{
+       int fd;
+       struct sockaddr_un sa;
+       FILE *f;
+
+       fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+
+       if (fd < 0)
+               return NULL;
+
+#ifndef __ASSUME_SOCK_CLOEXEC
+       fcntl_not_cancel(fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_UNIX;
+       strncpy(sa.sun_path, __UCLIBC_AVAHI_SOCKET_PATH__,
+               sizeof(sa.sun_path) - 1);
+       sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+       if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+               goto fail;
+
+       f = fdopen(fd, "r+");
+       if (!f)
+               goto fail;
+
+       return f;
+
+fail:
+       close(fd);
+
+       return NULL;
+}
+
+static int avahi_ends_with(const char *name, const char* suffix)
+{
+       size_t ln, ls;
+
+       if (!name || !suffix)
+               return 0;
+
+       if ((ls = strlen(suffix)) > (ln = strlen(name)))
+               return 0;
+
+       return strcasecmp(name + ln - ls, suffix) == 0;
+}
+
+int __avahi_resolve_name(int af, const char* name, void* data)
+{
+       FILE *f;
+       char *p;
+       int ret = -1;
+       char ln[256];
+
+       if (af != AF_INET && af != AF_INET6)
+               return ret;
+
+       if (!avahi_ends_with(name, ".local") &&
+           !avahi_ends_with(name, ".local."))
+               return ret;
+
+       f = avahi_open_socket();
+       if (!f)
+               return ret;
+
+       fprintf(f, "RESOLVE-HOSTNAME-IPV%d %s\n", af == AF_INET ? 4 : 6, name);
+       fflush(f);
+
+       if (!fgets(ln, sizeof(ln), f))
+               goto finish;
+
+       if (ln[0] != '+') {
+               ret = 1;
+               goto finish;
+       }
+
+       p = ln + 1;
+       p += strspn(p, WHITESPACE);
+
+       /* Skip interface */
+       p += strcspn(p, WHITESPACE);
+       p += strspn(p, WHITESPACE);
+
+       /* Skip protocol */
+       p += strcspn(p, WHITESPACE);
+       p += strspn(p, WHITESPACE);
+
+       /* Skip host name */
+       p += strcspn(p, WHITESPACE);
+       p += strspn(p, WHITESPACE);
+
+       /* Cut off end of line */
+       p[strcspn(p, "\n\r\t ")] = 0;
+
+       if (inet_pton(af, p, data) <= 0)
+               goto finish;
+
+       ret = 0;
+
+finish:
+       fclose(f);
+
+       return ret;
+}
+
+int __avahi_resolve_address(int af, const void *data, char* name,
+                           size_t name_len)
+{
+       FILE *f;
+       char *p;
+       int ret = -1;
+       char a[256], ln[256];
+
+       if (af != AF_INET && af != AF_INET6)
+               return ret;
+
+       f = avahi_open_socket();
+       if (!f)
+               return ret;
+
+       fprintf(f, "RESOLVE-ADDRESS %s\n", inet_ntop(af, data, a, sizeof(a)));
+
+       if (!fgets(ln, sizeof(ln), f))
+               goto finish;
+
+       if (ln[0] != '+') {
+               ret = 1;
+               goto finish;
+       }
+
+       p = ln + 1;
+       p += strspn(p, WHITESPACE);
+
+       /* Skip interface */
+       p += strcspn(p, WHITESPACE);
+       p += strspn(p, WHITESPACE);
+
+       /* Skip protocol */
+       p += strcspn(p, WHITESPACE);
+       p += strspn(p, WHITESPACE);
+
+       /* Cut off end of line */
+       p[strcspn(p, "\n\r\t ")] = 0;
+
+       strncpy(name, p, name_len - 1);
+       name[name_len - 1] = 0;
+
+       ret = 0;
+
+finish:
+       fclose(f);
+
+       return ret;
+}
diff --git a/libc/inet/avahi.h b/libc/inet/avahi.h
new file mode 100644
index 0000000..85df5f6
--- /dev/null
+++ b/libc/inet/avahi.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * (C) 2011 Weinmann GmbH, Hamburg, Germany
+ *
+ * Author: Nikolaus Voss <[email protected]>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ *
+ */
+
+#ifndef _UCLIBC_AVAHI_H_
+#define _UCLIBC_AVAHI_H_
+
+#ifdef __UCLIBC_HAS_AVAHI_RES__
+int __avahi_resolve_name(int af, const char* name, void* data) 
attribute_hidden;
+
+int __avahi_resolve_address(int af, const void *data, char* name,
+                           size_t name_len) attribute_hidden;
+#else
+static inline int __avahi_resolve_name(int af, const char* name, void* data)
+       attribute_hidden
+{
+       return 1;
+}
+
+static inline int __avahi_resolve_address(int af, const void *data, char* name,
+                                         size_t name_len) attribute_hidden
+{
+       return 1;
+}
+#endif
+
+#endif
diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c
index bc596b0..2527b50 100644
--- a/libc/inet/resolv.c
+++ b/libc/inet/resolv.c
@@ -318,6 +318,7 @@ Domain name in a message can be represented as either:
 #include <sys/stat.h>
 #include <bits/uClibc_mutex.h>
 #include "internal/parse_config.h"
+#include "avahi.h"
 
 /* poll() is not supported in kernel <= 2.0, therefore if __NR_poll is
  * not available, we assume an old Linux kernel is in use and we will
@@ -2073,10 +2074,11 @@ int gethostbyname_r(const char *name,
        alias[0] = alias0;
        alias[1] = NULL;
 
-       /* maybe it is already an address? */
+       /* maybe it is resolvable via avahi or already an address? */
        {
                struct in_addr *in = (struct in_addr *)(buf + 
sizeof(addr_list[0]) * 2);
-               if (inet_aton(name, in)) {
+               if (!__avahi_resolve_name(AF_INET, name, in) ||
+                   inet_aton(name, in)) {
                        addr_list[0] = in;
                        addr_list[1] = NULL;
                        result_buf->h_name = alias0;
@@ -2263,8 +2265,9 @@ int gethostbyname2_r(const char *name,
        strncpy(buf, name, buflen);
        buf[buflen] = '\0';
 
-       /* maybe it is already an address? */
-       if (inet_pton(AF_INET6, name, in)) {
+       /* maybe it is resolvable via avahi or already an address? */
+       if (!__avahi_resolve_name(AF_INET6, name, in) ||
+           inet_pton(AF_INET6, name, in)) {
                result_buf->h_name = buf;
                result_buf->h_addrtype = AF_INET6;
                result_buf->h_length = sizeof(*in);
@@ -2427,6 +2430,18 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen,
        addr_list[1] = NULL;
        memcpy(in, addr, addrlen);
 
+       /* is this a .local address? */
+       if (!__avahi_resolve_address(type, in, buf, buflen)) {
+               result_buf->h_name = buf;
+               result_buf->h_addrtype = type;
+               result_buf->h_length = addrlen;
+               result_buf->h_addr_list = (char **) addr_list;
+               result_buf->h_aliases = alias;
+               *result = result_buf;
+               *h_errnop = NETDB_SUCCESS;
+               return NETDB_SUCCESS;
+       }
+
        if (0) /* nothing */;
 #ifdef __UCLIBC_HAS_IPV4__
        else IF_HAS_BOTH(if (type == AF_INET)) {
diff --git a/libc/sysdeps/linux/common/bits/kernel-features.h 
b/libc/sysdeps/linux/common/bits/kernel-features.h
index 6bf5544..a322334 100644
--- a/libc/sysdeps/linux/common/bits/kernel-features.h
+++ b/libc/sysdeps/linux/common/bits/kernel-features.h
@@ -310,11 +310,12 @@
 #endif
 
 /* Support for various CLOEXEC and NONBLOCK flags was added for x86,
- *    x86-64, PPC, IA-64, and SPARC in 2.6.27.  */
+ *    x86-64, PPC, IA-64, ARM and SPARC in 2.6.27.  */
 #if __LINUX_KERNEL_VERSION >= 0x02061b \
     && (defined __i386__ || defined __x86_64__ || defined __powerpc__ \
-        || defined __ia64__ || defined __sparc__ || defined __s390__)
-/* # define __ASSUME_SOCK_CLOEXEC  1 */
+        || defined __ia64__ || defined __sparc__ || defined __s390__ \
+        || defined __arm__)
+# define __ASSUME_SOCK_CLOEXEC  1
 /* # define __ASSUME_IN_NONBLOCK   1 */
 # define __ASSUME_PIPE2         1
 /* # define __ASSUME_EVENTFD2      1 */
-- 
1.7.5.4

_______________________________________________
uClibc mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/uclibc

Reply via email to