XRootD
Loading...
Searching...
No Matches
XrdNetAddr.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d N e t A d d r . c c */
4/* */
5/* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* All Rights Reserved */
7/* Produced by Andrew Hanushevsky for Stanford University under contract */
8/* DE-AC02-76-SFO0515 with the Department of Energy */
9/* */
10/* This file is part of the XRootD software suite. */
11/* */
12/* XRootD is free software: you can redistribute it and/or modify it under */
13/* the terms of the GNU Lesser General Public License as published by the */
14/* Free Software Foundation, either version 3 of the License, or (at your */
15/* option) any later version. */
16/* */
17/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20/* License for more details. */
21/* */
22/* You should have received a copy of the GNU Lesser General Public License */
23/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25/* */
26/* The copyright holder's institutional names and contributor's names may not */
27/* be used to endorse or promote products derived from this software without */
28/* specific prior written permission of the institution or contributor. */
29/******************************************************************************/
30
31#include <cctype>
32#include <netdb.h>
33#include <cstdio>
34#include <unistd.h>
35#include <arpa/inet.h>
36#include <sys/types.h>
37
38#include "XrdNet/XrdNetAddr.hh"
39#include "XrdNet/XrdNetCache.hh"
41#include "XrdNet/XrdNetUtils.hh"
42#include "XrdSys/XrdSysE2T.hh"
43
44/******************************************************************************/
45/* P l a t f o r m D e p e n d e n c i e s */
46/******************************************************************************/
47
48// Linux defines s6_addr32 but MacOS does not and Solaris defines it only when
49// compiling the kernel. This is really standard stuff that should be here.
50//
51#ifndef s6_addr32
52#if defined(__solaris__)
53#define s6_addr32 _S6_un._S6_u32
54#elif defined(__APPLE__) || defined(__FreeBSD__)
55#define s6_addr32 __u6_addr.__u6_addr32
56#endif
57#endif
58
59/******************************************************************************/
60/* S t a t i c M e m b e r s */
61/******************************************************************************/
62
63namespace
64{
65bool OnlyIPV4()
66{
67 int fd;
68
69// Detect badly configured or non-extent IPv6 stacks and revert to IPv4 is so.
70//
71 if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) >= 0) close(fd);
72 else if (errno == EAFNOSUPPORT)
74 return true;
75 }
76 return false;
77}
78}
79
80struct addrinfo *XrdNetAddr::hostHints = XrdNetAddr::Hints(0, 0);
81
82struct addrinfo *XrdNetAddr::huntHintsTCP = XrdNetAddr::Hints(1, SOCK_STREAM);
83
84struct addrinfo *XrdNetAddr::huntHintsUDP = XrdNetAddr::Hints(2, SOCK_DGRAM);
85
86// The following must be initialzed after all of the hint structures!
87//
88bool XrdNetAddr::useIPV4 = OnlyIPV4();
89bool XrdNetAddr::dynDNS = false;
90
91/******************************************************************************/
92/* C o n s t r u c t o r */
93/******************************************************************************/
94
96{
97 const char *fqn = XrdNetIdentity::FQN();
98
99// The host name might not be resolvable. If we have an fqn then either the
100// name was manually set or we are using an IP address because reverse
101// lookup did not work. If we have an fqn, then use it as the name. Otherwise,
102// use localhost as that is always a safe fallback.
103//
104 if (!fqn || Set(fqn, port))
105 {Set("localhost", port);
106 if (fqn)
107 {if (hostName) free(hostName);
108 hostName = strdup(fqn);
109 }
110 }
111}
112
113/******************************************************************************/
114/* Private: H i n t s */
115/******************************************************************************/
116
117struct addrinfo *XrdNetAddr::Hints(int htype, int stype)
118{
119 static struct addrinfo theHints[3];
120
121// Return properly initialized hint structure. We need to do this dynamically
122// in a static constructor since the addrinfo layout differs by OS-type.
123//
124 memset(&theHints[htype], 0, sizeof(struct addrinfo));;
125 if (htype) theHints[htype].ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
126 else theHints[htype].ai_flags = AI_V4MAPPED | AI_CANONNAME;
127 theHints[htype].ai_family = AF_UNSPEC;
128 theHints[htype].ai_socktype = stype;
129 return &theHints[htype];
130}
131
132/******************************************************************************/
133/* Private: M a p 6 4 */
134/******************************************************************************/
135
136bool XrdNetAddr::Map64()
137{
138
139// The address must be a mapped IPV4 address
140//
141 if (!IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)) return false;
142
143// Now convert this down to an IPv4 address
144//
145 IP.v4.sin_addr.s_addr = IP.v6.sin6_addr.s6_addr32[3];
146 IP.v4.sin_family = AF_INET;
147 protType = PF_INET;
148 addrSize = sizeof(sockaddr_in);
149 return true;
150}
151
152/******************************************************************************/
153/* P o r t */
154/******************************************************************************/
155
156int XrdNetAddr::Port(int pNum)
157{
158// Make sure we have a proper address family here
159//
160 if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
161 return -1;
162
163// Return port number if so wanted. The port location is the same regardless of
164// the address family.
165//
166 if (pNum < 0) return ntohs(IP.v6.sin6_port);
167
168// Set port number if we have a valid address. The location of the port
169// is the same regardless of address family.
170//
171 if (pNum > 0xffff) return -1;
172 IP.v6.sin6_port = htons(static_cast<short>(pNum));
173 return pNum;
174}
175
176/******************************************************************************/
177/* R e g i s t e r */
178/******************************************************************************/
179
180bool XrdNetAddr::Register(const char *hName)
181{
182 XrdNetAddr *aListVec = 0;
183 int i, aListNum;
184
185// Step one is to make sure the incoming name is not an address
186//
187 if (!isHostName(hName)) return false;
188
189// The next step is to get all of the IP addresses registered for this name
190//
191 if (XrdNetUtils::GetAddrs(hName, &aListVec, aListNum,
193 return false;
194
195// In order to use the given name, one of the IP addresses in the list must
196// match our address. This is about as secure we can get.
197//
198 for (i = 0; i < aListNum; i++) {if (Same(&aListVec[i])) break;}
199 delete [] aListVec;
200
201// If we didn't find a match, report it
202//
203 if (i >= aListNum) return false;
204
205// Replace current hostname with the wanted one
206//
207 if (hostName) free(hostName);
208 hostName = strdup(hName);
209 return true;
210}
211
212/******************************************************************************/
213/* S e t */
214/******************************************************************************/
215
216const char *XrdNetAddr::Set(const char *hSpec, int pNum)
217{
218 static const char *badIPv4 = "invalid IPv4 address";
219 static const char *badIPv6 = "invalid IPv6 address";
220 static const char *badIP64 = "IPv6 address not IPv4 representable";
221 static const char *badName = "invalid host name";
222 static const int map46ID = htonl(0x0000ffff);
223
224 const char *Colon, *iP;
225 char aBuff[NI_MAXHOST+INET6_ADDRSTRLEN];
226 int aLen, n;
227 bool mapIt;
228
229// Clear translation if set (note unixPipe & sockAddr are the same).
230//
231 if (hostName) {free(hostName); hostName = 0;}
232 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
233 memset(&IP, 0, sizeof(IP));
234 addrSize = sizeof(sockaddr_in6);
235
236// Check for any address setting
237//
238 if (!hSpec)
239 {if (useIPV4)
240 {IP.v4.sin_family = AF_INET;
241 IP.v4.sin_addr.s_addr = INADDR_ANY;
242 protType = PF_INET;
243 addrSize = sizeof(sockaddr_in);
244 } else {
245 IP.v6.sin6_family = AF_INET6;
246 IP.v6.sin6_addr = in6addr_any;
247 protType = PF_INET6;
248 }
249 if (pNum < 0) pNum= -pNum;
250 IP.v6.sin6_port = htons(static_cast<short>(pNum));
251 return 0;
252 }
253
254// Check for Unix type address here
255//
256 if (*hSpec == '/')
257 {if (strlen(hSpec) >= sizeof(unixPipe->sun_path)) return "path too long";
258 unixPipe = new sockaddr_un;
259 strcpy(unixPipe->sun_path, hSpec);
260 unixPipe->sun_family = IP.Addr.sa_family = AF_UNIX;
261 addrSize = sizeof(sockaddr_un);
262 protType = PF_UNIX;
263 return 0;
264 }
265
266// Do length check to see if we can fit the host name in our buffer.
267//
268 aLen = strlen(hSpec);
269 if (aLen >= (int)sizeof(aBuff)) return "host id too long";
270
271// Convert the address as appropriate. Note that we do accept RFC5156 deprecated
272// IPV4 mapped IPV6 addresses(i.e. [::a.b.c.d]. This is historical.
273//
274 if (*hSpec == '[')
275 {const char *Brak = index(hSpec+1, ']');
276 if (!Brak) return badIPv6;
277 Colon = Brak+1;
278 if (!(*Colon)) Colon = 0;
279 else if (*Colon != ':') return badIPv6;
280 aLen = Brak - (hSpec+1);
281 if (aLen >= INET6_ADDRSTRLEN) return badIPv6;
282 mapIt = (*(hSpec+1) == ':' && *(hSpec+2) == ':'
283 && *(hSpec+3) >= '0' && *(hSpec+3) <= '9'
284 && (iP = index(hSpec+4, '.')) && iP < Brak);
285 strncpy(aBuff, hSpec+1, aLen); aBuff[aLen] = 0;
286 if (inet_pton(AF_INET6,aBuff,&IP.v6.sin6_addr) != 1) return badIPv6;
287 if (mapIt) IP.v6.sin6_addr.s6_addr32[2] = map46ID;
288 IP.v6.sin6_family = AF_INET6;
289 protType = PF_INET6;
290 if (useIPV4 && !Map64()) return badIP64;
291 }
292 else if (!isHostName(hSpec))
293 {if ((Colon = index(hSpec, ':')))
294 {aLen = Colon - hSpec;
295 if (aLen >= INET_ADDRSTRLEN) return badIPv4;
296 strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
297 } else iP = hSpec;
298 if (inet_pton(AF_INET ,iP, &IP.v6.sin6_addr.s6_addr32[3]) != 1)
299 return badIPv4;
300 IP.v6.sin6_addr.s6_addr32[2] = map46ID;
301 IP.v6.sin6_family = AF_INET6;
302 protType = PF_INET6;
303 if (useIPV4 && !Map64()) return badIPv4;
304 }
305 else if (*hSpec == 0) return badName;
306
307 else {struct addrinfo *rP = 0;
308 if ((Colon = index(hSpec, ':')))
309 {aLen = Colon - hSpec;
310 if (aLen > MAXHOSTNAMELEN) return badName;
311 strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
312 } else iP = hSpec;
313 n = getaddrinfo(iP, 0, hostHints, &rP);
314 if (n || !rP)
315 {if (rP) freeaddrinfo(rP);
316 if (n == EAI_NONAME && dynDNS)
317 return "Dynamic name or service not yet registered";
318 return (n ? gai_strerror(n) : "host not found");
319 }
320 memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
321 addrSize = rP->ai_addrlen;
322 protType = (IP.v6.sin6_family == AF_INET6 ? PF_INET6 : PF_INET);
323 if (rP->ai_canonname) hostName = LowCase(strdup(rP->ai_canonname));
324 freeaddrinfo(rP);
325 }
326
327// Now set the port number as needed (v4 & v6 port locations are the same)
328//
329 if (pNum == PortInSpec && !Colon) return "port not specified";
330 if (pNum <= 0 && Colon)
331 {char *eP;
332 pNum = strtol(Colon+1, &eP, 10);
333 if (pNum < 0 || pNum > 0xffff || *eP) return "invalid port number";
334 } else if (pNum < 0) pNum = -pNum;
335 IP.v6.sin6_port = htons(static_cast<short>(pNum));
336
337// All done
338//
339 return 0;
340}
341
342/******************************************************************************/
343
344const char *XrdNetAddr::Set(const char *hSpec, int &numIP, int maxIP,
345 int pNum, bool optUDP)
346{
347 struct addrinfo *hP, *rP = 0, *pP, *nP;
348 XrdNetAddr *aVec = this;
349 const char *hnBeg, *hnEnd, *pnBeg, *pnEnd;
350 char hBuff[MAXHOSTNAMELEN+8];
351 int hLen, n;
352
353// If only one address can be returned, just revert to standard processing
354//
355 if (!hSpec || !isalpha(*hSpec) || maxIP < 2)
356 {const char *eMsg = Set(hSpec, pNum);
357 numIP = (eMsg ? 0 : 1);
358 return eMsg;
359 }
360
361// Extract out host name
362//
363 if (!XrdNetUtils::Parse(hSpec, &hnBeg, &hnEnd, &pnBeg, &pnEnd))
364 return "invalid host specification";
365 hLen = hnEnd - hnBeg;
366 if (hLen > MAXHOSTNAMELEN) return "host name too long";
367 strncpy(hBuff, hSpec, hLen); hBuff[hLen] = 0;
368
369// Get the port number we will be setting
370//
371 if (pnBeg == hnEnd)
372 {if (pNum == PortInSpec) return "port not specified";
373 if (pNum < 0) pNum = -pNum;
374 } else {
375 if (*pnEnd || !(n = XrdNetUtils::ServPort(pnBeg, optUDP)))
376 return "invalid port";
377 if (pNum < 0) pNum = n;
378 }
379
380// Get all of the addresses
381//
382 hP = (optUDP ? huntHintsUDP : huntHintsTCP);
383 n = getaddrinfo(hBuff, 0, hP, &rP);
384 if (n || !rP)
385 {if (rP) freeaddrinfo(rP);
386 return (n ? gai_strerror(n) : "host not found");
387 }
388
389// Now self-referentially fill out ourselves with no duplicates
390//
391 n = 0; pP = 0; nP = rP;
392 do {if (!pP || pP->ai_addrlen != nP->ai_addrlen
393 || memcmp((const void *)pP->ai_addr, (const void *)nP->ai_addr,
394 nP->ai_addrlen)) {aVec[n].Set(nP, pNum); n++;}
395 pP = nP; nP = nP->ai_next;
396 } while(n < maxIP && nP);
397
398// All done
399//
400 numIP = n;
401 if (rP) freeaddrinfo(rP);
402 return 0;
403}
404
405/******************************************************************************/
406
407const char *XrdNetAddr::Set(const struct sockaddr *sockP, int sockFD)
408{
409// Clear translation if set
410//
411 if (hostName) {free(hostName); hostName = 0;}
412 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
413 sockNum = sockFD;
414
415// Copy the address based on address family
416//
417 if (sockP->sa_family == AF_INET6) {addrSize = sizeof(IP.v6);
418 protType = PF_INET6;
419 }
420 else if (sockP->sa_family == AF_INET) {addrSize = sizeof(IP.v4);
421 protType = PF_INET;
422 }
423 else if (sockP->sa_family == AF_UNIX)
424 {unixPipe = new sockaddr_un;
425 memcpy(unixPipe, sockP, sizeof(struct sockaddr_un));
426 unixPipe->sun_path[sizeof(unixPipe->sun_path)-1] = 0;
427 addrSize = sizeof(sockaddr_un);
428 memset(&IP, 0, sizeof(IP));
429 IP.Addr.sa_family = AF_UNIX;
430 protType = PF_UNIX;
431 return 0;
432 }
433 else return "invalid address family";
434
435// Copy the address and return
436//
437 memcpy(&IP, sockP, addrSize);
438 return 0;
439}
440
441/******************************************************************************/
442
443const char *XrdNetAddr::Set(int sockFD, bool peer)
444{
445 SOCKLEN_t aSize = static_cast<SOCKLEN_t>(sizeof(IP));
446 int rc;
447
448// Clear translation if set
449//
450 if (hostName) {free(hostName); hostName = 0;}
451 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
452 sockNum = sockFD;
453
454// Get the address on the appropriate side of this socket
455//
456 if (peer) rc = getpeername(sockFD, &IP.Addr, &aSize);
457 else rc = getsockname(sockFD, &IP.Addr, &aSize);
458 if (rc < 0)
459 {addrSize = 0;
460 return XrdSysE2T(errno);
461 }
462
463// Set the correct address size and protocol family
464//
465 addrSize = aSize;
466 protType = (IP.Addr.sa_family == AF_INET ? PF_INET : PF_INET6);
467
468// All done
469//
470 return 0;
471}
472
473/******************************************************************************/
474
475const char *XrdNetAddr::Set(struct addrinfo *rP, int Port, bool mapit)
476{
477 static const int map46ID = htonl(0x0000ffff);
478
479// See if we need to convert this address otherwise just copy it
480//
481 if (mapit && rP->ai_family == AF_INET)
482 {memset(&IP.Addr, 0, sizeof(IP.Addr));
483 IP.v6.sin6_family = AF_INET6;
484 memcpy(&IP.v6.sin6_addr.s6_addr32[3], (rP->ai_addr->sa_data)+2, 4);
485 IP.v6.sin6_addr.s6_addr32[2] = map46ID;
486 addrSize = sizeof(IP.v6);
487 protType = PF_INET6;
488 } else {
489 memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
490 addrSize = rP->ai_addrlen;
491 protType = rP->ai_protocol;
492 }
493
494// Cleanup pre-existing information
495//
496 if (hostName) free(hostName);
497 hostName = (rP->ai_canonname ? LowCase(strdup(rP->ai_canonname)) : 0);
498 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
499 IP.v6.sin6_port = htons(static_cast<short>(Port));
500 sockNum = 0;
501 return 0;
502}
503
504/******************************************************************************/
505/* S e t C a c h e */
506/******************************************************************************/
507
508void XrdNetAddr::SetCache(int keeptime)
509{
510 static XrdNetCache theCache;
511
512// Set the cache keep time
513//
514 theCache.SetKT(keeptime);
515 dnsCache = (keeptime > 0 ? &theCache : 0);
516}
517
518/******************************************************************************/
519/* S e t D y n D N S */
520/******************************************************************************/
521
522void XrdNetAddr::SetDynDNS(bool onoff) {dynDNS = onoff;}
523
524/******************************************************************************/
525/* S e t I P V 4 */
526/******************************************************************************/
527
529{
530
531// To force IPV4 mode we merely change the hints structure and set the IPV4
532// mode flag to reject IPV6 address unless they are mapped.
533//
534 hostHints->ai_flags = AI_CANONNAME;
535 hostHints->ai_family = AF_INET;
536
537 huntHintsTCP->ai_flags = AI_ADDRCONFIG;
538 huntHintsTCP->ai_family = AF_INET;
539
540 huntHintsUDP->ai_flags = AI_ADDRCONFIG;
541 huntHintsUDP->ai_family = AF_INET;
542
543 useIPV4 = true;
544
545// Inform NetUtils that we changed mode
546//
548}
549
550/******************************************************************************/
551/* S e t I P V 6 */
552/******************************************************************************/
553
555{
556
557// To force IPV6 mode we merely change the hints structure and set the IPV4
558// mode flag to accept IPV6 address.
559//
560 hostHints->ai_flags = AI_V4MAPPED | AI_CANONNAME;
561 hostHints->ai_family = AF_INET6;
562
563 huntHintsTCP->ai_flags = AI_V4MAPPED | AI_ALL;
564 huntHintsTCP->ai_family = AF_INET6;
565
566 huntHintsUDP->ai_flags = AI_V4MAPPED | AI_ALL;
567 huntHintsUDP->ai_family = AF_INET6;
568
569 useIPV4 = false;
570
571// Inform NetUtils that we changed mode
572//
574}
575
576/******************************************************************************/
577/* S e t L o c a t i o n */
578/******************************************************************************/
579
581{
582// Copy in the new location information but preserve the flags
583//
584 addrLoc = loc;
585}
586
587/******************************************************************************/
588/* S e t T L S */
589/******************************************************************************/
590
591void XrdNetAddr::SetTLS(bool val)
592{
593 if (val) protFlgs |= isTLS;
594 else protFlgs &= ~isTLS;
595}
struct sockaddr_in6 v6
struct sockaddr Addr
struct sockaddr_in v4
#define close(a)
Definition XrdPosix.hh:48
#define eMsg(x)
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:101
#define SOCKLEN_t
static XrdNetCache * dnsCache
static bool isHostName(const char *name)
unsigned char protFlgs
XrdNetSockAddr IP
int Same(const XrdNetAddrInfo *ipAddr, bool plusPort=false)
unsigned char protType
XrdNetAddrInfo(XrdNetAddrInfo const &oP)
Copy constructor.
char * LowCase(char *str)
static const char isTLS
Location using TLS.
unsigned short addrSize
XrdNetAddr()
Assignment operator and copy constructor are inherited, no need to define.
static void SetIPV4()
bool Register(const char *hName)
static void SetCache(int keeptime)
static void SetIPV6()
void SetLocation(XrdNetAddrInfo::LocInfo &loc)
static void SetDynDNS(bool onoff)
void SetTLS(bool val)
int Port(int pNum=-1)
static const int PortInSpec
const char * Set(const char *hSpec, int pNum=PortInSpec)
static const char * FQN(const char **etext=0)
static const int NoPortRaw
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
static int ServPort(const char *sName, bool isUDP=false, const char **eText=0)
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
static int SetAuto(AddrOpts aOpts=allIPMap)