/******************************************************************************/
/* */
/* X r d S y s D N S . c c */
/* */
/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
/* All Rights Reserved */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD 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 Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#include
#include
#include
#include
#include
#endif
#include "XrdSys/XrdSysDNS.hh"
#include "XrdSys/XrdSysHeaders.hh"
#include "XrdSys/XrdSysPlatform.hh"
#include "XrdSys/XrdSysPthread.hh"
/******************************************************************************/
/* g e t H o s t A d d r */
/******************************************************************************/
int XrdSysDNS::getHostAddr(const char *InetName,
struct sockaddr InetAddr[],
int maxipa,
char **errtxt)
{
#ifdef HAVE_NAMEINFO
struct addrinfo *rp, *np, *pnp=0;
struct addrinfo myhints;
memset(&myhints, 0, sizeof(myhints));
myhints.ai_flags = AI_CANONNAME;
#else
unsigned int addr;
struct hostent hent, *hp;
char **p, hbuff[1024];
#endif
struct sockaddr_in *ip;
int i, rc;
// Make sure we have something to lookup here
//
if (!InetName || !InetName[0])
{ip = (struct sockaddr_in *)&InetAddr[0];
ip->sin_family = AF_INET;
ip->sin_port = 0;
ip->sin_addr.s_addr = INADDR_ANY;
memset((void *)ip->sin_zero, 0, sizeof(ip->sin_zero));
return 1;
}
// Determine how we will resolve the name
//
rc = 0;
#ifndef HAVE_NAMEINFO
if (!isdigit((int)*InetName))
#ifdef __solaris__
gethostbyname_r(InetName, &hent, hbuff, sizeof(hbuff), &rc);
#else
gethostbyname_r(InetName, &hent, hbuff, sizeof(hbuff), &hp, &rc);
#endif
else if ((int)(addr = inet_addr(InetName)) == -1)
return (errtxt ? setET(errtxt, EINVAL) : 0);
#ifdef __solaris__
else gethostbyaddr_r(&addr,sizeof(addr), AF_INET, &hent,
hbuff, sizeof(hbuff), &rc);
#else
else gethostbyaddr_r((char *)&addr,sizeof(addr), AF_INET, &hent,
hbuff, sizeof(hbuff), &hp, &rc);
#endif
if (rc) return (errtxt ? setET(errtxt, rc) : 0);
// Check if we resolved the name
//
for (i = 0, p = hent.h_addr_list; *p != 0 && i < maxipa; p++, i++)
{ip = (struct sockaddr_in *)&InetAddr[i];
memcpy((void *)&(ip->sin_addr), (const void *)*p, sizeof(ip->sin_addr));
ip->sin_port = 0;
ip->sin_family = hent.h_addrtype;
memset((void *)ip->sin_zero, 0, sizeof(ip->sin_zero));
}
#else
// Disable IPv6 for 'localhost...' (potential confusion in the
// default /etc/hosts on some platforms, e.g. MacOsX)
//
// if (!strncmp(InetName,"localhost",9)) myhints.ai_family = AF_INET;
// pcal: force ipv4 (was only for MacOS: ifdef __APPLE__)
//#ifdef __APPLE__
// Disable IPv6 for MacOS X altogether for the time being
//
myhints.ai_family = AF_INET;
//#endif
// Translate the name to an address list
//
if (isdigit((int)*InetName)) myhints.ai_flags |= AI_NUMERICHOST;
rc = getaddrinfo(InetName,0,(const addrinfo *)&myhints, &rp);
if (rc || !(np = rp)) return (errtxt ? setETni(errtxt, rc) : 0);
// Return all of the addresses. On some platforms (like linux) this function is
// brain-dead in that it returns all addreses assoacted with all aliases even
// when those addresses are duplicates.
//
i = 0;
do {if (!pnp
|| memcmp((const void *)pnp->ai_addr, (const void *)np->ai_addr,
sizeof(struct sockaddr)))
memcpy((void *)&InetAddr[i++], (const void *)np->ai_addr,
sizeof(struct sockaddr));
pnp = np; np = np->ai_next;
} while(i < maxipa && np);
freeaddrinfo(rp);
#endif
// All done
//
return i;
}
/******************************************************************************/
/* g e t A d d r N a m e */
/******************************************************************************/
int XrdSysDNS::getAddrName(const char *InetName,
int maxipa, char **Addr, char **Name,
char **errtxt)
{
// Host or address and output arrays must be defined
//
if (!InetName || !Addr || !Name) return 0;
// Max 10 addresses and names
//
maxipa = (maxipa > 1 && maxipa <= 10) ? maxipa : 1;
// Number of addresses
struct sockaddr_in ip[10];
int n = XrdSysDNS::getHostAddr(InetName,(struct sockaddr *)ip, maxipa, errtxt);
// Fill address / name strings, if required
int i = 0;
for (; i < n; i++ ) {
// The address
char buf[255];
inet_ntop(ip[i].sin_family, &ip[i].sin_addr, buf, sizeof(buf));
Addr[i] = strdup(buf);
// The name
char *names[1] = {0};
struct sockaddr *ipaddr = (struct sockaddr *)&ip[i];
int hn = getHostName(*ipaddr, names, 1, errtxt);
if (hn)
Name[i] = strdup(names[0]);
else
Name[i] = strdup(Addr[i]);
// Cleanup
if (names[0]) free(names[0]);
}
// We are done
return n;
}
/******************************************************************************/
/* g e t H o s t I D */
/******************************************************************************/
char *XrdSysDNS::getHostID(struct sockaddr &InetAddr)
{
struct sockaddr_in *ip = (sockaddr_in *)&InetAddr;
char mybuff[256];
char *hname;
// Convert address
//
hname = (char *)inet_ntop(ip->sin_family,
(const void *)(&ip->sin_addr),
mybuff, sizeof(mybuff));
return (hname ? strdup(hname) : strdup("0.0.0.0"));
}
/******************************************************************************/
/* g e t H o s t N a m e ( V a r i a n t 1 ) */
/******************************************************************************/
char *XrdSysDNS::getHostName(const char *InetName, char **errtxt)
{
char myname[256];
const char *hp;
struct sockaddr InetAddr;
// Identify ourselves if we don't have a passed hostname
//
if (InetName) hp = InetName;
else if (gethostname(myname, sizeof(myname)))
{if (errtxt) setET(errtxt, errno); return strdup("0.0.0.0");}
else hp = myname;
// Get the address
//
if (!getHostAddr(hp, InetAddr, errtxt)) return strdup("0.0.0.0");
// Convert it to a fully qualified host name and return it
//
return getHostName(InetAddr, errtxt);
}
/******************************************************************************/
/* g e t H o s t N a m e ( V a r i a n t 2 ) */
/******************************************************************************/
char *XrdSysDNS::getHostName(struct sockaddr &InetAddr, char **errtxt)
{
char *result;
if (getHostName(InetAddr, &result, 1, errtxt)) return result;
{char dnbuff[64];
unsigned int ipaddr;
struct sockaddr_in *ip = (sockaddr_in *)&InetAddr;
memcpy(&ipaddr, &ip->sin_addr, sizeof(ipaddr));
IP2String(ipaddr, -1, dnbuff, sizeof(dnbuff));
return strdup(dnbuff);
}
}
/******************************************************************************/
/* g e t H o s t N a m e ( V a r i a n t 3 ) */
/******************************************************************************/
int XrdSysDNS::getHostName(struct sockaddr &InetAddr,
char *InetName[],
int maxipn,
char **errtxt)
{
char mybuff[256];
int i, rc;
// Preset errtxt to zero
//
if (errtxt) *errtxt = 0;
// Some platforms have nameinfo but getnameinfo() is broken. If so, we revert
// to using the gethostbyaddr().
//
#if defined(HAVE_NAMEINFO) && !defined(__APPLE__)
struct addrinfo *rp, *np;
struct addrinfo myhints;
memset(&myhints, 0, sizeof(myhints));
myhints.ai_flags = AI_CANONNAME;
#elif defined(HAVE_GETHBYXR)
struct sockaddr_in *ip = (sockaddr_in *)&InetAddr;
struct hostent hent, *hp;
char *hname, hbuff[1024];
#else
static XrdSysMutex getHN;
XrdSysMutexHelper getHNhelper;
struct sockaddr_in *ip = (sockaddr_in *)&InetAddr;
struct hostent *hp;
unsigned int ipaddr;
char *hname;
#endif
// Make sure we can return something
//
if (maxipn < 1) return (errtxt ? setET(errtxt, EINVAL) : 0);
// Check for unix family which is equl to localhost
//
if (InetAddr.sa_family == AF_UNIX)
{InetName[0] = strdup("localhost"); return 1;}
#if !defined(HAVE_NAMEINFO) || defined(__APPLE__)
// Convert it to a host name
//
rc = 0;
#ifdef HAVE_GETHBYXR
#ifdef __solaris__
gethostbyaddr_r(&(ip->sin_addr), sizeof(struct in_addr),
AF_INET, &hent, hbuff, sizeof(hbuff), &rc);
hp = &hent;
#else
gethostbyaddr_r(&(ip->sin_addr), sizeof(struct in_addr),
AF_INET, &hent, hbuff, sizeof(hbuff), &hp, &rc);
#endif
#else
memcpy(&ipaddr, &ip->sin_addr, sizeof(ipaddr));
getHNhelper.Lock(&getHN);
if (!(hp=gethostbyaddr((const char *)&ipaddr, sizeof(InetAddr), AF_INET)))
rc = (h_errno ? h_errno : EINVAL);
#endif
if (rc)
{hname = (char *)inet_ntop(ip->sin_family,
(const void *)(&ip->sin_addr),
mybuff, sizeof(mybuff));
if (!hname) return (errtxt ? setET(errtxt, errno) : 0);
InetName[0] = strdup(hname);
return 1;
}
// Return first result
//
InetName[0] = LowCase(strdup(hp->h_name));
// Return additional names
//
hname = *hp->h_aliases;
for (i = 1; i < maxipn && hname; i++, hname++)
InetName[i] = LowCase(strdup(hname));
#else
// Do lookup of canonical name. We can't use getaddrinfo() for this on all
// platforms because AI_CANONICAL was never well defined in the spec.
//
if ((rc = getnameinfo(&InetAddr, sizeof(struct sockaddr),
mybuff, sizeof(mybuff), 0, 0, 0)))
return (errtxt ? setETni(errtxt, rc) : 0);
// Return the result if aliases not wanted
//
if (maxipn < 2)
{InetName[0] = LowCase(strdup(mybuff));
return 1;
}
// Disable IPv6 for 'localhost...' (potential confusion in the
// default /etc/hosts on some platforms, e.g. MacOsX)
//
// if (!strncmp(mybuff,"localhost",9)) myhints.ai_family = AF_INET;
// pcal: force ipv4
//
myhints.ai_family = AF_INET;
// Get the aliases for this name
//
rc = getaddrinfo(mybuff,0,(const addrinfo *)&myhints, &rp);
if (rc || !(np = rp)) return (errtxt ? setETni(errtxt, rc) : 0);
// Return all of the names
//
for (i = 0; i < maxipn && np; i++)
{InetName[i] = LowCase(strdup(np->ai_canonname));
np = np->ai_next;
}
freeaddrinfo(rp);
#endif
// All done
//
return i;
}
/******************************************************************************/
/* g e t P o r t */
/******************************************************************************/
int XrdSysDNS::getPort(const char *servname,
const char *servtype,
char **errtxt)
{
int rc;
#ifdef HAVE_NAMEINFO
struct addrinfo *rp, *np;
struct addrinfo myhints;
int portnum = 0;
memset(&myhints, 0, sizeof(myhints));
#else
struct servent sent, *sp;
char sbuff[1024];
#endif
// Try to find minimum port number
//
#ifndef HAVE_NAMEINFO
#ifdef __solaris__
if ( !getservbyname_r(servname,servtype,&sent,sbuff,sizeof(sbuff)))
return (errtxt ? setET(errtxt, errno) : 0);
#else
if ((rc=getservbyname_r(servname,servtype,&sent,sbuff,sizeof(sbuff),&sp)))
return (errtxt ? setET(errtxt, rc) : 0);
#endif
return int(ntohs(sent.s_port));
#else
if ((rc = getaddrinfo(0,servname,(const struct addrinfo *)&myhints,&rp))
|| !(np = rp)) return (errtxt ? setETni(errtxt, rc) : 0);
while(np) if (np->ai_socktype == SOCK_STREAM && *servtype == 't') break;
else if (np->ai_socktype == SOCK_DGRAM && *servtype == 'u') break;
else np = np->ai_next;
if (np) portnum=int(ntohs(((struct sockaddr_in *)(np->ai_addr))->sin_port));
freeaddrinfo(rp);
if (!portnum) return (errtxt ? setET(errtxt, ESRCH) : 0);
return portnum;
#endif
}
/******************************************************************************/
int XrdSysDNS::getPort(int fd, char **errtxt)
{
struct sockaddr InetAddr;
struct sockaddr_in *ip = (struct sockaddr_in *)&InetAddr;
socklen_t slen = (socklen_t)sizeof(InetAddr);
int rc;
if ((rc = getsockname(fd, &InetAddr, &slen)))
{rc = errno;
if (errtxt) setET(errtxt, errno);
return -rc;
}
return static_cast(ntohs(ip->sin_port));
}
/******************************************************************************/
/* g e t P r o t o I D */
/******************************************************************************/
#define NET_IPPROTO_TCP 6
int XrdSysDNS::getProtoID(const char *pname)
{
#ifdef HAVE_PROTOR
struct protoent pp;
char buff[1024];
#else
static XrdSysMutex protomutex;
struct protoent *pp;
int protoid;
#endif
// Note that POSIX did include getprotobyname_r() in the last minute. Many
// platforms do not document this variant but just about all include it.
//
#ifdef __solaris__
if (!getprotobyname_r(pname, &pp, buff, sizeof(buff)))
return NET_IPPROTO_TCP;
return pp.p_proto;
#elif !defined(HAVE_PROTOR)
protomutex.Lock();
if (!(pp = getprotobyname(pname))) protoid = NET_IPPROTO_TCP;
else protoid = pp->p_proto;
protomutex.UnLock();
return protoid;
#else
struct protoent *ppp;
if (getprotobyname_r(pname, &pp, buff, sizeof(buff), &ppp))
return NET_IPPROTO_TCP;
return pp.p_proto;
#endif
}
/******************************************************************************/
/* H o s t 2 D e s t */
/******************************************************************************/
int XrdSysDNS::Host2Dest(const char *hostname,
struct sockaddr &DestAddr,
char **errtxt)
{ char *cp, hbuff[256];
int port, i;
struct sockaddr_in InetAddr;
// Find the colon in the host name
//
if (!(cp = (char *) index(hostname, (int)':')))
{if (errtxt) *errtxt = (char *)"port not specified";
return 0;
}
// Make sure hostname is not too long
//
if ((i = cp-hostname) >= static_cast(sizeof(hbuff)))
{if (errtxt) *errtxt = (char *)"hostname too long";
return 0;
}
strlcpy(hbuff, hostname, i+1);
// Convert hostname to an ip address
//
struct sockaddr *ip = (struct sockaddr *)&InetAddr;
if (!getHostAddr(hbuff, *ip, errtxt)) return 0;
// Insert port number in address
//
if (!(port = atoi(cp+1)) || port > 0xffff)
{if (errtxt) *errtxt = (char *)"invalid port number";
return 0;
}
// Compose the destination address
//
InetAddr.sin_family = AF_INET;
InetAddr.sin_port = htons(port);
memcpy((void *)&DestAddr, (const void *)&InetAddr, sizeof(sockaddr));
memset((void *)&InetAddr.sin_zero, 0, sizeof(InetAddr.sin_zero));
return 1;
}
/******************************************************************************/
/* H o s t 2 I P */
/******************************************************************************/
int XrdSysDNS::Host2IP(const char *hname, unsigned int *ipaddr)
{
struct sockaddr_in InetAddr;
// Convert hostname to an ascii ip address
//
struct sockaddr *ip = (struct sockaddr *)&InetAddr;
if (!getHostAddr(hname, *ip)) return 0;
if (ipaddr) memcpy(ipaddr, &InetAddr.sin_addr, sizeof(unsigned int));
return 1;
}
/******************************************************************************/
/* I P A d d r */
/******************************************************************************/
unsigned int XrdSysDNS::IPAddr(struct sockaddr *InetAddr)
{return (unsigned int)(((struct sockaddr_in *)InetAddr)->sin_addr.s_addr);}
/******************************************************************************/
/* I P F o r m a t */
/******************************************************************************/
int XrdSysDNS::IPFormat(const struct sockaddr *sAddr, char *bP, int bL, int fP)
{
union {const struct sockaddr *Vx;
const struct sockaddr_in *V4;
const struct sockaddr_in6 *V6;
} ip;
int TotLen;
// Make sure the buffer has some space
//
if (bL < (INET_ADDRSTRLEN+4)) return 0;
ip.Vx = sAddr;
// Format address; we always use the IPV6 RFC recommended representation.
//
if (sAddr->sa_family == AF_INET)
{strcpy(bP, "[::");
if (!inet_ntop(AF_INET, &(ip.V4->sin_addr), bP+3, bL-3)) return 0;
}
else if (sAddr->sa_family == AF_INET6)
{*bP = '[';
if (!inet_ntop(AF_INET6, &(ip.V6->sin6_addr),bP+1, bL-1)) return 0;
}
else return 0;
// Recalculate buffer position and length
//
TotLen = strlen(bP); bP += TotLen; bL -= TotLen;
// Add the port number if so wanted (note that the port number is the same
// position and same size regardless of address type).
//
if (fP)
{int n, pNum = ntohs(ip.V4->sin_port);
if ((n = snprintf(bP, bL, "]:%d", pNum)) >= bL) return 0;
TotLen += n;
} else {
if (bL < 2) return 0;
*bP++ = ']'; *bP++ = 0; TotLen++;
}
// All done
//
return TotLen;
}
/******************************************************************************/
/* I P 2 S t r i n g */
/******************************************************************************/
int XrdSysDNS::IP2String(unsigned int ipaddr, int port, char *buff, int blen)
{
struct in_addr in;
int sz;
// Convert the address
//
in.s_addr = ipaddr;
if (port <= 0)
sz = snprintf(buff,blen,"%s", inet_ntoa((const struct in_addr)in));
else
sz = snprintf(buff,blen,"%s:%d",inet_ntoa((const struct in_addr)in),port);
return (sz > blen ? blen : sz);
}
/******************************************************************************/
/* i s D o m a i n */
/******************************************************************************/
int XrdSysDNS::isDomain(const char *Hostname, const char *Domname, int Domlen)
{
int hlen = strlen(Hostname);
return (hlen >= Domlen && !strcmp(Hostname+(hlen-Domlen), Domname));
}
/******************************************************************************/
/* i s L o o p b a c k */
/******************************************************************************/
int XrdSysDNS::isLoopback(struct sockaddr &InetAddr)
{
struct sockaddr_in *ip = (struct sockaddr_in *)&InetAddr;
return ip->sin_addr.s_addr == 0x7f000001;
}
/******************************************************************************/
/* i s M a t c h */
/******************************************************************************/
int XrdSysDNS::isMatch(const char *HostName, char *HostPat)
{
struct sockaddr InetAddr[16];
char *mval;
int i, j, k, retc;
if (!strcmp(HostPat, HostName)) return 1;
if ((mval = index(HostPat, (int)'*')))
{*mval = '\0'; mval++;
k = strlen(HostName); j = strlen(mval); i = strlen(HostPat);
if ((i+j) > k
|| strncmp(HostName, HostPat,i)
|| strncmp((HostName+k-j),mval,j)) return 0;
return 1;
}
i = strlen(HostPat);
if (HostPat[i-1] != '+') i = 0;
else {HostPat[i-1] = '\0';
if (!(i = getHostAddr(HostPat, InetAddr, 16)))
return 0;
}
while(i--)
{mval = getHostName(InetAddr[i]);
retc = !strcmp(mval,HostName);
free(mval);
if (retc) return 1;
}
return 0;
}
/******************************************************************************/
/* P e e r n a m e */
/******************************************************************************/
char *XrdSysDNS::Peername(int snum, struct sockaddr *sap, char **errtxt)
{
struct sockaddr addr;
SOCKLEN_t addrlen = sizeof(addr);
// Get the address on the other side of this socket
//
if (!sap) sap = &addr;
if (getpeername(snum, (struct sockaddr *)sap, &addrlen) < 0)
{if (errtxt) setET(errtxt, errno);
return (char *)0;
}
// Convert it to a host name
//
return getHostName(*sap, errtxt);
}
/******************************************************************************/
/* s e t P o r t */
/******************************************************************************/
void XrdSysDNS::setPort(struct sockaddr &InetAddr, int port, int anyaddr)
{
unsigned short int sport = static_cast(port);
struct sockaddr_in *ip = (struct sockaddr_in *)&InetAddr;
ip->sin_port = htons(sport);
if (anyaddr) {ip->sin_family = AF_INET;
ip->sin_addr.s_addr = INADDR_ANY;
memset((void *)ip->sin_zero, 0, sizeof(ip->sin_zero));
}
}
/******************************************************************************/
/* P r i v a t e M e t h o d s */
/******************************************************************************/
/******************************************************************************/
/* L o w C a s e */
/******************************************************************************/
char *XrdSysDNS::LowCase(char *str)
{
char *sp = str;
while(*sp) {if (isupper((int)*sp)) *sp = (char)tolower((int)*sp); sp++;}
return str;
}
/******************************************************************************/
/* s e t E T */
/******************************************************************************/
int XrdSysDNS::setET(char **errtxt, int rc)
{
if (rc) *errtxt = strerror(rc);
else *errtxt = (char *)"unexpected error";
return 0;
}
/******************************************************************************/
/* s e t E T n i */
/******************************************************************************/
int XrdSysDNS::setETni(char **errtxt, int rc)
{
#ifndef HAVE_NAMEINFO
return setET(errtxt, rc);
#else
if (rc) *errtxt = (char *)gai_strerror(rc);
else *errtxt = (char *)"unexpected error";
return 0;
#endif
}