blob: 370227df03c7863eab787c58994a899bec4b09d7 [file] [log] [blame]
/*****************************************************************************\
* $Id$
*****************************************************************************
* Copyright (C) 2001-2002 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Chris Dunlap <cdunlap@llnl.gov>.
* UCRL-CODE-2002-009.
*
* This file is part of ConMan, a remote console management program.
* For details, see <http://www.llnl.gov/linux/conman/>.
*
* ConMan 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* ConMan 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 ConMan; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*****************************************************************************
* Refer to "util-net.h" for documentation on public functions.
\*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include "src/common/strlcpy.h"
#include "src/common/util-net.h"
#include "src/common/macros.h"
#ifndef INET_ADDRSTRLEN
# define INET_ADDRSTRLEN 16
#endif /* !INET_ADDRSTRLEN */
static pthread_mutex_t hostentLock = PTHREAD_MUTEX_INITIALIZER;
static int copy_hostent(const struct hostent *src, char *dst, int len);
#ifndef NDEBUG
static int validate_hostent_copy(
const struct hostent *src, const struct hostent *dst);
#endif /* !NDEBUG */
struct hostent * get_host_by_name(const char *name,
void *buf, int buflen, int *h_err)
{
/* gethostbyname() is not thread-safe, and there is no frelling standard
* for gethostbyname_r() -- the arg list varies from system to system!
*/
struct hostent *hptr;
int n = 0;
assert(name != NULL);
assert(buf != NULL);
slurm_mutex_lock(&hostentLock);
if ((hptr = gethostbyname(name)))
n = copy_hostent(hptr, buf, buflen);
if (h_err)
*h_err = h_errno;
slurm_mutex_unlock(&hostentLock);
if (n < 0) {
errno = ERANGE;
return(NULL);
}
return(hptr ? (struct hostent *) buf : NULL);
}
struct hostent * get_host_by_addr(const char *addr, int len, int type,
void *buf, int buflen, int *h_err)
{
/* gethostbyaddr() is not thread-safe, and there is no frelling standard
* for gethostbyaddr_r() -- the arg list varies from system to system!
*/
struct hostent *hptr;
int n = 0;
assert(addr != NULL);
assert(buf != NULL);
slurm_mutex_lock(&hostentLock);
if ((hptr = gethostbyaddr(addr, len, type)))
n = copy_hostent(hptr, buf, buflen);
if (h_err)
*h_err = h_errno;
slurm_mutex_unlock(&hostentLock);
if (n < 0) {
errno = ERANGE;
return(NULL);
}
return(hptr ? (struct hostent *) buf : NULL);
}
const char * host_strerror(int h_err)
{
if (h_err == HOST_NOT_FOUND)
return("Unknown host");
else if (h_err == TRY_AGAIN)
return("Transient host name lookup failure");
else if (h_err == NO_RECOVERY)
return("Unknown server error");
else if ((h_err == NO_ADDRESS) || (h_err == NO_DATA))
return("No address associated with name");
return("Unknown error");
}
int host_name_to_addr4(const char *name, struct in_addr *addr)
{
struct hostent *hptr;
unsigned char buf[HOSTENT_SIZE];
assert(name != NULL);
assert(addr != NULL);
if (!(hptr = get_host_by_name(name, buf, sizeof(buf), NULL)))
return(-1);
if (hptr->h_length > 4) {
errno = ERANGE;
return(-1);
}
memcpy(addr, hptr->h_addr_list[0], hptr->h_length);
return(0);
}
char * host_addr4_to_name(const struct in_addr *addr, char *dst, int dstlen)
{
struct hostent *hptr;
unsigned char buf[HOSTENT_SIZE];
assert(addr != NULL);
assert(dst != NULL);
if (!(hptr = get_host_by_addr((char *) addr, 4, AF_INET,
buf, sizeof(buf), NULL)))
return(NULL);
if (strlen(hptr->h_name) >= dstlen) {
errno = ERANGE;
return(NULL);
}
strcpy(dst, hptr->h_name);
return(dst);
}
char * host_name_to_cname(const char *src, char *dst, int dstlen)
{
struct hostent *hptr;
unsigned char buf[HOSTENT_SIZE];
struct in_addr addr;
assert(src != NULL);
assert(dst != NULL);
if (!(hptr = get_host_by_name(src, buf, sizeof(buf), NULL)))
return(NULL);
/*
* If 'src' is an ip-addr string, it will simply be copied to h_name.
* So, we need to perform a reverse query based on the in_addr
* in order to obtain the canonical name of the host.
* Besides, this additional query helps protect against DNS spoofing.
*/
memcpy(&addr, hptr->h_addr_list[0], hptr->h_length);
if (!(hptr = get_host_by_addr((char *) &addr, 4, AF_INET,
buf, sizeof(buf), NULL)))
return(NULL);
if (strlen(hptr->h_name) >= dstlen) {
errno = ERANGE;
return(NULL);
}
strcpy(dst, hptr->h_name);
return(dst);
}
static int copy_hostent(const struct hostent *src, char *buf, int len)
{
/* Copies the (src) hostent struct (and all of its associated data)
* into the buffer (buf) of length (len).
* Returns 0 if the copy is successful, or -1 if the length of the buffer
* is not large enough to hold the result.
*
* Note that the order in which data is copied into (buf) is done
* in such a way as to ensure everything is properly word-aligned.
* There is a method to the madness.
*/
struct hostent *dst;
int n;
char **p, **q;
assert(src != NULL);
assert(buf != NULL);
dst = (struct hostent *) buf;
if ((len -= sizeof(struct hostent)) < 0)
return(-1);
dst->h_addrtype = src->h_addrtype;
dst->h_length = src->h_length;
buf += sizeof(struct hostent);
/* Reserve space for h_aliases[].
*/
dst->h_aliases = (char **) buf;
for (p=src->h_aliases, q=dst->h_aliases, n=0; *p; p++, q++, n++) {;}
if ((len -= ++n * sizeof(char *)) < 0)
return(-1);
buf = (char *) (q + 1);
/* Reserve space for h_addr_list[].
*/
dst->h_addr_list = (char **) buf;
for (p=src->h_addr_list, q=dst->h_addr_list, n=0; *p; p++, q++, n++) {;}
if ((len -= ++n * sizeof(char *)) < 0)
return(-1);
buf = (char *) (q + 1);
/* Copy h_addr_list[] in_addr structs.
*/
for (p=src->h_addr_list, q=dst->h_addr_list; *p; p++, q++) {
if ((len -= src->h_length) < 0)
return(-1);
memcpy(buf, *p, src->h_length);
*q = buf;
buf += src->h_length;
}
*q = NULL;
/* Copy h_aliases[] strings.
*/
for (p=src->h_aliases, q=dst->h_aliases; *p; p++, q++) {
n = strlcpy(buf, *p, len);
*q = buf;
buf += ++n; /* allow for trailing NUL char */
if ((len -= n) < 0)
return(-1);
}
*q = NULL;
/* Copy h_name string.
*/
dst->h_name = buf;
n = strlcpy(buf, src->h_name, len);
buf += ++n; /* allow for trailing NUL char */
if ((len -= n) < 0)
return(-1);
assert(validate_hostent_copy(src, dst) >= 0);
return(0);
}
#ifndef NDEBUG
static int validate_hostent_copy(
const struct hostent *src, const struct hostent *dst)
{
/* Validates the src hostent struct has been successfully copied into dst.
* Returns 0 if the copy is good; o/w, returns -1.
*/
char **p, **q;
assert(src != NULL);
assert(dst != NULL);
if (!dst->h_name)
return(-1);
if (src->h_name == dst->h_name)
return(-1);
if (strcmp(src->h_name, dst->h_name))
return(-1);
if (src->h_addrtype != dst->h_addrtype)
return(-1);
if (src->h_length != dst->h_length)
return(-1);
for (p=src->h_aliases, q=dst->h_aliases; *p; p++, q++)
if ((!q) || (p == q) || (strcmp(*p, *q)))
return(-1);
for (p=src->h_addr_list, q=dst->h_addr_list; *p; p++, q++)
if ((!q) || (p == q) || (memcmp(*p, *q, src->h_length)))
return(-1);
return(0);
}
#endif /* !NDEBUG */
#ifndef HAVE_INET_PTON
int inet_pton(int family, const char *str, void *addr)
{
/* cf. Stevens UNPv1 p72.
*/
struct in_addr tmpaddr;
if (family != AF_INET) {
errno = EAFNOSUPPORT;
return(-1);
}
#ifdef HAVE_INET_ATON
if (!inet_aton(str, &tmpaddr))
return(0);
#else /* !HAVE_INET_ATON */
if ((tmpaddr.s_addr = inet_addr(str)) == -1)
return(0);
#endif /* !HAVE_INET_ATON */
memcpy(addr, &tmpaddr, sizeof(struct in_addr));
return(1);
}
#endif /* !HAVE_INET_PTON */
#ifndef HAVE_INET_NTOP
const char * inet_ntop(int family, const void *addr, char *str, size_t len)
{
/* cf. Stevens UNPv1 p72.
*/
const unsigned char *p = (const unsigned char *) addr;
char tmpstr[INET_ADDRSTRLEN];
assert(str != NULL);
if (family != AF_INET) {
errno = EAFNOSUPPORT;
return(NULL);
}
snprintf(tmpstr, sizeof(tmpstr), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
if (strlen(tmpstr) >= len) {
errno = ENOSPC;
return(NULL);
}
strcpy(str, tmpstr);
return(str);
}
#endif /* !HAVE_INET_NTOP */