/******************************************************************************/
/* */
/* X r d N e t A d d r . 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 "XrdNet/XrdNetAddr.hh"
#include "XrdNet/XrdNetCache.hh"
#include "XrdNet/XrdNetUtils.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 */
/******************************************************************************/
namespace
{
bool OnlyIPV4()
{
int fd;
// Detect badly configured or non-extent IPv6 stacks and revert to IPv4 is so.
//
if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) >= 0) close(fd);
else if (errno == EAFNOSUPPORT)
{XrdNetAddr::SetIPV4();
return true;
}
return false;
}
}
struct addrinfo *XrdNetAddr::hostHints = XrdNetAddr::Hints(0, 0);
struct addrinfo *XrdNetAddr::huntHintsTCP = XrdNetAddr::Hints(1, SOCK_STREAM);
struct addrinfo *XrdNetAddr::huntHintsUDP = XrdNetAddr::Hints(2, SOCK_DGRAM);
// The following must be initialzed after all of the hint structures!
//
bool XrdNetAddr::useIPV4 = OnlyIPV4();
/******************************************************************************/
/* C o n s t r u c t o r */
/******************************************************************************/
XrdNetAddr::XrdNetAddr(int port) : XrdNetAddrInfo()
{
char buff[1024];
// Get our host name and initialize this object with it
//
gethostname(buff, sizeof(buff));
Set(buff, port);
}
/******************************************************************************/
/* Private: H i n t s */
/******************************************************************************/
struct addrinfo *XrdNetAddr::Hints(int htype, int stype)
{
static struct addrinfo theHints[3];
// Return properly initialized hint structure. We need to do this dynamically
// in a static constructor since the addrinfo layout differs by OS-type.
//
memset(&theHints[htype], 0, sizeof(struct addrinfo));;
if (htype) theHints[htype].ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
else theHints[htype].ai_flags = AI_V4MAPPED | AI_CANONNAME;
theHints[htype].ai_family = AF_UNSPEC;
theHints[htype].ai_socktype = stype;
return &theHints[htype];
}
/******************************************************************************/
/* Private: M a p 6 4 */
/******************************************************************************/
bool XrdNetAddr::Map64()
{
// The address must be a mapped IPV4 address
//
if (!IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)) return false;
// Now convert this down to an IPv4 address
//
IP.v4.sin_addr.s_addr = IP.v6.sin6_addr.s6_addr32[3];
IP.v4.sin_family = AF_INET;
protType = PF_INET;
addrSize = sizeof(sockaddr_in);
return true;
}
/******************************************************************************/
/* P o r t */
/******************************************************************************/
int XrdNetAddr::Port(int pNum)
{
// 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 if so wanted. The port location is the same regardless of
// the address family.
//
if (pNum < 0) return ntohs(IP.v6.sin6_port);
// Set port number if we have a valid address. The location of the port
// is the same regardless of address family.
//
if (pNum > 0xffff) return -1;
IP.v6.sin6_port = htons(static_cast(pNum));
return pNum;
}
/******************************************************************************/
/* S e t */
/******************************************************************************/
const char *XrdNetAddr::Set(const char *hSpec, int pNum)
{
static const char *badIPv4 = "invalid IPv4 address";
static const char *badIPv6 = "invalid IPv6 address";
static const char *badIP64 = "IPv6 address not IPv4 representable";
static const char *badName = "invalid host name";
static const int map46ID = htonl(0x0000ffff);
const char *Colon, *iP;
char aBuff[NI_MAXHOST+INET6_ADDRSTRLEN];
int aLen, n;
bool mapIt;
// Clear translation if set (note unixPipe & sockAddr are the same).
//
if (hostName) {free(hostName); hostName = 0;}
if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
memset(&IP, 0, sizeof(IP));
addrSize = sizeof(sockaddr_in6);
// Check for any address setting
//
if (!hSpec)
{if (useIPV4)
{IP.v4.sin_family = AF_INET;
IP.v4.sin_addr.s_addr = INADDR_ANY;
protType = PF_INET;
addrSize = sizeof(sockaddr_in);
} else {
IP.v6.sin6_family = AF_INET6;
IP.v6.sin6_addr = in6addr_any;
protType = PF_INET6;
}
if (pNum < 0) pNum= -pNum;
IP.v6.sin6_port = htons(static_cast(pNum));
return 0;
}
// Check for Unix type address here
//
if (*hSpec == '/')
{if (strlen(hSpec) >= sizeof(unixPipe->sun_path)) return "path too long";
unixPipe = new sockaddr_un;
strcpy(unixPipe->sun_path, hSpec);
unixPipe->sun_family = IP.Addr.sa_family = AF_UNIX;
addrSize = sizeof(sockaddr_un);
protType = PF_UNIX;
return 0;
}
// Do length check to see if we can fit the host name in our buffer.
//
aLen = strlen(hSpec);
if (aLen >= (int)sizeof(aBuff)) return "host id too long";
// Convert the address as appropriate. Note that we do accept RFC5156 deprecated
// IPV4 mapped IPV6 addresses(i.e. [::a.b.c.d]. This is historical.
//
if (*hSpec == '[')
{const char *Brak = index(hSpec+1, ']');
if (!Brak) return badIPv6;
Colon = Brak+1;
if (!(*Colon)) Colon = 0;
else if (*Colon != ':') return badIPv6;
aLen = Brak - (hSpec+1);
if (aLen >= INET6_ADDRSTRLEN) return badIPv6;
mapIt = (*(hSpec+1) == ':' && *(hSpec+2) == ':'
&& *(hSpec+3) >= '0' && *(hSpec+3) <= '9'
&& (iP = index(hSpec+4, '.')) && iP < Brak);
strncpy(aBuff, hSpec+1, aLen); aBuff[aLen] = 0;
if (inet_pton(AF_INET6,aBuff,&IP.v6.sin6_addr) != 1) return badIPv6;
if (mapIt) IP.v6.sin6_addr.s6_addr32[2] = map46ID;
IP.v6.sin6_family = AF_INET6;
protType = PF_INET6;
if (useIPV4 && !Map64()) return badIP64;
}
else if (!isHostName(hSpec))
{if ((Colon = index(hSpec, ':')))
{aLen = Colon - hSpec;
if (aLen >= INET_ADDRSTRLEN) return badIPv4;
strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
} else iP = hSpec;
if (inet_pton(AF_INET ,iP, &IP.v6.sin6_addr.s6_addr32[3]) != 1)
return badIPv4;
IP.v6.sin6_addr.s6_addr32[2] = map46ID;
IP.v6.sin6_family = AF_INET6;
protType = PF_INET6;
if (useIPV4 && !Map64()) return badIPv4;
}
else if (*hSpec == 0) return badName;
else {struct addrinfo *rP = 0;
if ((Colon = index(hSpec, ':')))
{aLen = Colon - hSpec;
if (aLen > MAXHOSTNAMELEN) return badName;
strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
} else iP = hSpec;
n = getaddrinfo(iP, 0, hostHints, &rP);
if (n || !rP)
{if (rP) freeaddrinfo(rP);
return (n ? gai_strerror(n) : "host not found");
}
memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
protType = (IP.v6.sin6_family == AF_INET6 ? PF_INET6 : PF_INET);
if (rP->ai_canonname) hostName = strdup(rP->ai_canonname);
freeaddrinfo(rP);
}
// Now set the port number as needed (v4 & v6 port locations are the same)
//
if (pNum == PortInSpec && !Colon) return "port not specified";
if (pNum <= 0 && Colon)
{char *eP;
pNum = strtol(Colon+1, &eP, 10);
if (pNum < 0 || pNum > 0xffff || *eP) return "invalid port number";
} else if (pNum < 0) pNum = -pNum;
IP.v6.sin6_port = htons(static_cast(pNum));
// All done
//
return 0;
}
/******************************************************************************/
const char *XrdNetAddr::Set(const char *hSpec, int &numIP, int maxIP,
int pNum, bool optUDP)
{
struct addrinfo *hP, *rP = 0, *pP, *nP;
XrdNetAddr *aVec = this;
const char *hnBeg, *hnEnd, *pnBeg, *pnEnd;
char hBuff[MAXHOSTNAMELEN+8];
int hLen, n;
// If only one address can be returned, just revert to standard processing
//
if (!hSpec || !isalpha(*hSpec) || maxIP < 2)
{const char *eMsg = Set(hSpec, pNum);
numIP = (eMsg ? 0 : 1);
return eMsg;
}
// Extract out host name
//
if (!XrdNetUtils::Parse(hSpec, &hnBeg, &hnEnd, &pnBeg, &pnEnd))
return "invalid host specification";
hLen = hnEnd - hnBeg;
if (hLen > MAXHOSTNAMELEN) return "host name too long";
strncpy(hBuff, hSpec, hLen); hBuff[hLen] = 0;
// Get the port number we will be setting
//
if (pnBeg == hnEnd)
{if (pNum == PortInSpec) return "port not specified";
if (pNum < 0) pNum = -pNum;
} else {
if (*pnEnd || !(n = XrdNetUtils::ServPort(pnBeg, optUDP)))
return "invalid port";
if (pNum < 0) pNum = n;
}
// Get all of the addresses
//
hP = (optUDP ? huntHintsUDP : huntHintsTCP);
n = getaddrinfo(hBuff, 0, hP, &rP);
if (n || !rP)
{if (rP) freeaddrinfo(rP);
return (n ? gai_strerror(n) : "host not found");
}
// Now self-referentially fill out ourselves with no duplicates
//
n = 0; pP = 0; nP = rP;
do {if (!pP || pP->ai_addrlen != nP->ai_addrlen
|| memcmp((const void *)pP->ai_addr, (const void *)nP->ai_addr,
nP->ai_addrlen)) {aVec[n].Set(nP, pNum); n++;}
pP = nP; nP = nP->ai_next;
} while(n < maxIP && nP);
// All done
//
numIP = n;
if (rP) freeaddrinfo(rP);
return 0;
}
/******************************************************************************/
const char *XrdNetAddr::Set(const struct sockaddr *sockP, int sockFD)
{
// Make sure we won't loose any bits of sockFD (we should use an int)
//
if (sockFD >=0 && (sockFD & 0xffff0000) != 0) return "FD is out of range";
// Clear translation if set
//
if (hostName) {free(hostName); hostName = 0;}
if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
sockNum = static_cast(sockFD);
// Copy the address based on address family
//
if (sockP->sa_family == AF_INET6) {addrSize = sizeof(IP.v6);
protType = PF_INET6;
}
else if (sockP->sa_family == AF_INET) {addrSize = sizeof(IP.v4);
protType = PF_INET;
}
else if (sockP->sa_family == AF_UNIX)
{unixPipe = new sockaddr_un;
memcpy(unixPipe, sockP, sizeof(struct sockaddr_un));
unixPipe->sun_path[sizeof(unixPipe->sun_path)-1] = 0;
addrSize = sizeof(sockaddr_un);
memset(&IP, 0, sizeof(IP));
IP.Addr.sa_family = AF_UNIX;
protType = PF_UNIX;
return 0;
}
else return "invalid address family";
// Copy the address and return
//
memcpy(&IP, sockP, addrSize);
return 0;
}
/******************************************************************************/
const char *XrdNetAddr::Set(int sockFD, bool peer)
{
int rc;
// Make sure we won't loose any bits of sockFD (we should use an int)
//
if ((sockFD & 0xffff0000) != 0) return "FD is out of range";
// Clear translation if set
//
if (hostName) {free(hostName); hostName = 0;}
if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
addrSize = sizeof(sockaddr_in6);
sockNum = static_cast(sockFD);
// Get the address on the appropriate side of this socket
//
if (peer) rc = getpeername(sockFD, &IP.Addr, &addrSize);
else rc = getsockname(sockFD, &IP.Addr, &addrSize);
if (rc < 0)
{addrSize = 0;
return strerror(errno);
}
// Set the correct address size
//
if (IP.Addr.sa_family == AF_INET)
{addrSize = sizeof(sockaddr_in); protType = PF_INET;
} else {
addrSize = sizeof(sockaddr_in6); protType = PF_INET6;
}
// All done
//
return 0;
}
/******************************************************************************/
const char *XrdNetAddr::Set(struct addrinfo *rP, int Port, bool mapit)
{
static const int map46ID = htonl(0x0000ffff);
// See if we need to convert this address otherwise just copy it
//
if (mapit && rP->ai_family == AF_INET)
{memset(&IP.Addr, 0, sizeof(IP.Addr));
IP.v6.sin6_family = AF_INET6;
memcpy(&IP.v6.sin6_addr.s6_addr32[3], (rP->ai_addr->sa_data)+2, 4);
IP.v6.sin6_addr.s6_addr32[2] = map46ID;
addrSize = sizeof(IP.v6);
protType = PF_INET6;
} else {
memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
addrSize = rP->ai_addrlen;
protType = rP->ai_protocol;
}
// Cleanup pre-existing information
//
if (hostName) free(hostName);
hostName = (rP->ai_canonname ? strdup(rP->ai_canonname) : 0);
if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
IP.v6.sin6_port = htons(static_cast(Port));
sockNum = 0;
return 0;
}
/******************************************************************************/
/* S e t C a c h e */
/******************************************************************************/
void XrdNetAddr::SetCache(int keeptime)
{
static XrdNetCache theCache;
// Set the cache keep time
//
theCache.SetKT(keeptime);
dnsCache = (keeptime > 0 ? &theCache : 0);
}
/******************************************************************************/
/* S e t I P V 4 */
/******************************************************************************/
void XrdNetAddr::SetIPV4()
{
// To force IPV4 mode we merely change the hints structure and set the IPV4
// mode flag to reject IPV6 address unless they are mapped.
//
hostHints->ai_flags = AI_CANONNAME;
hostHints->ai_family = AF_INET;
huntHintsTCP->ai_flags = AI_ADDRCONFIG;
huntHintsTCP->ai_family = AF_INET;
huntHintsUDP->ai_flags = AI_ADDRCONFIG;
huntHintsUDP->ai_family = AF_INET;
useIPV4 = true;
// Inform NetUtils that we changed mode
//
XrdNetUtils::SetAuto(XrdNetUtils::onlyIPv4);
}
/******************************************************************************/
/* S e t I P V 6 */
/******************************************************************************/
void XrdNetAddr::SetIPV6()
{
// To force IPV6 mode we merely change the hints structure and set the IPV4
// mode flag to accept IPV6 address.
//
hostHints->ai_flags = AI_V4MAPPED | AI_CANONNAME;
hostHints->ai_family = AF_INET6;
huntHintsTCP->ai_flags = AI_V4MAPPED | AI_ALL;
huntHintsTCP->ai_family = AF_INET6;
huntHintsUDP->ai_flags = AI_V4MAPPED | AI_ALL;
huntHintsUDP->ai_family = AF_INET6;
useIPV4 = false;
// Inform NetUtils that we changed mode
//
XrdNetUtils::SetAuto(XrdNetUtils::allIPMap);
}