/******************************************************************************/
/* */
/* X r d N e t A d d r I n f o . 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 "XrdNet/XrdNetAddrInfo.hh"
#include "XrdNet/XrdNetCache.hh"
/******************************************************************************/
/* P l a t f o r m D e p e n d e n c i e s */
/******************************************************************************/
// Linux defines s6_addr32 but MacOS does not and Solaris defines it only when
// compiling the kernel. This is really standard stuff that should be here.
//
#ifndef s6_addr32
#if defined(__solaris__)
#define s6_addr32 _S6_un._S6_u32
#elif defined(__APPLE__) || defined(__FreeBSD__)
#define s6_addr32 __u6_addr.__u6_addr32
#endif
#endif
/******************************************************************************/
/* S t a t i c M e m b e r s */
/******************************************************************************/
XrdNetCache *XrdNetAddrInfo::dnsCache = 0;
namespace
{
static const char lbVal[13] ={0,0,0,0,0,0,0,0,0,0,0,0,0x7f};
};
/******************************************************************************/
/* F o r m a t */
/******************************************************************************/
int XrdNetAddrInfo::Format(char *bAddr, int bLen, fmtUse theFmt, int fmtOpts)
{
const char *pFmt = "]:%d";
int totLen, n, pNum, addBrak = 0;
int omitP = (fmtOpts & (noPort|noPortRaw));
int ipRaw = (fmtOpts & noPortRaw);
int ipOld = fmtOpts & (old6Map4 | prefipv4);
// Handle the degenerative case first
//
if (IP.Addr.sa_family == AF_UNIX)
{n = (omitP ? snprintf(bAddr, bLen, "localhost")
: snprintf(bAddr, bLen, "localhost:%s", unixPipe->sun_path));
return (n < bLen ? n : QFill(bAddr, bLen));
}
// Grab the potr. The port number is the same position and same size regardless
// of address type.
//
pNum = ntohs(IP.v4.sin_port);
// Resolve address if need be and return result if possible
//
if (theFmt == fmtName || theFmt == fmtAuto)
{if (!hostName && dnsCache && !(hostName = dnsCache->Find(this))
&& theFmt == fmtName) Resolve();
if (hostName)
{n = (omitP ? snprintf(bAddr, bLen, "%s", hostName)
: snprintf(bAddr, bLen, "%s:%d", hostName, pNum));
return (n < bLen ? n : QFill(bAddr, bLen));
}
theFmt = fmtAddr;
}
// Check if we can now produce an address format quickly. We used assume that
// the hostname was an address if it started with a non-alpha. But this does
// not correspond to RFC 1178 and RFC 3696, among others. We now only use
// the optimization path if the name is actually an IPV6/mapped4 address.
//
if (hostName && *hostName == '[' && !ipOld)
{n = (omitP ? snprintf(bAddr, bLen, "%s", hostName)
: snprintf(bAddr, bLen, "%s:%d", hostName, pNum));
return (n < bLen ? n : QFill(bAddr, bLen));
}
// Format address
//
if (IP.Addr.sa_family == AF_INET6)
{if (bLen < (INET6_ADDRSTRLEN+2)) return QFill(bAddr, bLen);
if (ipOld && IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr))
{ if (fmtOpts & prefipv4) {n = 0; pFmt = ":%d";}
else if (ipRaw) {strcpy(bAddr, "::"); n = 2;}
else {strcpy(bAddr, "[::"); n = 3; addBrak=1;}
if (!inet_ntop(AF_INET, &IP.v6.sin6_addr.s6_addr32[3],
bAddr+n, bLen-n)) return QFill(bAddr, bLen);
} else {
if (!ipRaw) {*bAddr = '['; n = 1; addBrak = 1;}
else n = 0;
if (!inet_ntop(AF_INET6,&(IP.v6.sin6_addr),bAddr+n,bLen-n))
return QFill(bAddr, bLen);
}
}
else if (IP.Addr.sa_family == AF_INET)
{if (theFmt != fmtAdv6) {n = 0; pFmt = ":%d";}
else {if (bLen < (INET_ADDRSTRLEN+9)) return QFill(bAddr, bLen);
if (fmtOpts & old6Map4) {strcpy(bAddr, "[::"); n = 3;}
else {strcpy(bAddr, "[::ffff:"); n = 8;}
if (ipRaw) {strcpy(bAddr, bAddr+1); n--;}
addBrak = 1;
}
if (!inet_ntop(AF_INET, &(IP.v4.sin_addr),bAddr+n,bLen-n))
return QFill(bAddr, bLen);
}
else return QFill(bAddr, bLen);
// Recalculate buffer position and length
//
totLen = strlen(bAddr); bAddr += totLen; bLen -= totLen;
// Process when no port number wanted
//
if (omitP)
{if (addBrak)
{if (bLen < 2) return QFill(bAddr, bLen);
*bAddr++ = ']'; *bAddr = 0; totLen++;
}
return totLen;
}
// Add the port number and return result
//
if ((n = snprintf(bAddr, bLen, pFmt, pNum)) >= bLen)
return QFill(bAddr, bLen);
return totLen+n;
}
/******************************************************************************/
/* i s L o o p b a c k */
/******************************************************************************/
bool XrdNetAddrInfo::isLoopback()
{
// Check for loopback address
//
if (IP.Addr.sa_family == AF_INET)
return !memcmp(&IP.v4.sin_addr.s_addr, &lbVal[12], 1);
if (IP.Addr.sa_family == AF_INET6)
return !memcmp(&IP.v6.sin6_addr, &in6addr_loopback, sizeof(in6_addr))
|| !memcmp(&IP.v6.sin6_addr, lbVal, sizeof(lbVal));
return false;
}
/******************************************************************************/
/* i s P r i v a t e */
/******************************************************************************/
bool XrdNetAddrInfo::isPrivate()
{
unsigned char *ipV4 = 0;
// For IPV6 addresses we will use the macro unless it is mapped
//
if (IP.Addr.sa_family == AF_INET6)
{if ((IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)))
ipV4 = (unsigned char *)&IP.v6.sin6_addr.s6_addr32[3];
else {if ((IN6_IS_ADDR_LINKLOCAL(&IP.v6.sin6_addr))
|| (IN6_IS_ADDR_SITELOCAL(&IP.v6.sin6_addr))
|| (IN6_IS_ADDR_LOOPBACK (&IP.v6.sin6_addr))) return true;
return false;
}
}
// If this is not an IPV4 address then we will consider it private
//
if (!ipV4)
{if (IP.Addr.sa_family != AF_INET) return true;
ipV4 = (unsigned char *)&IP.v4.sin_addr.s_addr;
}
// For IPV4 we use the RFC definition of private. Note that this includes
// mapped addresses which, as odd as it is, we could get.
//
if (ipV4[0] == 10
|| (ipV4[0] == 172 && ipV4[1] >= 16 && ipV4[1] <= 31)
|| (ipV4[0] == 192 && ipV4[1] == 168)
|| (ipV4[0] == 169 && ipV4[1] == 254)
|| ipV4[0] == 127) return true;
// Not a local address
//
return false;
}
/******************************************************************************/
/* i s R e g i s t e r e d */
/******************************************************************************/
bool XrdNetAddrInfo::isRegistered()
{
const char *hName;
// Simply see if we can resolve this name
//
if (!(hName = Name())) return false;
return isalpha(*hName);
}
/******************************************************************************/
/* Private: L o w C a s e */
/******************************************************************************/
char *XrdNetAddrInfo::LowCase(char *str)
{
char *sp = str;
while(*sp) {if (isupper((int)*sp)) *sp = (char)tolower((int)*sp); sp++;}
return str;
}
/******************************************************************************/
/* i s H o s t N a m e */
/******************************************************************************/
bool XrdNetAddrInfo::isHostName(const char *name)
{
const char *dot;
int dnum;
// First check for Iv6 format or hostname
//
if (*name == '[') return false;
if (!isdigit(*name)) return true;
// The IPv4 case is more complicated. The fastest way here is this is a
// host name if there are no dots or if the last component is not a digit
// according to the RFC spec.
//
if (!(dot = rindex(name, '.')) || !isdigit(*(dot+1))) return true;
// We are not out of the woods yet. Now we need to do a full check.
//
name++; dnum = 0;
while(*name)
{if (*name == '.') dnum++;
else if (!isdigit(*name)) return true;
name++;
}
return (dnum == 3 ? false : true);
}
/******************************************************************************/
/* N a m e */
/******************************************************************************/
const char *XrdNetAddrInfo::Name(const char *eName, const char **eText)
{
int rc;
// Preset errtxt to zero
//
if (eText) *eText = 0;
// Check for unix family which is equal to localhost.
//
if (IP.Addr.sa_family == AF_UNIX) return "localhost";
// If we already translated this name, just return the translation
//
if (hostName || (dnsCache && (hostName = dnsCache->Find(this))))
return hostName;
// Try to resolve this address
//
if (!(rc = Resolve())) return hostName;
// We failed resolving this address
//
if (eText) *eText = gai_strerror(rc);
return eName;
}
/******************************************************************************/
/* P o r t */
/******************************************************************************/
int XrdNetAddrInfo::Port()
{
// Make sure we have a proper address family here
//
if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
return -1;
// Return port number
//
return ntohs(IP.v6.sin6_port);
}
/******************************************************************************/
/* Private: Q F i l l */
/******************************************************************************/
int XrdNetAddrInfo::QFill(char *bAddr, int bLen)
{
static const char quests[] = "????????";
// Insert up to 8 question marks
//
if (bLen)
{strncpy(bAddr, quests, bLen);
bAddr[bLen-1] = 0;
}
return 0;
}
/******************************************************************************/
/* Private: R e s o l v e */
/******************************************************************************/
int XrdNetAddrInfo::Resolve()
{
char hBuff[NI_MAXHOST];
int n, rc;
// Free up hostname here
//
if (hostName) {free(hostName); hostName = 0;}
// Determine the actual size of the address structure
//
if (IP.Addr.sa_family == AF_INET ) n = sizeof(IP.v4);
else if (IP.Addr.sa_family == AF_INET6) n = sizeof(IP.v6);
else if (IP.Addr.sa_family == AF_UNIX )
{hostName = strdup("localhost");
return 0;
}
else return EAI_FAMILY;
// Do lookup of canonical name. If an error is returned we simply assume that
// the name is not resolvable and return the address as the host name.
//
if ((rc = getnameinfo(&IP.Addr, n, hBuff+1, sizeof(hBuff)-2, 0, 0, 0)))
{int ec = errno;
if (Format(hBuff, sizeof(hBuff), fmtAddr, noPort))
{hostName = strdup(hBuff); return 0;}
errno = ec;
return rc;
}
// Handle the case when the mapping returned an actual name or an address
// We always want numeric ipv6 addresses surrounded by brackets. Additionally,
// some implementations of getnameinfo() return the scopeid when a numeric
// address is returned. We check and remove it.
//
if (!index(hBuff+1, ':')) hostName = strdup(LowCase(hBuff+1));
else {char *perCent = index(hBuff+1, '%');
if (perCent) *perCent = 0;
n = strlen(hBuff+1);
hBuff[0] = '['; hBuff[n+1] = ']'; hBuff[n+2] = 0;
hostName = strdup(hBuff);
}
// Add the entry to the cache and return success
//
if (dnsCache) dnsCache->Add(this, hostName);
return 0;
}
/******************************************************************************/
/* S a m e */
/******************************************************************************/
#define IS_INET(x) (x == AF_INET || x == AF_INET6)
#define MY_FAMILY IP.Addr.sa_family
#define UR_FAMILY ipAddr->IP.Addr.sa_family
int XrdNetAddrInfo::Same(const XrdNetAddrInfo *ipAddr, bool plusPort)
{
static const int ipv4ASZ = sizeof(IP.v4.sin_addr);
bool isINet = IS_INET(MY_FAMILY) && IS_INET(UR_FAMILY);
// Do port comparison if requested and makes sense to do it
//
if (plusPort && isINet)
{in_port_t port1, port2;
port1 = (MY_FAMILY == AF_INET ? IP.v4.sin_port : IP.v6.sin6_port);
port2 = (UR_FAMILY == AF_INET ? ipAddr->IP.v4.sin_port
: ipAddr->IP.v6.sin6_port);
if (port1 != port2) return 0;
}
// If address families do not match, they are the same if the hostnames match.
// If we don't have a hostname and the addresses are convertable, we can
// compare the actual addresses.
//
if (MY_FAMILY != UR_FAMILY)
{if (!isINet) return 0;
if (hostName && ipAddr->hostName)
return !strcmp(hostName,ipAddr->hostName);
if (MY_FAMILY == AF_INET && ipAddr->isMapped())
return !memcmp(&IP.v4.sin_addr,
&(ipAddr->IP.v6.sin6_addr.s6_addr32[3]), ipv4ASZ);
if (isMapped() && UR_FAMILY == AF_INET)
return !memcmp(&IP.v6.sin6_addr.s6_addr32[3],
&(ipAddr->IP.v4.sin_addr), ipv4ASZ);
return 0;
}
// Now process to do the match
//
if (MY_FAMILY == AF_INET)
return !memcmp(&IP.v4.sin_addr, &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
else if (MY_FAMILY == AF_INET6)
return !memcmp(&IP.v6.sin6_addr, &(ipAddr->IP.v6.sin6_addr),
sizeof(IP.v6.sin6_addr));
else if (MY_FAMILY == AF_UNIX)
return !strcmp(unixPipe->sun_path, ipAddr->unixPipe->sun_path);
return 0;
}