summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2007-02-08 16:42:22 +0000
committerLennart Poettering <lennart@poettering.net>2007-02-08 16:42:22 +0000
commit00028b6b41d90462e4e156813778155675e9628c (patch)
treeb795b1d9cc13da0e8e865d34755d19a3ea28dd5f
parent650391f33529d12a0e7616ea0ba19a226128f042 (diff)
FreeBSD portability (Patch from Bruce M Simpson)
git-svn-id: file:///home/lennart/svn/public/nss-mdns/trunk@114 0ee8848e-81ea-0310-a63a-f631d1a40d77
-rw-r--r--configure.ac7
-rw-r--r--doc/README.html.in2
-rw-r--r--src/Makefile.am66
-rw-r--r--src/avahi-test.c1
-rw-r--r--src/bsdnss.c467
-rw-r--r--src/query.c89
6 files changed, 600 insertions, 32 deletions
diff --git a/configure.ac b/configure.ac
index 0cd6c3a..8592c2f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -83,6 +83,13 @@ AC_FUNC_MALLOC
AC_FUNC_SELECT_ARGTYPES
AC_CHECK_FUNCS([gethostbyaddr gethostbyname gettimeofday inet_ntoa memset select socket strcspn strdup strerror strncasecmp strcasecmp strspn])
+# FreeBSD has a slightly different NSS interface
+case ${host} in
+ *-freebsd*) freebsd="yes" ;;
+esac
+
+AM_CONDITIONAL([FREEBSD_NSS], [test "x$freebsd" = "xyes"])
+
# If using GCC specify some additional parameters
if test "x$GCC" = "xyes" ; then
CFLAGS="$CFLAGS -pipe -W -Wall -pedantic"
diff --git a/doc/README.html.in b/doc/README.html.in
index 2bfc24f..1c796ad 100644
--- a/doc/README.html.in
+++ b/doc/README.html.in
@@ -284,6 +284,8 @@ compilation and <tt>make install</tt> (as root) for installation of
<p>Philipp Zabel for ARM support.</p>
+<p>Bruce M Simpson for porting it to FreeBSD.</p>
+
<h2><a name="download">Download</a></h2>
<p>The newest release is always available from <a href="@PACKAGE_URL@">@PACKAGE_URL@</a></p>
diff --git a/src/Makefile.am b/src/Makefile.am
index c2c2c94..b770559 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,39 +29,46 @@ AM_CFLAGS = \
# This cool debug trap works on i386/gcc only
AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
-lib_LTLIBRARIES= \
+AM_LDFLAGS=-avoid-version -module -export-dynamic
+
+lib_LTLIBRARIES =
+
+if FREEBSD_NSS
+lib_LTLIBRARIES += \
+ nss_mdns.la \
+ nss_mdns4.la \
+ nss_mdns6.la \
+ nss_mdns_minimal.la \
+ nss_mdns4_minimal.la \
+ nss_mdns6_minimal.la
+else
+lib_LTLIBRARIES += \
libnss_mdns.la \
libnss_mdns4.la \
libnss_mdns6.la \
libnss_mdns_minimal.la \
libnss_mdns4_minimal.la \
libnss_mdns6_minimal.la
+endif
-noinst_PROGRAMS= \
- nss-test
-
-libnss_mdns_la_SOURCES= \
- nss.c \
- util.c util.h
-
-libnss_mdns_la_CFLAGS=$(AM_CFLAGS)
-libnss_mdns_la_LDFLAGS=-avoid-version -module -export-dynamic -shrext .so.2 -Wl,-version-script=$(srcdir)/map-file
+noinst_PROGRAMS = nss-test
+sources = util.c util.h
if ENABLE_AVAHI
-libnss_mdns_la_SOURCES += \
- avahi.c avahi.h
-
+sources += avahi.c avahi.h
noinst_PROGRAMS += avahi-test
endif
if ENABLE_LEGACY
-libnss_mdns_la_SOURCES += \
- query.c query.h \
- dns.c dns.h
-
+sources += query.c query.h dns.c dns.h
noinst_PROGRAMS += mdns-test
endif
+# GNU Libc
+libnss_mdns_la_SOURCES= $(sources) nss.c
+libnss_mdns_la_CFLAGS=$(AM_CFLAGS)
+libnss_mdns_la_LDFLAGS=$(AM_LDFLAGS) -shrext .so.2 -Wl,-version-script=$(srcdir)/map-file
+
libnss_mdns_minimal_la_SOURCES=$(libnss_mdns_la_SOURCES)
libnss_mdns_minimal_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DMDNS_MINIMAL
libnss_mdns_minimal_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
@@ -82,6 +89,31 @@ libnss_mdns6_minimal_la_SOURCES=$(libnss_mdns_la_SOURCES)
libnss_mdns6_minimal_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1 -DMDNS_MINIMAL
libnss_mdns6_minimal_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
+# FreeBSD
+nss_mdns_la_SOURCES=$(sources) bsdnss.c
+nss_mdns_la_CFLAGS=$(AM_CFLAGS)
+nss_mdns_la_LDFLAGS=$(AM_LDFLAGS) -shrext .so.1
+
+nss_mdns_minimal_la_SOURCES=$(nss_mdns_la_SOURCES)
+nss_mdns_minimal_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DMDNS_MINIMAL
+nss_mdns_minimal_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
+
+nss_mdns4_la_SOURCES=$(nss_mdns_la_SOURCES)
+nss_mdns4_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV4_ONLY=1
+nss_mdns4_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
+
+nss_mdns4_minimal_la_SOURCES=$(nss_mdns_la_SOURCES)
+nss_mdns4_minimal_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV4_ONLY=1 -DMDNS_MINIMAL
+nss_mdns4_minimal_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
+
+nss_mdns6_la_SOURCES=$(nss_mdns_la_SOURCES)
+nss_mdns6_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1
+nss_mdns6_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
+
+nss_mdns6_minimal_la_SOURCES=$(nss_mdns_la_SOURCES)
+nss_mdns6_minimal_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1 -DMDNS_MINIMAL
+nss_mdns6_minimal_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
+
avahi_test_SOURCES = \
avahi.c avahi.h \
util.c util.h \
diff --git a/src/avahi-test.c b/src/avahi-test.c
index b1be16d..e57d8b2 100644
--- a/src/avahi-test.c
+++ b/src/avahi-test.c
@@ -19,6 +19,7 @@
USA.
***/
+#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
diff --git a/src/bsdnss.c b/src/bsdnss.c
new file mode 100644
index 0000000..cdda366
--- /dev/null
+++ b/src/bsdnss.c
@@ -0,0 +1,467 @@
+/* $Id$ */
+
+/***
+ This file is part of nss-mdns.
+ nss-mdns 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.
+ nss-mdns is distributed in the hope that it will be useful, but1
+ 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 nss-mdns; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* Original author: Bruce M. Simpson <bms@FreeBSD.org> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/ktrace.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <nss.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "config.h"
+
+#ifdef MDNS_MINIMAL
+/*
+ * FreeBSD support prefers Avahi.
+ */
+#endif
+
+#if defined(NSS_IPV4_ONLY) || defined(NSS_IPV6_ONLY)
+/*
+ * FreeBSD's libc is always built with IPv4 support.
+ * There is no way of telling at compile time with a define if libc
+ * was built with -DINET6 or not; a configure test would be required.
+ * Therefore, distinguishing between the two makes no sense.
+ */
+#define NO_BUILD_BSD_NSS
+#endif
+
+#ifndef NO_BUILD_BSD_NSS
+/*
+ * To turn on utrace() records, compile with -DDEBUG_UTRACE.
+ */
+#ifdef DEBUG_UTRACE
+#define _NSS_UTRACE(msg) \
+ do { \
+ static const char __msg[] = msg ; \
+ (void)utrace(__msg, sizeof(__msg)); \
+ } while (0)
+#else
+#define _NSS_UTRACE(msg)
+#endif
+
+ns_mtab *nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg);
+
+extern enum nss_status _nss_mdns_gethostbyname_r (const char *name, struct hostent * result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop);
+
+extern enum nss_status _nss_mdns_gethostbyname2_r (const char *name, int af, struct hostent * result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop);
+extern enum nss_status _nss_mdns_gethostbyaddr_r (struct in_addr * addr, int len, int type,
+ struct hostent * result, char *buffer,
+ size_t buflen, int *errnop, int *h_errnop);
+extern enum nss_status _nss_mdns4_gethostbyname_r (const char *name, struct hostent * result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop);
+
+extern enum nss_status _nss_mdns4_gethostbyname2_r (const char *name, int af, struct hostent * result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop);
+extern enum nss_status _nss_mdns4_gethostbyaddr_r (struct in_addr * addr, int len, int type,
+ struct hostent * result, char *buffer,
+ size_t buflen, int *errnop, int *h_errnop);
+extern enum nss_status _nss_mdns6_gethostbyname_r (const char *name, struct hostent * result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop);
+
+extern enum nss_status _nss_mdns6_gethostbyname2_r (const char *name, int af, struct hostent * result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop);
+extern enum nss_status _nss_mdns6_gethostbyaddr_r (struct in_addr * addr, int len, int type,
+ struct hostent * result, char *buffer,
+ size_t buflen, int *errnop, int *h_errnop);
+
+typedef enum nss_status (*_bsd_nsstub_fn_t)(const char *, struct hostent *, char *, size_t, int *, int *);
+
+/* XXX: FreeBSD 5.x is not supported. */
+static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_getaddrinfo);
+static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_gethostbyaddr_r);
+static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_gethostbyname2_r);
+static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_ghbyaddr);
+static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_ghbyname);
+
+static ns_mtab methods[] = {
+ /* database, name, method, mdata */
+ { NSDB_HOSTS, "getaddrinfo", __nss_bsdcompat_getaddrinfo, NULL },
+ { NSDB_HOSTS, "gethostbyaddr_r", __nss_bsdcompat_gethostbyaddr_r, NULL },
+ { NSDB_HOSTS, "gethostbyname2_r", __nss_bsdcompat_gethostbyname2_r, NULL },
+ { NSDB_HOSTS, "ghbyaddr", __nss_bsdcompat_ghbyaddr, NULL },
+ { NSDB_HOSTS, "ghbyname", __nss_bsdcompat_ghbyname, NULL },
+};
+
+ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+
+ *mtabsize = sizeof(methods)/sizeof(methods[0]);
+ *unreg = NULL;
+ return (methods);
+}
+
+/*
+ * Calling convention:
+ * ap: const char *name (optional), struct addrinfo *pai (hints, optional)
+ * retval: struct addrinfo **
+ *
+ * TODO: Map all returned hostents, not just the first match.
+ *
+ * name must always be specified by libc; pai is allocated
+ * by libc and must always be specified.
+ *
+ * We can malloc() addrinfo instances and hang them off ai->next;
+ * canonnames may also be malloc()'d.
+ * libc is responsible for mapping our ns error return to gai_strerror().
+ *
+ * libc calls us only to look up qualified hostnames. We don't need to
+ * worry about port numbers; libc will call getservbyname() and explore
+ * the appropriate maps configured in nsswitch.conf(5).
+ *
+ * _errno and _h_errno are unused by getaddrinfo(), as it is
+ * [mostly] OS independent interface implemented by Win32.
+ */
+static int
+__nss_bsdcompat_getaddrinfo(void *retval, void *mdata __unused, va_list ap)
+{
+ struct addrinfo sentinel;
+ struct addrinfo *ai;
+ char *buffer;
+ void *cbufp; /* buffer handed to libc */
+ char *hap;
+ struct hostent *hp;
+ void *mbufp; /* buffer handed to mdns */
+ const char *name;
+ const struct addrinfo *pai;
+ struct sockaddr *psa; /* actually *sockaddr_storage */
+ struct addrinfo **resultp;
+ int _errno;
+ int _h_errno;
+ size_t mbuflen = 1024;
+ enum nss_status status;
+
+ _NSS_UTRACE("__nss_bsdcompat_getaddrinfo: called");
+
+ _h_errno = _errno = 0;
+ status = NSS_STATUS_UNAVAIL;
+
+ name = va_arg(ap, const char *);
+ pai = va_arg(ap, struct addrinfo *);
+ resultp = (struct addrinfo **)retval;
+
+ /* XXX: Will be used to hang off multiple matches later. */
+ memset(&sentinel, 0, sizeof(sentinel));
+
+ if (name == NULL || pai == NULL) {
+ *resultp = sentinel.ai_next;
+ return (NS_UNAVAIL);
+ }
+
+ mbufp = malloc((sizeof(struct hostent) + mbuflen));
+ if (mbufp == NULL) {
+ *resultp = sentinel.ai_next;
+ return (NS_UNAVAIL);
+ }
+ hp = (struct hostent *)mbufp;
+ buffer = (char *)(hp + 1);
+
+ cbufp = malloc(sizeof(struct addrinfo) +
+ sizeof(struct sockaddr_storage));
+ if (cbufp == NULL) {
+ free(mbufp);
+ *resultp = sentinel.ai_next;
+ return (NS_UNAVAIL);
+ }
+ ai = (struct addrinfo *)cbufp;
+ psa = (struct sockaddr *)(ai + 1);
+
+ /*
+ * 1. Select which function to call based on the address family.
+ * 2. Map hostent to addrinfo.
+ * 3. Hand-off buffer to libc.
+ */
+ switch (pai->ai_family) {
+ case AF_UNSPEC:
+ status = _nss_mdns_gethostbyname_r(name, hp, buffer, mbuflen,
+ &_errno, &_h_errno);
+ break;
+ case AF_INET:
+ status = _nss_mdns4_gethostbyname_r(name, hp, buffer, mbuflen,
+ &_errno, &_h_errno);
+ break;
+ case AF_INET6:
+ status = _nss_mdns6_gethostbyname_r(name, hp, buffer, mbuflen,
+ &_errno, &_h_errno);
+ break;
+ default:
+ break;
+ }
+ status = __nss_compat_result(status, _errno);
+
+ if (status == NS_SUCCESS) {
+ memset(ai, 0, sizeof(struct addrinfo));
+ ai->ai_flags = pai->ai_flags;
+ ai->ai_socktype = pai->ai_socktype;
+ ai->ai_protocol = pai->ai_protocol;
+ ai->ai_family = hp->h_addrtype;
+ memset(psa, 0, sizeof(struct sockaddr_storage));
+ psa->sa_len = ai->ai_addrlen;
+ psa->sa_family = ai->ai_family;
+ ai->ai_addr = psa;
+ hap = hp->h_addr_list[0];
+ switch (ai->ai_family) {
+ case AF_INET:
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ memcpy(&((struct sockaddr_in *)psa)->sin_addr, hap,
+ ai->ai_addrlen);
+ break;
+ case AF_INET6:
+ ai->ai_addrlen = sizeof(struct sockaddr_in6);
+ memcpy(&((struct sockaddr_in6 *)psa)->sin6_addr, hap,
+ ai->ai_addrlen);
+ break;
+ default:
+ ai->ai_addrlen = sizeof(struct sockaddr_storage);
+ memcpy(psa->sa_data, hap, ai->ai_addrlen);
+ }
+ sentinel.ai_next = ai;
+ free(mbufp);
+ }
+
+ if (sentinel.ai_next == NULL) {
+ free(cbufp);
+ free(mbufp);
+ }
+
+ *resultp = sentinel.ai_next;
+ return (status);
+}
+
+/*
+ * Calling convention:
+ * ap: const u_char *uaddr, socklen_t len, int af, struct hostent *hp,
+ * char *buf, size_t buflen, int ret_errno, int *h_errnop
+ * retval: should be set to NULL or hp passed in
+ */
+static int
+__nss_bsdcompat_gethostbyaddr_r(void *retval, void *mdata __unused, va_list ap)
+{
+ void *addr;
+ char *buf;
+ int *h_errnop;
+ struct hostent *hp;
+ struct hostent **resultp;
+ int af;
+ size_t buflen;
+ int len;
+ int ret_errno;
+ enum nss_status status;
+
+ addr = va_arg(ap, void *);
+ len = va_arg(ap, socklen_t);
+ af = va_arg(ap, int);
+ hp = va_arg(ap, struct hostent *);
+ buf = va_arg(ap, char *);
+ buflen = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int);
+ h_errnop = va_arg(ap, int *);
+ resultp = (struct hostent **)retval;
+
+ *resultp = NULL;
+ status = _nss_mdns_gethostbyaddr_r(addr, len, af, hp, buf, buflen,
+ &ret_errno, h_errnop);
+
+ status = __nss_compat_result(status, *h_errnop);
+ if (status == NS_SUCCESS)
+ *resultp = hp;
+ return (status);
+}
+
+/*
+ * Calling convention:
+ * ap: const char *name, int af, struct hostent *hp, char *buf,
+ * size_t buflen, int ret_errno, int *h_errnop
+ * retval is a struct hostent **result passed in by the libc client,
+ * which is responsible for allocating storage.
+ */
+static int
+__nss_bsdcompat_gethostbyname2_r(void *retval, void *mdata __unused,
+ va_list ap)
+{
+ char *buf;
+ const char *name;
+ int *h_errnop;
+ struct hostent *hp;
+ struct hostent **resultp;
+ int af;
+ size_t buflen;
+ int ret_errno;
+ enum nss_status status;
+
+ name = va_arg(ap, char *);
+ af = va_arg(ap, int);
+ hp = va_arg(ap, struct hostent *);
+ buf = va_arg(ap, char *);
+ buflen = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int);
+ h_errnop = va_arg(ap, int *);
+ resultp = (struct hostent **)retval;
+
+ *resultp = NULL;
+ if (hp == NULL)
+ return (NS_UNAVAIL);
+
+ status = _nss_mdns_gethostbyname2_r(name, af, hp, buf, buflen,
+ &ret_errno, h_errnop);
+
+ status = __nss_compat_result(status, *h_errnop);
+ if (status == NS_SUCCESS)
+ *resultp = hp;
+ return (status);
+}
+
+/*
+ * Used by getipnodebyaddr(3).
+ *
+ * Calling convention:
+ * ap: struct in[6]_addr *src, size_t len, int af, int *errp
+ * retval: pointer to a pointer to an uninitialized struct hostent,
+ * in which should be returned a single pointer to on-heap storage.
+ *
+ * This function is responsible for allocating on-heap storage.
+ * The caller is responsible for calling freehostent() on the returned
+ * storage.
+ */
+static int
+__nss_bsdcompat_ghbyaddr(void *retval, void *mdata __unused, va_list ap)
+{
+ char *buffer;
+ void *bufp;
+ int *errp;
+ struct hostent *hp;
+ struct hostent **resultp;
+ void *src;
+ int af;
+ size_t buflen = 1024;
+ size_t len;
+ int h_errnop;
+ enum nss_status status;
+
+ src = va_arg(ap, void *);
+ len = va_arg(ap, size_t);
+ af = va_arg(ap, int);
+ errp = va_arg(ap, int *);
+ resultp = (struct hostent **)retval;
+
+ _NSS_UTRACE("__nss_bsdcompat_ghbyaddr: called");
+
+ bufp = malloc((sizeof(struct hostent) + buflen));
+ if (bufp == NULL) {
+ *resultp = NULL;
+ return (NS_UNAVAIL);
+ }
+ hp = (struct hostent *)bufp;
+ buffer = (char *)(hp + 1);
+
+ status = _nss_mdns_gethostbyaddr_r(src, len, af, hp, buffer,
+ buflen, errp, &h_errnop);
+
+ status = __nss_compat_result(status, *errp);
+ if (status != NS_SUCCESS) {
+ free(bufp);
+ hp = NULL;
+ }
+ *resultp = hp;
+ return (status);
+}
+
+/*
+ * Used by getipnodebyname(3).
+ *
+ * Calling convention:
+ * ap: const char *name, int af, int *errp
+ * retval: pointer to a pointer to an uninitialized struct hostent.
+ *
+ * This function is responsible for allocating on-heap storage.
+ * The caller is responsible for calling freehostent() on the returned
+ * storage.
+ */
+static int
+__nss_bsdcompat_ghbyname(void *retval, void *mdata __unused, va_list ap)
+{
+ char *buffer;
+ void *bufp;
+ int *errp;
+ struct hostent *hp;
+ struct hostent **resultp;
+ char *name;
+ int af;
+ size_t buflen = 1024;
+ int h_errnop;
+ enum nss_status status;
+
+ name = va_arg(ap, char *);
+ af = va_arg(ap, int);
+ errp = va_arg(ap, int *);
+ resultp = (struct hostent **)retval;
+
+ bufp = malloc((sizeof(struct hostent) + buflen));
+ if (bufp == NULL) {
+ *resultp = NULL;
+ return (NS_UNAVAIL);
+ }
+ hp = (struct hostent *)bufp;
+ buffer = (char *)(hp + 1);
+
+ status = _nss_mdns_gethostbyname_r(name, hp, buffer, buflen, errp,
+ &h_errnop);
+
+ status = __nss_compat_result(status, *errp);
+ if (status != NS_SUCCESS) {
+ free(bufp);
+ hp = NULL;
+ }
+ *resultp = hp;
+ return (status);
+}
+
+#endif /* !NO_BUILD_BSD_NSS */
diff --git a/src/query.c b/src/query.c
index fe5d541..42549bd 100644
--- a/src/query.c
+++ b/src/query.c
@@ -87,13 +87,36 @@ int mdns_open_socket(void) {
if (bind(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0)
goto fail;
+#ifdef IP_PKTINFO
yes = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0)
- goto fail;
-
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0)
goto fail;
+#else
+
+#ifdef IP_RECVINTERFACE
+ yes = 1;
+ if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0)
+ goto fail;
+#elif defined(IP_RECVIF)
+ yes = 1;
+ if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0)
+ goto fail;
+#endif
+
+#ifdef IP_RECVDSTADDR
+ yes = 1;
+ if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0)
+ goto fail;
+#endif
+
+#endif /* IP_PKTINFO */
+#ifdef IP_RECVTTL
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0)
+ goto fail;
+#endif
+
if (set_cloexec(fd) < 0)
goto fail;
@@ -113,9 +136,15 @@ static int send_dns_packet(int fd, struct dns_packet *p) {
struct sockaddr_in sa;
struct msghdr msg;
struct iovec io;
+#ifdef IP_PKTINFO
struct cmsghdr *cmsg;
+ uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in_pktinfo))];
struct in_pktinfo *pkti;
- uint8_t cmsg_data[CMSG_LEN(sizeof(struct in_pktinfo))];
+#elif defined(IP_SENDSRCADDR)
+ struct cmsghdr *cmsg;
+ uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in_addr))];
+ struct in_addr *addr;
+#endif
int i, n;
struct ifreq ifreq[32];
struct ifconf ifconf;
@@ -131,24 +160,43 @@ static int send_dns_packet(int fd, struct dns_packet *p) {
io.iov_base = p->data;
io.iov_len = p->size;
- memset(cmsg_data, 0, sizeof(cmsg_data));
- cmsg = (struct cmsghdr*) cmsg_data;
- cmsg->cmsg_len = sizeof(cmsg_data);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
-
- pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
- pkti->ipi_ifindex = 0;
-
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;
+
+#ifdef IP_PKTINFO
+ memset(cmsg_data, 0, sizeof(cmsg_data));
msg.msg_control = cmsg_data;
msg.msg_controllen = sizeof(cmsg_data);
- msg.msg_flags = 0;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
+
+ msg.msg_controllen = cmsg->cmsg_len;
+#elif defined(IP_SENDSRCADDR)
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_SENDSRCADDR;
+
+ addr = (struct in_addr *)CMSG_DATA(cmsg);
+
+ msg.msg_controllen = cmsg->cmsg_len;
+#elif defined(__GNUC__)
+#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_SENDSRCADDR is not available"
+#endif
+
ifconf.ifc_req = ifreq;
ifconf.ifc_len = sizeof(ifreq);
@@ -183,6 +231,7 @@ static int send_dns_packet(int fd, struct dns_packet *p) {
if (ioctl(fd, SIOCGIFINDEX, &ifreq[i]) < 0)
continue; /* See above why we ignore this error */
+#ifdef IP_PKTINFO
/* Only send the the packet once per interface. We assume that
* multiple addresses assigned to the same interface follow
* immediately one after the other.*/
@@ -190,6 +239,11 @@ static int send_dns_packet(int fd, struct dns_packet *p) {
continue;
last_index = pkti->ipi_ifindex = ifreq[i].ifr_ifindex;
+#elif defined(IP_SENDSRCADDR)
+ addr->s_addr = ifsa->sin_addr.s_addr;
+#elif defined(__GNUC__)
+#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_SENDSRCADDR is not available"
+#endif
for (;;) {
@@ -241,7 +295,12 @@ static int recv_dns_packet(int fd, struct dns_packet **ret_packet, uint8_t *ret_
*ret_ttl = 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
- if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) {
+#ifdef SOL_IP
+ if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL)
+#else
+ if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL)
+#endif
+ {
*ret_ttl = (uint8_t) (*(uint32_t*) CMSG_DATA(cmsg));
break;
}