diff options
Diffstat (limited to 'src/nss.c')
-rw-r--r-- | src/nss.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/src/nss.c b/src/nss.c new file mode 100644 index 0000000..0515b2d --- /dev/null +++ b/src/nss.c @@ -0,0 +1,298 @@ +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <assert.h> +#include <netdb.h> +#include <sys/socket.h> +#include <nss.h> + +#include "query.h" + +#define MAX_ENTRIES 16 + +#ifdef NSS_IPV4_ONLY +#define _nss_mdns_gethostbyname2_r _nss_mdns4_gethostbyname2_r +#define _nss_mdns_gethostbyname_r _nss_mdns4_gethostbyname_r +#define _nss_mdns_gethostbyaddr_r _nss_mdns4_gethostbyaddr_r +#elif NSS_IPV6_ONLY +#define _nss_mdns_gethostbyname2_r _nss_mdns6_gethostbyname2_r +#define _nss_mdns_gethostbyname_r _nss_mdns6_gethostbyname_r +#define _nss_mdns_gethostbyaddr_r _nss_mdns6_gethostbyaddr_r +#endif + +struct userdata { + int count; + int data_len; /* only valid when doing reverse lookup */ + union { + ipv4_address_t ipv4[MAX_ENTRIES]; + ipv6_address_t ipv6[MAX_ENTRIES]; + char *name[MAX_ENTRIES]; + } data; +}; + +static void ipv4_callback(const ipv4_address_t *ipv4, void *userdata) { + struct userdata *u = userdata; + assert(ipv4 && userdata); + + if (u->count >= MAX_ENTRIES) + return; + + u->data.ipv4[u->count++] = *ipv4; + u->data_len += sizeof(ipv4_address_t); +} + +static void ipv6_callback(const ipv6_address_t *ipv6, void *userdata) { + struct userdata *u = userdata; + assert(ipv6 && userdata); + + if (u->count >= MAX_ENTRIES) + return; + + u->data.ipv6[u->count++] = *ipv6; + u->data_len += sizeof(ipv6_address_t); +} + +static void name_callback(const char*name, void *userdata) { + struct userdata *u = userdata; + assert(name && userdata); + + if (u->count >= MAX_ENTRIES) + return; + + u->data.name[u->count++] = strdup(name); + u->data_len += strlen(name)+1; +} + +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) { + + struct userdata u; + enum nss_status status = NSS_STATUS_UNAVAIL; + int fd = -1, r, i; + size_t address_length, l, index, astart; + +/* DEBUG_TRAP; */ + +#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; + } + + if ((fd = mdns_open_socket()) < 0) { + + *errnop = errno; + *h_errnop = NO_RECOVERY; + goto finish; + } + + u.count = 0; + u.data_len = 0; + + if ((r = mdns_query_name(fd, name, af == AF_INET ? ipv4_callback : NULL, af == AF_INET6 ? ipv6_callback : NULL, &u)) < 0) { + *errnop = ETIMEDOUT; + *h_errnop = HOST_NOT_FOUND; + goto finish; + } + + /* Alias names */ + *((char**) buffer) = NULL; + result->h_aliases = (char**) buffer; + index = sizeof(char*); + + /* Official name */ + strcpy(buffer+index, name); + result->h_name = buffer+index; + index += strlen(name)+1; + + result->h_addrtype = af; + result->h_length = address_length; + + /* Check if there's enough space for the addresses */ + if (buflen < index+u.data_len+sizeof(char*)*(u.count+1)) { + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + goto finish; + } + + /* Addresses */ + astart = index; + l = u.count*address_length; + memcpy(buffer+astart, &u.data, l); + index += l; + + /* Address array */ + for (i = 0; i < u.count; i++) + ((char**) (buffer+index))[i] = buffer+astart+address_length*i; + ((char**) (buffer+index))[i] = NULL; + + result->h_addr_list = (char**) (buffer+index); + + status = NSS_STATUS_SUCCESS; + +finish: + if (fd >= 0) + close(fd); + + return status; +} + +enum nss_status _nss_mdns_gethostbyname_r ( + const char *name, + struct hostent *result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop) { + + return _nss_mdns_gethostbyname2_r( + name, +#ifdef NSS_IPV6_ONLY + AF_INET6, +#else + AF_INET, +#endif + result, + buffer, + buflen, + errnop, + h_errnop); +} + +enum nss_status _nss_mdns_gethostbyaddr_r( + const void* addr, + int len, + int af, + struct hostent *result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop) { + + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + + struct userdata u; + enum nss_status status = NSS_STATUS_UNAVAIL; + int fd = -1, r; + size_t address_length, index, astart; + + u.count = 0; + u.data_len = 0; + + address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t); + + if (len != (int) address_length || +#ifdef NSS_IPV4_ONLY + af != AF_INET +#elif NSS_IPV6_ONLY + af != AF_INET6 +#else + (af != AF_INET && af != AF_INET6) +#endif + ) { + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + goto finish; + } + + if (buflen < + sizeof(char*)+ /* alias names */ + address_length) { /* address */ + + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + + goto finish; + } + + if ((fd = mdns_open_socket()) < 0) { + + *errnop = errno; + *h_errnop = NO_RECOVERY; + goto finish; + } + + if (af == AF_INET) + r = mdns_query_ipv4(fd, (ipv4_address_t*) addr, name_callback, &u); + else + r = mdns_query_ipv6(fd, (ipv6_address_t*) addr, name_callback, &u); + + if (r < 0) { + *errnop = ETIMEDOUT; + *h_errnop = HOST_NOT_FOUND; + goto finish; + } + + /* Alias names */ + *((char**) buffer) = NULL; + result->h_aliases = (char**) buffer; + index = sizeof(char*); + + assert(u.count > 0 && u.data.name[0]); + if (buflen < + strlen(u.data.name[0])+1+ /* official names */ + sizeof(char*)+ /* alias names */ + address_length+ /* address */ + sizeof(void*)*2) { /* address list */ + + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + goto finish; + } + + /* Official name */ + strcpy(buffer+index, u.data.name[0]); + result->h_name = buffer+index; + index += strlen(u.data.name[0])+1; + + result->h_addrtype = af; + result->h_length = address_length; + + /* Address */ + astart = index; + memcpy(buffer+astart, addr, address_length); + index += address_length; + + /* Address array */ + ((char**) (buffer+index))[0] = buffer+astart; + ((char**) (buffer+index))[1] = NULL; + result->h_addr_list = (char**) (buffer+index); + + status = NSS_STATUS_SUCCESS; + +finish: + if (fd >= 0) + close(fd); + + return status; +} + |