XRootD
XrdNetAddrInfo.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d N e t A d d r I n f o . 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 <cerrno>
33 #include <netdb.h>
34 #include <cstdio>
35 #include <arpa/inet.h>
36 #include <sys/types.h>
37 
38 #include "XrdNet/XrdNetAddrInfo.hh"
39 #include "XrdNet/XrdNetCache.hh"
40 
41 /******************************************************************************/
42 /* P l a t f o r m D e p e n d e n c i e s */
43 /******************************************************************************/
44 
45 // Linux defines s6_addr32 but MacOS does not and Solaris defines it only when
46 // compiling the kernel. This is really standard stuff that should be here.
47 //
48 #ifndef s6_addr32
49 #if defined(__solaris__)
50 #define s6_addr32 _S6_un._S6_u32
51 #elif defined(__APPLE__) || defined(__FreeBSD__)
52 #define s6_addr32 __u6_addr.__u6_addr32
53 #endif
54 #endif
55 
56 // The following tests for Unique Local Addresses (ULA) which Linux does not
57 // provide. The SITELOCAL macro only tests for the now deprecated non-routable
58 // addresses (RFC 3879). So, we need to implement the ULA test ourselves.
59 // Technically, only addresses starting with prefix 0xfd are ULA useable but
60 // RFC 4193 doesn't explicitly prohibit ULA's that start with 0xfc which may
61 // be used for registered ULA's in the future. So we test for both.
62 //
63 #define IN6_IS_ADDR_UNIQLOCAL(a) \
64  ( ((const uint8_t *)(a))[0] == 0xfc || ((const uint8_t *)(a))[0] == 0xfd )
65 
66 /******************************************************************************/
67 /* S t a t i c M e m b e r s */
68 /******************************************************************************/
69 
71 
72 namespace
73 {
74  static const char lbVal[13] ={0,0,0,0,0,0,0,0,0,0,0,0,0x7f};
75 };
76 
77 /******************************************************************************/
78 /* F o r m a t */
79 /******************************************************************************/
80 
81 int XrdNetAddrInfo::Format(char *bAddr, int bLen, fmtUse theFmt, int fmtOpts)
82 {
83  const char *pFmt = "]:%d";
84  int totLen, n, pNum, addBrak = 0;
85  int omitP = (fmtOpts & (noPort|noPortRaw));
86  int ipRaw = (fmtOpts & noPortRaw);
87  int ipOld = fmtOpts & (old6Map4 | prefipv4);
88 
89 // Handle the degenerative case first
90 //
91  if (IP.Addr.sa_family == AF_UNIX)
92  {n = (omitP ? snprintf(bAddr, bLen, "localhost")
93  : snprintf(bAddr, bLen, "localhost:%s", unixPipe->sun_path));
94  return (n < bLen ? n : QFill(bAddr, bLen));
95  }
96 
97 // Grab the potr. The port number is the same position and same size regardless
98 // of address type.
99 //
100  pNum = ntohs(IP.v4.sin_port);
101 
102 // Resolve address if need be and return result if possible
103 //
104  if (theFmt == fmtName || theFmt == fmtAuto)
105  {if (!hostName && dnsCache && !(hostName = dnsCache->Find(this))
106  && theFmt == fmtName) Resolve();
107  if (hostName)
108  {n = (omitP ? snprintf(bAddr, bLen, "%s", hostName)
109  : snprintf(bAddr, bLen, "%s:%d", hostName, pNum));
110  return (n < bLen ? n : QFill(bAddr, bLen));
111  }
112  theFmt = fmtAddr;
113  }
114 
115 // Check if we can now produce an address format quickly. We used assume that
116 // the hostname was an address if it started with a non-alpha. But this does
117 // not correspond to RFC 1178 and RFC 3696, among others. We now only use
118 // the optimization path if the name is actually an IPV6/mapped4 address.
119 //
120  if (hostName && *hostName == '[' && !ipOld)
121  {n = (omitP ? snprintf(bAddr, bLen, "%s", hostName)
122  : snprintf(bAddr, bLen, "%s:%d", hostName, pNum));
123  return (n < bLen ? n : QFill(bAddr, bLen));
124  }
125 
126 // Format address
127 //
128  if (IP.Addr.sa_family == AF_INET6)
129  {if (bLen < (INET6_ADDRSTRLEN+2)) return QFill(bAddr, bLen);
130  if (ipOld && IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr))
131  { if (fmtOpts & prefipv4) {n = 0; pFmt = ":%d";}
132  else if (ipRaw) {strcpy(bAddr, "::"); n = 2;}
133  else {strcpy(bAddr, "[::"); n = 3; addBrak=1;}
134  if (!inet_ntop(AF_INET, &IP.v6.sin6_addr.s6_addr32[3],
135  bAddr+n, bLen-n)) return QFill(bAddr, bLen);
136  } else {
137  if (!ipRaw) {*bAddr = '['; n = 1; addBrak = 1;}
138  else n = 0;
139  if (!inet_ntop(AF_INET6,&(IP.v6.sin6_addr),bAddr+n,bLen-n))
140  return QFill(bAddr, bLen);
141  }
142  }
143  else if (IP.Addr.sa_family == AF_INET)
144  {if (theFmt != fmtAdv6) {n = 0; pFmt = ":%d";}
145  else {if (bLen < (INET_ADDRSTRLEN+9)) return QFill(bAddr, bLen);
146  if (fmtOpts & old6Map4) {strcpy(bAddr, "[::"); n = 3;}
147  else {strcpy(bAddr, "[::ffff:"); n = 8;}
148  if (ipRaw) {strcpy(bAddr, bAddr+1); n--;}
149  addBrak = 1;
150  }
151  if (!inet_ntop(AF_INET, &(IP.v4.sin_addr),bAddr+n,bLen-n))
152  return QFill(bAddr, bLen);
153  }
154  else return QFill(bAddr, bLen);
155 
156 // Recalculate buffer position and length
157 //
158  totLen = strlen(bAddr); bAddr += totLen; bLen -= totLen;
159 
160 // Process when no port number wanted
161 //
162  if (omitP)
163  {if (addBrak)
164  {if (bLen < 2) return QFill(bAddr, bLen);
165  *bAddr++ = ']'; *bAddr = 0; totLen++;
166  }
167  return totLen;
168  }
169 
170 // Add the port number and return result
171 //
172  if ((n = snprintf(bAddr, bLen, pFmt, pNum)) >= bLen)
173  return QFill(bAddr, bLen);
174  return totLen+n;
175 }
176 
177 /******************************************************************************/
178 /* i s L o c a l */
179 /******************************************************************************/
180 
182 {
183  unsigned char *ipV4 = nullptr;
184 
185 // For IPv6, return true for link-local (fe80::/10) and loopback (::1)
186 //
187  if (IP.Addr.sa_family == AF_INET6)
188  {if ((IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)))
189  ipV4 = (unsigned char *)&IP.v6.sin6_addr.s6_addr32[3];
190  else {if ((IN6_IS_ADDR_LINKLOCAL(&IP.v6.sin6_addr))
191  || (IN6_IS_ADDR_LOOPBACK (&IP.v6.sin6_addr))) return true;
192  return false;
193  }
194  }
195 
196 // If this is not an IPv4 address, consider it local (e.g. AF_UNIX)
197 //
198  if (!ipV4)
199  {if (IP.Addr.sa_family != AF_INET) return true;
200  ipV4 = (unsigned char *)&IP.v4.sin_addr.s_addr;
201  }
202 
203 // For IPV4, return true for link-local (169.254.0.0/16) and loopback (127.0.0.0/8)
204 //
205  if ((ipV4[0] == 169 && ipV4[1] == 254) || ipV4[0] == 127)
206  return true;
207 
208 // Not a local address
209 //
210  return false;
211 }
212 
213 /******************************************************************************/
214 /* i s L o o p b a c k */
215 /******************************************************************************/
216 
218 {
219 
220 // Check for loopback address
221 //
222  if (IP.Addr.sa_family == AF_INET)
223  return !memcmp(&IP.v4.sin_addr.s_addr, &lbVal[12], 1);
224 
225  if (IP.Addr.sa_family == AF_INET6)
226  return !memcmp(&IP.v6.sin6_addr, &in6addr_loopback, sizeof(in6_addr))
227  || !memcmp(&IP.v6.sin6_addr, lbVal, sizeof(lbVal));
228 
229  return false;
230 }
231 
232 /******************************************************************************/
233 /* i s P r i v a t e */
234 /******************************************************************************/
235 
237 {
238  unsigned char *ipV4 = 0;
239 
240 // For IPV6 addresses we will use the macro unless it is mapped
241 //
242  if (IP.Addr.sa_family == AF_INET6)
243  {if ((IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)))
244  ipV4 = (unsigned char *)&IP.v6.sin6_addr.s6_addr32[3];
245  else {if ((IN6_IS_ADDR_LINKLOCAL(&IP.v6.sin6_addr))
246  || (IN6_IS_ADDR_SITELOCAL(&IP.v6.sin6_addr))
247  || (IN6_IS_ADDR_UNIQLOCAL(&IP.v6.sin6_addr))
248  || (IN6_IS_ADDR_LOOPBACK (&IP.v6.sin6_addr))) return true;
249  return false;
250  }
251  }
252 
253 // If this is not an IPV4 address then we will consider it private
254 //
255  if (!ipV4)
256  {if (IP.Addr.sa_family != AF_INET) return true;
257  ipV4 = (unsigned char *)&IP.v4.sin_addr.s_addr;
258  }
259 
260 // For IPV4 we use the RFC definition of private. Note that this includes
261 // mapped addresses which, as odd as it is, we could get.
262 //
263  if (ipV4[0] == 10
264  || (ipV4[0] == 172 && ipV4[1] >= 16 && ipV4[1] <= 31)
265  || (ipV4[0] == 192 && ipV4[1] == 168)
266  || (ipV4[0] == 169 && ipV4[1] == 254)
267  || ipV4[0] == 127) return true;
268 
269 // Not a local address
270 //
271  return false;
272 }
273 
274 /******************************************************************************/
275 /* i s R e g i s t e r e d */
276 /******************************************************************************/
277 
279 {
280  const char *hName;
281 
282 // Simply see if we can resolve this name
283 //
284  if (!(hName = Name())) return false;
285  return isHostName(hName);
286 }
287 
288 /******************************************************************************/
289 /* i s U s i n g T L S */
290 /******************************************************************************/
291 
293 {
294  return (protFlgs & isTLS) != 0;
295 }
296 
297 /******************************************************************************/
298 /* Private: L o w C a s e */
299 /******************************************************************************/
300 
301 char *XrdNetAddrInfo::LowCase(char *str)
302 {
303  unsigned char *sp = (unsigned char*)str;
304 
305  while(*sp) {if (isupper((int)*sp)) *sp = (char)tolower((int)*sp); sp++;}
306 
307  return str;
308 }
309 
310 /******************************************************************************/
311 /* i s H o s t N a m e */
312 /******************************************************************************/
313 
314 bool XrdNetAddrInfo::isHostName(const char *name)
315 {
316  const char *dot;
317  int dnum;
318 
319 // First check for Iv6 format or hostname
320 //
321  if (*name == '[') return false;
322  if (!isdigit(*name)) return true;
323 
324 // The IPv4 case is more complicated. The fastest way here is this is a
325 // host name if there are no dots or if the last component is not a digit
326 // according to the RFC spec.
327 //
328  if (!(dot = rindex(name, '.')) || !isdigit(*(dot+1))) return true;
329 
330 // We are not out of the woods yet. Now we need to do a full check.
331 //
332  name++; dnum = 0;
333  while(*name)
334  {if (*name == '.') dnum++;
335  else if (!isdigit(*name)) return true;
336  name++;
337  }
338  return (dnum == 3 ? false : true);
339 }
340 
341 /******************************************************************************/
342 /* N a m e */
343 /******************************************************************************/
344 
345 const char *XrdNetAddrInfo::Name(const char *eName, const char **eText)
346 {
347  int rc;
348 
349 // Preset errtxt to zero
350 //
351  if (eText) *eText = 0;
352 
353 // Check for unix family which is equal to localhost.
354 //
355  if (IP.Addr.sa_family == AF_UNIX) return "localhost";
356 
357 // If we already translated this name, just return the translation
358 //
359  if (hostName || (dnsCache && (hostName = dnsCache->Find(this))))
360  return hostName;
361 
362 // Try to resolve this address
363 //
364  if (!(rc = Resolve())) return hostName;
365 
366 // We failed resolving this address
367 //
368  if (eText) *eText = gai_strerror(rc);
369  return eName;
370 }
371 
372 /******************************************************************************/
373 /* P o r t */
374 /******************************************************************************/
375 
377 {
378 // Make sure we have a proper address family here
379 //
380  if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
381  return -1;
382 
383 // Return port number
384 //
385  return ntohs(IP.v6.sin6_port);
386 }
387 
388 /******************************************************************************/
389 /* Private: Q F i l l */
390 /******************************************************************************/
391 
392 int XrdNetAddrInfo::QFill(char *bAddr, int bLen)
393 {
394  static const char quests[] = "????????";
395 
396 // Insert up to 8 question marks
397 //
398  if (bLen)
399  {strncpy(bAddr, quests, bLen);
400  bAddr[bLen-1] = 0;
401  }
402  return 0;
403 }
404 
405 /******************************************************************************/
406 /* Private: R e s o l v e */
407 /******************************************************************************/
408 
410 {
411  char hBuff[NI_MAXHOST];
412  int n, rc;
413 
414 // Free up hostname here
415 //
416  if (hostName) {free(hostName); hostName = 0;}
417 
418 // Determine the actual size of the address structure
419 //
420  if (IP.Addr.sa_family == AF_INET ) n = sizeof(IP.v4);
421  else if (IP.Addr.sa_family == AF_INET6) n = sizeof(IP.v6);
422  else if (IP.Addr.sa_family == AF_UNIX )
423  {hostName = strdup("localhost");
424  return 0;
425  }
426  else return EAI_FAMILY;
427 
428 // Do lookup of canonical name. If an error is returned we simply assume that
429 // the name is not resolvable and return the address as the host name.
430 //
431  if ((rc = getnameinfo(&IP.Addr, n, hBuff+1, sizeof(hBuff)-2, 0, 0, 0)))
432  {int ec = errno;
433  if (Format(hBuff, sizeof(hBuff), fmtAddr, noPort))
434  {hostName = strdup(hBuff); return 0;}
435  errno = ec;
436  return rc;
437  }
438 
439 // Handle the case when the mapping returned an actual name or an address
440 // We always want numeric ipv6 addresses surrounded by brackets. Additionally,
441 // some implementations of getnameinfo() return the scopeid when a numeric
442 // address is returned. We check and remove it.
443 //
444  if (!index(hBuff+1, ':')) hostName = strdup(LowCase(hBuff+1));
445  else {char *perCent = index(hBuff+1, '%');
446  if (perCent) *perCent = 0;
447  n = strlen(hBuff+1);
448  hBuff[0] = '['; hBuff[n+1] = ']'; hBuff[n+2] = 0;
449  hostName = strdup(hBuff);
450  }
451 
452 // Add the entry to the cache and return success
453 //
454  if (dnsCache) dnsCache->Add(this, hostName);
455  return 0;
456 }
457 
458 /******************************************************************************/
459 /* S a m e */
460 /******************************************************************************/
461 
462 #define IS_INET(x) (x == AF_INET || x == AF_INET6)
463 
464 #define MY_FAMILY IP.Addr.sa_family
465 
466 #define UR_FAMILY ipAddr->IP.Addr.sa_family
467 
468 int XrdNetAddrInfo::Same(const XrdNetAddrInfo *ipAddr, bool plusPort)
469 {
470  static const int ipv4ASZ = sizeof(IP.v4.sin_addr);
471 
472  bool isINet = IS_INET(MY_FAMILY) && IS_INET(UR_FAMILY);
473 
474 // Do port comparison if requested and makes sense to do it
475 //
476  if (plusPort && isINet)
477  {in_port_t port1, port2;
478  port1 = (MY_FAMILY == AF_INET ? IP.v4.sin_port : IP.v6.sin6_port);
479  port2 = (UR_FAMILY == AF_INET ? ipAddr->IP.v4.sin_port
480  : ipAddr->IP.v6.sin6_port);
481  if (port1 != port2) return 0;
482  }
483 
484 // If address families do not match, they are the same if the hostnames match.
485 // If we don't have a hostname and the addresses are convertable, we can
486 // compare the actual addresses.
487 //
488  if (MY_FAMILY != UR_FAMILY)
489  {if (!isINet) return 0;
490  if (hostName && ipAddr->hostName)
491  return !strcmp(hostName,ipAddr->hostName);
492  if (MY_FAMILY == AF_INET && ipAddr->isMapped())
493  return !memcmp(&IP.v4.sin_addr,
494  &(ipAddr->IP.v6.sin6_addr.s6_addr32[3]), ipv4ASZ);
495  if (isMapped() && UR_FAMILY == AF_INET)
496  return !memcmp(&IP.v6.sin6_addr.s6_addr32[3],
497  &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
498  return 0;
499  }
500 
501 // Now process to do the match
502 //
503  if (MY_FAMILY == AF_INET)
504  return !memcmp(&IP.v4.sin_addr, &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
505  else if (MY_FAMILY == AF_INET6)
506  return !memcmp(&IP.v6.sin6_addr, &(ipAddr->IP.v6.sin6_addr),
507  sizeof(IP.v6.sin6_addr));
508  else if (MY_FAMILY == AF_UNIX)
509  return !strcmp(unixPipe->sun_path, ipAddr->unixPipe->sun_path);
510 
511  return 0;
512 }
#define UR_FAMILY
#define IS_INET(x)
#define MY_FAMILY
#define IN6_IS_ADDR_UNIQLOCAL(a)
struct sockaddr_in6 v6
struct sockaddr Addr
struct sockaddr_in v4
if(Avsz)
static XrdNetCache * dnsCache
static const int noPort
Do not add port number.
static const int old6Map4
Use deprecated IPV6 mapped format.
bool isMapped() const
static bool isHostName(const char *name)
unsigned char protFlgs
XrdNetSockAddr IP
static const int noPortRaw
Use raw address format (no port)
int Same(const XrdNetAddrInfo *ipAddr, bool plusPort=false)
static const int prefipv4
Use if mapped IPV4 actual format.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
char * LowCase(char *str)
int QFill(char *bAddr, int bLen)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
@ fmtName
Hostname if it is resolvable o/w use fmtAddr.
@ fmtAuto
Hostname if already resolved o/w use fmtAddr.
static const char isTLS
Location using TLS.
const char * Name(const char *eName=0, const char **eText=0)
void Add(XrdNetAddrInfo *hAddr, const char *hName)
Definition: XrdNetCache.cc:63
char * Find(XrdNetAddrInfo *hAddr)
Definition: XrdNetCache.cc:148