367 lines
8.9 KiB
C
367 lines
8.9 KiB
C
/*
|
|
* Copyright 2010 Oracle. All rights reserved.
|
|
*
|
|
* This file is part of nfs-utils.
|
|
*
|
|
* nfs-utils 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* nfs-utils is distributed in the hope that it will be useful,
|
|
* but 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 General Public License
|
|
* along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
|
|
#include "sockaddr.h"
|
|
#include "exportfs.h"
|
|
|
|
/**
|
|
* host_ntop - generate presentation address given a sockaddr
|
|
* @sap: pointer to socket address
|
|
* @buf: working storage
|
|
* @buflen: size of @buf in bytes
|
|
*
|
|
* Returns a pointer to a @buf.
|
|
*/
|
|
#ifdef HAVE_GETNAMEINFO
|
|
char *
|
|
host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
|
|
{
|
|
socklen_t salen = nfs_sockaddr_length(sap);
|
|
int error;
|
|
|
|
memset(buf, 0, buflen);
|
|
|
|
if (salen == 0) {
|
|
(void)strncpy(buf, "bad family", buflen - 1);
|
|
return buf;
|
|
}
|
|
|
|
error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
|
|
NULL, 0, NI_NUMERICHOST);
|
|
if (error != 0) {
|
|
buf[0] = '\0';
|
|
(void)strncpy(buf, "bad address", buflen - 1);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
#else /* !HAVE_GETNAMEINFO */
|
|
char *
|
|
host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
|
|
{
|
|
const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
|
|
|
|
memset(buf, 0, buflen);
|
|
|
|
if (sin->sin_family != AF_INET) {
|
|
(void)strncpy(buf, "bad family", buflen - 1);
|
|
return buf;
|
|
}
|
|
|
|
if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL)
|
|
return buf;
|
|
|
|
buf[0] = '\0';
|
|
(void)strncpy(buf, "bad address", buflen - 1);
|
|
return buf;
|
|
}
|
|
#endif /* !HAVE_GETNAMEINFO */
|
|
|
|
/**
|
|
* host_pton - return addrinfo for a given presentation address
|
|
* @paddr: pointer to a '\0'-terminated ASCII string containing an
|
|
* IP presentation address
|
|
*
|
|
* Returns address info structure, or NULL if an error occurs. Caller
|
|
* must free the returned structure with freeaddrinfo(3).
|
|
*/
|
|
__attribute__((__malloc__))
|
|
struct addrinfo *
|
|
host_pton(const char *paddr)
|
|
{
|
|
struct addrinfo *ai = NULL;
|
|
struct addrinfo hint = {
|
|
/* don't return duplicates */
|
|
.ai_protocol = (int)IPPROTO_UDP,
|
|
.ai_flags = AI_NUMERICHOST,
|
|
.ai_family = AF_UNSPEC,
|
|
};
|
|
struct sockaddr_in sin;
|
|
int error, inet4;
|
|
|
|
/*
|
|
* Although getaddrinfo(3) is easier to use and supports
|
|
* IPv6, it recognizes incomplete addresses like "10.4"
|
|
* as valid AF_INET addresses. It also accepts presentation
|
|
* addresses that end with a blank.
|
|
*
|
|
* inet_pton(3) is much stricter. Use it to be certain we
|
|
* have a real AF_INET presentation address, before invoking
|
|
* getaddrinfo(3) to generate the full addrinfo list.
|
|
*/
|
|
if (paddr == NULL) {
|
|
xlog(D_GENERAL, "%s: passed a NULL presentation address",
|
|
__func__);
|
|
return NULL;
|
|
}
|
|
inet4 = 1;
|
|
if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
|
|
inet4 = 0;
|
|
|
|
error = getaddrinfo(paddr, NULL, &hint, &ai);
|
|
switch (error) {
|
|
case 0:
|
|
if (!inet4 && ai->ai_addr->sa_family == AF_INET) {
|
|
xlog(D_GENERAL, "%s: failed to convert %s",
|
|
__func__, paddr);
|
|
nfs_freeaddrinfo(ai);
|
|
break;
|
|
}
|
|
return ai;
|
|
case EAI_NONAME:
|
|
break;
|
|
case EAI_SYSTEM:
|
|
xlog(L_WARNING, "%s: failed to convert %s: (%d) %m",
|
|
__func__, paddr, errno);
|
|
break;
|
|
default:
|
|
xlog(L_WARNING, "%s: failed to convert %s: %s",
|
|
__func__, paddr, gai_strerror(error));
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* host_addrinfo - return addrinfo for a given hostname
|
|
* @hostname: pointer to a '\0'-terminated ASCII string containing a hostname
|
|
*
|
|
* Returns address info structure with ai_canonname filled in, or NULL
|
|
* if no information is available for @hostname. Caller must free the
|
|
* returned structure with freeaddrinfo(3).
|
|
*/
|
|
__attribute__((__malloc__))
|
|
struct addrinfo *
|
|
host_addrinfo(const char *hostname)
|
|
{
|
|
struct addrinfo *ai = NULL;
|
|
struct addrinfo hint = {
|
|
#ifdef IPV6_SUPPORTED
|
|
.ai_family = AF_UNSPEC,
|
|
#else
|
|
.ai_family = AF_INET,
|
|
#endif
|
|
/* don't return duplicates */
|
|
.ai_protocol = (int)IPPROTO_UDP,
|
|
.ai_flags = AI_CANONNAME,
|
|
};
|
|
int error;
|
|
|
|
error = getaddrinfo(hostname, NULL, &hint, &ai);
|
|
switch (error) {
|
|
case 0:
|
|
return ai;
|
|
case EAI_SYSTEM:
|
|
xlog(D_PARSE, "%s: failed to resolve %s: (%d) %m",
|
|
__func__, hostname, errno);
|
|
break;
|
|
default:
|
|
xlog(D_PARSE, "%s: failed to resolve %s: %s",
|
|
__func__, hostname, gai_strerror(error));
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* host_canonname - return canonical hostname bound to an address
|
|
* @sap: pointer to socket address to look up
|
|
*
|
|
* Discover the canonical hostname associated with the given socket
|
|
* address. The host's reverse mapping is verified in the process.
|
|
*
|
|
* Returns a '\0'-terminated ASCII string containing a hostname, or
|
|
* NULL if no hostname can be found for @sap. Caller must free
|
|
* the string.
|
|
*/
|
|
#ifdef HAVE_GETNAMEINFO
|
|
__attribute__((__malloc__))
|
|
char *
|
|
host_canonname(const struct sockaddr *sap)
|
|
{
|
|
socklen_t salen = nfs_sockaddr_length(sap);
|
|
char buf[NI_MAXHOST];
|
|
int error;
|
|
|
|
if (salen == 0) {
|
|
xlog(D_GENERAL, "%s: unsupported address family %d",
|
|
__func__, sap->sa_family);
|
|
return NULL;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
|
|
NULL, 0, NI_NAMEREQD);
|
|
switch (error) {
|
|
case 0:
|
|
break;
|
|
case EAI_SYSTEM:
|
|
xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
|
|
__func__, errno);
|
|
return NULL;
|
|
default:
|
|
(void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
|
|
NULL, 0, NI_NUMERICHOST);
|
|
xlog(D_PARSE, "%s: failed to resolve %s: %s",
|
|
__func__, buf, gai_strerror(error));
|
|
return NULL;
|
|
}
|
|
|
|
return strdup(buf);
|
|
}
|
|
#else /* !HAVE_GETNAMEINFO */
|
|
__attribute__((__malloc__))
|
|
char *
|
|
host_canonname(const struct sockaddr *sap)
|
|
{
|
|
const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
|
|
const struct in_addr *addr = &sin->sin_addr;
|
|
struct hostent *hp;
|
|
|
|
if (sap->sa_family != AF_INET)
|
|
return NULL;
|
|
|
|
hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
|
|
if (hp == NULL)
|
|
return NULL;
|
|
|
|
return strdup(hp->h_name);
|
|
}
|
|
#endif /* !HAVE_GETNAMEINFO */
|
|
|
|
/**
|
|
* host_reliable_addrinfo - return addrinfo for a given address
|
|
* @sap: pointer to socket address to look up
|
|
*
|
|
* Reverse and forward lookups are performed to ensure the address has
|
|
* matching forward and reverse mappings.
|
|
*
|
|
* Returns addrinfo structure with just the provided address. If there
|
|
* is a problem with resolution or the resolved records don't match up
|
|
* properly then returns NULL.
|
|
*
|
|
* Caller must free the returned structure with freeaddrinfo(3).
|
|
*/
|
|
__attribute__((__malloc__))
|
|
struct addrinfo *
|
|
host_reliable_addrinfo(const struct sockaddr *sap)
|
|
{
|
|
struct addrinfo *ai, *a;
|
|
char *hostname;
|
|
|
|
ai = NULL;
|
|
hostname = host_canonname(sap);
|
|
if (hostname == NULL)
|
|
goto out;
|
|
|
|
ai = host_addrinfo(hostname);
|
|
free(hostname);
|
|
if (!ai)
|
|
goto out;
|
|
|
|
/* make sure there's a matching address in the list */
|
|
for (a = ai; a; a = a->ai_next)
|
|
if (nfs_compare_sockaddr(a->ai_addr, sap))
|
|
break;
|
|
|
|
nfs_freeaddrinfo(ai);
|
|
ai = NULL;
|
|
if (!a)
|
|
goto out;
|
|
|
|
/* get addrinfo with just the original address */
|
|
ai = host_numeric_addrinfo(sap);
|
|
|
|
out:
|
|
return ai;
|
|
}
|
|
|
|
/**
|
|
* host_numeric_addrinfo - return addrinfo without doing DNS queries
|
|
* @sap: pointer to socket address
|
|
*
|
|
* Returns address info structure, or NULL if an error occurred.
|
|
* Caller must free the returned structure with freeaddrinfo(3).
|
|
*/
|
|
#ifdef HAVE_GETNAMEINFO
|
|
__attribute__((__malloc__))
|
|
struct addrinfo *
|
|
host_numeric_addrinfo(const struct sockaddr *sap)
|
|
{
|
|
socklen_t salen = nfs_sockaddr_length(sap);
|
|
char buf[INET6_ADDRSTRLEN];
|
|
int error;
|
|
|
|
if (salen == 0) {
|
|
xlog(D_GENERAL, "%s: unsupported address family %d",
|
|
__func__, sap->sa_family);
|
|
return NULL;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
|
|
NULL, 0, NI_NUMERICHOST);
|
|
switch (error) {
|
|
case 0:
|
|
break;
|
|
case EAI_SYSTEM:
|
|
xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
|
|
__func__, errno);
|
|
return NULL;
|
|
default:
|
|
xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s",
|
|
__func__, gai_strerror(error));
|
|
return NULL;
|
|
}
|
|
|
|
return host_pton(buf);
|
|
}
|
|
#else /* !HAVE_GETNAMEINFO */
|
|
__attribute__((__malloc__))
|
|
struct addrinfo *
|
|
host_numeric_addrinfo(const struct sockaddr *sap)
|
|
{
|
|
const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
|
|
const struct in_addr *addr = &sin->sin_addr;
|
|
char buf[INET_ADDRSTRLEN];
|
|
|
|
if (sap->sa_family != AF_INET)
|
|
return NULL;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
if (inet_ntop(AF_INET, (char *)addr, buf,
|
|
(socklen_t)sizeof(buf)) == NULL)
|
|
return NULL;
|
|
|
|
return host_pton(buf);
|
|
}
|
|
#endif /* !HAVE_GETNAMEINFO */
|