/******************************************************************************/ /* */ /* 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); }