XRootD
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"
40 #include "XrdNet/XrdNetIdentity.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 
63 namespace
64 {
65 bool 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 
80 struct addrinfo *XrdNetAddr::hostHints = XrdNetAddr::Hints(0, 0);
81 
82 struct addrinfo *XrdNetAddr::huntHintsTCP = XrdNetAddr::Hints(1, SOCK_STREAM);
83 
84 struct addrinfo *XrdNetAddr::huntHintsUDP = XrdNetAddr::Hints(2, SOCK_DGRAM);
85 
86 // The following must be initialzed after all of the hint structures!
87 //
88 bool XrdNetAddr::useIPV4 = OnlyIPV4();
89 bool 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 
117 struct 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 
136 bool 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 
156 int 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 
180 bool 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 
216 const 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  protType = (IP.v6.sin6_family == AF_INET6 ? PF_INET6 : PF_INET);
322  if (rP->ai_canonname) hostName = LowCase(strdup(rP->ai_canonname));
323  freeaddrinfo(rP);
324  }
325 
326 // Now set the port number as needed (v4 & v6 port locations are the same)
327 //
328  if (pNum == PortInSpec && !Colon) return "port not specified";
329  if (pNum <= 0 && Colon)
330  {char *eP;
331  pNum = strtol(Colon+1, &eP, 10);
332  if (pNum < 0 || pNum > 0xffff || *eP) return "invalid port number";
333  } else if (pNum < 0) pNum = -pNum;
334  IP.v6.sin6_port = htons(static_cast<short>(pNum));
335 
336 // All done
337 //
338  return 0;
339 }
340 
341 /******************************************************************************/
342 
343 const char *XrdNetAddr::Set(const char *hSpec, int &numIP, int maxIP,
344  int pNum, bool optUDP)
345 {
346  struct addrinfo *hP, *rP = 0, *pP, *nP;
347  XrdNetAddr *aVec = this;
348  const char *hnBeg, *hnEnd, *pnBeg, *pnEnd;
349  char hBuff[MAXHOSTNAMELEN+8];
350  int hLen, n;
351 
352 // If only one address can be returned, just revert to standard processing
353 //
354  if (!hSpec || !isalpha(*hSpec) || maxIP < 2)
355  {const char *eMsg = Set(hSpec, pNum);
356  numIP = (eMsg ? 0 : 1);
357  return eMsg;
358  }
359 
360 // Extract out host name
361 //
362  if (!XrdNetUtils::Parse(hSpec, &hnBeg, &hnEnd, &pnBeg, &pnEnd))
363  return "invalid host specification";
364  hLen = hnEnd - hnBeg;
365  if (hLen > MAXHOSTNAMELEN) return "host name too long";
366  strncpy(hBuff, hSpec, hLen); hBuff[hLen] = 0;
367 
368 // Get the port number we will be setting
369 //
370  if (pnBeg == hnEnd)
371  {if (pNum == PortInSpec) return "port not specified";
372  if (pNum < 0) pNum = -pNum;
373  } else {
374  if (*pnEnd || !(n = XrdNetUtils::ServPort(pnBeg, optUDP)))
375  return "invalid port";
376  if (pNum < 0) pNum = n;
377  }
378 
379 // Get all of the addresses
380 //
381  hP = (optUDP ? huntHintsUDP : huntHintsTCP);
382  n = getaddrinfo(hBuff, 0, hP, &rP);
383  if (n || !rP)
384  {if (rP) freeaddrinfo(rP);
385  return (n ? gai_strerror(n) : "host not found");
386  }
387 
388 // Now self-referentially fill out ourselves with no duplicates
389 //
390  n = 0; pP = 0; nP = rP;
391  do {if (!pP || pP->ai_addrlen != nP->ai_addrlen
392  || memcmp((const void *)pP->ai_addr, (const void *)nP->ai_addr,
393  nP->ai_addrlen)) {aVec[n].Set(nP, pNum); n++;}
394  pP = nP; nP = nP->ai_next;
395  } while(n < maxIP && nP);
396 
397 // All done
398 //
399  numIP = n;
400  if (rP) freeaddrinfo(rP);
401  return 0;
402 }
403 
404 /******************************************************************************/
405 
406 const char *XrdNetAddr::Set(const struct sockaddr *sockP, int sockFD)
407 {
408 // Clear translation if set
409 //
410  if (hostName) {free(hostName); hostName = 0;}
411  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
412  sockNum = sockFD;
413 
414 // Copy the address based on address family
415 //
416  if (sockP->sa_family == AF_INET6) {addrSize = sizeof(IP.v6);
417  protType = PF_INET6;
418  }
419  else if (sockP->sa_family == AF_INET) {addrSize = sizeof(IP.v4);
420  protType = PF_INET;
421  }
422  else if (sockP->sa_family == AF_UNIX)
423  {unixPipe = new sockaddr_un;
424  memcpy(unixPipe, sockP, sizeof(struct sockaddr_un));
425  unixPipe->sun_path[sizeof(unixPipe->sun_path)-1] = 0;
426  addrSize = sizeof(sockaddr_un);
427  memset(&IP, 0, sizeof(IP));
428  IP.Addr.sa_family = AF_UNIX;
429  protType = PF_UNIX;
430  return 0;
431  }
432  else return "invalid address family";
433 
434 // Copy the address and return
435 //
436  memcpy(&IP, sockP, addrSize);
437  return 0;
438 }
439 
440 /******************************************************************************/
441 
442 const char *XrdNetAddr::Set(int sockFD, bool peer)
443 {
444  SOCKLEN_t aSize = static_cast<SOCKLEN_t>(sizeof(IP));
445  int rc;
446 
447 // Clear translation if set
448 //
449  if (hostName) {free(hostName); hostName = 0;}
450  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
451  sockNum = sockFD;
452 
453 // Get the address on the appropriate side of this socket
454 //
455  if (peer) rc = getpeername(sockFD, &IP.Addr, &aSize);
456  else rc = getsockname(sockFD, &IP.Addr, &aSize);
457  if (rc < 0)
458  {addrSize = 0;
459  return XrdSysE2T(errno);
460  }
461 
462 // Set the correct address size and protocol family
463 //
464  addrSize = aSize;
465  protType = (IP.Addr.sa_family == AF_INET ? PF_INET : PF_INET6);
466 
467 // All done
468 //
469  return 0;
470 }
471 
472 /******************************************************************************/
473 
474 const char *XrdNetAddr::Set(struct addrinfo *rP, int Port, bool mapit)
475 {
476  static const int map46ID = htonl(0x0000ffff);
477 
478 // See if we need to convert this address otherwise just copy it
479 //
480  if (mapit && rP->ai_family == AF_INET)
481  {memset(&IP.Addr, 0, sizeof(IP.Addr));
482  IP.v6.sin6_family = AF_INET6;
483  memcpy(&IP.v6.sin6_addr.s6_addr32[3], (rP->ai_addr->sa_data)+2, 4);
484  IP.v6.sin6_addr.s6_addr32[2] = map46ID;
485  addrSize = sizeof(IP.v6);
486  protType = PF_INET6;
487  } else {
488  memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
489  addrSize = rP->ai_addrlen;
490  protType = rP->ai_protocol;
491  }
492 
493 // Cleanup pre-existing information
494 //
495  if (hostName) free(hostName);
496  hostName = (rP->ai_canonname ? LowCase(strdup(rP->ai_canonname)) : 0);
497  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
498  IP.v6.sin6_port = htons(static_cast<short>(Port));
499  sockNum = 0;
500  return 0;
501 }
502 
503 /******************************************************************************/
504 /* S e t C a c h e */
505 /******************************************************************************/
506 
507 void XrdNetAddr::SetCache(int keeptime)
508 {
509  static XrdNetCache theCache;
510 
511 // Set the cache keep time
512 //
513  theCache.SetKT(keeptime);
514  dnsCache = (keeptime > 0 ? &theCache : 0);
515 }
516 
517 /******************************************************************************/
518 /* S e t D y n D N S */
519 /******************************************************************************/
520 
521 void XrdNetAddr::SetDynDNS(bool onoff) {dynDNS = onoff;}
522 
523 /******************************************************************************/
524 /* S e t I P V 4 */
525 /******************************************************************************/
526 
528 {
529 
530 // To force IPV4 mode we merely change the hints structure and set the IPV4
531 // mode flag to reject IPV6 address unless they are mapped.
532 //
533  hostHints->ai_flags = AI_CANONNAME;
534  hostHints->ai_family = AF_INET;
535 
536  huntHintsTCP->ai_flags = AI_ADDRCONFIG;
537  huntHintsTCP->ai_family = AF_INET;
538 
539  huntHintsUDP->ai_flags = AI_ADDRCONFIG;
540  huntHintsUDP->ai_family = AF_INET;
541 
542  useIPV4 = true;
543 
544 // Inform NetUtils that we changed mode
545 //
547 }
548 
549 /******************************************************************************/
550 /* S e t I P V 6 */
551 /******************************************************************************/
552 
554 {
555 
556 // To force IPV6 mode we merely change the hints structure and set the IPV4
557 // mode flag to accept IPV6 address.
558 //
559  hostHints->ai_flags = AI_V4MAPPED | AI_CANONNAME;
560  hostHints->ai_family = AF_INET6;
561 
562  huntHintsTCP->ai_flags = AI_V4MAPPED | AI_ALL;
563  huntHintsTCP->ai_family = AF_INET6;
564 
565  huntHintsUDP->ai_flags = AI_V4MAPPED | AI_ALL;
566  huntHintsUDP->ai_family = AF_INET6;
567 
568  useIPV4 = false;
569 
570 // Inform NetUtils that we changed mode
571 //
573 }
574 
575 /******************************************************************************/
576 /* S e t L o c a t i o n */
577 /******************************************************************************/
578 
580 {
581 // Copy in the new location information but preserve the flags
582 //
583  addrLoc = loc;
584 }
585 
586 /******************************************************************************/
587 /* S e t T L S */
588 /******************************************************************************/
589 
590 void XrdNetAddr::SetTLS(bool val)
591 {
592  if (val) protFlgs |= isTLS;
593  else protFlgs &= ~isTLS;
594 }
struct sockaddr_in6 v6
struct sockaddr Addr
struct sockaddr_in v4
#define close(a)
Definition: XrdPosix.hh:43
#define eMsg(x)
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
#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
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.
Definition: XrdNetAddr.hh:265
static void SetIPV4()
Definition: XrdNetAddr.cc:527
bool Register(const char *hName)
Definition: XrdNetAddr.cc:180
static void SetCache(int keeptime)
Definition: XrdNetAddr.cc:507
static void SetIPV6()
Definition: XrdNetAddr.cc:553
void SetLocation(XrdNetAddrInfo::LocInfo &loc)
Definition: XrdNetAddr.cc:579
static void SetDynDNS(bool onoff)
Definition: XrdNetAddr.cc:521
void SetTLS(bool val)
Definition: XrdNetAddr.cc:590
static const int PortInSpec
Definition: XrdNetAddr.hh:112
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
static const char * FQN(const char **etext=0)
static const int NoPortRaw
Definition: XrdNetUtils.hh:132
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
Definition: XrdNetUtils.cc:239
static int ServPort(const char *sName, bool isUDP=false, const char **eText=0)
Definition: XrdNetUtils.cc:839
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
Definition: XrdNetUtils.cc:745
static int SetAuto(AddrOpts aOpts=allIPMap)
Definition: XrdNetUtils.cc:880
XrdOucCache * theCache