/******************************************************************************/
/* */
/* X r d N e t U t i l s . c c */
/* */
/* (c) 2013 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
#include
#include
#include
#include
#include
#include "XrdNet/XrdNetAddr.hh"
#include "XrdNet/XrdNetIF.hh"
#include "XrdNet/XrdNetUtils.hh"
#include "XrdOuc/XrdOucTList.hh"
#include "XrdSys/XrdSysPlatform.hh"
#ifndef HAVE_PROTOR
#include "XrdSys/XrdSysPthread.hh"
#endif
/******************************************************************************/
/* S t a t i c M e m b e r s */
/******************************************************************************/
int XrdNetUtils::autoFamily;
// The following also sets autoFamily!
//
int XrdNetUtils::autoHints = XrdNetUtils::SetAuto(XrdNetUtils::prefAuto);
/******************************************************************************/
/* D e c o d e */
/******************************************************************************/
int XrdNetUtils::Decode(XrdNetSockAddr *sadr, const char *buff, int blen)
{
static const int ipv4Sz = sizeof(struct in_addr)*2+4;
static const int ipv6Sz = sizeof(struct in6_addr)*2+4;
char bval[sizeof(struct in6_addr)+2];
int isv6, n, i = 0, Odd = 0;
// Determine if this will be IPV4 or IPV6 (only ones allowed)
//
if (blen == ipv6Sz) isv6 = 1;
else if (blen == ipv4Sz) isv6 = 0;
else return -1;
// Convert the whole string to a temporary
//
while(blen--)
{ if (*buff >= '0' && *buff <= '9') n = *buff-48;
else if (*buff >= 'a' && *buff <= 'f') n = *buff-87;
else if (*buff >= 'A' && *buff <= 'F') n = *buff-55;
else return -1;
if (Odd) bval[i++] |= n;
else bval[i ] = n << 4;
buff++; Odd = ~Odd;
}
// Clear the address
//
memset(sadr, 0, sizeof(XrdNetSockAddr));
// Copy out the data, as needed
//
if (isv6)
{sadr->v6.sin6_family = AF_INET6;
memcpy(&(sadr->v6.sin6_port), bval, 2);
memcpy(&(sadr->v6.sin6_addr), &bval[2], sizeof(struct in6_addr));
} else {
sadr->v4.sin_family = AF_INET;
memcpy(&(sadr->v4.sin_port), bval, 2);
memcpy(&(sadr->v4.sin_addr), &bval[2], sizeof(struct in_addr));
}
// Return the converted port (it's the same for v4/v6)
//
return static_cast(ntohs(sadr->v6.sin6_port));
}
/******************************************************************************/
/* E n c o d e */
/******************************************************************************/
int XrdNetUtils::Encode(const XrdNetSockAddr *sadr, char *buff, int blen,
int port)
{
static const char *hv = "0123456789abcdef";
char *src, bval[sizeof(struct in6_addr)+2];
int asz, i, j = 0;
// Compute the size we need for the buffer (note we only support IP4/6)
//
if (sadr->Addr.sa_family == AF_INET6)
{src = (char *)&(sadr->v6.sin6_addr); asz = sizeof(struct in6_addr);}
else if (sadr->Addr.sa_family == AF_INET)
{src = (char *)&(sadr->v4.sin_addr); asz = sizeof(struct in_addr); }
else return 0;
if (blen < (asz*2)+5) return -((asz*2)+5);
// Get the port value in the first two bytes followed by the address.
//
if (port < 0) memcpy(bval, &(sadr->v6.sin6_port), 2);
else {short sPort = htons(static_cast(port));
memcpy(bval, &sPort, 2);
}
memcpy(&bval[2], src, asz);
asz += 2;
// Now convert to hex
//
for (i = 0; i < asz; i++)
{buff[j++] = hv[(bval[i] >> 4) & 0x0f];
buff[j++] = hv[ bval[i] & 0x0f];
}
buff[j] = '\0';
// All done
//
return asz*2;
}
/******************************************************************************/
/* G e t A d d r s */
/******************************************************************************/
const char *XrdNetUtils::GetAddrs(const char *hSpec,
XrdNetAddr *aListP[], int &aListN,
XrdNetUtils::AddrOpts opts, int pNum)
{
static const char *badHS = "invalid host specification";
struct addrinfo hints, *nP, *rP = 0;
XrdNetAddr *aVec;
struct {char ipMap[7]; // ::ffff: (length = 7)
char ipAdr[MAXHOSTNAMELEN+15];
} aBuff;
const char *ipAddr, *hnBeg, *hnEnd, *pnBeg, *pnEnd;
int n, map426 = 0;
// Prep the returned fields
//
*aListP = 0;
aListN = 0;
// Copy the host specification
//
if (!hSpec) return badHS;
strlcpy(aBuff.ipAdr, hSpec, sizeof(aBuff.ipAdr));
// Parse the host specification
//
if (pNum == NoPortRaw)
{hnBeg = aBuff.ipAdr;
pNum = 0;
} else {
if (!Parse(aBuff.ipAdr, &hnBeg, &hnEnd, &pnBeg, &pnEnd)) return badHS;
aBuff.ipAdr[hnEnd-aBuff.ipAdr] = 0;
if (pnBeg == hnEnd)
{if (pNum == PortInSpec) return "port not specified";
if (pNum < 0) pNum = -pNum;
} else {
const char *eText;
aBuff.ipAdr[pnEnd-aBuff.ipAdr] = 0;
n = ServPort(pnBeg, opts & onlyUDP, &eText);
if (!n) return eText;
if (pNum < 0) pNum = n;
}
}
// Setup the hints
//
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = (opts & onlyUDP ? SOCK_DGRAM : SOCK_STREAM);
opts = opts & ~onlyUDP;
switch(opts)
{case allIPMap: hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED | AI_ALL;
break;
case allIPv64: hints.ai_family = AF_UNSPEC;
break;
case allV4Map: hints.ai_family = AF_INET;
map426 = 1;
break;
case onlyIPv6: hints.ai_family = AF_INET6;
break;
case onlyIPv4: hints.ai_family = AF_INET;
break;
case prefIPv6: hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED;
break;
case prefAuto: hints.ai_family = autoFamily;
hints.ai_flags = autoHints;
break;
default: hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED | AI_ALL;
break;
}
// Check if we need to convert an ipv4 address to an ipv6 one
//
if (hints.ai_family == AF_INET6 && aBuff.ipAdr[0] != '['
&& !XrdNetAddrInfo::isHostName(aBuff.ipAdr))
{memcpy(aBuff.ipMap, "::ffff:", 7);
ipAddr = aBuff.ipMap;
} else ipAddr = hnBeg;
// Get all of the addresses
//
n = getaddrinfo(ipAddr, 0, &hints, &rP);
if (n || !rP)
{if (rP) freeaddrinfo(rP);
return (n ? gai_strerror(n) : "host not found");
}
// Count the number of entries we will return and get the addr vector
//
n = 0; nP = rP;
do {if (nP->ai_family == AF_INET6 || nP->ai_family == AF_INET) n++;
nP = nP->ai_next;
} while(nP);
if (!n) return 0;
aVec = new XrdNetAddr[n];
*aListP = aVec; aListN = n;
// Now fill out the addresses
//
n = 0; nP = rP;
do {if (nP->ai_family == AF_INET6 || nP->ai_family == AF_INET)
{aVec[n].Set(nP, pNum, map426); n++;}
nP = nP->ai_next;
} while(nP);
// All done
//
if (rP) freeaddrinfo(rP);
return 0;
}
/******************************************************************************/
/* H o s t s */
/******************************************************************************/
XrdOucTList *XrdNetUtils::Hosts(const char *hSpec, int hPort, int hWant,
int *sPort, const char **eText)
{
static const int hMax = 8;
XrdNetAddr myAddr(0), aList[hMax];
XrdOucTList *tList = 0;
const char *etext, *hName;
int numIP, i, k;
// Check if the port must be in the spec and set maximum
//
if (hPort < 0) hPort = XrdNetAddr::PortInSpec;
if (hWant > hMax) hWant = hMax;
else if (hWant < 1) hWant = 1;
// Initialze the list of addresses
//
if ((etext = aList[0].Set(hSpec, numIP, hWant, hPort)))
{if (eText) *eText = etext;
return 0;
}
// Create the tlist object list without duplicates. We may have duplicates as
// this may be a multi-homed node and we don't want to show that here.
//
for (i = 0; i < numIP; i++)
{if (sPort && myAddr.Same(&aList[i]))
{*sPort = aList[i].Port(); sPort = 0;}
hName = aList[i].Name("");
for (k = 0; k < i; k++) {if (!strcmp(hName, aList[k].Name(""))) break;}
if (k >= i) tList = new XrdOucTList(hName, aList[i].Port(), tList);
}
// All done, return the result
//
if (eText) *eText = (tList ? 0 : "unknown processing error");
return tList;
}
/******************************************************************************/
/* I P F o r m a t */
/******************************************************************************/
int XrdNetUtils::IPFormat(const struct sockaddr *sAddr,
char *bP, int bL, int opts)
{
XrdNetAddr theAddr;
int fmtopts = (opts & oldFmt ? XrdNetAddrInfo::old6Map4 : 0);
// Set the address
//
if (theAddr.Set(sAddr)) return 0;
// Now format the address
//
if (opts & noPort) fmtopts |= XrdNetAddrInfo::noPort;
return theAddr.Format(bP, bL, XrdNetAddrInfo::fmtAdv6, fmtopts);
}
/******************************************************************************/
int XrdNetUtils::IPFormat(int fd, char *bP, int bL, int opts)
{
XrdNetSockAddr theIP;
SOCKLEN_t addrSize = sizeof(theIP);
int rc;
// The the address wanted
//
rc = (fd > 0 ? getpeername( fd, &theIP.Addr, &addrSize)
: getsockname(-fd, &theIP.Addr, &addrSize));
if (rc) return 0;
// Now format it
//
return IPFormat(&theIP.Addr, bP, bL, opts);
}
/******************************************************************************/
/* M a t c h */
/******************************************************************************/
bool XrdNetUtils::Match(const char *HostName, const char *HostPat)
{
static const int maxIP = 16;
const char *mval;
int i, j, k;
// First check if this will match right away
//
if (!strcmp(HostPat, HostName)) return true;
// Check for an asterisk do prefix/suffix match
//
if ((mval = index(HostPat, (int)'*')))
{ i = mval - HostPat; mval++;
k = strlen(HostName); j = strlen(mval);
if ((i+j) > k
|| strncmp(HostName, HostPat,i)
|| strncmp((HostName+k-j),mval,j)) return false;
return 1;
}
// Now check for host expansion
//
i = strlen(HostPat);
if (i && HostPat[i-1] == '+')
{XrdNetAddr InetAddr[maxIP];
char hBuff[264];
if (i >= (int)sizeof(hBuff)) return false;
memcpy(hBuff, HostPat, i-1);
hBuff[i-1] = 0;
if (InetAddr[0].Set(hBuff, i, maxIP, 0)) return false;
while(i--) if ((mval = InetAddr[i].Name()) && !strcmp(mval, HostName))
return true;
}
// No matches
//
return false;
}
/******************************************************************************/
/* M y H o s t N a m e */
/******************************************************************************/
char *XrdNetUtils::MyHostName(const char *eName, const char **eText)
{
XrdNetAddr myAddr;
const char *etext, *myName;
char buff[1024];
// Get our host name and initialize this object with it
//
gethostname(buff, sizeof(buff));
if ((etext = myAddr.Set(buff,0))) myName = eName;
else myName = myAddr.Name(eName, &etext);
// Return result, we will always have something
//
if (eText) *eText = etext;
return (myName ? strdup(myName) : 0);
}
/******************************************************************************/
/* N e t C o n f i g */
/******************************************************************************/
XrdNetUtils::NetProt XrdNetUtils::NetConfig(XrdNetUtils::NetType netquery,
const char **eText)
{
XrdNetAddr *myAddrs;
const char *eMsg;
char buff[1024];
NetProt hasProt = hasNone;
int aCnt, ifType;
// Make sure we support this query
//
if (netquery != qryINET && netquery != qryINIF)
{if (eText) *eText = "unsupported NetType query";
return hasNone;
}
// We base the nonfig of the interface addresses unless we can't query the if's
//
if (netquery == qryINIF && (ifType = XrdNetIF::GetIF((XrdOucTList **)0,0)))
{if (ifType & XrdNetIF::haveIPv4) hasProt = NetProt(hasProt | hasIPv4);
if (ifType & XrdNetIF::haveIPv6) hasProt = NetProt(hasProt | hasIPv6);
if (ifType & XrdNetIF::havePub4) hasProt = NetProt(hasProt | hasPub4);
if (ifType & XrdNetIF::havePub6) hasProt = NetProt(hasProt | hasPub6);
return hasProt;
}
// Get our host name and initialize this object with it
//
gethostname(buff, sizeof(buff));
// Now get all of the addresses associated with this hostname
//
if ((eMsg = GetAddrs(buff, &myAddrs, aCnt, allIPv64, NoPortRaw)))
{if (eText) *eText = eMsg;
return hasNone;
}
// Now run through all of the addresses to see what we have
//
for (int i = 0; i < aCnt && hasProt != hasIP64; i++)
{ if (myAddrs[i].isIPType(XrdNetAddrInfo::IPv6))
{hasProt = NetProt(hasProt | hasIPv6);
if (!myAddrs[i].isPrivate())
hasProt = NetProt(hasProt | hasPub6);
}
else if (myAddrs[i].isIPType(XrdNetAddrInfo::IPv4))
{hasProt = NetProt(hasProt | hasIPv4);
if (!myAddrs[i].isPrivate())
hasProt = NetProt(hasProt | hasPub4);
}
}
// Delete the array and return what we have
//
delete [] myAddrs;
if (hasProt == hasNone && eText) *eText = "";
return hasProt;
}
/******************************************************************************/
/* P a r s e */
/******************************************************************************/
bool XrdNetUtils::Parse(const char *hSpec,
const char **hName, const char **hNend,
const char **hPort, const char **hPend)
{
const char *asep = 0;
// Parse the specification
//
if (*hSpec == '[')
{if (!(*hNend = index(hSpec+1, ']'))) return false;
*hName = hSpec+1; asep = (*hNend)+1;
} else {
*hName = hSpec;
if (!(*hNend = index(hSpec, ':'))) *hNend = hSpec + strlen(hSpec);
else asep = *hNend;
}
// See if we have a port to parse. We stop on a non-alphameric.
//
if (asep && *asep == ':')
{*hPort = ++asep;
while(isalnum(*asep)) asep++;
if (*hPort == asep) return false;
*hPend = asep;
} else *hPort = *hPend = *hNend;
// All done
//
return true;
}
/******************************************************************************/
/* P o r t */
/******************************************************************************/
int XrdNetUtils::Port(int fd, char **eText)
{
XrdNetSockAddr Inet;
SOCKLEN_t slen = (socklen_t)sizeof(Inet);
int rc;
if ((rc = getsockname(fd, &Inet.Addr, &slen)))
{rc = errno;
if (eText) setET(eText, errno);
return -rc;
}
return static_cast(ntohs(Inet.v6.sin6_port));
}
/******************************************************************************/
/* P r o t o I D */
/******************************************************************************/
#ifndef IPPROTO_TCP
#define IPPROTO_TCP 6
#endif
int XrdNetUtils::ProtoID(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 IPPROTO_TCP;
return pp.p_proto;
#elif !defined(HAVE_PROTOR)
protomutex.Lock();
if (!(pp = getprotobyname(pname))) protoid = 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 IPPROTO_TCP;
return pp.p_proto;
#endif
}
/******************************************************************************/
/* S e r v P o r t */
/******************************************************************************/
int XrdNetUtils::ServPort(const char *sName, bool isUDP, const char **eText)
{
struct addrinfo *rP = 0, myHints;
int rc, portnum = 0;
// First check if this is a plain number
//
if (isdigit(*sName))
{char *send;
portnum = strtol(sName, &send, 10);
if (portnum > 0 && portnum < 65536 && *send == 0) return portnum;
if (eText) *eText = "invalid port number";
return 0;
}
// Fill out the hints
//
memset(&myHints, 0, sizeof(myHints));
myHints.ai_socktype = (isUDP ? SOCK_DGRAM : SOCK_STREAM);
// Try to find the port number
//
rc = getaddrinfo(0, sName, &myHints, &rP);
if (rc || !rP)
{if (eText) *eText = (rc ? gai_strerror(rc) : "service not found");
if (rP) freeaddrinfo(rP);
return 0;
}
// Return the port number
//
portnum = int(ntohs(((struct sockaddr_in *)(rP->ai_addr))->sin_port));
freeaddrinfo(rP);
if (!portnum && eText) *eText = "service has no port";
return portnum;
}
/******************************************************************************/
/* S e t A u t o */
/******************************************************************************/
int XrdNetUtils::SetAuto(XrdNetUtils::AddrOpts aOpts)
{
// If a specific family is not specified, then determine which families to use
//
if (aOpts != onlyIPv4 && aOpts != allIPMap)
{int ifTypes = XrdNetIF::GetIF(0);
if (ifTypes & XrdNetIF::haveIPv6) aOpts = allIPMap;
else if (ifTypes & XrdNetIF::haveIPv4) aOpts = onlyIPv4;
else {autoFamily = AF_UNSPEC; autoHints = AI_V4MAPPED | AI_ADDRCONFIG;
return AI_V4MAPPED | AI_ADDRCONFIG;
}
}
// If this is forced IPv4 then we know how to set the hints
//
if (aOpts == onlyIPv4)
{autoFamily = AF_INET; autoHints = 0; return 0;}
// So, this is IPv6. Be as flexible as possible.
//
autoFamily = AF_INET6;
autoHints = AI_V4MAPPED | AI_ALL;
return AI_V4MAPPED | AI_ALL;
}
/******************************************************************************/
/* Private: s e t E T */
/******************************************************************************/
int XrdNetUtils::setET(char **errtxt, int rc)
{
if (rc) *errtxt = strerror(rc);
else *errtxt = (char *)"unexpected error";
return 0;
}