blob: f4900824501547efa7950542ec7205f364530b7d [file] [log] [blame]
/* $Id: bsdnss.c 114 2007-02-08 16:42:22Z lennart $ */
/***
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 */