From 5654318b39dae5e5a32e290849ec590cb85ee0b3 Mon Sep 17 00:00:00 2001 Message-Id: <5654318b39dae5e5a32e290849ec590cb85ee0b3.1322557154.git.n.voss@weinmann.de> In-Reply-To: References: From: Nikolaus Voss Date: Mon, 28 Nov 2011 16:18:30 +0100 Subject: [PATCH 1/2] gethostbyaddr_r: add space for alias pointers To: uclibc@uclibc.org addr_list and alias where on the same buffer offset. This led to corrupt addr_list which was overwritten by the resolved name. Signed-off-by: Nikolaus Voss --- libc/inet/resolv.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index 021d5bf..bc596b0 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -2402,6 +2402,8 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, */ #define in6 ((struct in6_addr *)in) alias = (char **)buf; + buf += sizeof(alias) * 2; + buflen -= sizeof(alias) * 2; addr_list = (struct in_addr**)buf; buf += sizeof(*addr_list) * 2; buflen -= sizeof(*addr_list) * 2; -- 1.7.5.4 From 5388e302e40b12fb6682899dd4206f3e3d7a84fa Mon Sep 17 00:00:00 2001 Message-Id: <5388e302e40b12fb6682899dd4206f3e3d7a84fa.1322557154.git.n.voss@weinmann.de> In-Reply-To: References: From: Nikolaus Voss Date: Mon, 28 Nov 2011 16:22:42 +0100 Subject: [PATCH 2/2] Make link-local addresses resolvable via running avahi-daemon To: uclibc@uclibc.org 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 --- 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 + * + * Derived from avahi.c / nss-mdns by Lennart Poettering. + * Copyright 2004-2007 Lennart Poettering + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 #include #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