/*****************************************************************************\ * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * UCRL-CODE-2002-009. * * This file is part of ConMan, a remote console management program. * For details, see . * * 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. \*****************************************************************************/ #include "config.h" #define _GNU_SOURCE #include #include #include #include /* for PATH_MAX */ #include #include #include #include #include #include #include #include "src/common/strlcpy.h" #include "src/common/util-net.h" #include "src/common/macros.h" #include "src/common/xstring.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); /* It appears gethostbyname leaks memory once. Under the covers it * calls gethostbyname_r (at least on Ubuntu 16.10). This leak doesn't * appear to get worst, meaning it only happens once, so we should be * ok. Though gethostbyname is obsolete now we can't really change * since aliases don't work we can't change. */ 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"); } 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); assert(buf != NULL); /* Used only to eliminate CLANG error */ 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 (xstrcmp(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) || (xstrcmp(*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 */ /* is_full_path() * * Test if the given path is a full or relative one. */ extern bool is_full_path(const char *path) { if (path && path[0] == '/') return true; return false; } /* make_full_path() * * Given a relative path in input make it full relative * to the current working directory. */ extern char *make_full_path(const char *rpath) { char *cwd; char *cwd2 = NULL; #ifdef HAVE_GET_CURRENT_DIR_NAME cwd = get_current_dir_name(); #else cwd = malloc(PATH_MAX); cwd = getcwd(cwd, PATH_MAX); #endif xstrfmtcat(cwd2, "%s/%s", cwd, rpath); free(cwd); return cwd2; } struct addrinfo * get_addr_info(const char *hostname) { struct addrinfo* result = NULL; struct addrinfo hints; int err; if (hostname == NULL) return NULL; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_CANONNAME; err = getaddrinfo(hostname, NULL, &hints, &result); if (err == EAI_SYSTEM) { error("%s: getaddrinfo() failed: %s: %m", __func__, gai_strerror(err)); return NULL; } else if (err != 0) { error("%s: getaddrinfo() failed: %s", __func__, gai_strerror(err)); return NULL; } return result; } int get_name_info(struct sockaddr *sa, socklen_t len, char *host) { int err; err = getnameinfo(sa, len, host, NI_MAXHOST, NULL, 0, 0); if (err != 0) { error("%s: getaddrinfo() failed: %s", __func__, gai_strerror(err)); return -1; } return 0; } void free_addr_info(struct addrinfo *info) { if (info == NULL) return; freeaddrinfo(info); }