/*** This file is part of nss-gns. Parts taken from: nss.c in nss-mdns nss-mdns is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ***/ #include #include #include #include #include #include #include #include #include #include #include #include "nss_gns_query.h" #include /** macro to align idx to 32bit boundary */ #define ALIGN(idx) do { \ if (idx % sizeof(void*)) \ idx += (sizeof(void*) - idx % sizeof(void*)); /* Align on 32 bit boundary */ \ } while (0) /** * The gethostbyname hook executed by nsswitch * * @param name the name to resolve * @param af the address family to resolve * @param result the result hostent * @param buffer the result buffer * @param buflen length of the buffer * @param errnop idk * @param h_errnop idk * @return a nss_status code */ enum nss_status _nss_gns_gethostbyname2_r (const char *name, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop) { struct userdata u; enum nss_status status = NSS_STATUS_UNAVAIL; int i; size_t address_length; size_t l; size_t idx; size_t astart; if (af == AF_UNSPEC) #ifdef NSS_IPV6_ONLY af = AF_INET6; #else af = AF_INET; #endif #ifdef NSS_IPV4_ONLY if (af != AF_INET) #elif NSS_IPV6_ONLY if (af != AF_INET6) #else if ((af != AF_INET) && (af != AF_INET6)) #endif { *errnop = EINVAL; *h_errnop = NO_RECOVERY; goto finish; } address_length = (af == AF_INET) ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t); if (buflen < sizeof(char*) /* alias names */ + strlen (name) + 1) { /* official name */ *errnop = ERANGE; *h_errnop = NO_RECOVERY; status = NSS_STATUS_TRYAGAIN; goto finish; } u.count = 0; u.data_len = 0; i = gns_resolve_name (af, name, &u); if (-1 == i) { *errnop = errno; status = NSS_STATUS_UNAVAIL; *h_errnop = NO_RECOVERY; goto finish; } if (-2 == i) { *errnop = ENOENT; *h_errnop = NO_RECOVERY; status = NSS_STATUS_UNAVAIL; goto finish; } if (-3 == i) { *errnop = ETIMEDOUT; *h_errnop = HOST_NOT_FOUND; status = NSS_STATUS_NOTFOUND; goto finish; } if (0 == u.count) { *errnop = 0; /* success */ *h_errnop = NO_DATA; /* success */ status = NSS_STATUS_NOTFOUND; goto finish; } /* Alias names */ *((char **) buffer) = NULL; result->h_aliases = (char **) buffer; idx = sizeof(char*); /* Official name */ strcpy (buffer + idx, name); result->h_name = buffer + idx; idx += strlen (name) + 1; ALIGN (idx); result->h_addrtype = af; result->h_length = address_length; /* Check if there's enough space for the addresses */ if (buflen < idx + u.data_len + sizeof(char*) * (u.count + 1)) { *errnop = ERANGE; *h_errnop = NO_RECOVERY; status = NSS_STATUS_TRYAGAIN; goto finish; } /* Addresses */ astart = idx; l = u.count * address_length; if (0 != l) memcpy (buffer + astart, &u.data, l); /* address_length is a multiple of 32bits, so idx is still aligned * correctly */ idx += l; /* Address array address_length is always a multiple of 32bits */ for (i = 0; i < u.count; i++) ((char **) (buffer + idx))[i] = buffer + astart + address_length * i; ((char **) (buffer + idx))[i] = NULL; result->h_addr_list = (char **) (buffer + idx); status = NSS_STATUS_SUCCESS; finish: return status; } /** * The gethostbyname hook executed by nsswitch * * @param name the name to resolve * @param result the result hostent * @param buffer the result buffer * @param buflen length of the buffer * @param errnop[out] the low-level error code to return to the application * @param h_errnop idk * @return a nss_status code */ enum nss_status _nss_gns_gethostbyname_r (const char *name, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop) { return _nss_gns_gethostbyname2_r (name, AF_UNSPEC, result, buffer, buflen, errnop, h_errnop); } /** * The gethostbyaddr hook executed by nsswitch * We can't do this so we always return NSS_STATUS_UNAVAIL * * @param addr the address to resolve * @param len the length of the address * @param af the address family of the address * @param result the result hostent * @param buffer the result buffer * @param buflen length of the buffer * @param errnop[out] the low-level error code to return to the application * @param h_errnop idk * @return NSS_STATUS_UNAVAIL */ enum nss_status _nss_gns_gethostbyaddr_r (const void*addr, int len, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop) { (void) addr; (void) len; (void) af; (void) result; (void) buffer; (void) buflen; *errnop = EINVAL; *h_errnop = NO_RECOVERY; /* NOTE we allow to leak this into DNS so no NOTFOUND */ return NSS_STATUS_UNAVAIL; }