--- mDNSPosix/nss_mdns.c.orig 2012-04-14 23:07:19.000000000 -0700 +++ mDNSPosix/nss_mdns.c 2015-05-20 13:18:19.000000000 -0700 @@ -2721,3 +2721,339 @@ return -1; } + +static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_gethostbyname2_r); +static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_gethostbyaddr_r); +static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_getaddrinfo); + +static ns_mtab methods[] = { + /* database, name, method, mdata */ + { NSDB_HOSTS, "gethostbyaddr_r", __nss_bsdcompat_gethostbyaddr_r, NULL }, + { NSDB_HOSTS, "gethostbyname2_r", __nss_bsdcompat_gethostbyname2_r, NULL }, + { NSDB_HOSTS, "getaddrinfo", __nss_bsdcompat_getaddrinfo, NULL }, +}; + +/* + * The prototype for gethostbyaddr_r is: + * int gethostbyaddr_r(const void *addr, socklen_t len, int type, + * struct hostent *ret, char *buf, size_t buflen, + * struct hostent **result, int *h_errnop); + * + * nsdispatch calls gethostbyaddr_r as: + * rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, + * "gethostbyaddr_r", default_src, uaddr, len, af, hp, buf, buflen, + * &ret_errno, h_errnop); + * which translates into us being (effectively) called as: + * rval = func(result, mdata, uaddr, len, type, ret, buf, buflen, &ret_errno, h_errnop) + * We turn result into a struct hostent **, set *result to NULL, and then + * call _nss_mdns_gethostbyaddr(). + */ +static int +__nss_bsdcompat_gethostbyaddr_r(void *retval, void *mdata __unused, va_list ap) +{ + const void *addr; + socklen_t len; + int type; + struct hostent *ret; + char *buf; + size_t buflen; + struct hostent **result = (struct hostent**)retval; + int *ret_errno; + int *h_errnop; + enum nss_status status; + + addr = va_arg(ap, const void*); + len = va_arg(ap, int); + type = va_arg(ap, int); + ret = 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 *); + + *result = NULL; + status = _nss_mdns_gethostbyaddr_r(addr, len, type, ret, buf, buflen, ret_errno, h_errnop); + status = __nss_compat_result(status, *h_errnop); + if (status == NS_SUCCESS) + *result = ret; + + return (status); +} + +/* + * This is a lot of guesswork on my part. + * nsdispatch calls getaddrinfo as: + * + * _nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", + * default_dns_files, hostname, pai) + * + * which translates to us being (effectively) called as: + * rval = func(result, mdata, hostname, pai) + * The first argument, result, is struct addrinfo **. + * + */ + +static struct addrinfo * +get_one_ai(const char *hostname, int af, const struct addrinfo *hints) +{ + nss_status lookup_status; + hostent hostent_buf; + char buf[1024] = { 0 }; + int err1, h_err1; + struct addrinfo *retval = NULL; + + memset(&hostent_buf, 0, sizeof(hostent_buf)); + lookup_status = mdns_gethostbyname2(hostname, + af, + &hostent_buf, + buf, + sizeof(buf), + &err1, + &h_err1); + lookup_status = __nss_compat_result(lookup_status, h_err1); + if (lookup_status == NS_SUCCESS) { + // Great, we found it, now what? + retval = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage)); + if (retval) { + // Take advantage of c's scaling + retval->ai_addr = (struct sockaddr*)(retval + 1); + retval->ai_flags = hints->ai_flags; + retval->ai_socktype = hints->ai_socktype; + retval->ai_protocol = hints->ai_protocol; + retval->ai_family = hostent_buf.h_addrtype; + retval->ai_addrlen = hostent_buf.h_length; + retval->ai_addr->sa_len = hostent_buf.h_length; + retval->ai_addr->sa_family = retval->ai_family; + + if (retval->ai_family == PF_INET) { + struct sockaddr_in *sin = (void*)retval->ai_addr; + retval->ai_addrlen = sizeof(*sin); + memcpy(&sin->sin_addr, hostent_buf.h_addr, hostent_buf.h_length); + } else if (retval->ai_family == PF_INET6) { + struct sockaddr_in6 *sin = (void*)retval->ai_addr; + retval->ai_addrlen = sizeof(*sin); + memcpy(&sin->sin6_addr, hostent_buf.h_addr, hostent_buf.h_length); + } else { + retval->ai_addrlen = sizeof(struct sockaddr_storage); + memcpy(retval->ai_addr->sa_data, + hostent_buf.h_addr, + hostent_buf.h_length); + } + retval->ai_canonname = strdup(hostname); +#ifdef DEBUG + fprintf(stderr, "%s: ai_canonname = %s, ai_addrlen = %d\n", __FUNCTION__, retval->ai_canonname, (int)(retval->ai_addrlen)); + { + char ascii[1024]; + void *sin_ptr; + + if (retval->ai_family == PF_INET) { + struct sockaddr_in *sin = (void*)retval->ai_addr; + sin_ptr = &sin->sin_addr; + } else if (retval->ai_family == PF_INET6) { + struct sockaddr_in6 *sin6 = (void*)retval->ai_addr; + sin_ptr = &sin6->sin6_addr; + } else { + sin_ptr = &retval->ai_addr; + } + if (inet_ntop(retval->ai_family, + sin_ptr, + ascii, + sizeof(ascii))) { + fprintf(stderr, "\tip = %s\n", ascii); + } else { + fprintf(stderr, "\tUnable to convert address to string\n"); + } + } +#endif + } + } +//#ifdef DEBUG + if (retval == NULL) + fprintf(stderr, "%s: returning %p, lookup_status = %d\n", __FUNCTION__, retval, lookup_status); +//#endif + return retval; +} + +#ifdef DEBUG +static void +PrintAddrinfo(struct addrinfo *ai) +{ + char buf[1024]; + void *sin_ptr; + if (ai == NULL) + return; + buf[0] = 0; + if (ai->ai_family == PF_INET) { + struct sockaddr_in *sin = (void*)ai->ai_addr; + sin_ptr = &sin->sin_addr; + } else if (ai->ai_family == PF_INET6) { + struct sockaddr_in6 *sin6 = (void*)ai->ai_addr; + sin_ptr = &sin6->sin6_addr; + } else { + sin_ptr = &ai->ai_addr; + } + inet_ntop(ai->ai_family, sin_ptr, buf, sizeof(buf)); + + fprintf(stderr, "struct addrinfo {\n"); + fprintf(stderr, "\tai_flags = %#x,\n", ai->ai_flags); + fprintf(stderr, "\tai_family = %d,\n", ai->ai_family); + fprintf(stderr, "\tai_socktype = %d,\n", ai->ai_socktype); + fprintf(stderr, "\tai_protocol = %d,\n", ai->ai_protocol); + fprintf(stderr, "\tai_addrlen = %d,\n", (int)ai->ai_addrlen); + fprintf(stderr, "\tai_addr = %s,\n", buf[0] ? buf : "(unknown?)"); + fprintf(stderr, "}\n"); + +} +#endif + +static int +__nss_bsdcompat_getaddrinfo(void *result, void *mdata __unused, va_list ap) +{ + const char *hostname; + const struct addrinfo *pai; + struct addrinfo **retval = (struct addrinfo **)result; + + hostname = va_arg(ap, const char *); + pai = va_arg(ap, const struct addrinfo *); + + *retval = NULL; + // Some quick error checking + if (hostname == NULL || pai == NULL) { +#ifdef DEBUG + fprintf(stderr, "%s(%d): Returning NS_UNAVAIL\n", __FUNCTION__, __LINE__); +#endif + return (NS_UNAVAIL); + } + +#ifdef DEBUG + { + fprintf(stderr, + "%s: *retval = %p, hostname = %s, pai = { ai_flags = %#x, ai_family = %#x, ai_socktype = %#x, ai_protocol = %#x }\n", + __FUNCTION__, *retval, hostname, pai->ai_flags, pai->ai_family, pai->ai_socktype, pai->ai_protocol); + } +#endif + + /* + * We can do three kinds of searches: + * PF_UNSPEC: whichever kind we find + * PF_INET: An IPv4 address + * PF_INET6: An IPv6 address. + * We'll use mdns_gethostbyname2_r(), + * which will handle PF_INET or PF_INET6. + * Buf if we're given PF_UNSPEC we should try each in + * succession, I suppose? Or perhaps we're supposed to + * return both if we can? + */ + switch (pai->ai_family) { + case PF_INET: + case PF_INET6: + *retval = get_one_ai(hostname, pai->ai_family, pai); +#ifdef DEBUG + fprintf(stderr, "Only needed one, *retval = %p\n", *retval); +#endif + break; + case PF_UNSPEC: + { + // Do both! + struct addrinfo *ipv4, *ipv6; + struct addrinfo sentinel, *tmp = &sentinel; + memset(&sentinel, 0, sizeof(sentinel)); + if ((ipv6 = get_one_ai(hostname, PF_INET6, pai)) != NULL) { + tmp->ai_next = ipv6; + tmp = ipv6; + } + if ((ipv4 = get_one_ai(hostname, PF_INET, pai)) != NULL) { + tmp->ai_next = ipv4; + tmp = ipv4; + } + *retval = sentinel.ai_next; + } + break; + default: +#ifdef DEBUG + fprintf(stderr, "%s(%d): Returning NS_UNAVAIL\n", __FUNCTION__, __LINE__); +#endif + return (NS_UNAVAIL); + } + + if (*retval == NULL) { +#ifdef DEBUG + fprintf(stderr, "%s(%d): Returning NS_NOTFOUND\n", __FUNCTION__, __LINE__); +#endif + return (NS_NOTFOUND); + } + +#ifdef DEBUG + { + fprintf(stderr, "%s: *retval = %p\n", __FUNCTION__, *retval); + } + struct addrinfo *tai; + for (tai = *retval; tai; tai = tai->ai_next) { + PrintAddrinfo(tai); + } +#endif + return (NS_SUCCESS); +} + +/* + * The prototype for gethostbyname2_r is + * gethostbyname2_r(const char *name, int af, struct hostent *he, char *buffer, + * size_t buflen, struct hostent **result, int *h_errnop) + * + * nsdispatch calls gethostbyname2_r as + * rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, + * "gethostbyname2_r", default_src, name, af, hp, buf, buflen, + * &ret_errno, h_errnop); + * which translates into us being (effectively) called as: + * rval = func(result, mdata, name, af, hp, buf, buflen, &ret_errno, h_errnop) + * We turn result into a struct hostent **, set *result to NULL, and then + * call _nss_mdns_gethostbyname2_r() + */ +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 = (struct hostent **)retval; + 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 *); + + if (hp == NULL) + return (NS_UNAVAIL); + + *resultp = NULL; + 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); +} + +ns_mtab * +nss_module_register(const char *source __unused, unsigned int *mtabsize, + nss_module_unregister_fn *unreg) +{ +#ifdef DEBUG + { + fprintf(stderr, "%s(%s, %p, %p)\n", __FUNCTION__, source, mtabsize, unreg); + } +#endif + *mtabsize = sizeof(methods)/sizeof(methods[0]); + *unreg = NULL; + return (methods); +}