| /* $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 */ |