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 // Otherwise, we cannot initialize this object so force an error!
100 //
101  if (!fqn) fqn = "No_DNS_Name!";
102  Set(fqn, port);
103 }
104 
105 /******************************************************************************/
106 /* Private: H i n t s */
107 /******************************************************************************/
108 
109 struct addrinfo *XrdNetAddr::Hints(int htype, int stype)
110 {
111  static struct addrinfo theHints[3];
112 
113 // Return properly initialized hint structure. We need to do this dynamically
114 // in a static constructor since the addrinfo layout differs by OS-type.
115 //
116  memset(&theHints[htype], 0, sizeof(struct addrinfo));;
117  if (htype) theHints[htype].ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
118  else theHints[htype].ai_flags = AI_V4MAPPED | AI_CANONNAME;
119  theHints[htype].ai_family = AF_UNSPEC;
120  theHints[htype].ai_socktype = stype;
121  return &theHints[htype];
122 }
123 
124 /******************************************************************************/
125 /* Private: M a p 6 4 */
126 /******************************************************************************/
127 
128 bool XrdNetAddr::Map64()
129 {
130 
131 // The address must be a mapped IPV4 address
132 //
133  if (!IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)) return false;
134 
135 // Now convert this down to an IPv4 address
136 //
137  IP.v4.sin_addr.s_addr = IP.v6.sin6_addr.s6_addr32[3];
138  IP.v4.sin_family = AF_INET;
139  protType = PF_INET;
140  addrSize = sizeof(sockaddr_in);
141  return true;
142 }
143 
144 /******************************************************************************/
145 /* P o r t */
146 /******************************************************************************/
147 
148 int XrdNetAddr::Port(int pNum)
149 {
150 // Make sure we have a proper address family here
151 //
152  if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
153  return -1;
154 
155 // Return port number if so wanted. The port location is the same regardless of
156 // the address family.
157 //
158  if (pNum < 0) return ntohs(IP.v6.sin6_port);
159 
160 // Set port number if we have a valid address. The location of the port
161 // is the same regardless of address family.
162 //
163  if (pNum > 0xffff) return -1;
164  IP.v6.sin6_port = htons(static_cast<short>(pNum));
165  return pNum;
166 }
167 
168 /******************************************************************************/
169 /* R e g i s t e r */
170 /******************************************************************************/
171 
172 bool XrdNetAddr::Register(const char *hName)
173 {
174  XrdNetAddr *aListVec = 0;
175  int i, aListNum;
176 
177 // Step one is to make sure the incoming name is not an address
178 //
179  if (!isHostName(hName)) return false;
180 
181 // The next step is to get all of the IP addresses registered for this name
182 //
183  if (XrdNetUtils::GetAddrs(hName, &aListVec, aListNum,
185  return false;
186 
187 // In order to use the given name, one of the IP addresses in the list must
188 // match our address. This is about as secure we can get.
189 //
190  for (i = 0; i < aListNum; i++) {if (Same(&aListVec[i])) break;}
191  delete [] aListVec;
192 
193 // If we didn't find a match, report it
194 //
195  if (i >= aListNum) return false;
196 
197 // Replace current hostname with the wanted one
198 //
199  if (hostName) free(hostName);
200  hostName = strdup(hName);
201  return true;
202 }
203 
204 /******************************************************************************/
205 /* S e t */
206 /******************************************************************************/
207 
208 const char *XrdNetAddr::Set(const char *hSpec, int pNum)
209 {
210  static const char *badIPv4 = "invalid IPv4 address";
211  static const char *badIPv6 = "invalid IPv6 address";
212  static const char *badIP64 = "IPv6 address not IPv4 representable";
213  static const char *badName = "invalid host name";
214  static const int map46ID = htonl(0x0000ffff);
215 
216  const char *Colon, *iP;
217  char aBuff[NI_MAXHOST+INET6_ADDRSTRLEN];
218  int aLen, n;
219  bool mapIt;
220 
221 // Clear translation if set (note unixPipe & sockAddr are the same).
222 //
223  if (hostName) {free(hostName); hostName = 0;}
224  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
225  memset(&IP, 0, sizeof(IP));
226  addrSize = sizeof(sockaddr_in6);
227 
228 // Check for any address setting
229 //
230  if (!hSpec)
231  {if (useIPV4)
232  {IP.v4.sin_family = AF_INET;
233  IP.v4.sin_addr.s_addr = INADDR_ANY;
234  protType = PF_INET;
235  addrSize = sizeof(sockaddr_in);
236  } else {
237  IP.v6.sin6_family = AF_INET6;
238  IP.v6.sin6_addr = in6addr_any;
239  protType = PF_INET6;
240  }
241  if (pNum < 0) pNum= -pNum;
242  IP.v6.sin6_port = htons(static_cast<short>(pNum));
243  return 0;
244  }
245 
246 // Check for Unix type address here
247 //
248  if (*hSpec == '/')
249  {if (strlen(hSpec) >= sizeof(unixPipe->sun_path)) return "path too long";
250  unixPipe = new sockaddr_un;
251  strcpy(unixPipe->sun_path, hSpec);
252  unixPipe->sun_family = IP.Addr.sa_family = AF_UNIX;
253  addrSize = sizeof(sockaddr_un);
254  protType = PF_UNIX;
255  return 0;
256  }
257 
258 // Do length check to see if we can fit the host name in our buffer.
259 //
260  aLen = strlen(hSpec);
261  if (aLen >= (int)sizeof(aBuff)) return "host id too long";
262 
263 // Convert the address as appropriate. Note that we do accept RFC5156 deprecated
264 // IPV4 mapped IPV6 addresses(i.e. [::a.b.c.d]. This is historical.
265 //
266  if (*hSpec == '[')
267  {const char *Brak = index(hSpec+1, ']');
268  if (!Brak) return badIPv6;
269  Colon = Brak+1;
270  if (!(*Colon)) Colon = 0;
271  else if (*Colon != ':') return badIPv6;
272  aLen = Brak - (hSpec+1);
273  if (aLen >= INET6_ADDRSTRLEN) return badIPv6;
274  mapIt = (*(hSpec+1) == ':' && *(hSpec+2) == ':'
275  && *(hSpec+3) >= '0' && *(hSpec+3) <= '9'
276  && (iP = index(hSpec+4, '.')) && iP < Brak);
277  strncpy(aBuff, hSpec+1, aLen); aBuff[aLen] = 0;
278  if (inet_pton(AF_INET6,aBuff,&IP.v6.sin6_addr) != 1) return badIPv6;
279  if (mapIt) IP.v6.sin6_addr.s6_addr32[2] = map46ID;
280  IP.v6.sin6_family = AF_INET6;
281  protType = PF_INET6;
282  if (useIPV4 && !Map64()) return badIP64;
283  }
284  else if (!isHostName(hSpec))
285  {if ((Colon = index(hSpec, ':')))
286  {aLen = Colon - hSpec;
287  if (aLen >= INET_ADDRSTRLEN) return badIPv4;
288  strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
289  } else iP = hSpec;
290  if (inet_pton(AF_INET ,iP, &IP.v6.sin6_addr.s6_addr32[3]) != 1)
291  return badIPv4;
292  IP.v6.sin6_addr.s6_addr32[2] = map46ID;
293  IP.v6.sin6_family = AF_INET6;
294  protType = PF_INET6;
295  if (useIPV4 && !Map64()) return badIPv4;
296  }
297  else if (*hSpec == 0) return badName;
298 
299  else {struct addrinfo *rP = 0;
300  if ((Colon = index(hSpec, ':')))
301  {aLen = Colon - hSpec;
302  if (aLen > MAXHOSTNAMELEN) return badName;
303  strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
304  } else iP = hSpec;
305  n = getaddrinfo(iP, 0, hostHints, &rP);
306  if (n || !rP)
307  {if (rP) freeaddrinfo(rP);
308  if (n == EAI_NONAME && dynDNS)
309  return "Dynamic name or service not yet registered";
310  return (n ? gai_strerror(n) : "host not found");
311  }
312  memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
313  protType = (IP.v6.sin6_family == AF_INET6 ? PF_INET6 : PF_INET);
314  if (rP->ai_canonname) hostName = strdup(rP->ai_canonname);
315  freeaddrinfo(rP);
316  }
317 
318 // Now set the port number as needed (v4 & v6 port locations are the same)
319 //
320  if (pNum == PortInSpec && !Colon) return "port not specified";
321  if (pNum <= 0 && Colon)
322  {char *eP;
323  pNum = strtol(Colon+1, &eP, 10);
324  if (pNum < 0 || pNum > 0xffff || *eP) return "invalid port number";
325  } else if (pNum < 0) pNum = -pNum;
326  IP.v6.sin6_port = htons(static_cast<short>(pNum));
327 
328 // All done
329 //
330  return 0;
331 }
332 
333 /******************************************************************************/
334 
335 const char *XrdNetAddr::Set(const char *hSpec, int &numIP, int maxIP,
336  int pNum, bool optUDP)
337 {
338  struct addrinfo *hP, *rP = 0, *pP, *nP;
339  XrdNetAddr *aVec = this;
340  const char *hnBeg, *hnEnd, *pnBeg, *pnEnd;
341  char hBuff[MAXHOSTNAMELEN+8];
342  int hLen, n;
343 
344 // If only one address can be returned, just revert to standard processing
345 //
346  if (!hSpec || !isalpha(*hSpec) || maxIP < 2)
347  {const char *eMsg = Set(hSpec, pNum);
348  numIP = (eMsg ? 0 : 1);
349  return eMsg;
350  }
351 
352 // Extract out host name
353 //
354  if (!XrdNetUtils::Parse(hSpec, &hnBeg, &hnEnd, &pnBeg, &pnEnd))
355  return "invalid host specification";
356  hLen = hnEnd - hnBeg;
357  if (hLen > MAXHOSTNAMELEN) return "host name too long";
358  strncpy(hBuff, hSpec, hLen); hBuff[hLen] = 0;
359 
360 // Get the port number we will be setting
361 //
362  if (pnBeg == hnEnd)
363  {if (pNum == PortInSpec) return "port not specified";
364  if (pNum < 0) pNum = -pNum;
365  } else {
366  if (*pnEnd || !(n = XrdNetUtils::ServPort(pnBeg, optUDP)))
367  return "invalid port";
368  if (pNum < 0) pNum = n;
369  }
370 
371 // Get all of the addresses
372 //
373  hP = (optUDP ? huntHintsUDP : huntHintsTCP);
374  n = getaddrinfo(hBuff, 0, hP, &rP);
375  if (n || !rP)
376  {if (rP) freeaddrinfo(rP);
377  return (n ? gai_strerror(n) : "host not found");
378  }
379 
380 // Now self-referentially fill out ourselves with no duplicates
381 //
382  n = 0; pP = 0; nP = rP;
383  do {if (!pP || pP->ai_addrlen != nP->ai_addrlen
384  || memcmp((const void *)pP->ai_addr, (const void *)nP->ai_addr,
385  nP->ai_addrlen)) {aVec[n].Set(nP, pNum); n++;}
386  pP = nP; nP = nP->ai_next;
387  } while(n < maxIP && nP);
388 
389 // All done
390 //
391  numIP = n;
392  if (rP) freeaddrinfo(rP);
393  return 0;
394 }
395 
396 /******************************************************************************/
397 
398 const char *XrdNetAddr::Set(const struct sockaddr *sockP, int sockFD)
399 {
400 // Clear translation if set
401 //
402  if (hostName) {free(hostName); hostName = 0;}
403  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
404  sockNum = sockFD;
405 
406 // Copy the address based on address family
407 //
408  if (sockP->sa_family == AF_INET6) {addrSize = sizeof(IP.v6);
409  protType = PF_INET6;
410  }
411  else if (sockP->sa_family == AF_INET) {addrSize = sizeof(IP.v4);
412  protType = PF_INET;
413  }
414  else if (sockP->sa_family == AF_UNIX)
415  {unixPipe = new sockaddr_un;
416  memcpy(unixPipe, sockP, sizeof(struct sockaddr_un));
417  unixPipe->sun_path[sizeof(unixPipe->sun_path)-1] = 0;
418  addrSize = sizeof(sockaddr_un);
419  memset(&IP, 0, sizeof(IP));
420  IP.Addr.sa_family = AF_UNIX;
421  protType = PF_UNIX;
422  return 0;
423  }
424  else return "invalid address family";
425 
426 // Copy the address and return
427 //
428  memcpy(&IP, sockP, addrSize);
429  return 0;
430 }
431 
432 /******************************************************************************/
433 
434 const char *XrdNetAddr::Set(int sockFD, bool peer)
435 {
436  SOCKLEN_t aSize = static_cast<SOCKLEN_t>(sizeof(IP));
437  int rc;
438 
439 // Clear translation if set
440 //
441  if (hostName) {free(hostName); hostName = 0;}
442  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
443  sockNum = sockFD;
444 
445 // Get the address on the appropriate side of this socket
446 //
447  if (peer) rc = getpeername(sockFD, &IP.Addr, &aSize);
448  else rc = getsockname(sockFD, &IP.Addr, &aSize);
449  if (rc < 0)
450  {addrSize = 0;
451  return XrdSysE2T(errno);
452  }
453 
454 // Set the correct address size and protocol family
455 //
456  addrSize = aSize;
457  protType = (IP.Addr.sa_family == AF_INET ? PF_INET : PF_INET6);
458 
459 // All done
460 //
461  return 0;
462 }
463 
464 /******************************************************************************/
465 
466 const char *XrdNetAddr::Set(struct addrinfo *rP, int Port, bool mapit)
467 {
468  static const int map46ID = htonl(0x0000ffff);
469 
470 // See if we need to convert this address otherwise just copy it
471 //
472  if (mapit && rP->ai_family == AF_INET)
473  {memset(&IP.Addr, 0, sizeof(IP.Addr));
474  IP.v6.sin6_family = AF_INET6;
475  memcpy(&IP.v6.sin6_addr.s6_addr32[3], (rP->ai_addr->sa_data)+2, 4);
476  IP.v6.sin6_addr.s6_addr32[2] = map46ID;
477  addrSize = sizeof(IP.v6);
478  protType = PF_INET6;
479  } else {
480  memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
481  addrSize = rP->ai_addrlen;
482  protType = rP->ai_protocol;
483  }
484 
485 // Cleanup pre-existing information
486 //
487  if (hostName) free(hostName);
488  hostName = (rP->ai_canonname ? strdup(rP->ai_canonname) : 0);
489  if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
490  IP.v6.sin6_port = htons(static_cast<short>(Port));
491  sockNum = 0;
492  return 0;
493 }
494 
495 /******************************************************************************/
496 /* S e t C a c h e */
497 /******************************************************************************/
498 
499 void XrdNetAddr::SetCache(int keeptime)
500 {
501  static XrdNetCache theCache;
502 
503 // Set the cache keep time
504 //
505  theCache.SetKT(keeptime);
506  dnsCache = (keeptime > 0 ? &theCache : 0);
507 }
508 
509 /******************************************************************************/
510 /* S e t D y n D N S */
511 /******************************************************************************/
512 
513 void XrdNetAddr::SetDynDNS(bool onoff) {dynDNS = onoff;}
514 
515 /******************************************************************************/
516 /* S e t I P V 4 */
517 /******************************************************************************/
518 
520 {
521 
522 // To force IPV4 mode we merely change the hints structure and set the IPV4
523 // mode flag to reject IPV6 address unless they are mapped.
524 //
525  hostHints->ai_flags = AI_CANONNAME;
526  hostHints->ai_family = AF_INET;
527 
528  huntHintsTCP->ai_flags = AI_ADDRCONFIG;
529  huntHintsTCP->ai_family = AF_INET;
530 
531  huntHintsUDP->ai_flags = AI_ADDRCONFIG;
532  huntHintsUDP->ai_family = AF_INET;
533 
534  useIPV4 = true;
535 
536 // Inform NetUtils that we changed mode
537 //
539 }
540 
541 /******************************************************************************/
542 /* S e t I P V 6 */
543 /******************************************************************************/
544 
546 {
547 
548 // To force IPV6 mode we merely change the hints structure and set the IPV4
549 // mode flag to accept IPV6 address.
550 //
551  hostHints->ai_flags = AI_V4MAPPED | AI_CANONNAME;
552  hostHints->ai_family = AF_INET6;
553 
554  huntHintsTCP->ai_flags = AI_V4MAPPED | AI_ALL;
555  huntHintsTCP->ai_family = AF_INET6;
556 
557  huntHintsUDP->ai_flags = AI_V4MAPPED | AI_ALL;
558  huntHintsUDP->ai_family = AF_INET6;
559 
560  useIPV4 = false;
561 
562 // Inform NetUtils that we changed mode
563 //
565 }
566 
567 /******************************************************************************/
568 /* S e t L o c a t i o n */
569 /******************************************************************************/
570 
572 {
573 // Copy in the new location information but preserve the flags
574 //
575  addrLoc = loc;
576 }
577 
578 /******************************************************************************/
579 /* S e t T L S */
580 /******************************************************************************/
581 
582 void XrdNetAddr::SetTLS(bool val)
583 {
584  if (val) protFlgs |= isTLS;
585  else protFlgs &= ~isTLS;
586 }
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:99
#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
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:519
bool Register(const char *hName)
Definition: XrdNetAddr.cc:172
static void SetCache(int keeptime)
Definition: XrdNetAddr.cc:499
static void SetIPV6()
Definition: XrdNetAddr.cc:545
void SetLocation(XrdNetAddrInfo::LocInfo &loc)
Definition: XrdNetAddr.cc:571
static void SetDynDNS(bool onoff)
Definition: XrdNetAddr.cc:513
void SetTLS(bool val)
Definition: XrdNetAddr.cc:582
static const int PortInSpec
Definition: XrdNetAddr.hh:112
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:208
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:238
static int ServPort(const char *sName, bool isUDP=false, const char **eText=0)
Definition: XrdNetUtils.cc:837
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
Definition: XrdNetUtils.cc:743
static int SetAuto(AddrOpts aOpts=allIPMap)
Definition: XrdNetUtils.cc:878
XrdOucCache * theCache